testing asp.net - progressive.net
DESCRIPTION
Presentation from Progressive.NET 2012 workshop on Testing ASP.NET web applications.TRANSCRIPT
Me?
Aim to this talk
• Allow you to make an informed, educated decision for your project.
• Know how to quickly get started if you want to• Know alternative approaches if you don’t
Agenda
• UI Browser Automation• Databases and Data Creation• Below the UI Testing• Group therapy
• Pain points, problems, concerns• Best approaches to deal with these
What do I mean by ASP.NET automated acceptance testing?
DEMO OF AUTOMATED TESTINGGoogle Search
var driver = new FirefoxDriver(new FirefoxProfile());driver.Navigate().GoToUrl("http://www.google.co.uk");IWebElement searchbox = driver.FindElement(By.Name("q"));
searchbox.SendKeys("ASP.net");searchbox.SendKeys(Keys.Enter);
var results = driver.FindElement(By.LinkText("Get Started with ASP.NET & ASP.NET MVC : Official Microsoft Site"))
Assert.IsNotNull(results);
Testing pipeline
• Outside in, driven from requirements• TDD to drive inside aspects once UI
automated
• Idealistic.. Doesn’t work. Expensive! (Will define this later)
Real World...
• Bring flexible to change• Regression testing• Pushing forward, rapidly.• Protecting your own arse
http://4.bp.blogspot.com/-v6mzllgkJlM/Tm-yiM4fPEI/AAAAAAAAE34/7-BEetlvyHo/s1600/matrix1.jpg
• Focus on solving a pain you have.• Automated UI Testing is one way, which works,
but it’s not the only way.• Hire a manual tester? Short-term gain, long
term pain.
Pain you may have in the future
Depends on the system / scenario.UI Tests may not be the best way
Spike and Stabilise
Get something out, get feedback, make it right.
It’s not all about code quality!
Should not be the driving force
• Driving quality of your code via UI tests will kill your motivation for the entire project.
• IT HURTS! Been there, done that!
• Focus on what will help you deliver value • Automated tests are expensive.
• How do you define value?• Justify cost by delivering faster? Less bugs?
Company loses less money?
Are “bugs” really bugs if they don’t cost the company money nor annoy users?
WHAT SHOULD YOU TEST?
Scenarios
• UI test should focus on scenarios– Behaviour.– Not actions.
A single test can save your career
Example of a 7digital career saver test
• 1) Can registered users add to basket and checkout?
• 2) Can people register
• Everything else is null and void if those don’t work
80/20 Rule
80% of the value comes from 20% of tests
Design UI for testability
• Good CSS• Good UX• A bad UX will be extremely difficult to test– Code/Test smell!
<div> <div> <p>Some random text</p> <div> <p>Error Message</p> </div> </div></div>
<div> <div> <p>Some random text</p> <div> <p class=“error”>Error Message</p> </div> </div></div>
Much easier to test!
Safety net when refactoring legacy code
• TDD Rules do apply – naming• TDD doesn’t – single assertion
Automated tests against production
• Everything before (TDD, Staging etc) is just a precaution
• If it goes live and it’s dead – it’s pointless.• Focused smoke tests
CODE & TOOLS
Selenium WebDriver
WebDriver is AMAZING!!!
Google, Mozilla, Community
Creating a browser instance
new FirefoxDriver(new FirefoxProfile());
new InternetExplorerDriver();
new ChromeDriver(@".")
chromedriver.exe
Element Locator Strategies
Basically jQuery
driver.FindElement(By.Name("q"));driver.FindElement(By.ClassName(“error-msg"))driver.FindElement(By.Id("album-list"))
Page Interactions
Basically jQuery
.Text
.Click()
.Submit()
.SendKeys() Used for inputing text
Executing JS
public static T ExecuteJS<T>(IWebDriver driver, string js) { IJavaScriptExecutor jsExecute = driver as IJavaScriptExecutor; return (T)jsExecute.ExecuteScript(js); }
$(“.NewHeader.header .earn .dropdown-menu”).show();
Patiently Waiting
var time = new TimeSpan(0, 0, 30);var timeouts = driver.Manage().Timeouts();timeouts.ImplicitlyWait(time);
Patiently Waiting Longer
Use JS code / test hooks in application
window.SWAPP.isReady === true
[TestFixtureSetUp] public void CreateDriver() { _driver = new FirefoxDriver(new FirefoxProfile()); _driver.Manage().Timeouts().ImplicitlyWait(new TimeSpan(0, 0, 30)); }
[SetUp] public void NavigateToWebpage()
{ _driver.Navigate().GoToUrl(WebApp.URL);}
[TestFixtureTearDown]public void FixtureTearDown() { if (_driver != null) _driver.Close();}
But I told everyone to use Ruby
When tests fail have good debugging output
public static string TakeScreenshot(IWebDriver driver){ string file = Path.GetTempFileName() + ".png";
var s = ((ITakesScreenshot)driver);
s.GetScreenshot().SaveAsFile(file, ImageFormat.Png);
return file;}
FUNDAMENTAL PROBLEM
Websites change. Or at least they should.
public class ItemListing{ IWebDriver _driver; public ItemListing(IWebDriver driver) { _driver = driver; }
public void AddToCart() { _driver.FindElement(By.Id("addToCart")).Click(); }}
TEA PLEASE
• MVC Music Store
• http://www.github.com• /BenHall/ProgNet2012
• HelloWorldExamples/• Installers/• MVCMusicStore/
1. Test that item appears on the homepage2. Automate adding an item to the basket
• Pair – Great opportunity.
YOUR TURNhttp://www.github.com/BenHall/ProgNet2012
public int HeaderCount(string cartCount) {var regex = new Regex(@"^Cart \((\d+)\)$");
var match = regex.Match(cartCount); var i = match.Groups[1].Captures[0].Value; return Convert.ToInt32(i); }
ThanksSo what did I do?
SLOW!!
http://www.flickr.com/photos/57734740@N00/184375292/
Internet Explorer
SetUp : System.InvalidOperationException : Unexpected error launching Internet Explorer. Protected Mode must be set to the same value (enabled or disabled) for all zones. (NoSuchDriver)
Complex Xpath / CSS Selectors
table[@id='foo2']/tbody/tr/td/a[contains(text(),'whatever')]
Don’t care about the minor details
• CSS downloading correctly... • Other ways to solve that problem without UI
testing
Form validation
• Could it be a unit test?• Do you need to run it every time if it hasn’t
changed?• It’s a risk – how calculated is it?
DATABASES
http://www.flickr.com/photos/gagilas/2659695352/
CACHING
http://www.flickr.com/photos/gagilas/2659695352/
LEGACY DATABASE SCHEMA
Highly Coupled workflow
• UI Tests become highly coupled to the UI and existing workflow
• UI changes – or at least should. Tests will break, need maintenance etc - BAD
Data Builders
• Be able to test website against a clean empty database
• Build only the data your tests need• REALLY hard with a legacy system– Focus on long term and take small steps to get
there
Example
var u =new UserCreator(“Name”, 13) .WithOptionalValue(“Of Something”) .WithCart(p1, p2, p3) .Build();
UserDeleter.Delete(u)
Simple.Data / Lightweight ORM
db.Users.Insert(Name: "steve", Age: 50)
https://github.com/markrendle/Simple.Data/wiki/Inserting-and-updating-data
Shared database
• Be careful• Tests clash• Use random keys when inserting (not 1-10)• Delete only the data you insert
TEA PLEASE
WHY USE A BROWSER?
MVC Test Automation Framework
Hosts ASP.NET in it’s own AppDomainDidn’t work for me.
CassiniDev
CassiniDevServer server = new CassiniDevServer();server.StartServer(Path.Combine(Environment.Curre
ntDirectory, @"..\..\..\CassiniDevHostingExample"));
string url = server.NormalizeUrl("/");
EasyHTTP and CSQuery
EasyHTTP – Get / Dynamic
var http = new HttpClient(); http.Request.Accept =
HttpContentTypes.ApplicationJson; var response = http.Get("url"); var customer = response.DynamicBody;Console.WriteLine("Name {0}", customer.Name);
EasyHTTP - Post
var customer = new Customer(); customer.Name = "Joe"; customer.Email = "[email protected]"; var http = new HttpClient(); http.Post("url", customer,
HttpContentTypes.ApplicationJson);
CSQuery
var sel = dom.Select("a");
var id = dom[0].id;var href = dom[0]["href"];
dom.Each((i,e) => { if (e.id == "remove-this-id") { e.Parent().RemoveChild(e); }
});
var url = "http://www.amazon.co.uk/s/ref=nb_sb_noss_2?url=search-alias%3Daps&field-keywords=Testing+ASP.net&x=0&y=0"
var httpClient = new HttpClient();var response = httpClient.Get(url);var dom = CsQuery.CQ.Create(response.RawText);StringAssert.Contains("Ben Hall", dom.Find(".ptBrand").Text());
var url = " http://search.twitter.com/search.json?q=prognet&&rpp=5&include_entities=true&result_type=mixed "
var httpClient = new HttpClient(); var response = httpClient.Get(url); Assert.That(response.DynamicBody.results.Length > 0);
1. Host MVCMusicStore using CassiniDev2. Test that item appears on the homepage
• Feel free to download completed solution
YOUR TURN
• Record / Playback• VSTest
• Not every tool helps!
VSTest & Record / Playback
http://www.flickr.com/photos/buro9/298994863/
• Chrome Dev Tool + Console - AMAZING
http://www.flickr.com/photos/leon_homan/2856628778/
Before you write a test think
what would happen if this failed in production for 30 minutes? 4 hours? 3 days?
Would anyone care? Would something else catch it?
Focus on true value
• Mix different approaches – Hezies 57
http://www.gourmetsteaks.com/wp-content/uploads/steak-sauce-heinz-57.jpg
“Running UI Tests”
http://www.flickr.com/photos/philliecasablanca/2456840986/