SlideShare une entreprise Scribd logo
1  sur  68
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:ourceControlerddinner-23425pecs>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:ourceControlerddinner-23425pecs>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:ourceControlerddinner-23425pecs>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:ourceControlerddinner-23425eatures>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 “([^amp;quot;]*)” $/ do |name|    @repo = CustomerRepository.new(Customer.new(:name => name)endWhen /^I search for customer “([^amp;quot;]*)”$/ do |name|    @customer = @repo.FindByName(name)endThen /^”([^amp;quot;]*)” 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 amp;quot;(.*)amp;quot;$")]         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 amp;quot;(.*)amp;quot; 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 "([^amp;quot;]*)"$/ 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 amp;quot;(.*)amp;quot; 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 dbdDO.rb to rubyite_ruby.8BDDO.rb

Contenu connexe

Tendances

Models, controllers and views
Models, controllers and viewsModels, controllers and views
Models, controllers and views
priestc
 
Automated javascript unit testing
Automated javascript unit testingAutomated javascript unit testing
Automated javascript unit testing
ryan_chambers
 
Rails 3: Dashing to the Finish
Rails 3: Dashing to the FinishRails 3: Dashing to the Finish
Rails 3: Dashing to the Finish
Yehuda Katz
 

Tendances (20)

Workshop 1: Good practices in JavaScript
Workshop 1: Good practices in JavaScriptWorkshop 1: Good practices in JavaScript
Workshop 1: Good practices in JavaScript
 
RSpec
RSpecRSpec
RSpec
 
節子、それViewControllerやない...、FatViewControllerや...。
節子、それViewControllerやない...、FatViewControllerや...。節子、それViewControllerやない...、FatViewControllerや...。
節子、それViewControllerやない...、FatViewControllerや...。
 
Models, controllers and views
Models, controllers and viewsModels, controllers and views
Models, controllers and views
 
VC「もしかして...」Model「私たち...」「「入れ替わってるー!?」」を前前前世から防ぐ方法
VC「もしかして...」Model「私たち...」「「入れ替わってるー!?」」を前前前世から防ぐ方法VC「もしかして...」Model「私たち...」「「入れ替わってるー!?」」を前前前世から防ぐ方法
VC「もしかして...」Model「私たち...」「「入れ替わってるー!?」」を前前前世から防ぐ方法
 
Automated javascript unit testing
Automated javascript unit testingAutomated javascript unit testing
Automated javascript unit testing
 
Thomas Fuchs Presentation
Thomas Fuchs PresentationThomas Fuchs Presentation
Thomas Fuchs Presentation
 
描画とビジネスをクリーンに分ける(公開用)
描画とビジネスをクリーンに分ける(公開用)描画とビジネスをクリーンに分ける(公開用)
描画とビジネスをクリーンに分ける(公開用)
 
DrupalCon Dublin 2016 - Automated browser testing with Nightwatch.js
DrupalCon Dublin 2016 - Automated browser testing with Nightwatch.jsDrupalCon Dublin 2016 - Automated browser testing with Nightwatch.js
DrupalCon Dublin 2016 - Automated browser testing with Nightwatch.js
 
Making and Breaking Web Services with Ruby
Making and Breaking Web Services with RubyMaking and Breaking Web Services with Ruby
Making and Breaking Web Services with Ruby
 
Service approach for development REST API in Symfony2
Service approach for development REST API in Symfony2Service approach for development REST API in Symfony2
Service approach for development REST API in Symfony2
 
Boost your angular app with web workers
Boost your angular app with web workersBoost your angular app with web workers
Boost your angular app with web workers
 
Denver emberjs-sept-2015
Denver emberjs-sept-2015Denver emberjs-sept-2015
Denver emberjs-sept-2015
 
Workshop 10: ECMAScript 6
Workshop 10: ECMAScript 6Workshop 10: ECMAScript 6
Workshop 10: ECMAScript 6
 
Rails 3: Dashing to the Finish
Rails 3: Dashing to the FinishRails 3: Dashing to the Finish
Rails 3: Dashing to the Finish
 
Good karma: UX Patterns and Unit Testing in Angular with Karma
Good karma: UX Patterns and Unit Testing in Angular with KarmaGood karma: UX Patterns and Unit Testing in Angular with Karma
Good karma: UX Patterns and Unit Testing in Angular with Karma
 
Alloy Tips & Tricks #TiLon
Alloy Tips & Tricks #TiLonAlloy Tips & Tricks #TiLon
Alloy Tips & Tricks #TiLon
 
Service approach for development Rest API in Symfony2
Service approach for development Rest API in Symfony2Service approach for development Rest API in Symfony2
Service approach for development Rest API in Symfony2
 
Workshop 5: JavaScript testing
Workshop 5: JavaScript testingWorkshop 5: JavaScript testing
Workshop 5: JavaScript testing
 
Introductionandgreetings
IntroductionandgreetingsIntroductionandgreetings
Introductionandgreetings
 

En vedette (6)

Orientaciones Foros 2010
Orientaciones Foros 2010Orientaciones Foros 2010
Orientaciones Foros 2010
 
Mujeres y ciencia
Mujeres y cienciaMujeres y ciencia
Mujeres y ciencia
 
Itb travel-trends-r'11
Itb travel-trends-r'11Itb travel-trends-r'11
Itb travel-trends-r'11
 
Documentos secundaria-cienciay ambiente-vii
Documentos secundaria-cienciay ambiente-viiDocumentos secundaria-cienciay ambiente-vii
Documentos secundaria-cienciay ambiente-vii
 
Clever Joomla! Templating Tips and Tricks
Clever Joomla! Templating Tips and TricksClever Joomla! Templating Tips and Tricks
Clever Joomla! Templating Tips and Tricks
 
講道1002
講道1002講道1002
講道1002
 

Similaire à Testing ASP.net Web Applications using Ruby

Spring 3.x - Spring MVC
Spring 3.x - Spring MVCSpring 3.x - Spring MVC
Spring 3.x - Spring MVC
Guy Nir
 
Web CrawlersrcedusmulylecrawlerController.javaWeb Crawler.docx
Web CrawlersrcedusmulylecrawlerController.javaWeb Crawler.docxWeb CrawlersrcedusmulylecrawlerController.javaWeb Crawler.docx
Web CrawlersrcedusmulylecrawlerController.javaWeb Crawler.docx
celenarouzie
 

Similaire à Testing ASP.net Web Applications using Ruby (20)

What's Coming in Spring 3.0
What's Coming in Spring 3.0What's Coming in Spring 3.0
What's Coming in Spring 3.0
 
Rugalytics | Ruby Manor Nov 2008
Rugalytics | Ruby Manor Nov 2008Rugalytics | Ruby Manor Nov 2008
Rugalytics | Ruby Manor Nov 2008
 
Introduction to ASP.NET MVC
Introduction to ASP.NET MVCIntroduction to ASP.NET MVC
Introduction to ASP.NET MVC
 
Effecient javascript
Effecient javascriptEffecient javascript
Effecient javascript
 
My java file
My java fileMy java file
My java file
 
Using ArcGIS Server with Ruby on Rails
Using ArcGIS Server with Ruby on RailsUsing ArcGIS Server with Ruby on Rails
Using ArcGIS Server with Ruby on Rails
 
Advanced and Hidden WordPress APIs
Advanced and Hidden WordPress APIsAdvanced and Hidden WordPress APIs
Advanced and Hidden WordPress APIs
 
ActiveWeb: Chicago Java User Group Presentation
ActiveWeb: Chicago Java User Group PresentationActiveWeb: Chicago Java User Group Presentation
ActiveWeb: Chicago Java User Group Presentation
 
Spring MVC Annotations
Spring MVC AnnotationsSpring MVC Annotations
Spring MVC Annotations
 
Chaincode Development 區塊鏈鏈碼開發
Chaincode Development 區塊鏈鏈碼開發Chaincode Development 區塊鏈鏈碼開發
Chaincode Development 區塊鏈鏈碼開發
 
Google Cloud Endpoints: Building Third-Party APIs on Google AppEngine
Google Cloud Endpoints: Building Third-Party APIs on Google AppEngineGoogle Cloud Endpoints: Building Third-Party APIs on Google AppEngine
Google Cloud Endpoints: Building Third-Party APIs on Google AppEngine
 
Multi Client Development with Spring - Josh Long
Multi Client Development with Spring - Josh Long Multi Client Development with Spring - Josh Long
Multi Client Development with Spring - Josh Long
 
Spring MVC 3 Restful
Spring MVC 3 RestfulSpring MVC 3 Restful
Spring MVC 3 Restful
 
SproutCore and the Future of Web Apps
SproutCore and the Future of Web AppsSproutCore and the Future of Web Apps
SproutCore and the Future of Web Apps
 
Cross Domain Web
Mashups with JQuery and Google App Engine
Cross Domain Web
Mashups with JQuery and Google App EngineCross Domain Web
Mashups with JQuery and Google App Engine
Cross Domain Web
Mashups with JQuery and Google App Engine
 
Spring 3.x - Spring MVC
Spring 3.x - Spring MVCSpring 3.x - Spring MVC
Spring 3.x - Spring MVC
 
Angular Workshop_Sarajevo2
Angular Workshop_Sarajevo2Angular Workshop_Sarajevo2
Angular Workshop_Sarajevo2
 
Do I need tests when I have the compiler - Andrzej Jóźwiak - TomTom Dev Day 2020
Do I need tests when I have the compiler - Andrzej Jóźwiak - TomTom Dev Day 2020Do I need tests when I have the compiler - Andrzej Jóźwiak - TomTom Dev Day 2020
Do I need tests when I have the compiler - Andrzej Jóźwiak - TomTom Dev Day 2020
 
Web CrawlersrcedusmulylecrawlerController.javaWeb Crawler.docx
Web CrawlersrcedusmulylecrawlerController.javaWeb Crawler.docxWeb CrawlersrcedusmulylecrawlerController.javaWeb Crawler.docx
Web CrawlersrcedusmulylecrawlerController.javaWeb Crawler.docx
 
Rack is Spectacular
Rack is SpectacularRack is Spectacular
Rack is Spectacular
 

Plus de Ben Hall

Plus de Ben Hall (20)

The Art Of Documentation - NDC Porto 2022
The Art Of Documentation - NDC Porto 2022The Art Of Documentation - NDC Porto 2022
The Art Of Documentation - NDC Porto 2022
 
The Art Of Documentation for Open Source Projects
The Art Of Documentation for Open Source ProjectsThe Art Of Documentation for Open Source Projects
The Art Of Documentation for Open Source Projects
 
Three Years of Lessons Running Potentially Malicious Code Inside Containers
Three Years of Lessons Running Potentially Malicious Code Inside ContainersThree Years of Lessons Running Potentially Malicious Code Inside Containers
Three Years of Lessons Running Potentially Malicious Code Inside Containers
 
Containers without docker
Containers without dockerContainers without docker
Containers without docker
 
Deploying windows containers with kubernetes
Deploying windows containers with kubernetesDeploying windows containers with kubernetes
Deploying windows containers with kubernetes
 
The Art of Documentation and Readme.md for Open Source Projects
The Art of Documentation and Readme.md for Open Source ProjectsThe Art of Documentation and Readme.md for Open Source Projects
The Art of Documentation and Readme.md for Open Source Projects
 
How Secure Are Docker Containers?
How Secure Are Docker Containers?How Secure Are Docker Containers?
How Secure Are Docker Containers?
 
The Challenges of Becoming Cloud Native
The Challenges of Becoming Cloud NativeThe Challenges of Becoming Cloud Native
The Challenges of Becoming Cloud Native
 
Scaling Docker Containers using Kubernetes and Azure Container Service
Scaling Docker Containers using Kubernetes and Azure Container ServiceScaling Docker Containers using Kubernetes and Azure Container Service
Scaling Docker Containers using Kubernetes and Azure Container Service
 
The art of documentation and readme.md
The art of documentation and readme.mdThe art of documentation and readme.md
The art of documentation and readme.md
 
Experimenting and Learning Kubernetes and Tensorflow
Experimenting and Learning Kubernetes and TensorflowExperimenting and Learning Kubernetes and Tensorflow
Experimenting and Learning Kubernetes and Tensorflow
 
Running .NET on Docker
Running .NET on DockerRunning .NET on Docker
Running .NET on Docker
 
Real World Lessons on the Pain Points of Node.JS Application
Real World Lessons on the Pain Points of Node.JS ApplicationReal World Lessons on the Pain Points of Node.JS Application
Real World Lessons on the Pain Points of Node.JS Application
 
Tips on solving E_TOO_MANY_THINGS_TO_LEARN with Kubernetes
Tips on solving E_TOO_MANY_THINGS_TO_LEARN with KubernetesTips on solving E_TOO_MANY_THINGS_TO_LEARN with Kubernetes
Tips on solving E_TOO_MANY_THINGS_TO_LEARN with Kubernetes
 
Deploying applications to Windows Server 2016 and Windows Containers
Deploying applications to Windows Server 2016 and Windows ContainersDeploying applications to Windows Server 2016 and Windows Containers
Deploying applications to Windows Server 2016 and Windows Containers
 
The How and Why of Windows containers
The How and Why of Windows containersThe How and Why of Windows containers
The How and Why of Windows containers
 
Lessons from running potentially malicious code inside containers
Lessons from running potentially malicious code inside containersLessons from running potentially malicious code inside containers
Lessons from running potentially malicious code inside containers
 
Deploying Windows Containers on Windows Server 2016
Deploying Windows Containers on Windows Server 2016Deploying Windows Containers on Windows Server 2016
Deploying Windows Containers on Windows Server 2016
 
Learning Patterns for the Overworked Developer
Learning Patterns for the Overworked DeveloperLearning Patterns for the Overworked Developer
Learning Patterns for the Overworked Developer
 
Real World Lessons on the Pain Points of Node.js Applications
Real World Lessons on the Pain Points of Node.js ApplicationsReal World Lessons on the Pain Points of Node.js Applications
Real World Lessons on the Pain Points of Node.js Applications
 

Dernier

Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Safe Software
 

Dernier (20)

MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024
 
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
HTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation StrategiesHTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation Strategies
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
Top 10 Most Downloaded Games on Play Store in 2024
Top 10 Most Downloaded Games on Play Store in 2024Top 10 Most Downloaded Games on Play Store in 2024
Top 10 Most Downloaded Games on Play Store in 2024
 

Testing ASP.net Web Applications using Ruby

  • 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
  • 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
  • 16.
  • 18.
  • 19.
  • 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());}
  • 27. describe “when editing” do it “should return countries where dinners can be hosted” end
  • 28. D:ourceControlerddinner-23425pecs>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
  • 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:ourceControlerddinner-23425pecs>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
  • 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
  • 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:ourceControlerddinner-23425eatures>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 “([^amp;quot;]*)” $/ do |name| @repo = CustomerRepository.new(Customer.new(:name => name)endWhen /^I search for customer “([^amp;quot;]*)”$/ do |name| @customer = @repo.FindByName(name)endThen /^”([^amp;quot;]*)” should be found and returned$/ do |name| @customer.Name.should == nameend
  • 55. visit click_link fill_in click_button check and uncheck choose select attach_file
  • 56. EXAMPLES Cucumber, WebRat and Automated UI testing
  • 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 amp;quot;(.*)amp;quot;$")]         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 amp;quot;(.*)amp;quot; 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 "([^amp;quot;]*)"$/ 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 amp;quot;(.*)amp;quot; 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 dbdDO.rb to rubyite_ruby.8BDDO.rb