Testing ASP.net Web Applications using Ruby

  • 1,567 views
Uploaded on

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.

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
1,567
On Slideshare
0
From Embeds
0
Number of Embeds
1

Actions

Shares
Downloads
31
Comments
0
Likes
1

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Testing ASP.net using Ruby
    Meerkatalyst
    @Ben_HallBen@BenHall.me.ukBlog.BenHall.me.uk
  • 2. 1| Why you should care2| Object Level testing3| UI level testing
  • 3. What do I mean by Testing ASP.net?
  • 4. Co-Author of
    Testing ASP.net Web Applications
    http://www.testingaspnet.com
  • 5. WHY TEST?
    http://www.flickr.com/photos/atomicpuppy/2132073976/
  • 6. It is 2010. Automated testing is no longer controversial.
  • 7. 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());}
  • 8. 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());}
  • 9. [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
  • 10. [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
  • 11. 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"));}); }
  • 12. RECORD AND PLAYBACK
    http://www.flickr.com/photos/gagilas/2659695352/
  • 13. You can make C# readable
    But it’s hard
  • 14. http://www.flickr.com/photos/buro9/298994863/
    RUBY?
  • 15. Natural Language
  • 16.
  • 17. http://www.flickr.com/photos/mag3737/1914076277/
  • 18.
  • 19.
  • 20. RSpec
    http://www.flickr.com/photos/dodgsun/467076780/
  • 21. Behaviour Driven Development
  • 22. Intent
  • 23. [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());}
  • 24. describe
  • 25. describe “when editing”do
  • 26. describe “when editing” do
    it
    end
  • 27. describe “when editing” do
    it “should return countries where dinners can be hosted”
    end
  • 28. 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
  • 29. describeNerdDinner::Controllers::DinnersController, “when editing”do
  • 30. require ‘NerdDinner.dll’
    describe NerdDinner::Controllers::DinnersController, “when editing” do
  • 31. $: << ‘../NerdDinner/bin’
    require ‘NerdDinner.dll’
    describe NerdDinner::Controllers::DinnersController, “when editing” do
  • 32. $: << ‘../NerdDinner/bin’
    require ‘NerdDinner.dll’
    Include NerdDinner::Controllers
    describe DinnersController, “when editing” do
  • 33. it “returns countries where dinners can be hosted” do
    controller = DinnersController.new
    end
  • 34. it “returns countries where dinners can be hosted” do
    controller = DinnersController.new(dinner_repos(dinners))
    end
  • 35. it “returns countries where dinners can be hosted” do
    controller = DinnersController.new(dinner_repos(dinners))
    result = controller.Edit(1).ViewData.Model
    end
  • 36. 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
  • 37. 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
  • 38. D:SourceControl erddinner-23425specs>ispecDinnersController_specs.rb
    .
    Finished in 0.4355469 seconds
    1 example, 0 failures
  • 39. require ‘caricature’
    def dinner_repos(test_data)
    IDinnerRepository.isolate(:FindUpcomingDinners) {returns test_data}
    End
  • 40. def create_dinners(count=13)
    dinners = []
    count.timesdo |i|
    dinners << Dinner.new(:country => “Value#{i}”)
    end
    end
  • 41. 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
  • 42. 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!
  • 43. 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
  • 44. 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
  • 45. Outside-in Development
  • 46. Cucumber
    http://www.flickr.com/photos/vizzzual-dot-com/2738586453/
  • 47. Documentation, automated tests and development-aid
  • 48. [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"));}); }
  • 49. 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
  • 50. 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
  • 51. 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
  • 52. 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
  • 53. 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
  • 54. WebRat
    http://www.flickr.com/photos/whatwhat/22624256/
  • 55. visit
    click_link
    fill_in
    click_button
    check and uncheck
    choose
    select
    attach_file
  • 56. EXAMPLES
    Cucumber, WebRat and Automated UI testing
  • 57. One more thing...
  • 58. Meerkatalyst
  • 59. http://blog.benhall.me.uk/2009/12/sneak-peek-at-meerkatalystlonestar.html
  • 60. http://www.flickr.com/photos/leon_homan/2856628778/
  • 61. Expressing intent
  • 62. Ruby -> C#
  • 63. http://www.flickr.com/photos/philliecasablanca/2456840986/
    @Ben_HallBen@BenHall.me.ukBlog.BenHall.me.uk
  • 64. 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));
            }
        }
    }
  • 65. 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
  • 66. 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
  • 67. 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
  • 68. Getting SQL Server to work
    gem install activerecord-sqlserver-adapter
    Download dbi-0.2.2.zip
    Extract dbdADO.rb to rubysite_ruby1.8DBDADO.rb