testing asp.net web applications using ruby
DESCRIPTION
Presentation from CodeMash on 14th January 2010. Code-samples can be found at http://github.com/BenHall/KonaTRANSCRIPT
UK based C# MVPWeb Developer @ 7digital.com
Working on a number of Open Source Projects
Co-Author of Testing ASP.net Web Applicationshttp://www.testingaspnet.com
1| Why you should care2| Object Level testing3| UI level testing
What do I mean by Testing ASP.net?
WHY?
http://www.flickr.com/photos/atomicpuppy/2132073976/
It is 2010. Automated testing is no longer controversial.
[TestMethod()] public void DebitTest() {
string customerName = "Mr. Bryan Walton"; double balance = 11.99; BankAccount target = new BankAccount(customerName, balance); double amount = 11.22; target.Debit(amount); Assert.AreEqual((System.Convert.ToDouble(0.77)), target.Balance,
0.05); // 0.05 is tolerance for floating-point comparison //Assert.Inconclusive("A method that does not return a value cannot be
verified."); }
[TestMethod()] public void DebitTest() {
string customerName = "Mr. Bryan Walton"; double balance = 11.99; BankAccount target = new BankAccount(customerName, balance); double amount = 11.22; target.Debit(amount); Assert.AreEqual((System.Convert.ToDouble(0.77)), target.Balance,
0.05); // 0.05 is tolerance for floating-point comparison //Assert.Inconclusive("A method that does not return a value cannot be
verified."); }
[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"));});
}
=()=>
[Description] public class Transferring_between_from_account_and_to_account { static Account fromAccount; static Account toAccount; Context before_each =()=> { fromAccount = new Account {Balance = 1m}; toAccount = new Account {Balance = 1m}; }; When the_transfer_is_made =()=> { fromAccount.Transfer(1m, toAccount); }; It should_debit_the_from_account_by_the_amount_transferred =()=> { fromAccount.Balance.ShouldEqual(0m); }; It should_credit_the_to_account_by_the_amount_transferred =()=> { toAccount.Balance.ShouldEqual(2m); }; }
[Test]public void WatiNSearchText_SearchWatiN_TextDisplayedOnScreen(){ IE ie = new IE("http://localhost:49992/WatiNSite/"); ie.TextField(Find.ByName("searchText")).TypeText("WatiN"); ie.Button(Find.ByValue("Search")).Click(); bool result = ie.Text.Contains("WatiN"); Assert.IsTrue(result); }
You can make C# readable
But it’s hard
http://www.flickr.com/photos/gagilas/2659695352/
RECORD AND PLAYBACK
WHY RUBY?http://www.flickr.com/photos/buro9/298994863/
http://www.flickr.com/photos/mag3737/1914076277/
RSpec
http://www.flickr.com/photos/dodgsun/467076780/
Behaviour Driven Development
describe MyObject doit “should do something cool” doend
end
[TestMethod()] public void DebitTest() {
string customerName = "Mr. Bryan Walton"; double balance = 11.99; BankAccount target = new BankAccount(customerName, balance); double amount = 11.22; target.Debit(amount); Assert.AreEqual((System.Convert.ToDouble(0.77)), target.Balance,
0.05); // 0.05 is tolerance for floating-point comparison //Assert.Inconclusive("A method that does not return a value cannot be
verified."); }
describe BankAccount, “Transactions” do it “can debit pence from an account” do customer_name = "Mr. Bryan Walton"; current_balance = 1.50; account = BankAccount(customer_name, current_balance).new amount_to_debit = 1.25; account.Debit(amount); account.Balance.should == 0.25 endend
describe BankAccount, “Transactions” do before(:each) do current_balance = 1.50; @account = BankAccount.new("Mr. Bryan Walton", current_balance) end
it “can debit pence from an account” do amount_to_debit = 1.25 @account.Debit amount @account.Balance.should == 0.25 endend
Intent
IronRuby
Thank you
DEMORSpec
Outside-in Development
Executable Specification
Cucumber
http://www.flickr.com/photos/vizzzual-dot-com/2738586453/
Feature: Creating a scheduleIn Order to book meetingsThe managerNeeds to be able to view a teams calendar
Scenario: View calendar of another viewGiven Bob has a public calendarWhen the manager views Bob’s calendarThen an empty calendar should be
shown
Given /^([^\"]*) has a public calendar$/ do |user|
end
When /^ the manager views ([^\"]*)’s calendar$/ do |user|
end
Then /^ an empty calendar should be shown $/ do
end
[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 support
Scenario: Find by name Given a set of valid customers When I ask for an existing name Then the correct customer is found and returned
Given /^a set of valid customers$/ do @repo = CreateDummyRepo()end
When /^I ask for an existing name$/ do @customer = @repo.FindByName("Joe Schmoe")end
Then /^the correct customer is found and returned$/ do @customer.Name.should == "Joe Schmoe“end
NOT BEST PRACTICE!!
WebRat
http://www.flickr.com/photos/whatwhat/22624256/
visitclick_link
fill_inclick_button
check and uncheckchooseselect
attach_file
DEMOCucumber, WebRat and Automated UI testing
Where are 7digital?
http://www.flickr.com/photos/michelangelo_mi/1314320102/
One more thing...
Meerkatalyst
http://www.flickr.com/photos/leon_homan/2856628778/
Expressing intent
http://www.flickr.com/photos/philliecasablanca/2456840986/
using Cuke4Nuke.Framework;using NUnit.Framework;using WatiN.Core;namespace Google.StepDefinitions{ public class SearchSteps { Browser _browser; [Before] public void SetUp() { _browser = new WatiN.Core.IE(); } [After] public void TearDown() { if (_browser != null) { _browser.Dispose(); } } [When(@"^(?:I'm on|I go to) the search page$")] public void GoToSearchPage() { _browser.GoTo("http://www.google.com/"); }
[When("^I search for \"(.*)\"$")] public void SearchFor(string query) { _browser.TextField(Find.ByName("q")).TypeText(query); _browser.Button(Find.ByName("btnG")).Click(); } [Then("^I should be on the search page$")] public void IsOnSearchPage() { Assert.That(_browser.Title == "Google"); } [Then("^I should see \"(.*)\" in the results$")] public void ResultsContain(string expectedResult) { Assert.That(_browser.ContainsText(expectedResult)); } }}
Given /^(?:I'm on|I go to) the search page$/ do visit 'http://www.google.com'end When /^I search for "([^\"]*)"$/ do |query| fill_in 'q', :with => query click_button 'Google Search'end Then /^I should be on the search page$/ do dom.search('title').should == "Google"end Then /^I should see \"(.*)\" in the results$/ do |text| response.should contain(text)end
Software
• Recommended:– IronRuby– Ruby– Cucumber– Rspec– WebRat– mechanize– Selenium RC– selenium-client– Caricature– activerecord-sqlserver-
adapter
• Optional:– XSP \ Mono– JetBrain’s RubyMine– JRuby – Capybara– Celerity– Active record – active-record-model-
generator– Faker– Guid
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
1. gem install activerecord-sqlserver-adapter1. Download dbi-0.2.2.zip 2. Extract dbd\ADO.rb to ruby\site_ruby\1.8\DBD\
ADO.rb