Testing ASP.net Web Applications using Ruby

68
Testing ASP.net using Ruby @Ben_Hall [email protected] Blog.BenHall.me.uk Meerkatalyst

description

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

Transcript of Testing ASP.net Web Applications using Ruby

Page 1: Testing ASP.net Web Applications using Ruby

Testing ASP.net using Ruby

@[email protected]

Meerkatalyst

Page 2: Testing ASP.net Web Applications using Ruby

1| Why you should care2| Object Level testing3| UI level testing

Page 3: Testing ASP.net Web Applications using Ruby

What do I mean by Testing ASP.net?

Page 4: Testing ASP.net Web Applications using Ruby

Co-Author of Testing ASP.net Web Applicationshttp://www.testingaspnet.com

Page 5: Testing ASP.net Web Applications using Ruby

WHY TEST?

http://www.flickr.com/photos/atomicpuppy/2132073976/

Page 6: Testing ASP.net Web Applications using Ruby

It is 2010. Automated testing is no longer controversial.

Page 7: Testing ASP.net Web Applications using Ruby

[TestMethod]public void EditAction_Retrieves_Dinner_1_From_Repo_And_Countries_And_Sets_DinnerViewModel() { // Arrange var controller = CreateDinnersControllerAs("someuser");

// Act ViewResult result = controller.Edit(1) as ViewResult;

// Assert DinnerFormViewModel model = result.ViewData.Model as DinnerFormViewModel; Assert.AreEqual(13, model.Countries.Count());}

http://nerddinner.codeplex.com/SourceControl/changeset/view/23425#439968

Page 8: Testing ASP.net Web Applications using Ruby

[TestMethod]public void EditAction_Retrieves_Dinner_1_From_Repo_And_Countries_And_Sets_DinnerViewModel() { // Arrange var controller = CreateDinnersControllerAs("someuser");

// Act ViewResult result = controller.Edit(1) as ViewResult;

// Assert DinnerFormViewModel model = result.ViewData.Model as DinnerFormViewModel; Assert.AreEqual(13, model.Countries.Count());}

http://nerddinner.codeplex.com/SourceControl/changeset/view/23425#439968

Page 9: Testing ASP.net Web Applications using Ruby

[Fact] public void RsdReturnsValidRsdDoc() { FakeAreaService areaService = 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);

XDocument rsdDoc = XDocument.Parse(result.Content); XNamespace rsdNamespace = "http://archipelago.phrasewise.com/rsd"; XElement rootElement = 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);

XElement apisElement = rootElement.Descendants(rsdNamespace + "apis").SingleOrDefault(); Assert.NotNull(apisElement); Assert.Equal(1, apisElement.Elements().Count());

XElement apiElement = 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

Page 10: Testing ASP.net Web Applications using Ruby

[Fact] public void RsdReturnsValidRsdDoc() { FakeAreaService areaService = 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);

XDocument rsdDoc = XDocument.Parse(result.Content); XNamespace rsdNamespace = "http://archipelago.phrasewise.com/rsd"; XElement rootElement = 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);

XElement apisElement = rootElement.Descendants(rsdNamespace + "apis").SingleOrDefault(); Assert.NotNull(apisElement); Assert.Equal(1, apisElement.Elements().Count());

XElement apiElement = 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

Page 11: Testing ASP.net Web Applications using Ruby

