SlideShare a Scribd company logo
1 of 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

More Related Content

What's hot

Workshop 1: Good practices in JavaScript
Workshop 1: Good practices in JavaScriptWorkshop 1: Good practices in JavaScript
Workshop 1: Good practices in JavaScriptVisual Engineering
 
節子、それViewControllerやない...、FatViewControllerや...。
節子、それViewControllerやない...、FatViewControllerや...。節子、それViewControllerやない...、FatViewControllerや...。
節子、それViewControllerやない...、FatViewControllerや...。Kenji Tanaka
 
Models, controllers and views
Models, controllers and viewsModels, controllers and views
Models, controllers and viewspriestc
 
VC「もしかして...」Model「私たち...」「「入れ替わってるー!?」」を前前前世から防ぐ方法
VC「もしかして...」Model「私たち...」「「入れ替わってるー!?」」を前前前世から防ぐ方法VC「もしかして...」Model「私たち...」「「入れ替わってるー!?」」を前前前世から防ぐ方法
VC「もしかして...」Model「私たち...」「「入れ替わってるー!?」」を前前前世から防ぐ方法Kenji Tanaka
 
Automated javascript unit testing
Automated javascript unit testingAutomated javascript unit testing
Automated javascript unit testingryan_chambers
 
描画とビジネスをクリーンに分ける(公開用)
描画とビジネスをクリーンに分ける(公開用)描画とビジネスをクリーンに分ける(公開用)
描画とビジネスをクリーンに分ける(公開用)Kenji Tanaka
 
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.jsVladimir Roudakov
 
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 Rubyerr
 
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 Symfony2Sumy PHP User Grpoup
 
Denver emberjs-sept-2015
Denver emberjs-sept-2015Denver emberjs-sept-2015
Denver emberjs-sept-2015Ron White
 
Rails 3: Dashing to the Finish
Rails 3: Dashing to the FinishRails 3: Dashing to the Finish
Rails 3: Dashing to the FinishYehuda Katz
 
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 KarmaExoLeaders.com
 
Alloy Tips & Tricks #TiLon
Alloy Tips & Tricks #TiLonAlloy Tips & Tricks #TiLon
Alloy Tips & Tricks #TiLonFokke Zandbergen
 
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 Symfony2Sumy PHP User Grpoup
 
Workshop 5: JavaScript testing
Workshop 5: JavaScript testingWorkshop 5: JavaScript testing
Workshop 5: JavaScript testingVisual Engineering
 
Introductionandgreetings
IntroductionandgreetingsIntroductionandgreetings
IntroductionandgreetingsPozz ZaRat
 

What's hot (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
 

Viewers also liked

Viewers also liked (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
 

Similar to Testing ASP.net Web Applications using Ruby

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.0Matt Raible
 
Rugalytics | Ruby Manor Nov 2008
Rugalytics | Ruby Manor Nov 2008Rugalytics | Ruby Manor Nov 2008
Rugalytics | Ruby Manor Nov 2008Rob
 
Introduction to ASP.NET MVC
Introduction to ASP.NET MVCIntroduction to ASP.NET MVC
Introduction to ASP.NET MVCMaarten Balliauw
 
Effecient javascript
Effecient javascriptEffecient javascript
Effecient javascriptmpnkhan
 
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 RailsDave Bouwman
 
Advanced and Hidden WordPress APIs
Advanced and Hidden WordPress APIsAdvanced and Hidden WordPress APIs
Advanced and Hidden WordPress APIsandrewnacin
 
ActiveWeb: Chicago Java User Group Presentation
ActiveWeb: Chicago Java User Group PresentationActiveWeb: Chicago Java User Group Presentation
ActiveWeb: Chicago Java User Group Presentationipolevoy
 
Spring MVC Annotations
Spring MVC AnnotationsSpring MVC Annotations
Spring MVC AnnotationsJordan Silva
 
Chaincode Development 區塊鏈鏈碼開發
Chaincode Development 區塊鏈鏈碼開發Chaincode Development 區塊鏈鏈碼開發
Chaincode Development 區塊鏈鏈碼開發HO-HSUN LIN
 
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 AppEngineRoman Kirillov
 
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 jaxconf
 
Spring MVC 3 Restful
Spring MVC 3 RestfulSpring MVC 3 Restful
Spring MVC 3 Restfulknight1128
 
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 AppsMike Subelsky
 
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 EngineAndy McKay
 
Spring 3.x - Spring MVC
Spring 3.x - Spring MVCSpring 3.x - Spring MVC
Spring 3.x - Spring MVCGuy Nir
 
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 2020Andrzej Jóźwiak
 
Web CrawlersrcedusmulylecrawlerController.javaWeb Crawler.docx
Web CrawlersrcedusmulylecrawlerController.javaWeb Crawler.docxWeb CrawlersrcedusmulylecrawlerController.javaWeb Crawler.docx
Web CrawlersrcedusmulylecrawlerController.javaWeb Crawler.docxcelenarouzie
 
Rack is Spectacular
Rack is SpectacularRack is Spectacular
Rack is SpectacularBryce Kerley
 

Similar to 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
 

More from Ben Hall

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 2022Ben Hall
 
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 ProjectsBen Hall
 
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 ContainersBen Hall
 
Containers without docker
Containers without dockerContainers without docker
Containers without dockerBen Hall
 
Deploying windows containers with kubernetes
Deploying windows containers with kubernetesDeploying windows containers with kubernetes
Deploying windows containers with kubernetesBen Hall
 
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 ProjectsBen Hall
 
How Secure Are Docker Containers?
How Secure Are Docker Containers?How Secure Are Docker Containers?
How Secure Are Docker Containers?Ben Hall
 
The Challenges of Becoming Cloud Native
The Challenges of Becoming Cloud NativeThe Challenges of Becoming Cloud Native
The Challenges of Becoming Cloud NativeBen Hall
 
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 ServiceBen Hall
 
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.mdBen Hall
 
Experimenting and Learning Kubernetes and Tensorflow
Experimenting and Learning Kubernetes and TensorflowExperimenting and Learning Kubernetes and Tensorflow
Experimenting and Learning Kubernetes and TensorflowBen Hall
 
Running .NET on Docker
Running .NET on DockerRunning .NET on Docker
Running .NET on DockerBen Hall
 
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 ApplicationBen Hall
 
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 KubernetesBen Hall
 
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 ContainersBen Hall
 
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 containersBen Hall
 
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 containersBen Hall
 
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 2016Ben Hall
 
Learning Patterns for the Overworked Developer
Learning Patterns for the Overworked DeveloperLearning Patterns for the Overworked Developer
Learning Patterns for the Overworked DeveloperBen Hall
 
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 ApplicationsBen Hall
 

More from 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
 

Recently uploaded

Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebUiPathCommunity
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024Lorenzo Miniero
 
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostLeverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostZilliz
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsPixlogix Infotech
 
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfHyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfPrecisely
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):comworks
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024Stephanie Beckett
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...Fwdays
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Mark Simos
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Manik S Magar
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Enterprise Knowledge
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.Curtis Poe
 
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo DayH2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo DaySri Ambati
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsRizwan Syed
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity PlanDatabarracks
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenHervé Boutemy
 
DSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningDSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningLars Bell
 

Recently uploaded (20)

Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio Web
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024
 
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostLeverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and Cons
 
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfHyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.
 
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo DayH2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL Certs
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity Plan
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache Maven
 
DSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningDSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine Tuning
 

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