Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Testing C# and ASP.net using Ruby<br />Meerkatalyst<br />@Ben_HallBen@BenHall.me.ukBlog.BenHall.me.ukCodeBetter.com/blogs/...
London based C# MVPWeb Developer @ 7digital.com Working on a number of Open Source Projects<br />Co-Author of <br />Testin...
Outside-in development<br />UI level testing<br />Cucumber<br />Object level testing using RSpec<br />
WHY?<br />http://www.flickr.com/photos/atomicpuppy/2132073976/<br />
VALUE<br />
State of .NET today<br />
Realised Ruby doesn’t have as many problems as .net<br />
Innovation<br />
Outside-in Development<br />
Behaviour Driven Development<br />Cucumber, RSpec and many others<br />
Direct communication with customers<br />
Focus<br />
UI Testing is not hard...<br />Unless you make an effort<br />
What does this do?<br />[Test]public void WatiNSearchText_SearchWatiN_TextDisplayedOnScreen(){    IE ie = new IE("http://l...
WebRat<br />http://www.flickr.com/photos/whatwhat/22624256/<br />
visit<br />click_link<br />fill_in<br />click_button<br />check and uncheck<br />choose<br />select<br />attach_file<br />
visit ‘http://localhost’<br />
Reduce Ceremony<br />
visit ‘http://www.google.com’<br />fill_in :q, :with => ‘asp.net’<br />click_button ‘Google Search’<br />response_body.sho...
Design UI for testability<br />Outside-in development encourages this<br />
DATA!<br />http://www.flickr.com/photos/gagilas/2659695352/<br />
Hard and Important<br />Production data in dev anyone?<br />
Active Record and SQL Server<br />Order.create<br />
class Order < ActiveRecord::Base<br />set_primary_key :OrderID<br />has_many :OrderItem<br />has_one  :OrderStatus,       ...
Test Data Builders<br />ProductCatalogBuilder<br />          .create(10.products)<br />          .for_shop(123)        <br...
Customer.find_or_create(Faker::Internet.user_name)<br />>>> Faker::Internet.user_name<br /><ul><li>"akeem_kris“</li></ul>>...
2 rules of data generation<br />Always insert<br />Always clean up<br />
We still need to write the tests in a successful manner<br />
Cucumber<br />http://www.flickr.com/photos/vizzzual-dot-com/2738586453/<br />
Feature:Search Google<br />In order to find information<br />As a researcher<br />I want to search google<br /> <br /> Sce...
Given /^I visit "([^"]*)"$/ do |website|<br />  visit website<br />end<br /> <br />When /^I enter a search for "([^"]*)"$/...
Thanks to TekPub for Kona sample<br />
Thanks to TekPub for Kona sample<br />
Feature: Display Products<br />  In order to browse the catalog<br />  The customer<br />  Needs to see products<br />Scen...
Given /^the featured product "([^"]*)"$/ do |product_name|<br />Product.createproduct_name, ‘featured’<br />end<br />
When /^I visit the homepage$/ do<br />visit url + '/'<br />end<br />
“Designed” UI for Testability<br />Then /^I should see "([^"]*)" listed under "([^"]*)"$/ do |product, area|<br />  within...
Using a ‘real’ framework<br />Unit testing frameworks for UI testing? Really?!<br />
Before do<br />open_connection<br />empty_database<br />end<br />at_exit do<br />empty_database<br />End<br />def empty_da...
require 'cucumber_screenshot'<br />After do |scenario| <br />  screenshot if scenario.failed?<br />end<br />AfterStep('@sc...
def url<br />  "http://www.website.local"<br />end<br />Webrat.configure do |config|<br />config.mode = :selenium<br />con...
Webrat.configure do |config|<br />config.mode = :selenium<br />config.application_framework = :external<br />config.seleni...
cucumber --profile selenium browser=*firefox<br />cucumber --profile selenium browser=*safari<br />cucumber --profile sele...
env_headless.rb<br />Webrat.configure do |config|<br />  config.mode = :mechanize<br />end<br />cucumber --profile webrat<...
ANTI-PATTERNS<br />http://www.flickr.com/photos/gagilas/2659695352/<br />
Noisy, repeated scenarios<br />Scenario: Blowout Specials<br />    Given "Cascade Fur-lined Hiking Boots" in "Blowout Spec...
Single scenario, multiple examples<br />Scenario Outline: Blowout Specials<br />    Given <product> in "Blowout Specials"<...
Long scenarios<br />When I go to the "Checkout" page<br />And I enter “Mr A. Nonymous ” in “Card Name” textbox<br />And I ...
When I go to the "Checkout" page<br />And I enter a new credit card:<br />       | type        | field                    ...
Clear, re-useable, readable<br />When I go to the "Checkout" page<br />And I enter a valid credit card <br />And I click t...
Implementation details in steps<br />When I go to the "Checkout" page<br />And I enter “Mr A. Nonymous ” in “Card Name” te...
Business focused<br />When I go to the "Checkout"<br />And I enter a valid credit card <br />And pay<br />Then I should be...
Conjunction Steps<br />Scenario: Recommendations should include other similar purchased products<br />  Given customer ord...
Reuse and recombine<br />Scenario: Recommendations should include other similar purchased products<br />  Given the follow...
All steps within a single file<br />
Steps focused around application domain<br />
One environment file<br />
Small, focused environment config<br />
Multiple extension frameworks<br />Test command line applications<br />Test SilverlightFlex<br />Test WPF based desktop ap...
Lonestar<br />
Outside-in Development<br />
RSpec<br />http://www.flickr.com/photos/dodgsun/467076780/<br />
Context  Specification<br />
Shift focus<br />Final Intent<br />
Code Coverage<br />Feature coverage is far more important<br />
Ruby (via IronRuby)<br />p = ProductController.new(nil)<br />C#<br />public class ProductController : Controller<br />{<br...
Define the context<br />C#<br />public ActionResult Index(int? id)<br />{<br />ProductListViewModel model = null;<br />   ...
Create the context<br />$: << ‘../../Kona/bin/’<br />require ‘Kona.dll’<br />Include Kona::Controllers<br />describe Produ...
Create the context<br />require '../spec_helper‘<br />describe ProductController, “Product homepage requested" do<br />  c...
Create the context<br />require '../spec_helper‘<br />describe ProductController, “Product homepage requested" do<br />  c...
Define the spec<br />require '../spec_helper‘<br />describe ProductController, “Product homepage requested" do<br />  cont...
Stubbing via caricature<br />context “with validCategory ID" do<br />   Before(:each)<br />view_model = product_list('Test...
http://www.flickr.com/photos/buro9/298994863/<br />WHY RUBY?<br />
[Subject(typeof(HomeController))]public class when_the_navigation_controller_is_asked_for_the_header :           specifica...
describe HomeController, “When the navigation controller is asked for the header” do   before(:all) do       @identity_tas...
Consider your test suite containing > 500 tests<br />Each test matters.<br />But each problem hurts more<br />
http://www.flickr.com/photos/leon_homan/2856628778/<br />
Focus on finding true value<br />Look at other communities for advice, support and inspiration<br />
@Ben_Hall<br />Ben@BenHall.me.uk<br />Blog.BenHall.me.uk<br />http://lolcatgenerator.com/lolcat/120/<br />
Tests and Databases<br />describe Services::StoreService do<br />  before(:all) do<br />session = NHibernate::create_sessi...
Share specs<br />share_examples_for "unauthorized user" do    it 'sets an-error message' do result.view_model.error_messag...
Dynamically create specs<br />def find_products(category)Product.Find(:all, :conditions=> “category #{category}”)enddescri...
INTEGRATION TESTS<br />http://www.flickr.com/photos/gagilas/2659695352/<br />
Useful Links<br />http://www.github.com/BenHall<br />http://blog.benhall.me.uk<br />http://www.codebetter.com/blogs/benhal...
using Cuke4Nuke.Framework;<br />usingNUnit.Framework;<br />usingWatiN.Core;<br />namespaceGoogle.StepDefinitions<br />{<br...
Given /^(?:I'm on|I go to) the search page$/ do<br />  visit 'http://www.google.com'<br />end<br /> <br />When /^I search ...
Software<br />Recommended:<br />IronRuby<br />Ruby<br />Cucumber<br />Rspec<br />WebRat<br />mechanize<br />Selenium RC<br...
SQL Server and Ruby<br />gem install activerecord-sqlserver-adapter<br />Download dbi-0.2.2.zip <br />Extract dbdADO.rb to...
Upcoming SlideShare
Loading in …5
×

Testing ASP.net and C# using Ruby (NDC2010)

4,460 views

Published on

Session given at NDC2010, Oslo, Norway on Testing ASP.net using Ruby

Published in: Technology
  • Be the first to comment

  • Be the first to like this

Testing ASP.net and C# using Ruby (NDC2010)

  1. 1. Testing C# and ASP.net using Ruby<br />Meerkatalyst<br />@Ben_HallBen@BenHall.me.ukBlog.BenHall.me.ukCodeBetter.com/blogs/benhall<br />
  2. 2. London based C# MVPWeb Developer @ 7digital.com Working on a number of Open Source Projects<br />Co-Author of <br />Testing ASP.net Web Applications<br />http://www.testingaspnet.com<br />
  3. 3. Outside-in development<br />UI level testing<br />Cucumber<br />Object level testing using RSpec<br />
  4. 4. WHY?<br />http://www.flickr.com/photos/atomicpuppy/2132073976/<br />
  5. 5. VALUE<br />
  6. 6. State of .NET today<br />
  7. 7. Realised Ruby doesn’t have as many problems as .net<br />
  8. 8. Innovation<br />
  9. 9. Outside-in Development<br />
  10. 10. Behaviour Driven Development<br />Cucumber, RSpec and many others<br />
  11. 11. Direct communication with customers<br />
  12. 12. Focus<br />
  13. 13.
  14. 14. UI Testing is not hard...<br />Unless you make an effort<br />
  15. 15.
  16. 16. What does this do?<br />[Test]public void WatiNSearchText_SearchWatiN_TextDisplayedOnScreen(){    IE ie = new IE("http://localhost:49992/WatiNSite/");    ie.TextField(Find.ByName(“q")).TypeText("WatiN");    ie.Button(Find.ByValue("Search")).Click();    bool result = ie.Text.Contains("WatiN");    Assert.IsTrue(result); }<br />
  17. 17.
  18. 18. WebRat<br />http://www.flickr.com/photos/whatwhat/22624256/<br />
  19. 19. visit<br />click_link<br />fill_in<br />click_button<br />check and uncheck<br />choose<br />select<br />attach_file<br />
  20. 20. visit ‘http://localhost’<br />
  21. 21. Reduce Ceremony<br />
  22. 22. visit ‘http://www.google.com’<br />fill_in :q, :with => ‘asp.net’<br />click_button ‘Google Search’<br />response_body.should contain(‘Microsoft’)<br />
  23. 23. Design UI for testability<br />Outside-in development encourages this<br />
  24. 24. DATA!<br />http://www.flickr.com/photos/gagilas/2659695352/<br />
  25. 25. Hard and Important<br />Production data in dev anyone?<br />
  26. 26. Active Record and SQL Server<br />Order.create<br />
  27. 27. class Order < ActiveRecord::Base<br />set_primary_key :OrderID<br />has_many :OrderItem<br />has_one :OrderStatus, :primary_key => "OrderStatusID", :foreign_key => "OrderStatusID"<br />belongs_to :Customer, :primary_key => "UserName", :foreign_key => "UserName"<br /> def self.create()<br /> order = Order.new (:modified_on => DateTime.now, <br /> :created_on => DateTime.now, <br /> :order_id => Guid.new.to_s)<br />order.OrderStatusID = OrderStatus.find_or_create<br />order.Customer = Customer.find_or_create(Faker::Internet.user_name)<br />order.save!<br /> return order<br /> end<br />end<br />Legacy Database<br />Looking at auto-generating<br />model for testing<br />
  28. 28. Test Data Builders<br />ProductCatalogBuilder<br /> .create(10.products)<br /> .for_shop(123) <br />Customer<br /> .create(“Bob”)<br /> .owning(10.products)<br /> .with_credit_card(CreditCard.create)<br /> .with_address(Address.create) <br />
  29. 29. Customer.find_or_create(Faker::Internet.user_name)<br />>>> Faker::Internet.user_name<br /><ul><li>"akeem_kris“</li></ul>>>> Faker::Internet.user_name<br /><ul><li>"taylor.hodkiewicz“</li></ul>>>> Faker::Internet.user_name<br />=> "nicolette.nitzsche"<br />>>> Faker::Internet.email<br /><ul><li>dejon@hayeswalsh.uk</li></ul>>>> Faker::Name.name<br />=> "Murray Hermiston"<br />
  30. 30. 2 rules of data generation<br />Always insert<br />Always clean up<br />
  31. 31. We still need to write the tests in a successful manner<br />
  32. 32. Cucumber<br />http://www.flickr.com/photos/vizzzual-dot-com/2738586453/<br />
  33. 33. Feature:Search Google<br />In order to find information<br />As a researcher<br />I want to search google<br /> <br /> Scenario: Searching for a valid term<br />GivenI visit "http://www.google.com/"<br />    WhenI enter a search for "asp.net"<br />    AndI click the "Google Search" button<br />    ThenI should see the first result "The Official Microsoft ASP.NET Site"<br />
  34. 34. Given /^I visit "([^"]*)"$/ do |website|<br />  visit website<br />end<br /> <br />When /^I enter a search for "([^"]*)"$/ do |term|<br />  fill_in :q, :with => term<br />end<br /> <br />When /^I click the "([^"]*)" button$/ do |button|<br />  click_button button<br />end<br /> <br />Then /^I should see the first result "([^"]*)"$/ do |result|<br />  response_body.should contain(result)<br />end<br />
  35. 35. Thanks to TekPub for Kona sample<br />
  36. 36. Thanks to TekPub for Kona sample<br />
  37. 37. Feature: Display Products<br /> In order to browse the catalog<br /> The customer<br /> Needs to see products<br />Scenario: Single featured product on homepage<br /> Given the featured product "Adventure Works 20x30 Binoculars"<br /> When I visit the homepage<br /> Then I should see "Adventure Works 20x30 Binoculars" listed under "Featured"<br />
  38. 38. Given /^the featured product "([^"]*)"$/ do |product_name|<br />Product.createproduct_name, ‘featured’<br />end<br />
  39. 39. When /^I visit the homepage$/ do<br />visit url + '/'<br />end<br />
  40. 40. “Designed” UI for Testability<br />Then /^I should see "([^"]*)" listed under "([^"]*)"$/ do |product, area|<br /> within div_id(area) do |products|<br />products.should contain(product)<br /> end<br />End<br />def div_id(area)<br /> "#" + area.downcase.underscore<br />end<br />
  41. 41. Using a ‘real’ framework<br />Unit testing frameworks for UI testing? Really?!<br />
  42. 42. Before do<br />open_connection<br />empty_database<br />end<br />at_exit do<br />empty_database<br />End<br />def empty_database<br />date_of_last_restore = '01/01/2010'<br />OrderItem.delete_all["DateAdded > ?", date_of_last_restore]<br />Order.delete_all["CreatedOn > ?", date_of_last_restore]<br />end<br />Multiple Hooks<br />
  43. 43. require 'cucumber_screenshot'<br />After do |scenario| <br /> screenshot if scenario.failed?<br />end<br />AfterStep('@screenshot') do |scenario| <br /> screenshot if screenshot_due? <br />end<br />
  44. 44. def url<br /> "http://www.website.local"<br />end<br />Webrat.configure do |config|<br />config.mode = :selenium<br />config.application_framework = :external<br />end<br />env_selenium.rb<br />
  45. 45. Webrat.configure do |config|<br />config.mode = :selenium<br />config.application_framework = :external<br />config.selenium_browser_key = get_browser_key<br />end<br />def get_browser_key()<br /> command = "*firefox"<br /> command = ENV['BROWSER'] if ENV['BROWSER']<br /> return command<br />end<br />env_selenium.rb<br />
  46. 46. cucumber --profile selenium browser=*firefox<br />cucumber --profile selenium browser=*safari<br />cucumber --profile selenium browser=*iexplore<br />cucumber --profile selenium browser=*googlechrome<br />
  47. 47. env_headless.rb<br />Webrat.configure do |config|<br />  config.mode = :mechanize<br />end<br />cucumber --profile webrat<br />
  48. 48. ANTI-PATTERNS<br />http://www.flickr.com/photos/gagilas/2659695352/<br />
  49. 49. Noisy, repeated scenarios<br />Scenario: Blowout Specials<br /> Given "Cascade Fur-lined Hiking Boots" in "Blowout Specials"<br /> When I visit the homepage<br /> Then I should see "Cascade Fur-lined Hiking Boots" listed under "Blowout Specials“<br />Scenario: Blowout Specials<br /> Given " Trailhead Locking Carabiner " in "Blowout Specials"<br /> When I visit the homepage<br /> Then I should see "Trailhead Locking Carabiner " listed under "Blowout Specials"<br />Scenario: Blowout Specials<br /> Given " Climbing Rope with Single Caribiner " in "Blowout Specials"<br /> When I visit the homepage<br /> Then I should see " Climbing Rope with Single Caribiner " listed under "Blowout Specials"<br />
  50. 50. Single scenario, multiple examples<br />Scenario Outline: Blowout Specials<br /> Given <product> in "Blowout Specials"<br /> When I visit the homepage<br /> Then I should see <product> listed under "Blowout Specials"<br /> Examples:<br /> | product |<br /> | "Cascade Fur-lined Hiking Boots" |<br /> | "Trailhead Locking Carabiner" |<br /> | "Climbing Rope with Single Caribiner" |<br />
  51. 51. Long scenarios<br />When I go to the "Checkout" page<br />And I enter “Mr A. Nonymous ” in “Card Name” textbox<br />And I enter “1111111111111111” in “Card Number” textbox<br />And I enter “GU1 2PE” in “Card Post Code” textbox<br />And I select “01” in “Card Start Month” textbox<br />And I select “09” in “Card Start Year” textbox<br />And I select “01” in “Card End Month” textbox<br />And I select “19” in “Card End Year” textbox<br />And I enter “123” in “Card Validation” textbox<br />And I click the "Pay with Card (Add Card)" button<br />Then I should be on the "Download" page<br />
  52. 52. When I go to the "Checkout" page<br />And I enter a new credit card:<br /> | type       | field     | value             |<br /> | textbox   | Card Name         | Mr A. Nonymous   |<br /> | textbox   | Card Number       | 4444333322221111 |<br /> | textbox   | Card Post Code   | GU1 2PE           |<br /> | select     | Card Start Month | 01               |<br /> | select   | Card Start Year  | 09               |<br /> | select     | Card End Month   | 01               |<br /> | select   | Card End Year    | 19               |<br /> | textbox   | Card Validation  | 123              |<br />And I click the "Pay with Card (Add Card)" button<br />Then I should be on the "Download" page<br />
  53. 53. Clear, re-useable, readable<br />When I go to the "Checkout" page<br />And I enter a valid credit card <br />And I click the "Pay with Card (Add Card)" button<br />Then I should be on the "Download" page<br />
  54. 54. Implementation details in steps<br />When I go to the "Checkout" page<br />And I enter “Mr A. Nonymous ” in “Card Name” textbox<br />And I enter “1111111111111111” in “Card Number” textbox<br />And I enter “GU1 2PE” in “Card Post Code” textbox<br />And I select “01” in “Card Start Month” textbox<br />And I select “09” in “Card Start Year” textbox<br />And I select “01” in “Card End Month” textbox<br />And I select “19” in “Card End Year” textbox<br />And I enter “123” in “Card Validation” textbox<br />And I click the "Pay with Card (Add Card)" button<br />Then I should be on the "Download" page<br />
  55. 55. Business focused<br />When I go to the "Checkout"<br />And I enter a valid credit card <br />And pay<br />Then I should be taken to my Downloads<br />
  56. 56. Conjunction Steps<br />Scenario: Recommendations should include other similar purchased products<br /> Given customer order for "Trailhead Locking Carabiner" and "Climbing Rope with Single Caribiner"<br /> And customer order for "Trailhead Locking Carabiner" and "North Star Compass"<br />Given I have shades and a brand new Mustang<br />
  57. 57. Reuse and recombine<br />Scenario: Recommendations should include other similar purchased products<br /> Given the following customers and orders:<br /> |customer | order_list |<br /> |Customer A | Trailhead Locking Carabiner, Climbing Rope with Single Caribiner |<br /> |Customer B | Trailhead Locking Carabiner, North Star Compass |<br />Given I have shades <br />And I have a brand new Mustang<br />
  58. 58. All steps within a single file<br />
  59. 59. Steps focused around application domain<br />
  60. 60. One environment file<br />
  61. 61. Small, focused environment config<br />
  62. 62. Multiple extension frameworks<br />Test command line applications<br />Test SilverlightFlex<br />Test WPF based desktop applications<br />Execute in parallel across multiple machines<br />
  63. 63. Lonestar<br />
  64. 64. Outside-in Development<br />
  65. 65. RSpec<br />http://www.flickr.com/photos/dodgsun/467076780/<br />
  66. 66. Context Specification<br />
  67. 67. Shift focus<br />Final Intent<br />
  68. 68. Code Coverage<br />Feature coverage is far more important<br />
  69. 69.
  70. 70. Ruby (via IronRuby)<br />p = ProductController.new(nil)<br />C#<br />public class ProductController : Controller<br />{<br />IStoreService _service;<br /> public ProductController(IStoreService service)<br /> {<br /> _service = service;<br /> }<br />}<br />Thanks to TekPub for Kona sample<br />
  71. 71. Define the context<br />C#<br />public ActionResult Index(int? id)<br />{<br />ProductListViewModel model = null;<br /> if (id.HasValue)<br /> model = _service.GetHomeModel(id.Value);<br /> else<br /> return RedirectToAction("Index", "Home");<br /> return View(model);<br />}<br />Ruby (via IronRuby)<br />describe ProductController, “Product homepage requested" do<br /> context "when Category ID is empty" do<br /> end<br />end<br />Thanks to TekPub for Kona sample<br />
  72. 72. Create the context<br />$: << ‘../../Kona/bin/’<br />require ‘Kona.dll’<br />Include Kona::Controllers<br />describe ProductController, “Product homepage requested" do<br /> context "when Category ID is empty" do<br /> end<br />end<br />Thanks to TekPub for Kona sample<br />
  73. 73. Create the context<br />require '../spec_helper‘<br />describe ProductController, “Product homepage requested" do<br /> context "when Category ID is empty" do<br /> end<br />end<br />Thanks to TekPub for Kona sample<br />
  74. 74. Create the context<br />require '../spec_helper‘<br />describe ProductController, “Product homepage requested" do<br /> context "when Category ID is empty" do<br /> before(:each) do<br /> controller = ProductController.new nil<br />category_id = nil<br /> @result = controller.Indexcategory_id<br /> end<br /> end<br />end<br />Thanks to TekPub for Kona sample<br />
  75. 75. Define the spec<br />require '../spec_helper‘<br />describe ProductController, “Product homepage requested" do<br /> context “with empty Category ID" do<br /> before(:each) do ... End<br /> it "should redirect to main landing page" do<br /> @result.route_values[‘controller'].should == ‘Index’<br /> @result.route_values[‘home'].should == ‘Home’<br /> end<br />end<br />Thanks to TekPub for Kona sample<br />
  76. 76. Stubbing via caricature<br />context “with validCategory ID" do<br /> Before(:each)<br />view_model = product_list('Test Category')<br /> service = isolate Services::IStoreService<br />service.when_receiving(:get_home_model).with(valid_category_id).return(view_model)<br /> controller = ProductController.new service<br /> @result = controller.Indexvalid_category_id<br /> end<br /> it “returns a view model” do<br /> @result.view_model.should_not == nil<br /> end<br /> it “should return name of selected category” do<br /> @result.view_model.selected_category.name.should == 'Test Category‘<br /> end<br />end<br />Thanks to TekPub for Kona sample<br />
  77. 77. http://www.flickr.com/photos/buro9/298994863/<br />WHY RUBY?<br />
  78. 78. [Subject(typeof(HomeController))]public class when_the_navigation_controller_is_asked_for_the_header : specification_for_navigation_controller{ static ActionResult result; Establish context = () => identity_tasks.Stub(i => i.IsSignedIn()).Return(true); Because of = () => result = subject.Menu(); It should_ask_the_identity_tasks_if_the_user_is_signed_in = () => identity_tasks.AssertWasCalled(x => x.IsSignedIn()); It should_return_the_default_view = () => result.ShouldBeAView().And().ViewName.ShouldBeEmpty(); It should_not_use_a_master_page = () => result.ShouldBeAView().And().MasterName.ShouldBeEmpty(); It should_set_the_view_model_property_to_a_new_menu_view_model = () =>result.Model<MenuViewModel>().ShouldNotBeNull(); It should_set_the_properties_of_the_view_model_correctly = () => result.Model<MenuViewModel>().IsLoggedIn.ShouldBeTrue(); }<br />
  79. 79. describe HomeController, “When the navigation controller is asked for the header” do before(:all) do @identity_tasks.Stub(i => i.IsSignedIn()).Return(true); @result = subject.Menu(); end it “should ask the identity tasks if the user is signed in” do @identity_tasks.did_receive(:is_signed_in) .should be_successful end it “should return the default view” do @result.shouldbe_view @result.view_name.shouldbe_empty end it “should not use a master page” do @result.shouldbe_view @result.master_name.shouldbe_empty end it “should set the view model property to a new menu view model” do @result.shouldbe_view @result.model.should_notbe_nil end it “should set the properties of the view model correctly” do @result.model.is_logged_in.shouldbe_true endend<br />
  80. 80. Consider your test suite containing > 500 tests<br />Each test matters.<br />But each problem hurts more<br />
  81. 81. http://www.flickr.com/photos/leon_homan/2856628778/<br />
  82. 82. Focus on finding true value<br />Look at other communities for advice, support and inspiration<br />
  83. 83. @Ben_Hall<br />Ben@BenHall.me.uk<br />Blog.BenHall.me.uk<br />http://lolcatgenerator.com/lolcat/120/<br />
  84. 84. Tests and Databases<br />describe Services::StoreService do<br /> before(:all) do<br />session = NHibernate::create_session<br />NHibernate::insert_category session<br /> Services::StoreService.new session<br /> @model = service.get_home_model 0<br /> end<br /> it "should return constructed object" do<br /> @model.should_notbe_nil<br /> end<br /> it "should return all categories from database" do<br /> @model.Categories.to_a.Count.should == 1<br /> end<br />end<br />Thanks to TekPub for Kona sample<br />
  85. 85. Share specs<br />share_examples_for "unauthorized user" do it 'sets an-error message' do result.view_model.error_message.should == "Sorry, you need to login to access." end it 'redirects to the login page' do result.view_name.should end end describe CheckoutController do it_should_behave_like “unauthorized user“describe AccountController doit_should_behave_like “unauthorized user“ <br />
  86. 86. Dynamically create specs<br />def find_products(category)Product.Find(:all, :conditions=> “category #{category}”)enddescribe ‘products should be able to be added to basket' do before(:all) do @basket = Basket.new endfind_products(‘Boots’).each do |p| it “can add #{p.product_name} to basket" do @basket.add_item p.id @basket.should contain(p) end endend<br />
  87. 87. INTEGRATION TESTS<br />http://www.flickr.com/photos/gagilas/2659695352/<br />
  88. 88. Useful Links<br />http://www.github.com/BenHall<br />http://blog.benhall.me.uk<br />http://www.codebetter.com/blogs/benhall<br />http://stevehodgkiss.com/2009/11/14/using-activerecord-migrator-standalone-with-sqlite-and-sqlserver-on-windows.html<br />http://www.testingaspnet.com<br />http://msdn.microsoft.com/en-us/magazine/dd434651.aspx<br />http://msdn.microsoft.com/en-us/magazine/dd453038.aspx<br />http://www.cukes.info<br />http://github.com/aslakhellesoy/cucumber<br />http://github.com/brynary/webrat<br />
  89. 89. using Cuke4Nuke.Framework;<br />usingNUnit.Framework;<br />usingWatiN.Core;<br />namespaceGoogle.StepDefinitions<br />{<br />    publicclassSearchSteps<br />    {<br />        Browser _browser;<br /> [Before]<br />        publicvoidSetUp()<br />        {<br />            _browser = new WatiN.Core.IE();<br />        }<br /> [After]<br />        publicvoidTearDown()<br />        {<br />            if (_browser != null)<br />            {<br />                _browser.Dispose();<br />            }<br />        }<br /> [When(@"^(?:I'm on|I go to) the search page$")]<br />        publicvoidGoToSearchPage()<br />        {<br />            _browser.GoTo("http://www.google.com/");<br />        }<br /> [When("^I search for "(.*)"$")]<br />        publicvoidSearchFor(string query)<br />        {<br />            _browser.TextField(Find.ByName("q")).TypeText(query);<br />            _browser.Button(Find.ByName("btnG")).Click();<br />        }<br /> [Then("^I should be on the search page$")]<br />        publicvoidIsOnSearchPage()<br />        {<br />            Assert.That(_browser.Title == "Google");<br />        }<br /> [Then("^I should see "(.*)" in the results$")]<br />        publicvoidResultsContain(stringexpectedResult)<br />        {<br />            Assert.That(_browser.ContainsText(expectedResult));<br />        }<br />    }<br />}<br />
  90. 90. Given /^(?:I'm on|I go to) the search page$/ do<br />  visit 'http://www.google.com'<br />end<br /> <br />When /^I search for "([^"]*)"$/ do|query|<br />  fill_in 'q', :with => query<br />  click_button 'Google Search'<br />end<br /> <br />Then /^I should be on the search page$/ do<br /> dom.search('title').should == "Google"<br />end<br /> <br />Then /^I should see "(.*)" in the results$/ do|text|<br />  response.should contain(text)<br />end<br />
  91. 91. Software<br />Recommended:<br />IronRuby<br />Ruby<br />Cucumber<br />Rspec<br />WebRat<br />mechanize<br />Selenium RC<br />selenium-client<br />Caricature<br />activerecord-sqlserver-adapter<br />Optional:<br />XSP Mono<br />JetBrain’sRubyMine<br />JRuby<br />Capybara<br />Celerity<br />Active record <br />active-record-model-generator<br />Faker<br />Guid<br />
  92. 92. SQL Server and Ruby<br />gem install activerecord-sqlserver-adapter<br />Download dbi-0.2.2.zip <br />Extract dbdADO.rb to rubysite_ruby1.8DBDADO.rb<br />

×