[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"));}); }

http://grabbagoft.blogspot.com/2007/09/authoring-stories-with-nbehave-03.html

Page 12: Testing ASP.net Web Applications using Ruby

http://www.flickr.com/photos/gagilas/2659695352/

RECORD AND PLAYBACK

Page 13: Testing ASP.net Web Applications using Ruby

You can make C# readable

But it’s hard

Page 14: Testing ASP.net Web Applications using Ruby

RUBY?

http://www.flickr.com/photos/buro9/298994863/

Page 15: Testing ASP.net Web Applications using Ruby

Natural Language

Page 16: Testing ASP.net Web Applications using Ruby
Page 17: Testing ASP.net Web Applications using Ruby

http://www.flickr.com/photos/mag3737/1914076277/

Page 18: Testing ASP.net Web Applications using Ruby
Page 19: Testing ASP.net Web Applications using Ruby
Page 20: Testing ASP.net Web Applications using Ruby

RSpec

http://www.flickr.com/photos/dodgsun/467076780/

Page 21: Testing ASP.net Web Applications using Ruby

Behaviour Driven Development

Page 22: Testing ASP.net Web Applications using Ruby

Intent

Page 23: Testing ASP.net Web Applications using Ruby

[TestMethod]public void EditAction_Retrieves_Dinner_1_From_Repo_And_Countries_And_Sets_DinnerViewModel() { // Arrange var controller = CreateDinnersControllerAs("someuser");

// Act ViewResult result = controller.Edit(1) as ViewResult;

// Assert DinnerFormViewModel model = result.ViewData.Model as DinnerFormViewModel; Assert.AreEqual(13, model.Countries.Count());}

Page 24: Testing ASP.net Web Applications using Ruby

describe

Page 25: Testing ASP.net Web Applications using Ruby

describe “when editing” do

Page 26: Testing ASP.net Web Applications using Ruby

describe “when editing” do itend

Page 27: Testing ASP.net Web Applications using Ruby

describe “when editing” do it “should return countries where dinners can be hosted”end

Page 28: Testing ASP.net Web Applications using Ruby

D:\SourceControl\nerddinner-23425\specs>ispec DinnersController_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

Page 29: Testing ASP.net Web Applications using Ruby

describe NerdDinner::Controllers::DinnersController, “when editing” do

Page 30: Testing ASP.net Web Applications using Ruby

require ‘NerdDinner.dll’describe NerdDinner::Controllers::DinnersController, “when

editing” do

Page 31: Testing ASP.net Web Applications using Ruby

$: << ‘../NerdDinner/bin’require ‘NerdDinner.dll’describe NerdDinner::Controllers::DinnersController, “when

editing” do

Page 32: Testing ASP.net Web Applications using Ruby

$: << ‘../NerdDinner/bin’require ‘NerdDinner.dll’Include NerdDinner::Controllers

describe DinnersController, “when editing” do

Page 33: Testing ASP.net Web Applications using Ruby

it “returns countries where dinners can be hosted” do controller = DinnersController.newend

Page 34: Testing ASP.net Web Applications using Ruby

it “returns countries where dinners can be hosted” do controller = DinnersController.new(dinner_repos(dinners))end

Page 35: Testing ASP.net Web Applications using Ruby

it “returns countries where dinners can be hosted” do controller = DinnersController.new(dinner_repos(dinners)) result = controller.Edit(1).ViewData.Modelend

Page 36: Testing ASP.net Web Applications using Ruby

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.lengthend

RSpec has really powerful matchers

Page 37: Testing ASP.net Web Applications using Ruby

D:\SourceControl\nerddinner-23425\specs>ispec DinnersController_specs.rbF

1)'NerdDinner::Controllers::DinnersController when editing should return

countries where dinners can be hosted' FAILEDexpected: 13, got: nil (using ==)./DinnersController_specs.rb:8:Finished in 0.4824219 seconds

1 example, 1 failure

Page 38: Testing ASP.net Web Applications using Ruby

D:\SourceControl\nerddinner-23425\specs>ispec DinnersController_specs.rb.

Finished in 0.4355469 seconds

1 example, 0 failures

Page 39: Testing ASP.net Web Applications using Ruby

require ‘caricature’

def dinner_repos(test_data) IDinnerRepository.isolate(:FindUpcomingDinners) {returns test_data} End

Page 40: Testing ASP.net Web Applications using Ruby

def create_dinners(count=13) dinners = [] count.times do |i| dinners << Dinner.new(:country => “Value#{i}”) endend

Page 41: Testing ASP.net Web Applications using Ruby

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 endend

Page 42: Testing ASP.net Web Applications using Ruby

result.Countries.Count().should == dinners.length result.Countries.should have_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) endend

Duck Typing FTW!

Page 43: Testing ASP.net Web Applications using Ruby

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 endend

Page 44: Testing ASP.net Web Applications using Ruby

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_not be_nil end

it "can create session" do session_factory = @cfg.BuildSessionFactory() session = session_factory.open_session session.should_not be_nil endend

Page 45: Testing ASP.net Web Applications using Ruby

Outside-in Development

Page 46: Testing ASP.net Web Applications using Ruby

Cucumber

http://www.flickr.com/photos/vizzzual-dot-com/2738586453/

Page 47: Testing ASP.net Web Applications using Ruby

Documentation, automated tests and development-aid

Page 48: Testing ASP.net Web Applications using Ruby

[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"));});

}

Page 49: Testing ASP.net Web Applications using Ruby

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 Given a set of valid customers When I ask for an existing name Then the correct customer is found and returned

Page 50: Testing ASP.net Web Applications using Ruby

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 Given a set of valid customers When I ask for an existing name Then the correct customer is found and returned

Page 51: Testing ASP.net Web Applications using Ruby

D:\SourceControl\nerddinner-23425\features>cucumber list.featureFeature: 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 hadend

When /^I ask for an existing name$/ do pending # express the regexp above with the code you wish you hadend

Then /^the correct customer is found and returned$/ do pending # express the regexp above with the code you wish you hadend

Page 52: Testing ASP.net Web Applications using Ruby

Given /^a set of valid customers$/ do @repo = CreateDummyRepo()end

When /^I ask for an existing name$/ do @customer = @repo.FindByName("Joe Schmoe")end

Then /^the correct customer is found and returned$/ do @customer.Name.should == "Joe Schmoe“end

NOT BEST PRACTICE!!

Page 53: Testing ASP.net Web Applications using Ruby

Given /^customer “([^\"]*)” $/ do |name| @repo = CustomerRepository.new(Customer.new(:name => name)end

When /^I search for customer “([^\"]*)”$/ do |name| @customer = @repo.FindByName(name)end

Then /^”([^\"]*)” should be found and returned$/ do |name| @customer.Name.should == nameend

Page 54: Testing ASP.net Web Applications using Ruby

WebRat

http://www.flickr.com/photos/whatwhat/22624256/

Page 55: Testing ASP.net Web Applications using Ruby

visitclick_link

fill_inclick_button

check and uncheckchooseselect

attach_file

Page 56: Testing ASP.net Web Applications using Ruby

EXAMPLESCucumber, WebRat and Automated UI testing

Page 57: Testing ASP.net Web Applications using Ruby

One more thing...

Page 58: Testing ASP.net Web Applications using Ruby

Meerkatalyst

Page 59: Testing ASP.net Web Applications using Ruby

http://blog.benhall.me.uk/2009/12/sneak-peek-at-meerkatalystlonestar.html

Page 60: Testing ASP.net Web Applications using Ruby

http://www.flickr.com/photos/leon_homan/2856628778/

Page 61: Testing ASP.net Web Applications using Ruby

Expressing intent

Page 62: Testing ASP.net Web Applications using Ruby

Ruby -> C#

Page 63: Testing ASP.net Web Applications using Ruby

http://www.flickr.com/photos/philliecasablanca/2456840986/

@[email protected]

Page 64: Testing ASP.net Web Applications using Ruby

using Cuke4Nuke.Framework;using NUnit.Framework;using WatiN.Core;namespace Google.StepDefinitions{ public class SearchSteps { Browser _browser; [Before] public void SetUp() { _browser = new WatiN.Core.IE(); } [After] public void TearDown() { if (_browser != null) { _browser.Dispose(); } } [When(@"^(?:I'm on|I go to) the search page$")] public void GoToSearchPage() { _browser.GoTo("http://www.google.com/"); }

[When("^I search for \"(.*)\"$")] public void SearchFor(string query) { _browser.TextField(Find.ByName("q")).TypeText(query); _browser.Button(Find.ByName("btnG")).Click(); } [Then("^I should be on the search page$")] public void IsOnSearchPage() { Assert.That(_browser.Title == "Google"); } [Then("^I should see \"(.*)\" in the results$")] public void ResultsContain(string expectedResult) { Assert.That(_browser.ContainsText(expectedResult)); } }}

Page 65: Testing ASP.net Web Applications using Ruby

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

Page 66: Testing ASP.net Web Applications using Ruby

Software

• Recommended:– IronRuby– Ruby– Cucumber– Rspec– WebRat– mechanize– Selenium RC– selenium-client– Caricature– activerecord-sqlserver-

adapter

• Optional:– XSP \ Mono– JetBrain’s RubyMine– JRuby – Capybara– Celerity– Active record – active-record-model-

generator– Faker– Guid

Page 67: Testing ASP.net Web Applications using Ruby

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

Page 68: Testing ASP.net Web Applications using Ruby

Getting SQL Server to work

1. gem install activerecord-sqlserver-adapter1. Download dbi-0.2.2.zip 2. Extract dbd\ADO.rb to ruby\site_ruby\1.8\DBD\

ADO.rb