Testing ASP.net Web Applications using Ruby
Upcoming SlideShare
Loading in...5
×
 

Testing ASP.net Web Applications using Ruby

on

  • 2,594 views

Presentation of Testing ASP.net using Ruby I gave at DDD8, January 30th 2010.

Presentation of Testing ASP.net using Ruby I gave at DDD8, January 30th 2010.

Statistics

Views

Total Views
2,594
Views on SlideShare
2,287
Embed Views
307

Actions

Likes
1
Downloads
31
Comments
0

4 Embeds 307

http://blog.benhall.me.uk 221
http://blog2.benhall.me.uk 74
http://www.slideshare.net 10
http://feeds.feedburner.com 2

Accessibility

Categories

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Testing ASP.net Web Applications using Ruby Testing ASP.net Web Applications using Ruby Presentation Transcript

    • Testing ASP.net using Ruby
      Meerkatalyst
      @Ben_HallBen@BenHall.me.ukBlog.BenHall.me.uk
    • 1| Why you should care2| Object Level testing3| UI level testing
    • What do I mean by Testing ASP.net?
    • Co-Author of
      Testing ASP.net Web Applications
      http://www.testingaspnet.com
    • WHY TEST?
      http://www.flickr.com/photos/atomicpuppy/2132073976/
    • It is 2010. Automated testing is no longer controversial.
    • http://nerddinner.codeplex.com/SourceControl/changeset/view/23425#439968
      [TestMethod]public void EditAction_Retrieves_Dinner_1_From_Repo_And_Countries_And_Sets_DinnerViewModel() { // Arrangevar controller = CreateDinnersControllerAs("someuser"); // ActViewResult result = controller.Edit(1) as ViewResult; // AssertDinnerFormViewModel model = result.ViewData.Model as DinnerFormViewModel;Assert.AreEqual(13, model.Countries.Count());}
    • http://nerddinner.codeplex.com/SourceControl/changeset/view/23425#439968
      [TestMethod]public void EditAction_Retrieves_Dinner_1_From_Repo_And_Countries_And_Sets_DinnerViewModel() { // Arrangevar controller = CreateDinnersControllerAs("someuser"); // ActViewResultresult = controller.Edit(1) as ViewResult; // AssertDinnerFormViewModel model = result.ViewData.Model as DinnerFormViewModel;Assert.AreEqual(13, model.Countries.Count());}
    • [Fact] public void RsdReturnsValidRsdDoc() {FakeAreaServiceareaService = new FakeAreaService();areaService.StoredAreas.Add("test", new Oxite.Models.Area(false, DateTime.MinValue, null, null, Guid.NewGuid(), DateTime.MinValue, "test"));RouteCollection routes = new RouteCollection();routes.Add("Posts", new Route("", new MvcRouteHandler()));UrlHelper helper = new UrlHelper(new RequestContext(new FakeHttpContext(new Uri("http://oxite.net/"),"~/"), new RouteData()), routes); Site site = new Site() { Host = new Uri("http://oxite.net") };AreaController controller = new AreaController(site, areaService, null, null, null, null, null) { Url = helper };ContentResult result = controller.Rsd("test");Assert.NotNull(result);XDocumentrsdDoc = XDocument.Parse(result.Content);XNamespacersdNamespace = "http://archipelago.phrasewise.com/rsd";XElementrootElement = rsdDoc.Element(rsdNamespace + "rsd");Assert.NotNull(rootElement);Assert.NotNull(rootElement.Attribute("version"));Assert.Equal("1.0", rootElement.Attribute("version").Value);Assert.Equal("Oxite", rootElement.Descendants(rsdNamespace + "engineName").SingleOrDefault().Value);Assert.Equal("http://oxite.net", rootElement.Descendants(rsdNamespace + "engineLink").SingleOrDefault().Value);Assert.Equal("http://oxite.net/", rootElement.Descendants(rsdNamespace + "homePageLink").SingleOrDefault().Value);XElementapisElement = rootElement.Descendants(rsdNamespace + "apis").SingleOrDefault();Assert.NotNull(apisElement);Assert.Equal(1, apisElement.Elements().Count());XElementapiElement = apisElement.Elements().SingleOrDefault();Assert.NotNull(apiElement);Assert.Equal(rsdNamespace + "api", apiElement.Name);Assert.Equal("MetaWeblog", apiElement.Attribute("name").Value);Assert.Equal(areaService.StoredAreas["test"].ID.ToString("N"), apiElement.Attribute("blogID").Value);Assert.Equal("true", apiElement.Attribute("preferred").Value);Assert.Equal("http://oxite.net/MetaWeblog.svc", apiElement.Attribute("apiLink").Value); }
      http://oxite.codeplex.com/SourceControl/changeset/view/54721#419183
    • [Fact] public void RsdReturnsValidRsdDoc() {FakeAreaServiceareaService = new FakeAreaService();areaService.StoredAreas.Add("test", new Oxite.Models.Area(false, DateTime.MinValue, null, null, Guid.NewGuid(), DateTime.MinValue, "test"));RouteCollection routes = new RouteCollection();routes.Add("Posts", new Route("", new MvcRouteHandler()));UrlHelper helper = new UrlHelper(new RequestContext(new FakeHttpContext(new Uri("http://oxite.net/"),"~/"), new RouteData()), routes); Site site = new Site() { Host = new Uri("http://oxite.net") };AreaController controller = new AreaController(site, areaService, null, null, null, null, null) { Url = helper };ContentResult result = controller.Rsd("test");Assert.NotNull(result);XDocumentrsdDoc = XDocument.Parse(result.Content);XNamespacersdNamespace = "http://archipelago.phrasewise.com/rsd";XElementrootElement = rsdDoc.Element(rsdNamespace + "rsd");Assert.NotNull(rootElement);Assert.NotNull(rootElement.Attribute("version"));Assert.Equal("1.0", rootElement.Attribute("version").Value);Assert.Equal("Oxite", rootElement.Descendants(rsdNamespace + "engineName").SingleOrDefault().Value);Assert.Equal("http://oxite.net", rootElement.Descendants(rsdNamespace + "engineLink").SingleOrDefault().Value);Assert.Equal("http://oxite.net/", rootElement.Descendants(rsdNamespace + "homePageLink").SingleOrDefault().Value);XElementapisElement = rootElement.Descendants(rsdNamespace + "apis").SingleOrDefault();Assert.NotNull(apisElement);Assert.Equal(1, apisElement.Elements().Count());XElementapiElement = apisElement.Elements().SingleOrDefault();Assert.NotNull(apiElement);Assert.Equal(rsdNamespace + "api", apiElement.Name);Assert.Equal("MetaWeblog", apiElement.Attribute("name").Value);Assert.Equal(areaService.StoredAreas["test"].ID.ToString("N"), apiElement.Attribute("blogID").Value);Assert.Equal("true", apiElement.Attribute("preferred").Value);Assert.Equal("http://oxite.net/MetaWeblog.svc", apiElement.Attribute("apiLink").Value); }
      http://oxite.codeplex.com/SourceControl/changeset/view/54721#419183
    • http://grabbagoft.blogspot.com/2007/09/authoring-stories-with-nbehave-03.html
      [Story] public void Should_find_customers_by_name_when_name_matches() { Story story = new Story("List customers by name"); story.AsA("customer support staff") .IWant("to search for customers in a very flexible manner") .SoThat("I can find a customer record and provide meaningful support"); CustomerRepository repo = null; Customer customer = null; story.WithScenario("Find by name") .Given("a set of valid customers", delegate { repo = CreateDummyRepo(); }) .When("I ask for an existing name", "Joe Schmoe", delegate(string name) { customer = repo.FindByName(name); }) .Then("the correct customer is found and returned", delegate {Assert.That(customer.Name, Is.EqualTo("Joe Schmoe"));}); }
    • RECORD AND PLAYBACK
      http://www.flickr.com/photos/gagilas/2659695352/
    • You can make C# readable
      But it’s hard
    • http://www.flickr.com/photos/buro9/298994863/
      RUBY?
    • Natural Language
    • http://www.flickr.com/photos/mag3737/1914076277/
    • RSpec
      http://www.flickr.com/photos/dodgsun/467076780/
    • Behaviour Driven Development
    • Intent
    • [TestMethod]public void EditAction_Retrieves_Dinner_1_From_Repo_And_Countries_And_Sets_DinnerViewModel() { // Arrangevar controller = CreateDinnersControllerAs("someuser"); // ActViewResult result = controller.Edit(1) as ViewResult; // AssertDinnerFormViewModel model = result.ViewData.Model as DinnerFormViewModel;Assert.AreEqual(13, model.Countries.Count());}
    • describe
    • describe “when editing”do
    • describe “when editing” do
      it
      end
    • describe “when editing” do
      it “should return countries where dinners can be hosted”
      end
    • D:SourceControl erddinner-23425specs>ispecDinnersController_specs.rb
      *
      Pending:
      when editing should return countries where dinners can be hosted (Not Yet Implemented)
      ./DinnersController_specs.rb:2
      Finished in 0.3511328 seconds
      1 example, 0 failures, 1 pending
    • describeNerdDinner::Controllers::DinnersController, “when editing”do
    • require ‘NerdDinner.dll’
      describe NerdDinner::Controllers::DinnersController, “when editing” do
    • $: << ‘../NerdDinner/bin’
      require ‘NerdDinner.dll’
      describe NerdDinner::Controllers::DinnersController, “when editing” do
    • $: << ‘../NerdDinner/bin’
      require ‘NerdDinner.dll’
      Include NerdDinner::Controllers
      describe DinnersController, “when editing” do
    • it “returns countries where dinners can be hosted” do
      controller = DinnersController.new
      end
    • it “returns countries where dinners can be hosted” do
      controller = DinnersController.new(dinner_repos(dinners))
      end
    • it “returns countries where dinners can be hosted” do
      controller = DinnersController.new(dinner_repos(dinners))
      result = controller.Edit(1).ViewData.Model
      end
    • it “returns countries where dinners can be hosted” do
      controller = DinnersController.new(dinner_repos(dinners))
      result = controller.Edit(1).ViewData.Model
      result.Countries.Count().should == test_data.length
      end
      RSpec has really powerful matchers
    • D:SourceControl erddinner-23425specs>ispecDinnersController_specs.rb
      F
      1)
      'NerdDinner::Controllers::DinnersController when editing should return countries where dinners can be hosted' FAILED
      expected: 13,
      got: nil (using ==)
      ./DinnersController_specs.rb:8:
      Finished in 0.4824219 seconds
      1 example, 1 failure
    • D:SourceControl erddinner-23425specs>ispecDinnersController_specs.rb
      .
      Finished in 0.4355469 seconds
      1 example, 0 failures
    • require ‘caricature’
      def dinner_repos(test_data)
      IDinnerRepository.isolate(:FindUpcomingDinners) {returns test_data}
      End
    • def create_dinners(count=13)
      dinners = []
      count.timesdo |i|
      dinners << Dinner.new(:country => “Value#{i}”)
      end
      end
    • describe DinnersController, "when editing" do
      let(:dinners) {create_dinners}
      let(:controller) {DinnersController.new(dinner_repos dinners)}
      it "returns countries where dinners can be hosted" do
      result = controller.Edit(dinners.first.id).view_model
      result.Countries.Count().should == dinners.length
      end
      end
    • result.Countries.Count().should == dinners.length
      result.Countries.shouldhave_same_count(dinners)
      module Matchers
      class CountEqual
      def initialize(expected)
      @expected = expected
      end
      def matches?(actual)
      actual.Count() == @expected.Count()
      end
      end
      def have_same_count(expected)
      CountEqual.new(expected)
      end
      end
      Duck Typing FTW!
    • describe DinnersController, “Managing dinner reservations” do
      let(:dinners) { valid_dinners }
      let(:controller) {DinnersController.new(dinner_repository dinners)}
      describe “when editing“it_should_behave_like “valid dinners” it "returns countries where dinners can be hosted"
      end
      describe “when saving“ do
      describe “the validation for invalid dinners” do
      let(:dinners) { bad_dinners(1) }it “should reject a dinner without a name”
      it “should reject a dinner without a email address”
      it “should accept a dinner if it has a name and email address”
      end
      describe “confirmation” do
      it “should send an email to the organiser once saved”
      end
      describe “valid dinners” do
      it “redirects to thank you page after completing"
      end
      end
      end
    • describe "NHibernate" do
      before do
      config = Configuration.new
      @cfg = config.configure(File.join(Dir.pwd, "nhibernate.config"))
      end
      it "can create session factory" do
      session_factory = @cfg.BuildSessionFactory()
      session_factory.should_notbe_nil
      end
      it "can create session" do
      session_factory = @cfg.BuildSessionFactory()
      session = session_factory.open_session
      session.should_notbe_nil
      end
      end
    • Outside-in Development
    • Cucumber
      http://www.flickr.com/photos/vizzzual-dot-com/2738586453/
    • Documentation, automated tests and development-aid
    • [Story] public void Should_find_customers_by_name_when_name_matches() { Story story = new Story("List customers by name"); story.AsA("customer support staff") .IWant("to search for customers in a very flexible manner") .SoThat("I can find a customer record and provide meaningful support"); CustomerRepository repo = null; Customer customer = null; story.WithScenario("Find by name") .Given("a set of valid customers", delegate { repo = CreateDummyRepo(); }) .When("I ask for an existing name", "Joe Schmoe", delegate(string name) { customer = repo.FindByName(name); }) .Then("the correct customer is found and returned", delegate {Assert.That(customer.Name, Is.EqualTo("Joe Schmoe"));}); }
    • Feature: List customers by name As a customer support staff I want to search for customers in a very flexible manner So that I can find a customer record and provide meaningful supportScenario: Find by name Given a set of valid customers When I ask for an existing name Then the correct customer is found and returned
    • Feature: List customers by nameAs a customer support staffI want to search for customers in a very flexible mannerSo that I can find a customer record and provide meaningful supportScenario: Find by name Given a set of valid customersWhen I ask for an existing nameThen the correct customer is found and returned
    • D:SourceControl erddinner-23425features>cucumber list.feature
      Feature: List customers by name
      As a customer support staff
      I want to search for customers in a very flexible manner
      So that I can find a customer record and provide meaningful support
      Scenario: Find by name # list.feature:6
      Given a set of valid customers # list.feature:7
      When I ask for an existing name # list.feature:8
      Then the correct customer is found and returned # list.feature:9
      1 scenario (1 undefined)
      3 steps (3 undefined)
      0m0.020s
      You can implement step definitions for undefined steps with these snippets:
      Given /^a set of valid customers$/ do
      pending # express the regexp above with the code you wish you had
      end
      When /^I ask for an existing name$/ do
      pending # express the regexp above with the code you wish you had
      end
      Then /^the correct customer is found and returned$/ do
      pending # express the regexp above with the code you wish you had
      end
    • NOT BEST PRACTICE!!
      Given /^a set of valid customers$/ do @repo = CreateDummyRepo()endWhen /^I ask for an existing name$/ do @customer = @repo.FindByName("Joe Schmoe")endThen /^the correct customer is found and returned$/ do @customer.Name.should == "Joe Schmoe“end
    • Given /^customer “([^"]*)” $/ do |name| @repo = CustomerRepository.new(Customer.new(:name => name)endWhen /^I search for customer “([^"]*)”$/ do |name| @customer = @repo.FindByName(name)endThen /^”([^"]*)” should be found and returned$/ do |name| @customer.Name.should == nameend
    • WebRat
      http://www.flickr.com/photos/whatwhat/22624256/
    • visit
      click_link
      fill_in
      click_button
      check and uncheck
      choose
      select
      attach_file
    • EXAMPLES
      Cucumber, WebRat and Automated UI testing
    • One more thing...
    • Meerkatalyst
    • http://blog.benhall.me.uk/2009/12/sneak-peek-at-meerkatalystlonestar.html
    • http://www.flickr.com/photos/leon_homan/2856628778/
    • Expressing intent
    • Ruby -> C#
    • http://www.flickr.com/photos/philliecasablanca/2456840986/
      @Ben_HallBen@BenHall.me.ukBlog.BenHall.me.uk
    • using Cuke4Nuke.Framework;
      usingNUnit.Framework;
      usingWatiN.Core;
      namespaceGoogle.StepDefinitions
      {
          publicclassSearchSteps
          {
              Browser _browser;
      [Before]
              publicvoidSetUp()
              {
                  _browser = new WatiN.Core.IE();
              }
      [After]
              publicvoidTearDown()
              {
                  if (_browser != null)
                  {
                      _browser.Dispose();
                  }
              }
      [When(@"^(?:I'm on|I go to) the search page$")]
              publicvoidGoToSearchPage()
              {
                  _browser.GoTo("http://www.google.com/");
              }
      [When("^I search for "(.*)"$")]
              publicvoidSearchFor(string query)
              {
                  _browser.TextField(Find.ByName("q")).TypeText(query);
                  _browser.Button(Find.ByName("btnG")).Click();
              }
      [Then("^I should be on the search page$")]
              publicvoidIsOnSearchPage()
              {
                  Assert.That(_browser.Title == "Google");
              }
      [Then("^I should see "(.*)" in the results$")]
              publicvoidResultsContain(stringexpectedResult)
              {
                  Assert.That(_browser.ContainsText(expectedResult));
              }
          }
      }
    • Given /^(?:I'm on|I go to) the search page$/ do
        visit 'http://www.google.com'
      end
       
      When /^I search for "([^"]*)"$/ do|query|
        fill_in 'q', :with => query
        click_button 'Google Search'
      end
       
      Then /^I should be on the search page$/ do
       dom.search('title').should == "Google"
      end
       
      Then /^I should see "(.*)" in the results$/ do|text|
        response.should contain(text)
      end
    • Software
      Recommended:
      IronRuby
      Ruby
      Cucumber
      Rspec
      WebRat
      mechanize
      Selenium RC
      selenium-client
      Caricature
      activerecord-sqlserver-adapter
      Optional:
      XSP Mono
      JetBrain’sRubyMine
      JRuby
      Capybara
      Celerity
      Active record
      active-record-model-generator
      Faker
      Guid
    • Useful Links
      http://www.github.com/BenHall
      http://blog.benhall.me.uk
      http://stevehodgkiss.com/2009/11/14/using-activerecord-migrator-standalone-with-sqlite-and-sqlserver-on-windows.html
      http://www.testingaspnet.com
      http://
      http://msdn.microsoft.com/en-us/magazine/dd434651.aspx
      http://msdn.microsoft.com/en-us/magazine/dd453038.aspx
      http://www.cukes.info
    • Getting SQL Server to work
      gem install activerecord-sqlserver-adapter
      Download dbi-0.2.2.zip
      Extract dbdADO.rb to rubysite_ruby1.8DBDADO.rb