<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0"><id>tag:blogger.com,1999:blog-10748614</id><updated>2012-01-24T15:29:03.693-05:00</updated><category term="#31DaysOfTesting" /><title type="text">FrazzledDad</title><subtitle type="html">Bleary-eyed ruminations of a work at home Father.</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://frazzleddad.blogspot.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://frazzleddad.blogspot.com/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default?start-index=26&amp;max-results=25&amp;redirect=false" /><author><name>Jim Holmes</name><uri>http://www.blogger.com/profile/05869146736565695900</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://3.bp.blogspot.com/_LOCL59IyTaA/Spykv6_NRpI/AAAAAAAAAAM/S2ZeQAzrL5o/s1600-R/3853180914_14858ae15f_o.jpg" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>1127</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/Frazzleddad" /><feedburner:info uri="frazzleddad" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:emailServiceId>Frazzleddad</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><feedburner:browserFriendly>This is an XML content feed. It is intended to be viewed in a newsreader or syndicated to another site.</feedburner:browserFriendly><entry><id>tag:blogger.com,1999:blog-10748614.post-7113648248621687064</id><published>2012-01-09T10:46:00.001-05:00</published><updated>2012-01-09T10:46:01.015-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="#31DaysOfTesting" /><title type="text">31 Days of Testing—Hiatus This Week</title><content type="html">&lt;p&gt;No #31DaysOfTesting posts this week. I’m &lt;a href="http://codemash.org"&gt;a mite busy with a few other things&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;More testing stuff starting next Monday!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;div class="lqm_ad" lqm_publisher="lqm.frazzleddad.site" lqm_zone="ron" lqm_format="125x125" lqm_tgs="HTML5%2cWeb%2cAgile%2cALM%2cVS2010%2cSharePoint%2cWPF%2cXML%2cSilverlight%2cTesting%2cTFS%2cMVC%2cAJAX%2c"&gt;&lt;/div&gt;
&lt;script type='text/javascript' language='Javascript' src='http://s1.lqcdn.com/m.min.js?dt=2.3.110104.1'&gt;&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10748614-7113648248621687064?l=frazzleddad.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Frazzleddad/~4/-laEQyKOBRA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://frazzleddad.blogspot.com/feeds/7113648248621687064/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=10748614&amp;postID=7113648248621687064" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/7113648248621687064" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/7113648248621687064" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Frazzleddad/~3/-laEQyKOBRA/31-days-of-testinghiatus-this-week.html" title="31 Days of Testing—Hiatus This Week" /><author><name>Jim Holmes</name><uri>http://www.blogger.com/profile/05869146736565695900</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://3.bp.blogspot.com/_LOCL59IyTaA/Spykv6_NRpI/AAAAAAAAAAM/S2ZeQAzrL5o/s1600-R/3853180914_14858ae15f_o.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://frazzleddad.blogspot.com/2012/01/31-days-of-testinghiatus-this-week.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10748614.post-1051610685283059917</id><published>2012-01-06T16:25:00.001-05:00</published><updated>2012-01-06T16:25:33.504-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="#31DaysOfTesting" /><title type="text">31 Days of Testing—Day 25: Performance Testing, Part 2</title><content type="html">&lt;p&gt;&lt;a href="http://frazzleddad.blogspot.com/2011/12/31-days-of-testing-kickoff.html"&gt;Index to all posts in this series is here&lt;/a&gt;!&lt;/p&gt;  &lt;p&gt;My past post laid out some overview and planning issues around performance testing. This post points out what you might be interested in, and lays out some resources I’ve found very useful.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;What do I Monitor?&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;Figuring out which metrics, measurements, and counters to monitor can be extremely daunting—there are &lt;i&gt;hundreds&lt;/i&gt; of individual counters in Performance Monitor alone! In most cases you don’t need anywhere near the entire set of metrics. A few counters will give us all the information you generally need for starting your performance testing work.&lt;/p&gt;  &lt;p&gt;Most performance testing gurus will tell you just a few items will get you started in good shape:&lt;/p&gt;  &lt;ul&gt;   &lt;ul&gt;     &lt;li&gt;Processor utilization percentage &lt;/li&gt;      &lt;li&gt;ASP.NET requests per second &lt;/li&gt;      &lt;li&gt;SQL Server batch requests per second &lt;/li&gt;      &lt;li&gt;Memory usage (total usage on the server, caching usage) &lt;/li&gt;      &lt;li&gt;Disk IO usage &lt;/li&gt;      &lt;li&gt;Network card IO &lt;/li&gt;   &lt;/ul&gt; &lt;/ul&gt;  &lt;p&gt;If you’re doing load testing you’ll likely be interested in errors per second and queued requests. Often times soak or endurance testing will look to counters associated with memory leaks and garbage collection too—these help you understand how your application holds up over a long period of stress. However, those are different scenarios. The few counters mentioned above will get you started in good shape.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Where Can I Learn More?&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;Microsoft’s “&lt;a href="http://perftestingguide.codeplex.com/"&gt;Performance Testing Guide for Web Applications&lt;/a&gt;” is somewhat older, but remains a tremendous resource for learning about performance testing. It’s an extensive, exhaustive discussion of everything around planning, setting up for, executing, and analyzing results from your performance testing. The guide is freely available on Codeplex.&lt;/p&gt;  &lt;p&gt;Steve Smith of NimblePros in Kent, Ohio, has been extremely influential in my learning about performance testing. Steve’s been appointed by Microsoft as a Regional Director because of his technical expertise in many areas. &lt;a href="http://stevesmithblog.com/"&gt;He blogs extensively&lt;/a&gt; on many software topics and has great practical examples for performance testing. He also has &lt;a href="http://www.pluralsight-training.net/microsoft/courses/tableofcontents?courseName=web-perf"&gt;an online commercial course offered through Pluralsight&lt;/a&gt; that’s well worth checking in to.&lt;/p&gt;  &lt;p&gt;The website &lt;a href="http://performance-testing.org/"&gt;Performance Testing&lt;/a&gt; has a great number of references to performance testing information across the Web. The site lists blogs, articles, training material, and other highly helpful information.&lt;/p&gt;  &lt;p&gt;I’ve recently come across two folks on Twitter who I’ve found a wealth of information from:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;a href="http://BenSimo.QualityFrog.com/"&gt;Ben Simo&lt;/a&gt;, aka &lt;a href="http://twitter.com/qualityfrog"&gt;Quality Frog&lt;/a&gt;, writes and Tweets extensively about testing, but also talks specifically about performance issues regularly.&lt;/li&gt;    &lt;li&gt;&lt;a href="http://twitter.com/sbarber"&gt;Scott Barber&lt;/a&gt; has &lt;a href="http://scott-barber.blogspot.com/"&gt;an amazing blog&lt;/a&gt; with scads of information on it, plus he Tweets amazingly good reads on a regular basis.&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;One of the things Scott Tweeted recently was &lt;a href="http://loadstorm.com/category/web-performance-optimization"&gt;this nice series on web performance optimization&lt;/a&gt;. There’s some tremendously valuable information in its articles.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Go! Get Started!&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;Spend some time planning out your performance testing effort. Make sure you work &lt;b&gt;&lt;i&gt;HARD&lt;/i&gt;&lt;/b&gt; to only change one variable at a time. Don’t get flooded with information; more often less information can be more helpful at the start.&lt;/p&gt;  &lt;p&gt;Performance testing is a tremendous asset to your projects, and it can also be an extremely fun, interesting, and rewarding domain to work in.&lt;/p&gt;  &lt;p&gt;Go! Get started!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;div class="lqm_ad" lqm_publisher="lqm.frazzleddad.site" lqm_zone="ron" lqm_format="125x125" lqm_tgs="HTML5%2cWeb%2cAgile%2cALM%2cVS2010%2cSharePoint%2cWPF%2cXML%2cSilverlight%2cTesting%2cTFS%2cMVC%2cAJAX%2c"&gt;&lt;/div&gt;
&lt;script type='text/javascript' language='Javascript' src='http://s1.lqcdn.com/m.min.js?dt=2.3.110104.1'&gt;&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10748614-1051610685283059917?l=frazzleddad.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Frazzleddad/~4/S6D2K0WNYBc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://frazzleddad.blogspot.com/feeds/1051610685283059917/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=10748614&amp;postID=1051610685283059917" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/1051610685283059917" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/1051610685283059917" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Frazzleddad/~3/S6D2K0WNYBc/31-days-of-testingday-25-performance.html" title="31 Days of Testing—Day 25: Performance Testing, Part 2" /><author><name>Jim Holmes</name><uri>http://www.blogger.com/profile/05869146736565695900</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://3.bp.blogspot.com/_LOCL59IyTaA/Spykv6_NRpI/AAAAAAAAAAM/S2ZeQAzrL5o/s1600-R/3853180914_14858ae15f_o.jpg" /></author><thr:total>1</thr:total><feedburner:origLink>http://frazzleddad.blogspot.com/2012/01/31-days-of-testingday-25-performance.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10748614.post-2394895845174315090</id><published>2012-01-05T22:48:00.001-05:00</published><updated>2012-01-06T01:21:47.854-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="#31DaysOfTesting" /><title type="text">31 Days of Testing—Day 24: Getting Serious About Performance</title><content type="html">&lt;p&gt;&lt;strong&gt;Updated:&lt;/strong&gt; Fixed wrong day # in title. Duh.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://frazzleddad.blogspot.com/2011/12/31-days-of-testing-kickoff.html"&gt;Index to all posts in this series is here&lt;/a&gt;!&lt;/p&gt;  &lt;p&gt;In this post I’d like to cover something that too often gets ignored: performance testing. I thought I’d take some time to lay down some of my opinions and experiences around performance testing in general.&lt;/p&gt;  &lt;p&gt;The phrase “performance testing” can mean a great many things to different people in different scenarios, so covering a few of the different types of tests may be helpful.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Performance Testing&lt;/b&gt; is generally an umbrella term covering a number of different, more complex test environments. I’ve also used the term to describe a very simple set of scenarios meant to provide a baseline for performance regressions.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Load Testing &lt;/b&gt;generally uses a number of concurrent users to see how the system performs and find bottlenecks&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Stress Testing&lt;/b&gt; throws a huge number of concurrent users against your system in order to find “tipping points” – the point where your system rolls over and crashes due to a huge amount of traffic&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Endurance/Soak Testing&lt;/b&gt; checks your system’s behavior over long periods to look for things like degradation, memory leaks, etc.&lt;/p&gt;  &lt;p&gt;Wikipedia’s &lt;a href="http://en.wikipedia.org/wiki/Software_performance_testing"&gt;Software Performance Testing page&lt;/a&gt; has some very readable information on the categories.&lt;/p&gt;  &lt;p&gt;You can also look at performance testing as a slice of your system’s performance. You can use a specific scenario to dive down in to specific areas of your system, environment, or hardware.&lt;/p&gt;  &lt;p&gt;Load, stress, and endurance testing are all that, but turned up to 11. (A reference to Spinal Tap for those who’ve not seen the movie.)&lt;/p&gt;  &lt;p&gt;With that in mind, I generally think of performance testing in two categories: testing to ensure the system meets specified performance requirements, and testing to ensure performance regressions haven’t crept into your system. Those two may sound the same, but they’re not.&lt;/p&gt;  &lt;p&gt;Performance testing to meet requirements means you’ll need lots of detail around expected hardware configurations, baseline datasets, network configurations, and user load. You’ll also need to ensure you’re getting the hardware and environment to support those requirements. There’s absolutely no getting around the need for infrastructure if your customers/stakeholders are serious about specific performance metrics!&lt;/p&gt;  &lt;p&gt;Performance testing to guard against regressions can be a bit more relaxed. I’ve had great successes running a set of baseline tests in a rather skimpy environment, then simply re-running those tests on a regular basis in the exact same environment. You’re not concerned with specific metric datapoints in this situation – you’re concerned about &lt;i&gt;trends&lt;/i&gt;. If your test suite shows a sudden degradation in memory usage or IO contention then you know something’s changed in your codebase. This works fine as long as you keep the environment exactly the same from run to run—which is a perfect segue into my next point.&lt;/p&gt;  &lt;p&gt;Regardless of whether you’re validating performance requirements, guarding against regressions, or flooding your system in a load test designed to make your database server weep, you absolutely must approach your testing with a logical, empirical mindset. You’ll need to spend some time considering your environment, hardware, baseline datasets, and how to configure your system itself. &lt;/p&gt;  &lt;p&gt;Performance testing isn’t something you can slap together and figure out as you go. While you certainly can (and likely will!) adjust your approach as you move through your project, you do indeed need to sit down and get some specifics laid out around your testing effort before you begin working.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;First and foremost: set expectations and goals. &lt;/b&gt;&lt;/p&gt;  &lt;p&gt;Ensure everyone’s clear on &lt;i&gt;why&lt;/i&gt; you’re undertaking the performance testing project. If you are looking to meet specific metrics for delivering your system then you’ll need to be extremely detailed and methodical in your initial coordination. Does your system have specific metrics you’re looking to meet? If so, are those metrics clearly understood – and more importantly &lt;b&gt;&lt;i&gt;reasonable&lt;/i&gt;&lt;/b&gt;? &lt;/p&gt;  &lt;p&gt;Keep in mind that your customer/stakeholder may be giving you metrics you think are unreasonable, but it may fit business needs of their which you’re unaware of. You have to put in the extra effort to ensure you understand those higher-level needs. &lt;/p&gt;  &lt;p&gt;Your customer may also be giving you vague requirements simply due to their lack of experience or understanding. “We want the page to load fast!” is an oft-heard phrase from stakeholders, but what do they really mean? &lt;/p&gt;  &lt;p&gt;&lt;b&gt;Define your environment&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;If those same metrics are critical to your delivery, then they will also need to be defined based on a number of specific environment criteria such as exact hardware setups, network topologies, etc. These environments should be the same exact environment you recommend to your customers. If you’re telling your system’s users they need a database server with four eight-core CPUs, 32 GB of RAM, and a specific RAID configuration for the storage, then you should look to get that same hardware in place for your testing.&lt;/p&gt;  &lt;p&gt;(A tangential topic: it’s happened more than once that a server and environment acquired for performance testing somehow gets borrowed or time-shared out to other uses. Timesharing your performance environment &lt;b&gt;&lt;i&gt;can&lt;/i&gt;&lt;/b&gt; be a highly effective use of expensive resources, but you’ll need to ensure nothing, &lt;b&gt;&lt;i&gt;absolutely nothing&lt;/i&gt;&lt;/b&gt;, is being utilized on that server once your performance runs start – you have to have dedicated access to the server to ensure your metrics aren’t being skewed by other processes.)&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Agree on baseline data&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;Something that’s commonly overlooked is the impact of your system’s baseline dataset on your performance tests. You likely won’t get anything near an accurate assessment of a reporting or data analysis system if you’ve only got ten or thirty rows of data in your database. &lt;/p&gt;  &lt;p&gt;Creating baseline data can be an extremely complex task if your system is sensitive to the “shape” of the data. For example, a reporting system will need its baseline data laid out across different users, different content types, different date patterns.&lt;/p&gt;  &lt;p&gt;Often the easiest route to handle this is to find a live dataset somewhere and use that. I’ve had great success coordinating with users of systems to get their datasets for our testing. You may need to scrub the dataset to clear out any potential sensitive information such as e-mail addresses, usernames, passwords, etc.&lt;/p&gt;  &lt;p&gt;If using a live dataset isn’t an option, you’ll need to figure out tooling to generate that dataset for you.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Determine your usage scenarios&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;Talk through the scenarios you want to measure. Make sure you’re looking to measure the most critical scenarios. Your scenarios might be UI driven, or they could be API driven. Steve Smith has a terrific walkthrough of a real world scenario that gives a great example of this.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Set up your tooling&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;Once you’ve got a handle on the things I’ve discussed above, look to get your tooling in place. Performance testing utterly relies on an exact, repeatable process. You’ll need to do a large amount of work getting everything set up and configured each time you do a perf run. Avoid doing this work manually; instead, look to tooling to do this for you. You shouldn’t rely on doing the setup manually for two reasons. One: automating setup ensures you’ll cut out any chance of human error. Two: it’s really boring.&lt;/p&gt;  &lt;p&gt;Build servers like Hudson, Team City, or TFS can interface with your source control and get your environment properly configured each time you need to run a perf pass. Scripting tools like PowerShell, Ruby, or even good old command files can handle tasks like setting up databases and websites for you.&lt;/p&gt;  &lt;p&gt;You’ll also need to ensure you’re setting up your tooling to handle reporting of your perf test runs. Make sure you’re keeping all the output data from your runs stored so you can keep track of your trends and history.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Change only one variable at a time. Compare apples to apples!&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;It’s critical you take extraordinary care with the execution of your performance testing scenarios! You need to ensure you’re only changing one variable at a time during your test passes, or you won’t understand the impact of your changes.&lt;/p&gt;  &lt;p&gt;For example, don’t change your database server’s disk configuration at the same time you push a new build to your test environment. You won’t know if performance changes were due to the disk change or code changes in the build itself.&lt;/p&gt;  &lt;p&gt;In a similar vein, ensure no other folks are interacting with the server during your performance run. I alluded to shared servers earlier; it’s great to share expensive servers for multiple uses, but you can’t afford for someone to be running processes of any shape or form while you’re doing your performance passes.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Profiling: Taking the simple route for great information&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;All the work above can seem extraordinarily intimidating. There’s a lot to consider and take in to account when moving through some of the more heavyweight scenarios I laid out in my introductory post.&lt;/p&gt;  &lt;p&gt;That said, you can look to simpler performance profiling as a means to get great insight in to how your application is behaving. Profiling enables you to use one scenario, or a very small set, and see in a slice how your application’s behaving. Depending on the tooling you can see results of performance back to the browser, dive in to performance metrics on the server (think CPU or disk usage, for example). You may even be able to dig down in to the application’s codebase to see detailed metrics around specific components of the system. &lt;/p&gt;  &lt;p&gt;Profiling is a great way to start building a history of your application’s performance. You can run regular profiling tests and compare the historical performance to ensure you’re not ending up with performance regressions.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Start small, start smart&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;As you’ve seen in this post, performance testing can be particularly complex when you’re looking to ensure high performance, reliability, and scalability. You need to approach the effort with good planning, and you need to ensure you’re not changing variables as you move through the testing.&lt;/p&gt;  &lt;p&gt;Make sure your performance efforts get you the information you need. Start with small environments and scenarios, ensure you’ve clearly laid out your goals and expectations, and keep a careful eye out as you’re running your tests.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;div class="lqm_ad" lqm_publisher="lqm.frazzleddad.site" lqm_zone="ron" lqm_format="125x125" lqm_tgs="HTML5%2cWeb%2cAgile%2cALM%2cVS2010%2cSharePoint%2cWPF%2cXML%2cSilverlight%2cTesting%2cTFS%2cMVC%2cAJAX%2c"&gt;&lt;/div&gt;
&lt;script type='text/javascript' language='Javascript' src='http://s1.lqcdn.com/m.min.js?dt=2.3.110104.1'&gt;&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10748614-2394895845174315090?l=frazzleddad.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Frazzleddad/~4/EVDpD_r35fc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://frazzleddad.blogspot.com/feeds/2394895845174315090/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=10748614&amp;postID=2394895845174315090" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/2394895845174315090" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/2394895845174315090" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Frazzleddad/~3/EVDpD_r35fc/31-days-of-testingday-22-getting.html" title="31 Days of Testing—Day 24: Getting Serious About Performance" /><author><name>Jim Holmes</name><uri>http://www.blogger.com/profile/05869146736565695900</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://3.bp.blogspot.com/_LOCL59IyTaA/Spykv6_NRpI/AAAAAAAAAAM/S2ZeQAzrL5o/s1600-R/3853180914_14858ae15f_o.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://frazzleddad.blogspot.com/2012/01/31-days-of-testingday-22-getting.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10748614.post-2279784650389916361</id><published>2012-01-04T13:05:00.001-05:00</published><updated>2012-01-04T15:19:41.941-05:00</updated><title type="text">31 Days of Testing—Day 23: Acceptance Tests &amp; Criteria in the Real World</title><content type="html">&lt;p&gt;&lt;strong&gt;UPDATED:&lt;/strong&gt; I goofed and Andy caught it, thankfully. They’re using &lt;em&gt;Watir&lt;/em&gt;, not &lt;em&gt;WatiN&lt;/em&gt; in their work. I knew that and still fat-fingered the post. Fixed!&lt;/p&gt;  &lt;p&gt;&lt;a href="http://frazzleddad.blogspot.com/2011/12/31-days-of-testing-kickoff.html"&gt;Index to all posts in this series is here&lt;/a&gt;!&lt;/p&gt;  &lt;p&gt;Today’s post is by &lt;a href="http://twitter.com/#!/andrewvida"&gt;Andrew Vida&lt;/a&gt;, another smart pal in the Heartland region. I’ve chatted with Andy a number of times at various conferences, and I’ve enjoyed hearing about the work he and &lt;a href="http://twitter.com/#!/bramhaghosh"&gt;Bramha Ghosh&lt;/a&gt; do at Grange Insurance in Columbus, OH. &lt;/p&gt;  &lt;p&gt;We three have spent a pretty good amount of time moaning about our shared pain in getting great, reliable, valuable functional test suites in place. Andy and Bramha are working in Ruby and Watir, but their issues are my issues are the same issues seen in any technology: dealing with data, environments, timing, and of course the inevitable hardest part: “soft” problems in ensuring clarity of communication between folks on the project team.&lt;/p&gt;  &lt;p&gt;Andy offered up the following article for my series based on the work they’ve done trying to get a smooth flow around well-defined acceptance criteria. This is a perfect follow on to &lt;a href="http://frazzleddad.blogspot.com/2012/01/31-days-of-testingday-22-why.html"&gt;yesterday’s post by Jon Kruger&lt;/a&gt;!&lt;/p&gt;  &lt;hr /&gt;  &lt;h3&gt;Using Acceptance Tests to Define Done&lt;/h3&gt;  &lt;p&gt;Have you ever been on a team and was asked &amp;quot;What is the definition of done?&amp;quot;&amp;#160; You respond by saying, &amp;quot;When all of your automated tests pass, and there are no bugs, then you have satisfied the acceptance criteria.&amp;#160; Done!&amp;quot;&amp;#160; Which then is responded to by &amp;quot;Well, how do I define the acceptance criteria?&amp;quot; Good question!&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Understanding the Feature&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;First things first - you have to understand what feature you'll be building. Building the right product and building the product right takes communication and collaboration between your product owner and your team.&lt;/p&gt;  &lt;p&gt;The reason for all of the collaboration is that we're trying to build a shared understanding of what needs to be done and also produce examples that are easy to maintain. There are many ways to work collaboratively and ultimately, you have to decide what works best for your team. &lt;/p&gt;  &lt;p&gt;The team I'm currently on has found that smaller workshops work best for us. Those workshops, otherwise known as &amp;quot;Three Amigos&amp;quot;, include a business analyst, a developer and a tester who share a similar understanding of the domain.&lt;/p&gt;  &lt;p&gt;Lets hypothetically say you're discussing a shopping cart feature for your site.&amp;#160; Start by defining the goals of this feature. By starting with the goal, you'll let everyone know why they're spending their time on implementing the feature.&amp;#160; If you can't come up with a good reason why, then maybe the product owner is wasting everyone's time.&amp;#160; &lt;/p&gt;  &lt;p&gt;We've used the Feature Injection template from Chris Matts and Liz Keogh to help us successfully describe why:&lt;/p&gt;  &lt;p&gt;&lt;b&gt;As a&lt;/b&gt; &amp;lt;type of stakeholder&amp;gt;&lt;/p&gt;  &lt;p&gt;&lt;b&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; I want&lt;/b&gt; &amp;lt;a feature&amp;gt;&lt;/p&gt;  &lt;p&gt;&lt;b&gt;So that&lt;/b&gt; &amp;lt;I can meet some goal&amp;gt;&lt;/p&gt;  &lt;p&gt;Here's our feature description:&lt;/p&gt;  &lt;div id="codeSnippetWrapper"&gt;   &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;     &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;As an online shopper&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;I want to add items to my shopping cart&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;So that I can purchase them&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;b&gt;Determining Acceptance Criteria&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;Next, your team needs to determine what the system needs to do to meet those goals-the Acceptance Criteria. &lt;/p&gt;

&lt;p&gt;In your Three Amigos meeting, be sure to ask questions to clear up assumptions, such as &amp;quot;Are there any products that cannot be purchased online?&amp;quot; or &amp;quot;Does the shopper need to be authenticated to purchase?” &lt;/p&gt;

&lt;p&gt;Remember, the scope of feature should be high level as we only want to identify &lt;b&gt;&lt;i&gt;what &lt;/i&gt;&lt;/b&gt;the application needs to do and not &lt;b&gt;&lt;i&gt;how&lt;/i&gt;&lt;/b&gt; it's implemented. Leave that part to the people that know how to design software. It was determined by the team that the following are in scope:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Only authenticated shoppers can add items to the shopping cart. &lt;/li&gt;

  &lt;li&gt;Cannot add refrigerators to shopping cart. &lt;/li&gt;

  &lt;li&gt;Only 25 items can be added. &lt;/li&gt;

  &lt;li&gt;Shopper can remove items from shopping cart. &lt;/li&gt;

  &lt;li&gt;Shopper can change quantity of items after adding it to the cart. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hey, now we have some acceptance criteria!&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Acceptance Criteria lead to Acceptance Tests&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;We've used communication and collaboration to determine why a feature is necessary and what the system needs to do to at a high level, so now we can come up with some examples to test our acceptance criteria.&lt;/p&gt;

&lt;p&gt;To do this, we'll write some Cucumber scenarios.&amp;#160; We've chosen Cucumber for all of the reasons mentioned in &lt;a href="http://frazzleddad.blogspot.com/2011/12/31-days-of-testingday-15-cucumber-is.html"&gt;Tim Wingfield's post on Day 15&lt;/a&gt;. If you haven't read it, go back and check it out.&amp;#160; It's an excellent post on the benefits of employing Cucumber.&lt;/p&gt;

&lt;p&gt;Here are a few scenarios that were created:&lt;/p&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;Given the shopper is a guest&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;When they try to add an item to their shopping cart&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;Then they will receive the error &amp;quot;Only authenticated shoppers can add items to their shopping cart&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;&amp;quot;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;Given an authenticated shopper&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;When they click the &amp;quot;Add Item to Cart&amp;quot; button&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;Then they will have an item in their shopping cart&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;Given an authenticated shopper with an item in their shopping cart&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;When they click the &amp;quot;Remove Item&amp;quot; button&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;Then that item is no longer in their shopping cart&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;These are only a few of the examples that were developed as part of the Three Amigos meeting.&amp;#160; On our team, the output of the Three Amigos is a Cucumber feature file.&amp;#160; We now have a shared understanding and a definition of done!&amp;#160; We can pass on our failing acceptance tests to the Dev team to begin their work.&amp;#160; They will begin by creating failing unit tests and writing enough code to make them pass.&amp;#160; Once they are passing, they can then run the acceptance tests.&amp;#160; Once those are passing then the feature is complete.&amp;#160; We're done!&amp;#160; Those acceptance tests will be added to the regression suite to be ran anytime to ensure that the feature remains done.&amp;#160; Now the feature can be demonstrated to the product owner at the next review.&amp;#160; &lt;/p&gt;

&lt;p&gt;What we've just done is taken a trip around the Acceptance Test Driven Development cycle.&amp;#160; Just remember, it's not about the tools or the technology, but rather the communication and collaboration.&amp;#160; Our ultimate goal is to deliver high quality software that functions as the product owner intended.&amp;#160; By including QA in the entire process, we can eliminate many of the problems that plague us earlier so that they don't make it to production.&amp;#160; Quality is not just a QA function, it's a team function.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;div class="lqm_ad" lqm_publisher="lqm.frazzleddad.site" lqm_zone="ron" lqm_format="125x125" lqm_tgs="HTML5%2cWeb%2cAgile%2cALM%2cVS2010%2cSharePoint%2cWPF%2cXML%2cSilverlight%2cTesting%2cTFS%2cMVC%2cAJAX%2c"&gt;&lt;/div&gt;
&lt;script type='text/javascript' language='Javascript' src='http://s1.lqcdn.com/m.min.js?dt=2.3.110104.1'&gt;&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10748614-2279784650389916361?l=frazzleddad.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Frazzleddad/~4/qwnMjTCR6As" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://frazzleddad.blogspot.com/feeds/2279784650389916361/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=10748614&amp;postID=2279784650389916361" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/2279784650389916361" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/2279784650389916361" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Frazzleddad/~3/qwnMjTCR6As/31-days-of-testingday-23-acceptance.html" title="31 Days of Testing—Day 23: Acceptance Tests &amp;amp; Criteria in the Real World" /><author><name>Jim Holmes</name><uri>http://www.blogger.com/profile/05869146736565695900</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://3.bp.blogspot.com/_LOCL59IyTaA/Spykv6_NRpI/AAAAAAAAAAM/S2ZeQAzrL5o/s1600-R/3853180914_14858ae15f_o.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://frazzleddad.blogspot.com/2012/01/31-days-of-testingday-23-acceptance.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10748614.post-6303121038657628668</id><published>2012-01-03T12:19:00.001-05:00</published><updated>2012-01-03T12:49:58.847-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="#31DaysOfTesting" /><title type="text">31 Days of Testing—Day 22: Why Collaboration Matters (A Real World Example)</title><content type="html">&lt;p&gt;&lt;strong&gt;Updated:&lt;/strong&gt; &lt;a href="http://frazzleddad.blogspot.com/2011/12/31-days-of-testing-kickoff.html"&gt;Index to all posts in this series is here&lt;/a&gt;!&lt;/p&gt;  &lt;p&gt;Today’s post is reposted from &lt;a href="http://jonkruger.com/"&gt;Jon Kruger’s blog&lt;/a&gt;. Jon is a tremendously smart, passionate indie working out of Columbus, Ohio. I was lucky enough to work with Jon some years back, and I’ve always had great regard for his views and thoughts.&lt;/p&gt;  &lt;p&gt;Jon’s post today really hit home for me because it’s all about communication and collaboration &lt;strong&gt;&lt;em&gt;early &lt;/em&gt;&lt;/strong&gt;in the cycle. I can’t jump up and down enough about how critical this is--and Jon’s post is a real-world example of why it’s so important.&lt;/p&gt;  &lt;p&gt;I read Jon’s blog this morning and immediately pinged him on IM to see if he’d let me drop his article in to my #31DaysOfTesting series. Thankfully he agreed!&lt;/p&gt;  &lt;p&gt;Follow &lt;a href="http://twitter.com/jonkruger"&gt;Jon on Twitter&lt;/a&gt;, and definitely bookmark or subscribe to his blog. Lots of great stuff in both spots!&lt;/p&gt;  &lt;hr /&gt;  &lt;h3&gt;Just Another Run of the Mill Wednesday&lt;/h3&gt;  &lt;p&gt;On my current project, we release every 2 weeks. We do the push to production on Saturday, so we set a deadline of Wednesday night for everything to be developed and tested so that we can have two days for demos and UAT. &lt;/p&gt;  &lt;p&gt;I remember a certain Wednesday a couple of months ago where things were chaotic to say the least. We looked at the board on Wednesday in the early afternoon and there were 20 items where testing was not complete. We were running around trying to make sure that everything got tested. The entire development team was helping out with testing. Many people stayed past dinnertime to get everything done.&lt;/p&gt;  &lt;p&gt;This past Wednesday was much different. Everyone was very relaxed. There was only one item on the board that was still being tested. We were all working on getting stuff ready for the next iteration. And oh by the way, one of the QA testers was out on vacation and another one had been moved to another project.&lt;/p&gt;  &lt;p&gt;I immediately thought back to that chaotic Wednesday a few months ago and thought about everything that has happened since then. We certainly had come a long way to get to the point where things were much more relaxed. So what happened?&lt;/p&gt;  &lt;h5&gt;The Three Amigos&lt;/h5&gt;  &lt;p&gt;Before development can start on a feature, we have a “three amigos” meeting where developers, business analysts, and QA people get together and decide on the acceptance criteria for the feature. This helps us all get on the same page and make sure that we know what we’re building. It also gets the QA team involved very early in the process, so when it comes time for them to manually test the feature, they already know it inside out.&lt;/p&gt;  &lt;h5&gt;Automating acceptance tests&lt;/h5&gt;  &lt;p&gt;The outcome of the three amigos meeting is acceptance criteria. Developers take these and automate them whenever possible (we use a combination of unit tests and acceptance tests using SpecFlow). The development workflow now looks something like this:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Work with the QA team to write out the acceptance tests in SpecFlow &lt;/li&gt;    &lt;li&gt;Develop all of the components needed to make the feature work, writing unit tests along the way &lt;/li&gt;    &lt;li&gt;Try and get the acceptance tests to pass, fixing any problems we find along the way &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;When I’m working on the “try and get the acceptance tests to pass” phase, I’m going to find pretty much all of the coding errors that we made during development. The development ticket is still marked as “In Development” at this point, which is very important. We all take quality seriously, both QA testers and developers. I’m not going to hand it over to be tested by the QA team until I can get all of the acceptance tests to pass. &lt;/p&gt;  &lt;h5&gt;Almost no bugs&lt;/h5&gt;  &lt;p&gt;Because we knew what we were building up front and because we automated pretty much all of the testing, the QA team is finding very few bugs in the new features that we’re developing. One of our testers brought this up in our retrospective this past week and mentioned how they got everything tested so much faster because they weren’t finding bugs, writing up bugs, waiting for bugs to be fixed, and retesting bug fixes. &lt;/p&gt;  &lt;p&gt;We had looked at the schedule earlier in the week and we had thought that developers might have to help out with testing because one of the testers was on vacation and they had some items to test that we thought would take a long time. In the end, no developers had to help with testing and testing got done ahead of schedule! &lt;/p&gt;  &lt;p&gt;Everyone talks about how bugs are a waste of time, how they slow you down, etc., but it was really cool to see it play out. Yeah, getting those acceptance tests to pass takes a little extra time, but now I can hand a completed feature over to QA and have a good chance of not having any bugs. We had two developers working for a week on the feature that we completed, and we did it with no bugs. Not only that, we have automated acceptance tests that will do our regression testing for us. &lt;/p&gt;  &lt;h5&gt;Recap&lt;/h5&gt;  &lt;p&gt;A lot of the changes that we’ve made seem to be relatively minor, but they’ve produced huge dividends. Much of it comes down to discipline, not cutting corners, communicating effectively, and taking pride in your work. I’m really excited about what we’re going to be able to do from here and I expect to have even more stories to tell in the near future. &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;div class="lqm_ad" lqm_publisher="lqm.frazzleddad.site" lqm_zone="ron" lqm_format="125x125" lqm_tgs="HTML5%2cWeb%2cAgile%2cALM%2cVS2010%2cSharePoint%2cWPF%2cXML%2cSilverlight%2cTesting%2cTFS%2cMVC%2cAJAX%2c"&gt;&lt;/div&gt;
&lt;script type='text/javascript' language='Javascript' src='http://s1.lqcdn.com/m.min.js?dt=2.3.110104.1'&gt;&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10748614-6303121038657628668?l=frazzleddad.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Frazzleddad/~4/-rU522kGUb4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://frazzleddad.blogspot.com/feeds/6303121038657628668/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=10748614&amp;postID=6303121038657628668" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/6303121038657628668" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/6303121038657628668" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Frazzleddad/~3/-rU522kGUb4/31-days-of-testingday-22-why.html" title="31 Days of Testing—Day 22: Why Collaboration Matters (A Real World Example)" /><author><name>Jim Holmes</name><uri>http://www.blogger.com/profile/05869146736565695900</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://3.bp.blogspot.com/_LOCL59IyTaA/Spykv6_NRpI/AAAAAAAAAAM/S2ZeQAzrL5o/s1600-R/3853180914_14858ae15f_o.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://frazzleddad.blogspot.com/2012/01/31-days-of-testingday-22-why.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10748614.post-4865631582653662484</id><published>2011-12-28T01:20:00.001-05:00</published><updated>2011-12-28T01:20:48.552-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="#31DaysOfTesting" /><title type="text">31 Days of Testing—Day 21: Data Driving Your Functional Tests</title><content type="html">&lt;p&gt;&lt;a href="http://frazzleddad.blogspot.com/2011/12/31-days-of-testing-kickoff.html"&gt;Index to all posts in this series is here&lt;/a&gt;!&lt;/p&gt;  &lt;p&gt;This post shouldn’t be confused with Seth’s awesome post he lent me on &lt;a href="http://frazzleddad.blogspot.com/2011/12/31-days-of-testingday-17-rules-for.html"&gt;Rules for Effective Data-Driven Tests&lt;/a&gt;. He was talking about testing interactions with the database. This post will show you how to push sets of data through a functional test.&lt;/p&gt;  &lt;p&gt;This post’s examples are in C# with Selenium. If you’d like to see how the same test rolls in &lt;a href="http://www.telerik.com/automated-testing-tools.aspx"&gt;Test Studio&lt;/a&gt;, please go check out the short video I recorded on &lt;a href="http://tv.telerik.com/watch/automated-testing-tools/ajax-data-driving-dynamically-loaded-elements"&gt;Data Driving Dynamically Loaded Elements&lt;/a&gt; for Telerik TV.&lt;/p&gt;  &lt;p&gt;I’m going to refer back to the post I wrote on &lt;a href="http://frazzleddad.blogspot.com/2011/12/31-days-of-testingday-13-functional.html"&gt;day 13: Functional Test 201 (Common Problems)&lt;/a&gt;, specifically, case 3 where the elements you are working with are loaded up in the DOM, but have no content. That example used the &lt;a href="http://www.asp.net/ajaxLibrary/AjaxControlToolkitSampleSite/CascadingDropDown/CascadingDropDown.aspx"&gt;ASP.NET AJAX Cascading Drop Down site&lt;/a&gt;. Feel free to go explore the original post and AJAX site if you need to refresh yourselves. I’ll wait.&lt;/p&gt;  &lt;p&gt;The example site I used offers up three option lists (make, model, color) and gives you a matrix of combinations to deal with. Writing up separate test scripts for each combination would be insanity, so let’s not. Instead, we’ll create a list of items to pass through one test, and iterate that test repeatedly through the list.&lt;/p&gt;  &lt;p&gt;First off, I’ve refactored the original example from day 13 to make it more modular and readable. Day 13 worked for an elementary example, but let’s move on to something more real-world-ish. Here’s the crux of the new test:&lt;/p&gt;  &lt;div id="codeSnippetWrapper"&gt;   &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;     &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060"&gt; &lt;strong&gt;Test: Working_with_no_content_data_driven&lt;/strong&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum1"&gt;   1:&lt;/span&gt; [Test]&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum2"&gt;   2:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; Working_with_no_content_data_driven()&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum3"&gt;   3:&lt;/span&gt; {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum4"&gt;   4:&lt;/span&gt;     IList&amp;lt;Car&amp;gt; cars = CarFactory.Return_three_valid_cars();&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum5"&gt;   5:&lt;/span&gt;&amp;#160; &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum6"&gt;   6:&lt;/span&gt;     browser.Navigate().GoToUrl(&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum7"&gt;   7:&lt;/span&gt;         &lt;span style="color: #006080"&gt;&amp;quot;http://www.asp.net/ajaxLibrary/AjaxControlToolkitSampleSite/CascadingDropDown/CascadingDropDown.aspx&amp;quot;&lt;/span&gt;);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum8"&gt;   8:&lt;/span&gt;     WebDriverWait wait = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; WebDriverWait(browser, TimeSpan.FromSeconds(10));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum9"&gt;   9:&lt;/span&gt;&amp;#160; &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum10"&gt;  10:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;foreach&lt;/span&gt; (Car car &lt;span style="color: #0000ff"&gt;in&lt;/span&gt; cars)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum11"&gt;  11:&lt;/span&gt;     {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum12"&gt;  12:&lt;/span&gt;         browser.Navigate().Refresh();&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum13"&gt;  13:&lt;/span&gt;     &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum14"&gt;  14:&lt;/span&gt;         Select_make(car, wait);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum15"&gt;  15:&lt;/span&gt;         Select_model(car, wait);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum16"&gt;  16:&lt;/span&gt;         Select_color(car, wait);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum17"&gt;  17:&lt;/span&gt;         Validate_message(car, wait);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum18"&gt;  18:&lt;/span&gt;     }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum19"&gt;  19:&lt;/span&gt; }&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;First I’m using a factory to build a list of three cars. Here’s what that looks like:&lt;/p&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060"&gt; &lt;strong&gt;Class: CarFactory&lt;/strong&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum1"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;class&lt;/span&gt; CarFactory&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum2"&gt;   2:&lt;/span&gt; {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum3"&gt;   3:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; IList&amp;lt;Car&amp;gt; Return_three_valid_cars()&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum4"&gt;   4:&lt;/span&gt;     {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum5"&gt;   5:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; List&amp;lt;Car&amp;gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum6"&gt;   6:&lt;/span&gt;         {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum7"&gt;   7:&lt;/span&gt;             &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; Car { Make = &lt;span style="color: #006080"&gt;&amp;quot;Acura&amp;quot;&lt;/span&gt;, Model = &lt;span style="color: #006080"&gt;&amp;quot;Integra&amp;quot;&lt;/span&gt;, Color = &lt;span style="color: #006080"&gt;&amp;quot;Sea Green&amp;quot;&lt;/span&gt;,&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum8"&gt;   8:&lt;/span&gt;                 Message = &lt;span style="color: #006080"&gt;&amp;quot;Sea Green Acura Integra&amp;quot;&lt;/span&gt; },&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum9"&gt;   9:&lt;/span&gt;             &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; Car { Make = &lt;span style="color: #006080"&gt;&amp;quot;Audi&amp;quot;&lt;/span&gt;, Model = &lt;span style="color: #006080"&gt;&amp;quot;S4&amp;quot;&lt;/span&gt;, Color = &lt;span style="color: #006080"&gt;&amp;quot;Metallic&amp;quot;&lt;/span&gt;, &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum10"&gt;  10:&lt;/span&gt;                 Message = &lt;span style="color: #006080"&gt;&amp;quot;Metallic Audi S4&amp;quot;&lt;/span&gt; },&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum11"&gt;  11:&lt;/span&gt;             &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; Car { Make = &lt;span style="color: #006080"&gt;&amp;quot;BMW&amp;quot;&lt;/span&gt;, Model = &lt;span style="color: #006080"&gt;&amp;quot;7 series&amp;quot;&lt;/span&gt;, Color = &lt;span style="color: #006080"&gt;&amp;quot;Brown&amp;quot;&lt;/span&gt;, &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum12"&gt;  12:&lt;/span&gt;                 Message = &lt;span style="color: #006080"&gt;&amp;quot;Brown BMW 7 series&amp;quot;&lt;/span&gt; }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum13"&gt;  13:&lt;/span&gt;         };&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum14"&gt;  14:&lt;/span&gt;     }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum15"&gt;  15:&lt;/span&gt; }&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Here I’m simply creating a list of Cars right in the method. This factory method could just as easily reach out to a database, read from an Excel file, etc., etc. The point being, the test itself has no idea what the data source is—&lt;strong&gt;&lt;em&gt;and that’s exactly how it should be!&lt;/em&gt;&lt;/strong&gt; Hiding the data source in the Factory lets me change the source as needed without impacting any of the tests which rely on that Factory.&lt;/p&gt;

&lt;p&gt;Here’s the inspiring Car class:&lt;/p&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060"&gt; &lt;strong&gt;Class: Car&lt;/strong&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum1"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;class&lt;/span&gt; Car&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum2"&gt;   2:&lt;/span&gt; {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum3"&gt;   3:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; Make { get; set; }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum4"&gt;   4:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; Model { get; set; }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum5"&gt;   5:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; Color { get; set; }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum6"&gt;   6:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; Message { get; set; }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum7"&gt;   7:&lt;/span&gt; }&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Back to the actual test now!&lt;/p&gt;

&lt;p&gt;Lines 6-8 navigate to the site and set up our wait, quite similarly to day 13’s example.&lt;/p&gt;

&lt;p&gt;Lines 10-18 let us iterate through our list of Cars. If you’re working in Python, Java, Ruby, or some other platform then obviously things will look different. The idea is we loop through our cars and run the same test each time.&lt;/p&gt;

&lt;p&gt;Note that line 12 explicitly refreshes the browser each time through. Because we’re pulling data back from service calls, I’ve found the page DOM can hold old contents around. Refreshing each iteration ensures I have exactly the DOM I expect to work with.&lt;/p&gt;

&lt;p&gt;Line 14, Select_make() calls a newly extracted method to interact with the page’s drop down for Make.&lt;/p&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum1"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; Select_make(Car car, WebDriverWait wait)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum2"&gt;   2:&lt;/span&gt; {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum3"&gt;   3:&lt;/span&gt;     var listOfMakes = browser.FindElement(By.Id(&lt;span style="color: #006080"&gt;&amp;quot;ctl00_SampleContent_DropDownList1&amp;quot;&lt;/span&gt;));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum4"&gt;   4:&lt;/span&gt;     wait.Until&amp;lt;IWebElement&amp;gt;((d) =&amp;gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum5"&gt;   5:&lt;/span&gt;     {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum6"&gt;   6:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; d.FindElement(By.XPath(&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum7"&gt;   7:&lt;/span&gt;                                       &lt;span style="color: #006080"&gt;&amp;quot;id('ctl00_SampleContent_DropDownList1')/option[text()='&amp;quot;&lt;/span&gt; +&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum8"&gt;   8:&lt;/span&gt;                                       car.Make + &lt;span style="color: #006080"&gt;&amp;quot;']&amp;quot;&lt;/span&gt;));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum9"&gt;   9:&lt;/span&gt;     });&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum10"&gt;  10:&lt;/span&gt;     var makeOptions = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; SelectElement(listOfMakes);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum11"&gt;  11:&lt;/span&gt;     makeOptions.SelectByText(car.Make);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum12"&gt;  12:&lt;/span&gt; }&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;This works exactly the same as described in the previous post: get the drop down list, wait until its contents populate with the desired make for this iteration. I’m passing in the Car class here, which as I’m writing this makes me realize I should instead be passing in only the Make property from the Car, not the entire Car. Select_make shouldn’t have to know how to deal with a Car, only with what it expects to.&lt;/p&gt;

&lt;p&gt;Enough ponderings on software design for now. I’ll refactor later.&lt;/p&gt;

&lt;p&gt;The calls to Select_model() and Select_color() work in exactly the same fashion:&lt;/p&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum1"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; Select_model(Car car, WebDriverWait wait)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum2"&gt;   2:&lt;/span&gt; {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum3"&gt;   3:&lt;/span&gt;     var listOfModels = browser.FindElement(By.Id(&lt;span style="color: #006080"&gt;&amp;quot;ctl00_SampleContent_DropDownList2&amp;quot;&lt;/span&gt;));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum4"&gt;   4:&lt;/span&gt;     wait.Until&amp;lt;IWebElement&amp;gt;((d) =&amp;gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum5"&gt;   5:&lt;/span&gt;     {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum6"&gt;   6:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; d.FindElement(By.XPath(&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum7"&gt;   7:&lt;/span&gt;                                       &lt;span style="color: #006080"&gt;&amp;quot;id('ctl00_SampleContent_DropDownList2')/option[text()='&amp;quot;&lt;/span&gt; +&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum8"&gt;   8:&lt;/span&gt;                                       car.Model + &lt;span style="color: #006080"&gt;&amp;quot;']&amp;quot;&lt;/span&gt;));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum9"&gt;   9:&lt;/span&gt;     });&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum10"&gt;  10:&lt;/span&gt;     var modelOptions = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; SelectElement(listOfModels);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum11"&gt;  11:&lt;/span&gt;     modelOptions.SelectByText(car.Model);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum12"&gt;  12:&lt;/span&gt; }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum13"&gt;  13:&lt;/span&gt;&amp;#160; &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum14"&gt;  14:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; Select_color(Car car, WebDriverWait wait)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum15"&gt;  15:&lt;/span&gt; {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum16"&gt;  16:&lt;/span&gt;     var listOfColors = browser.FindElement(By.Id(&lt;span style="color: #006080"&gt;&amp;quot;ctl00_SampleContent_DropDownList3&amp;quot;&lt;/span&gt;));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum17"&gt;  17:&lt;/span&gt;     wait.Until&amp;lt;IWebElement&amp;gt;((d) =&amp;gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum18"&gt;  18:&lt;/span&gt;     {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum19"&gt;  19:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; d.FindElement(By.XPath(&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum20"&gt;  20:&lt;/span&gt;                                       &lt;span style="color: #006080"&gt;&amp;quot;id('ctl00_SampleContent_DropDownList3')/option[text()='&amp;quot;&lt;/span&gt; +&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum21"&gt;  21:&lt;/span&gt;                                       car.Color + &lt;span style="color: #006080"&gt;&amp;quot;']&amp;quot;&lt;/span&gt;));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum22"&gt;  22:&lt;/span&gt;     });&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum23"&gt;  23:&lt;/span&gt;     var colorOptions = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; SelectElement(listOfColors);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum24"&gt;  24:&lt;/span&gt;     colorOptions.SelectByText(car.Color);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum25"&gt;  25:&lt;/span&gt; }&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Now for the validation which checks that the expected message is correctly displayed:&lt;/p&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum1"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; Validate_message(Car car, WebDriverWait wait)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum2"&gt;   2:&lt;/span&gt; {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum3"&gt;   3:&lt;/span&gt;     var messageActual = wait.Until&amp;lt;IWebElement&amp;gt;((d) =&amp;gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum4"&gt;   4:&lt;/span&gt;     {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum5"&gt;   5:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; d.FindElement(By.XPath(&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum6"&gt;   6:&lt;/span&gt;                                       &lt;span style="color: #006080"&gt;&amp;quot;id('ctl00_SampleContent_Label1')[contains(.,'&amp;quot;&lt;/span&gt; +&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum7"&gt;   7:&lt;/span&gt;                                       car.Message + &lt;span style="color: #006080"&gt;&amp;quot;')]&amp;quot;&lt;/span&gt;));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum8"&gt;   8:&lt;/span&gt;     });&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum9"&gt;   9:&lt;/span&gt;     Assert.IsTrue(messageActual.Text.Contains(car.Message), &lt;span style="color: #006080"&gt;&amp;quot;Message: &amp;quot;&lt;/span&gt; +&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum10"&gt;  10:&lt;/span&gt;                                                             messageActual.Text);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum11"&gt;  11:&lt;/span&gt; }&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;The XPath in this method uses the “contains” function to check contents under the element pointed to by id ct100_SampleContent_Label1. I don’t check for an exact match—I only want to check that the message property’s contents for the current car are somewhere in that element.&lt;/p&gt;

&lt;p&gt;There you have it: a simple data driven example for your functional test. Key takeaway: Construct your actual data list behind some sort of façade, be it a Factory or some other equivalent. Never let your tests themselves be responsible for constructing the data list. This ensures you’ll always have the easy flexibility to change how the data is built, where it’s built from, what it looks like, etc.&lt;/p&gt;

&lt;p&gt;I should also point out that, as with all my examples, I’m not making use of the Page Object pattern. My examples here are all very linear with locators defined right in the tests. Why am I not using Page Objects for you to read? Two reasons. First, the examples are long enough and I’m trying to keep things fairly simple. Secondly, quite frankly I’ve not worked with it enough to be confident in showing you proper examples. Go do your own research on it, get to know it, and decide if it’s a sensible path for you to follow.&lt;/p&gt;

&lt;p&gt;If you’re interested, you can find the complete source for this example (and the ones from Day 13) in &lt;a href="https://github.com/jimholmes/TestStudioDemos/tree/master/31Days-FunctionalTestIntro"&gt;my GitHub repository&lt;/a&gt;.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;div class="lqm_ad" lqm_publisher="lqm.frazzleddad.site" lqm_zone="ron" lqm_format="125x125" lqm_tgs="HTML5%2cWeb%2cAgile%2cALM%2cVS2010%2cSharePoint%2cWPF%2cXML%2cSilverlight%2cTesting%2cTFS%2cMVC%2cAJAX%2c"&gt;&lt;/div&gt;
&lt;script type='text/javascript' language='Javascript' src='http://s1.lqcdn.com/m.min.js?dt=2.3.110104.1'&gt;&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10748614-4865631582653662484?l=frazzleddad.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Frazzleddad/~4/kCPbzRBs6MU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://frazzleddad.blogspot.com/feeds/4865631582653662484/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=10748614&amp;postID=4865631582653662484" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/4865631582653662484" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/4865631582653662484" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Frazzleddad/~3/kCPbzRBs6MU/31-days-of-testingday-21-data-driving.html" title="31 Days of Testing—Day 21: Data Driving Your Functional Tests" /><author><name>Jim Holmes</name><uri>http://www.blogger.com/profile/05869146736565695900</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://3.bp.blogspot.com/_LOCL59IyTaA/Spykv6_NRpI/AAAAAAAAAAM/S2ZeQAzrL5o/s1600-R/3853180914_14858ae15f_o.jpg" /></author><thr:total>1</thr:total><feedburner:origLink>http://frazzleddad.blogspot.com/2011/12/31-days-of-testingday-21-data-driving.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10748614.post-2222291527787926134</id><published>2011-12-27T02:23:00.001-05:00</published><updated>2011-12-27T02:23:45.428-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="#31DaysOfTesting" /><title type="text">31 Days of Testing—Day 20: Refactoring a Monster Test, Part 2</title><content type="html">&lt;p&gt;&lt;a href="http://frazzleddad.blogspot.com/2011/12/31-days-of-testing-kickoff.html"&gt;Index to all posts in this series is here&lt;/a&gt;!&lt;/p&gt;  &lt;p&gt;First off, I hope everyone’s had a great holiday break (or at least readers in areas where you got a holiday break!). I’ve been mostly offline since the 23rd and have greatly enjoyed the respite.&lt;/p&gt;  &lt;p&gt;I left off &lt;a href="http://frazzleddad.blogspot.com/2011/12/31-days-of-testingday-19-refactoring.html"&gt;the last post&lt;/a&gt; having pulled out the logon functionality into a separate method, plus gave it a bit of&amp;#160; robustness check to make it less brittle—the logon now checks to see if it actually needs to log on.&lt;/p&gt;  &lt;p&gt;The logon test looks like this:&lt;/p&gt;  &lt;div id="codeSnippetWrapper"&gt;   &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;     &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060"&gt;&lt;strong&gt;Test: Logon If Needed&lt;/strong&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum1"&gt;   1:&lt;/span&gt; Navigate to : '/welcome'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum2"&gt;   2:&lt;/span&gt; IF (Verify Exists 'LoginLinkLink') THEN&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum3"&gt;   3:&lt;/span&gt;    Click 'LoginLinkLink'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum4"&gt;   4:&lt;/span&gt;    Enter text 'testuser' in 'UsernameText'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum5"&gt;   5:&lt;/span&gt;    Enter text 'abc123' in 'PasswordPassword'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum6"&gt;   6:&lt;/span&gt;    Click 'LoginButtonSubmit'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum7"&gt;   7:&lt;/span&gt; ELSE&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Our “main” test now looks like this:&lt;/p&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060"&gt;&lt;strong&gt;Test: Retrieve a newly created user and validate user's data is correct&lt;/strong&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum1"&gt;   1:&lt;/span&gt; Execute test 'Log In If Needed'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum2"&gt;   2:&lt;/span&gt; Click 'NewContactLink'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum3"&gt;   3:&lt;/span&gt; Connect to pop-up window : 'http://localhost:3000/contacts/new'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum4"&gt;   4:&lt;/span&gt; Set 'ContactFirstNameText' text to 'New'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum5"&gt;   5:&lt;/span&gt; Set 'ContactLastNameText' text to 'User'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum6"&gt;   6:&lt;/span&gt; Set 'ContactEmailEmail' text to 'new&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;user@foo&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;com'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum7"&gt;   7:&lt;/span&gt; Set 'ContactLinkedinProfileText' text to 'http://linkedin&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;com/newuser'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum8"&gt;   8:&lt;/span&gt; Check 'ContactGovtContractCheckBox' to be 'True'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum9"&gt;   9:&lt;/span&gt; Check 'ContactDodCheckBox' to be 'True'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum10"&gt;  10:&lt;/span&gt; Check 'ContactOtherCheckBox' to be 'True'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum11"&gt;  11:&lt;/span&gt; Desktop command: Drag &amp;amp;amp; Drop Neutral Lead Image to Lead Type Drop Target&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum12"&gt;  12:&lt;/span&gt; Click 'CommitSubmit'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum13"&gt;  13:&lt;/span&gt; Wait for 'TextContent' 'Contains' 'New' on 'NewTableCell'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum14"&gt;  14:&lt;/span&gt; Verify 'TextContent' 'Contains' 'User' on 'UserTableCell'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum15"&gt;  15:&lt;/span&gt; Verify 'TextContent' 'Contains' 'new&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;user@foo&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;com' on 'NewUserFooTableCell'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum16"&gt;  16:&lt;/span&gt; Verify 'TextContent' 'Contains' 'http://linkedin&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;com/newuser' on 'HttpLinkedinTableCell'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum17"&gt;  17:&lt;/span&gt; Verify attribute 'alt' has 'Contains' value of 'Neutral' on 'New User Lead Type'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum18"&gt;  18:&lt;/span&gt; Extract attribute 'href' on 'ViewContactLink' into DataBindVariable &lt;span style="color: #cc6633"&gt;$&lt;/span&gt;(ContactLinkUrl)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum19"&gt;  19:&lt;/span&gt; Coded Step: [Retrieve_a_newly_created_user_and_validate_users_data_is_correct_CodedStep1]&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum20"&gt;  20:&lt;/span&gt; Click 'ViewContactLink'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum21"&gt;  21:&lt;/span&gt; Coded Step: [Retrieve_a_newly_created_user_and_validate_users_data_is_correct_CodedStep]&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum22"&gt;  22:&lt;/span&gt;             Connect to pop-up window : 'http://localhost:3000/contacts/7', ConnectToPopup=True&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum23"&gt;  23:&lt;/span&gt; Verify input 'ContactFirstNameText' value 'Exact' 'New'&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum24"&gt;  24:&lt;/span&gt; Verify input 'ContactLastNameText' value 'Exact' 'User'&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum25"&gt;  25:&lt;/span&gt; Verify attribute 'value' has 'Same' value of 'new&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;user@foo&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;com' on 'ContactEmailEmail'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum26"&gt;  26:&lt;/span&gt; Verify input 'ContactLinkedinProfileText' value 'Exact' 'http://linkedin&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;com/newuser'&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum27"&gt;  27:&lt;/span&gt; Verify attribute 'alt' has 'Same' value of 'Neutral' on 'LeadTypeImage'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum28"&gt;  28:&lt;/span&gt; Close pop-up window : 'http://localhost:3000/contacts'&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;In this post I want to concentrate on getting the steps for creating the test contact out of this test and in to its own test/method. In these posts I’m using Test Studio for my examples, but the concept here is completely and absolutely the same regardless if you’re using Selenium, Watir, Visual Studio’s web test, or some other tool.&lt;/p&gt;

&lt;p&gt;The eventual goal would be to have the actual creation of the user done via some form of backing API or service call. Getting the browser out of the business of creating your test data is a &lt;strong&gt;Good Thing&lt;/strong&gt;, but it will likely be a multi-step process, especially if you’re new to automation and are just building up your team and toolset. &lt;/p&gt;

&lt;p&gt;I’ve worked through this process a number of times, and sometimes the easiest thing is to first go ahead and get the browser doing those actions for you, but centralize the functionality so it’s only happening in one place. While this is slower and more brittle for test execution, it may be your only option at the start – in prior projects/jobs my automation team and I didn’t have skills and/or knowledge right away to start making system calls for doing this setup work. Leaving the browser to do the setup enabled us continue getting automation built as we learned more about the system, what we needed, and worked with other developers to gradually replace those browser-based setup pieces with real service calls.&lt;/p&gt;

&lt;p&gt;For now, I’ll chop steps 2-17 in the above “main” test out to a new test. That test now looks like this:&lt;/p&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060"&gt;&lt;strong&gt;Test: Create Test Contact&lt;/strong&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum1"&gt;   1:&lt;/span&gt; Execute test 'Log in if needed'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum2"&gt;   2:&lt;/span&gt; Click 'NewContactLink'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum3"&gt;   3:&lt;/span&gt; Connect to pop-up window : 'http://localhost:3000/contacts/new'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum4"&gt;   4:&lt;/span&gt; Enter text 'New' in 'ContactFirstNameText'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum5"&gt;   5:&lt;/span&gt; Enter text 'User' in 'ContactLastNameText'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum6"&gt;   6:&lt;/span&gt; Enter text 'new&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;user@foo&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;com' in 'ContactEmailEmail'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum7"&gt;   7:&lt;/span&gt; Enter text 'http://linkedin&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;com/newuser' in 'ContactLinkedinProfileText'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum8"&gt;   8:&lt;/span&gt; Check 'ContactGovtContractCheckBox' to be 'True'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum9"&gt;   9:&lt;/span&gt; Check 'ContactDodCheckBox' to be 'True'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum10"&gt;  10:&lt;/span&gt; Check 'ContactOtherCheckBox' to be 'True'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum11"&gt;  11:&lt;/span&gt; Desktop command: Drag &amp;amp;amp; Drop Neutral Lead Image to Lead Type Drop Target&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum12"&gt;  12:&lt;/span&gt; Click 'CommitSubmit'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum13"&gt;  13:&lt;/span&gt; Wait for 'TextContent' 'Contains' 'New' on 'NewTableCell'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum14"&gt;  14:&lt;/span&gt; Verify 'TextContent' 'Contains' 'User' on 'UserTableCell'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum15"&gt;  15:&lt;/span&gt; Verify 'TextContent' 'Contains' 'new&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;user@foo&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;com' on 'NewUserFooTableCell'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum16"&gt;  16:&lt;/span&gt; Verify 'TextContent' 'Contains' 'http://linkedin&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;com/newuser' on 'HttpLinkedinTableCell'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum17"&gt;  17:&lt;/span&gt; Verify attribute 'alt' has 'Contains' value of 'Neutral' on 'New User Lead Type'&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Here’s what the main test now looks like after refactoring. I've also renamed a couple steps to make things clearer--something I hadn't paid enough attention to despite looking at this a number of times already!&lt;/p&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060"&gt;&lt;strong&gt;Test: Retrieve a newly created user and validate user's data is correct&lt;/strong&gt;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum1"&gt;   1:&lt;/span&gt; Execute test 'Log in if needed'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum2"&gt;   2:&lt;/span&gt; Execute test 'Create Test Contact'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum3"&gt;   3:&lt;/span&gt; Extract attribute 'href' on 'ViewContactLink' into DataBindVariable &lt;span style="color: #cc6633"&gt;$&lt;/span&gt;(ContactLinkUrl)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum4"&gt;   4:&lt;/span&gt; [LogExtractedContactLinkUrl] : @&amp;quot;New Coded Step&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum5"&gt;   5:&lt;/span&gt; Open a pop up window so we can connect to it with correct URL&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum6"&gt;   6:&lt;/span&gt; [Retrieve_a_newly_created_user_and_validate_users_data_is_correct_CodedStep] : &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum7"&gt;   7:&lt;/span&gt;     @&amp;quot;Connect to pop-up window for newly created contact&amp;quot;, ConnectToPopup=true&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum8"&gt;   8:&lt;/span&gt; Verify input 'ContactFirstNameText' value 'Exact' 'New'&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum9"&gt;   9:&lt;/span&gt; Verify input 'ContactLastNameText' value 'Exact' 'User'&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum10"&gt;  10:&lt;/span&gt; Verify attribute 'value' has 'Same' value of 'new&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;user@foo&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;com' on 'ContactEmailEmail'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum11"&gt;  11:&lt;/span&gt; Verify input 'ContactLinkedinProfileText' value 'Exact' 'http://linkedin&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;com/newuser'&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum12"&gt;  12:&lt;/span&gt; Verify attribute 'alt' has 'Same' value of 'Neutral' on 'LeadTypeImage'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum13"&gt;  13:&lt;/span&gt; Close pop-up window : 'http://localhost:3000/contacts'&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;OK, now we’ve got things nicely separated out. This test does its setup by logging on and creating a new contact. If I was in straight Selenium or Watir, I’d have those two steps consolidated in a fixture setup. If I was driving tests with Cucumber, Fitnesse, or something similar I’d again have those setup steps consolidated in their equivalent. &lt;/p&gt;

&lt;p&gt;Now the overall main tests passes, but I’m still using the browser for setting up my test prerequisites. It works, but as I’ve repeatedly said, it’s slow and it’s brittle. Because I’ve moved that setup out to its own test, it’s a simple matter to replace the guts of that test with a call to some sort of a service or system call.&lt;/p&gt;

&lt;p&gt;Don’t get all fancy and complex at the start. Look to the simplest thing that will work for you, even if it seems a bit clunky at first. Because my demo app is so trivial, I can go seriously low-tech for my solution here and simply call out to the command line to invoke SQLITE3.EXE to create my test user for me. In the Create Test User test above, I can completely replace steps 2-13 with one coded step:&lt;/p&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum1"&gt;   1:&lt;/span&gt; [CodedStep(&lt;span style="color: #006080"&gt;@&amp;quot;Create Test Contact&amp;quot;&lt;/span&gt;)]&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum2"&gt;   2:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; Create_Test_Contact_CodedStep()&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum3"&gt;   3:&lt;/span&gt; {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum4"&gt;   4:&lt;/span&gt;     System.Diagnostics.Process proc = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; System.Diagnostics.Process();&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum5"&gt;   5:&lt;/span&gt;     proc.StartInfo.FileName = &lt;span style="color: #006080"&gt;@&amp;quot;d:\temp\sqlite3.exe&amp;quot;&lt;/span&gt;;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum6"&gt;   6:&lt;/span&gt;     proc.StartInfo.Arguments = &lt;span style="color: #006080"&gt;&amp;quot;development.sqlite3 \&amp;quot;delete from contacts&amp;quot;&lt;/span&gt;+&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum7"&gt;   7:&lt;/span&gt;         &lt;span style="color: #006080"&gt;&amp;quot;where email like '%foo.com';insert into contacts &amp;quot;&lt;/span&gt;+&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum8"&gt;   8:&lt;/span&gt;         &lt;span style="color: #006080"&gt;&amp;quot;(first_name, last_name,email, linkedin_profile, lead_type) &amp;quot;&lt;/span&gt;+&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum9"&gt;   9:&lt;/span&gt;         &lt;span style="color: #006080"&gt;&amp;quot;values &amp;quot;&lt;/span&gt;+&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum10"&gt;  10:&lt;/span&gt;         &lt;span style="color: #006080"&gt;&amp;quot;('New', 'User', 'new.user@foo.com', 'http://linkedin.com/newuser','NEUTRAL');&amp;quot;&lt;/span&gt;+&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum11"&gt;  11:&lt;/span&gt;         &lt;span style="color: #006080"&gt;&amp;quot;\&amp;quot;&amp;quot;&lt;/span&gt;;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum12"&gt;  12:&lt;/span&gt;     proc.StartInfo.WorkingDirectory = &lt;span style="color: #006080"&gt;@&amp;quot;D:\projects\Telerik-Demo\db&amp;quot;&lt;/span&gt;;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum13"&gt;  13:&lt;/span&gt;     proc.Start();&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum14"&gt;  14:&lt;/span&gt;     proc.WaitForExit();&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum15"&gt;  15:&lt;/span&gt; }&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Yes, there are a number of ugly things here: I’ve hard wired in paths to the executable, working directory, and database name. Certainly I could move these off to configuration settings for a more dynamic, flexible approach—&lt;strong&gt;&lt;em&gt;and it would be a waste of time for this project.&lt;/em&gt;&lt;/strong&gt; Take care with how far you go on these sorts of things. Be very Lean in your approach and only solve problems you need to.&lt;/p&gt;

&lt;p&gt;If my system were more complex, the logical next step would be to move this from a command line call to a service or stored procedure. That would save me spawning up a separate Process, and would simplify the 15 lines above down to something like:&lt;/p&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum1"&gt;   1:&lt;/span&gt; [CodedStep(&lt;span style="color: #006080"&gt;@&amp;quot;Create Test User&amp;quot;&lt;/span&gt;)]&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum2"&gt;   2:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;int&lt;/span&gt; Create_Test_Contact_CodedStep()&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum3"&gt;   3:&lt;/span&gt; {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum4"&gt;   4:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; My_backing_framework.Create_test_contact(&lt;span style="color: #006080"&gt;&amp;quot;New&amp;quot;&lt;/span&gt;, &lt;span style="color: #006080"&gt;&amp;quot;User&amp;quot;&lt;/span&gt;,&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum5"&gt;   5:&lt;/span&gt;         &lt;span style="color: #006080"&gt;&amp;quot;new.user@foo.com&amp;quot;&lt;/span&gt;, &lt;span style="color: #006080"&gt;&amp;quot;http://linkedin.com/newuser&amp;quot;&lt;/span&gt;, &lt;span style="color: #006080"&gt;&amp;quot;NEUTRAL&amp;quot;&lt;/span&gt;);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum6"&gt;   6:&lt;/span&gt; }&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;(The returned value could very well be the database’s ID value of the newly created user. Get creative and use these as opportunities to do stuff that makes your testing easier.)&lt;/p&gt;

&lt;p&gt;At my last job, my pals &lt;a href="http://twitter.com/danhounshell"&gt;Dan&lt;/a&gt;, &lt;a href="http://twitter.com/jaymed"&gt;Jayme&lt;/a&gt;, and &lt;a href="http://twitter.com/tankete"&gt;Jose&lt;/a&gt; were responsible for our web services extensibility endpoints. They spent a lot of time building up a framework to create their prerequisites. It was fairly tailored to their particular environment, but I knew it was something my testing group could leverage for our Selenium work. It took several months of part-time hacking around on all sides, but eventually we got their “test pack” setup framework playing nicely with our automation framework. It was a long, slow process, but well worth the effort—however, we didn’t start down that road until we were very certain we needed such an extensive supporting framework for us.&lt;/p&gt;

&lt;p&gt;So there you have it. Step two in refactoring this test: getting setup steps out from your test, then moved off to a backing API or simple system calls. Do these moves gradually and always take the simplest steps possible.&lt;/p&gt;

&lt;p&gt;In my next post I’ll wrap up this effort with some other thoughts about getting rid of “monster” tests.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;div class="lqm_ad" lqm_publisher="lqm.frazzleddad.site" lqm_zone="ron" lqm_format="125x125" lqm_tgs="HTML5%2cWeb%2cAgile%2cALM%2cVS2010%2cSharePoint%2cWPF%2cXML%2cSilverlight%2cTesting%2cTFS%2cMVC%2cAJAX%2c"&gt;&lt;/div&gt;
&lt;script type='text/javascript' language='Javascript' src='http://s1.lqcdn.com/m.min.js?dt=2.3.110104.1'&gt;&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10748614-2222291527787926134?l=frazzleddad.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Frazzleddad/~4/wq2yk0rReLs" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://frazzleddad.blogspot.com/feeds/2222291527787926134/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=10748614&amp;postID=2222291527787926134" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/2222291527787926134" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/2222291527787926134" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Frazzleddad/~3/wq2yk0rReLs/31-days-of-testingday-20-refactoring.html" title="31 Days of Testing—Day 20: Refactoring a Monster Test, Part 2" /><author><name>Jim Holmes</name><uri>http://www.blogger.com/profile/05869146736565695900</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://3.bp.blogspot.com/_LOCL59IyTaA/Spykv6_NRpI/AAAAAAAAAAM/S2ZeQAzrL5o/s1600-R/3853180914_14858ae15f_o.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://frazzleddad.blogspot.com/2011/12/31-days-of-testingday-20-refactoring.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10748614.post-3351816782838578622</id><published>2011-12-23T06:38:00.001-05:00</published><updated>2011-12-23T06:38:52.120-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="#31DaysOfTesting" /><title type="text">31 Days of Testing—Day 19: Refactoring a “Monster” Functional Test, Part 1</title><content type="html">&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A day late on this post. My daughter had her tonsils removed yesterday, so I was rather caught up with life.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://frazzleddad.blogspot.com/2011/12/31-days-of-testing-kickoff.html"&gt;Index to all posts in this series is here&lt;/a&gt;!&lt;/p&gt;  &lt;p&gt;Folks new to functional test automation can often times dive straight off the deep end when trying to get started. I’ve often seen examples of “my first test!” where those test scripts/files/fixtures are hundreds of steps long and test many different cases. They’ll start out with one script that starts the browser, loads up the site, logs on as an administrative user, creates some users and test data, logs on in a different role, takes some actions using the setup data, does some validations, then logs back on as the administrative user and cleans up the data created to run the test.&lt;/p&gt;  &lt;p&gt;A good automation test fixture/case/script is concise and focused on testing one specific thing. Data setup should be handled in either helper scripts or pushed off to a backing framework that calls stored procedures, web services, or internal APIs to perform setup data. Good cases shouldn’t generally be switching between roles; that same sort of configuration or prerequisite actions should be handled again by helpers or APIs.&lt;/p&gt;  &lt;p&gt;These “monster” automation scripts suffer from exactly the same maintainability and functionality issues that bad code in the system’s codebase does: high complexity, mixed concerns, duplication of functionality, etc. Monster scripts are nothing but a recipe for pain—regardless of the good intentions by the folks who created them in the first place.&lt;/p&gt;  &lt;p&gt;Over the next few blog posts I’m going to take an example of one of these monster scripts and break it apart into a better set of more focused test cases and support modules. I’ve had to do this more than once with scripts written by customers, contacts, pals, &lt;b&gt;&lt;i&gt;and me.&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;em&gt;Aufpassen!&lt;/em&gt;&lt;/strong&gt; Before you dive in to this kind of refactoring, step back and see if it’s a worthwhile use of your time. You may likely be better off just rewriting the dang thing. I’m rolling with it now because it’s an interesting exercise and I hope you’ll find it useful.&lt;/p&gt;  &lt;p&gt;For this set of posts I’ll be using the demo app my group at Telerik had built to help us demonstrate specific testing problem areas like AJAX, multiple windows, etc. You can find &lt;a href="http://growing-planet-634.herokuapp.com/welcome"&gt;the demo app hosted out at Heroku&lt;/a&gt;. Logon credentials are testuser/abc123 if you feel like playing around a bit.&lt;/p&gt;  &lt;p&gt;This example is using Test Studio, but the concepts are absolutely the same regardless of what testing framework/tool you’re using.&lt;/p&gt;  &lt;p&gt;Let’s start off by discussing what the &lt;i&gt;intended&lt;/i&gt; test is: validating that a newly created user can successfully be retrieved from the system. We might express that test case in the form&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Given I have created a new user with input data of &amp;lt;a list here&amp;gt; &lt;/li&gt;    &lt;li&gt;When I click on that user in the main grid &lt;/li&gt;    &lt;li&gt;Then I should see the new user displayed with their correct data. &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;I built a single script to run this test soup to nuts – exactly the sort of “monster” script I talked about above. In Test Studio this iteration of the test has 32 steps (33 lines below because of a continuation). I did a little transform-fu of the test file to come up with some :&lt;/p&gt;  &lt;div&gt;   &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;     &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum1"&gt;   1:&lt;/span&gt; Navigate to : 'http://localhost:3000/'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum2"&gt;   2:&lt;/span&gt; Click 'LoginLinkLink'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum3"&gt;   3:&lt;/span&gt; Set 'UsernameText' text to 'testuser'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum4"&gt;   4:&lt;/span&gt; Set 'PasswordPassword' text to 'abc123'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum5"&gt;   5:&lt;/span&gt; Click 'LoginButtonSubmit'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum6"&gt;   6:&lt;/span&gt; Click 'NewContactLink'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum7"&gt;   7:&lt;/span&gt; Connect to pop-up window : 'http://localhost:3000/contacts/new'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum8"&gt;   8:&lt;/span&gt; Set 'ContactFirstNameText' text to 'New'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum9"&gt;   9:&lt;/span&gt; Set 'ContactLastNameText' text to 'User'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum10"&gt;  10:&lt;/span&gt; Set 'ContactEmailEmail' text to 'new&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;user@foo&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;com'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum11"&gt;  11:&lt;/span&gt; Set 'ContactLinkedinProfileText' text to 'http://linkedin&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;com/newuser'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum12"&gt;  12:&lt;/span&gt; Check 'ContactGovtContractCheckBox' to be 'True'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum13"&gt;  13:&lt;/span&gt; Check 'ContactDodCheckBox' to be 'True'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum14"&gt;  14:&lt;/span&gt; Check 'ContactOtherCheckBox' to be 'True'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum15"&gt;  15:&lt;/span&gt; Desktop command: Drag &amp;amp;amp; Drop Neutral Lead Image to Lead Type Drop Target&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum16"&gt;  16:&lt;/span&gt; Click 'CommitSubmit'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum17"&gt;  17:&lt;/span&gt; Wait for 'TextContent' 'Contains' 'New' on 'NewTableCell'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum18"&gt;  18:&lt;/span&gt; Verify 'TextContent' 'Contains' 'User' on 'UserTableCell'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum19"&gt;  19:&lt;/span&gt; Verify 'TextContent' 'Contains' 'new&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;user@foo&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;com' on 'NewUserFooTableCell'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum20"&gt;  20:&lt;/span&gt; Verify 'TextContent' 'Contains' 'http://linkedin&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;com/newuser' on 'HttpLinkedinTableCell'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum21"&gt;  21:&lt;/span&gt; Verify attribute 'alt' has 'Contains' value of 'Neutral' on 'New User Lead Type'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum22"&gt;  22:&lt;/span&gt; Extract attribute 'href' on 'ViewContactLink' into DataBindVariable &lt;span style="color: #cc6633"&gt;$&lt;/span&gt;(ContactLinkUrl)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum23"&gt;  23:&lt;/span&gt; Coded Step: [Retrieve_a_newly_created_user_and_validate_users_data_is_correct_CodedStep1]&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum24"&gt;  24:&lt;/span&gt; Click 'ViewContactLink'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum25"&gt;  25:&lt;/span&gt; Coded Step: [Retrieve_a_newly_created_user_and_validate_users_data_is_correct_CodedStep]&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum26"&gt;  26:&lt;/span&gt;             Connect to pop-up window : 'http://localhost:3000/contacts/7', ConnectToPopup=True&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum27"&gt;  27:&lt;/span&gt; Verify input 'ContactFirstNameText' value 'Exact' 'New'&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum28"&gt;  28:&lt;/span&gt; Verify input 'ContactLastNameText' value 'Exact' 'User'&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum29"&gt;  29:&lt;/span&gt; Verify attribute 'value' has 'Same' value of 'new&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;user@foo&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;com' on 'ContactEmailEmail'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum30"&gt;  30:&lt;/span&gt; Verify input 'ContactLinkedinProfileText' value 'Exact' 'http://linkedin&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;com/newuser'&lt;span style="color: #ff0000"&gt;.&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum31"&gt;  31:&lt;/span&gt; Verify attribute 'alt' has 'Same' value of 'Neutral' on 'LeadTypeImage'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum32"&gt;  32:&lt;/span&gt; Close pop-up window : 'http://localhost:3000/contacts'&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;The test logs on (lines 1-5), creates a new user (6-16), verifies the user was correctly created on the main grid (17-21), figures out which link on the main grid to click to open the new user in the edit contact form (22-23), opens the new user in the edit contact form (24-25), then validates all the new user’s info is as expected (26-31). Finally we close out any new browser windows that were spawned.&lt;/p&gt;

&lt;p&gt;Things would look very similar if I was using Watir, Selenium, or some other testing tool. The syntax would obviously be different, but the idea of stringing together huge numbers of steps in a functional test is a common one, and no framework or tool automatically fixes this issue for you…&lt;/p&gt;

&lt;p&gt;Here’s a video of the test in action:&lt;/p&gt;
&lt;!-- copy and paste. Modify height and width if desired. --&gt;

&lt;div&gt;&lt;object id="scPlayer" width="800" height="600" type="application/x-shockwave-flash" data="http://content.screencast.com/users/JimHolmesTelerik/folders/Camtasia/media/1a70a04a-b5e7-4295-a2ca-0398a7a7a8e8/mp4h264player.swf" &gt; &lt;param name="movie" value="http://content.screencast.com/users/JimHolmesTelerik/folders/Camtasia/media/1a70a04a-b5e7-4295-a2ca-0398a7a7a8e8/mp4h264player.swf" /&gt; &lt;param name="quality" value="high" /&gt; &lt;param name="bgcolor" value="#FFFFFF" /&gt; &lt;param name="flashVars" value="thumb=http://content.screencast.com/users/JimHolmesTelerik/folders/Camtasia/media/1a70a04a-b5e7-4295-a2ca-0398a7a7a8e8/FirstFrame.jpg&amp;amp;containerwidth=1280&amp;amp;containerheight=720&amp;amp;content=http://content.screencast.com/users/JimHolmesTelerik/folders/Camtasia/media/1a70a04a-b5e7-4295-a2ca-0398a7a7a8e8/First%20Run%20Test%20Case%20Example.mp4&amp;amp;blurover=false" /&gt; &lt;param name="allowFullScreen" value="true" /&gt; &lt;param name="scale" value="showall" /&gt; &lt;param name="allowScriptAccess" value="always" /&gt; &lt;param name="base" value="http://content.screencast.com/users/JimHolmesTelerik/folders/Camtasia/media/1a70a04a-b5e7-4295-a2ca-0398a7a7a8e8/" /&gt; &lt;iframe type="text/html" frameborder="0" scrolling="no" style="overflow:hidden;" src="http://www.screencast.com/users/JimHolmesTelerik/folders/Camtasia/media/1a70a04a-b5e7-4295-a2ca-0398a7a7a8e8/embed" height="720" width="1280"&gt;&lt;/iframe&gt; &lt;/object&gt;&lt;/div&gt;

&lt;p&gt;Before diving in to clean this mess up, let’s take a step back and figure out some goals to get us back in line with how a good script should work:&lt;/p&gt;

&lt;div&gt;
  &lt;ul&gt;
    &lt;li&gt;Setup / configuration / prerequisites done outside the browser in helpers or internal APIs &lt;/li&gt;

    &lt;li&gt;No duplication, or at least minimal duplication carefully selected for clarity &lt;/li&gt;

    &lt;li&gt;Minimize navigation or unneeded steps &lt;/li&gt;

    &lt;li&gt;Careful bullet-proofing of the tests &lt;/li&gt;
  &lt;/ul&gt;
&lt;/div&gt;

&lt;p&gt;Before I do anything else, I’ll ask myself: is this test worth working on? Answer, yes, firstly because if not, then I’d have to figure out something else to write this blog post on. Secondly, this test has plenty of things of value in it which can get moved out to other useful steps.&lt;/p&gt;

&lt;p&gt;After a bit of planning, here’s how I’m going to run with this:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Move the steps for logging on to a separate test (define the action once, reuse it as needed) &lt;/li&gt;

  &lt;li&gt;Move the steps for creating a new user out to an external API call &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s focus on the first thing: modularizing the login action. It’s easy to separate out that action. In a code-based framework I’d move that off to a new Page Object. In Test Studio I’m moving it off to its own test.&lt;/p&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum1"&gt;   1:&lt;/span&gt; Navigate to : 'http://localhost:3000/'   &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum2"&gt;   2:&lt;/span&gt; Click 'LoginLinkLink'   &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum3"&gt;   3:&lt;/span&gt; Set 'UsernameText' text to 'testuser'   &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum4"&gt;   4:&lt;/span&gt; Set 'PasswordPassword' text to 'abc123'   &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum5"&gt;   5:&lt;/span&gt; Click 'LoginButtonSubmit'   &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum6"&gt;   6:&lt;/span&gt; Verify 'TextContent' 'Contains' 'Logout' on 'LogoutLink'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum7"&gt;   7:&lt;/span&gt; Verify attribute 'href' has 'Same' value of '/logout' on 'LogoutLink'&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;One additional step here. Generally I believe in avoiding conditionals in tests—that usually indicates you’re looking at two different tests. However, long experience in making a logon function modular has made me a believer in first checking that you actually need to log on—you may already be logged on, and the test would fail, breaking your script.&lt;/p&gt;

&lt;p&gt;Do this by first checking whether the login link appears. If that’s there, log on. Otherwise, take no action.&lt;/p&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum1"&gt;   1:&lt;/span&gt; IF (Verify Exists 'LoginLinkLink') THEN&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum2"&gt;   2:&lt;/span&gt;     Navigate to : 'http://localhost:3000/'   &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum3"&gt;   3:&lt;/span&gt;     Click 'LoginLinkLink'   &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum4"&gt;   4:&lt;/span&gt;     Set 'UsernameText' text to 'testuser'   &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum5"&gt;   5:&lt;/span&gt;     Set 'PasswordPassword' text to 'abc123'   &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum6"&gt;   6:&lt;/span&gt;     Click 'LoginButtonSubmit'   &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum7"&gt;   7:&lt;/span&gt;     Verify 'TextContent' 'Contains' 'Logout' on 'LogoutLink'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum8"&gt;   8:&lt;/span&gt;     Verify attribute 'href' has 'Same' value of '/logout' on 'LogoutLink'&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060" id="lnum9"&gt;   9:&lt;/span&gt; ELSE&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;In the next post I’ll work on moving out the user creation to an external API call!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;div class="lqm_ad" lqm_publisher="lqm.frazzleddad.site" lqm_zone="ron" lqm_format="125x125" lqm_tgs="HTML5%2cWeb%2cAgile%2cALM%2cVS2010%2cSharePoint%2cWPF%2cXML%2cSilverlight%2cTesting%2cTFS%2cMVC%2cAJAX%2c"&gt;&lt;/div&gt;
&lt;script type='text/javascript' language='Javascript' src='http://s1.lqcdn.com/m.min.js?dt=2.3.110104.1'&gt;&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10748614-3351816782838578622?l=frazzleddad.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Frazzleddad/~4/QFvTUL2bMNA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://frazzleddad.blogspot.com/feeds/3351816782838578622/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=10748614&amp;postID=3351816782838578622" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/3351816782838578622" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/3351816782838578622" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Frazzleddad/~3/QFvTUL2bMNA/31-days-of-testingday-19-refactoring.html" title="31 Days of Testing—Day 19: Refactoring a “Monster” Functional Test, Part 1" /><author><name>Jim Holmes</name><uri>http://www.blogger.com/profile/05869146736565695900</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://3.bp.blogspot.com/_LOCL59IyTaA/Spykv6_NRpI/AAAAAAAAAAM/S2ZeQAzrL5o/s1600-R/3853180914_14858ae15f_o.jpg" /></author><thr:total>1</thr:total><feedburner:origLink>http://frazzleddad.blogspot.com/2011/12/31-days-of-testingday-19-refactoring.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10748614.post-9005318907901573812</id><published>2011-12-21T23:46:00.001-05:00</published><updated>2011-12-21T23:46:32.280-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="#31DaysOfTesting" /><title type="text">31 Days of Testing—Day 18: Baseline Datasets</title><content type="html">&lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;a href="http://frazzleddad.blogspot.com/2011/12/31-days-of-testing-kickoff.html"&gt;Index to all posts in this series is here&lt;/a&gt;!&lt;/p&gt;  &lt;p&gt;&lt;a href="http://frazzleddad.blogspot.com/2011/12/31-days-of-testingday-17-rules-for.html"&gt;Yesterday’s post by Seth Petry-Johnson on Data-Driven Tests&lt;/a&gt; got me pondering another aspect of test data: baseline datasets. I love baseline datasets, but you need to carefully think when and how to use them.&lt;/p&gt;  &lt;h3&gt;Why Use Baseline Sets?&lt;/h3&gt;  &lt;p&gt;Seth’s spot on in his assertion that tests should be responsible for setting up their own test data. Most of the time. &lt;/p&gt;  &lt;p&gt;In some cases, however, it makes terrific sense to have a pre-built set of data carefully constructed to cover specific scenarios. Here are a few scenarios I’ve run in to in the past where a baseline dataset has really helped us out:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;b&gt;Manual testing.&lt;/b&gt; Baseline datasets can be a great help in a couple areas of manual testing.       &lt;ul&gt;       &lt;li&gt;&lt;b&gt;Setup&lt;/b&gt;. Yeah, I really want to spend three days setting up data by hand so I can run through effective exploratory testing, or through our manual testing guides. &lt;b&gt;&lt;i&gt;NOT!&lt;/i&gt;&lt;/b&gt; A baseline dataset gets me rolling right away. &lt;/li&gt;        &lt;li&gt;&lt;b&gt;Visual UI validation. &lt;/b&gt;A baseline dataset with large sets of data is also a great help for validating your UI handles lots of data well. Paging of grids? Long threads of conversations? Thousands of users in your site’s admin section? Look to your pre-built data to help here! &lt;/li&gt;     &lt;/ul&gt;   &lt;/li&gt;    &lt;li&gt;&lt;b&gt;Load testing.&lt;/b&gt; You can’t be serious about your load testing if you’re doing it against an empty or nearly empty database. You’ve got to have a realistic set of data in order to understand how your data access layer/module/strategery works, and how your business and UX layers deal with things. &lt;/li&gt;    &lt;li&gt;&lt;b&gt;Automated testing.&lt;/b&gt; Yes, even your regular tests can be helped out greatly with a baseline dataset. Sure, your tests need to handle their prerequisites, but you can also look to have supporting data pre-configured—users in place, content created, etc. &lt;/li&gt;    &lt;li&gt;&lt;b&gt;Testing BI, reporting, or data analysis systems. &lt;/b&gt;Systems that process large amounts of data need, well, large amounts of data to test with. It’s insane to think your test harness/suite/fixtures can create this sort of infrastructure each time you need to run your tests. Look to a baseline dataset! &lt;/li&gt;    &lt;li&gt;&lt;b&gt;Ease of test setup and teardown.&lt;/b&gt; Yes, tests should clean up after themselves, but this can be extremely hard to do and quite brittle. You know what the easiest way for your test suite to handle that clean up is? DROP DATABASE is a thing of beauty in many scenarios. &lt;/li&gt; &lt;/ul&gt;  &lt;h3&gt;Considerations for Baseline Datasets&lt;/h3&gt;  &lt;p&gt;Be careful, very, very careful, when moving to implementing a baseline dataset. While baseline datasets are extremely useful, you need to make sure you construct them to meet specific scenarios, and you need to make sure you understand how these datasets will impact all aspects of your testing.&lt;/p&gt;  &lt;p&gt;Here are some questions I’ve asked myself and my teams when we’ve dealt with standing up baseline datasets in the past.&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;b&gt;What scenarios are we testing for?&lt;/b&gt; Make sure you know the test cases you’re trying to cover. Don’t just go creating scads of content in your database. Create sets of data to solve specific test needs, otherwise you’ll potentially risk side effects. (See below.) &lt;/li&gt;    &lt;li&gt;&lt;b&gt;How much data do you need?&lt;/b&gt; What’s realistic for your scenarios? Do you honestly need 250GB of blog post content and comments? Are you trying to test blogs.msdn.microsoft.com? No? Then reel that desire for insane amounts of data back in a bit, please. Ensure you’ve got enough to meet your needs, but not too much more. &lt;/li&gt;    &lt;li&gt;&lt;b&gt;What “shape” of data do you need?&lt;/b&gt; I’m sure the hard core data folks have a fancy term for this, but I’ve always used “shape” of data to describe patterns in how the data is laid out. “Shape” to me is a number of properties around how your data is constructed: how many users; users in what roles; types of content created by those users; the time period over which that content is created or your users interact with the system; etc., etc. Shape of your data becomes crucially important when you’re working with BI, reporting, or analysis testing. &lt;/li&gt;    &lt;li&gt;&lt;b&gt;If shape is important, how long will your data be good for?&lt;/b&gt; You spent a lot of time this year building up a carefully crafted baseline dataset for your neat analysis tool. Will that dataset work as expected come 1 January of next year? Now all of a sudden you’re looking at a “Previous Year” window, not the “Current Year.” Ooops. &lt;/li&gt;    &lt;li&gt;&lt;b&gt;Do we need real data or will dummy data be good enough?&lt;/b&gt; Working with geographic systems? Actuarial or financial systems? You’ll likely need something close to realism. Maybe you can get away with generated data, maybe not. Ask the question and get the answer. &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;You’ll notice all these considerations are extremely specific to the project you’re working on. There’s little or no chance you’ll be able to re-use anything about a baseline dataset from one project to another. Sorry.&lt;/p&gt;  &lt;h3&gt;Constructing Baseline Datasets&lt;/h3&gt;  &lt;p&gt;So you’ve carefully looked at the considerations above, and all the others unique to your project. You’ve got an idea of what you need, now how do you build it?&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;b&gt;Sanitize and reshape live data.&lt;/b&gt; This is generally most applicable to load testing where you just need masses of data that’s close in shape to real data. Why not go get real data from one of your large customers and use that? You’ll likely need some form of non-disclosure agreement in place, and you’ll likely have to scrub out personal information, but that’s easily done with some SQL-fu. &lt;/li&gt;    &lt;li&gt;&lt;b&gt;Use a data generation tool.&lt;/b&gt; Visual Studio’s Data Dude (or whatever the official marketing-speak name for it is) will look at a database’s schema and help you create data in amazing detail. Red Gate has their SQL Data Generator tool, and there are any number of other tools as well. Spend a little time researching and finding out what will work for your team, environment, and project. &lt;/li&gt;    &lt;li&gt;&lt;b&gt;Use your automated tests.&lt;/b&gt; If you have a suitable suite of automated tests, use those! Ensure you’re not deleting any created data or dropping databases after each run, then simply set up a series of runs and have your tests create everything you need. Handy, that. &lt;/li&gt;    &lt;li&gt;&lt;b&gt;Build some custom SQL.&lt;/b&gt; Extremely useful if you have critically sensitive shapes required for your data. You’ll have to hand-build your data to ensure you’re meeting your very specific needs. &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Once you’ve got your data constructed, zip it up and get it in your source control system. Yes, store this huge binary file in your source control. It’s a critical project asset, and will likely be tied to specific releases of your software. Treat this asset with the same respect you’re treating other project artifacts.&lt;/p&gt;  &lt;h3&gt;Wrapping Up&lt;/h3&gt;  &lt;p&gt;By all means, keep your tests granular, self-reliant, and have them handle their own setup/teardown where feasible.&lt;/p&gt;  &lt;p&gt;That said, there are situations where a baseline dataset makes extreme sense. Keep an eye open for those situations and make use of baseline datasets to help save your sanity and deliver better software&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;div class="lqm_ad" lqm_publisher="lqm.frazzleddad.site" lqm_zone="ron" lqm_format="125x125" lqm_tgs="HTML5%2cWeb%2cAgile%2cALM%2cVS2010%2cSharePoint%2cWPF%2cXML%2cSilverlight%2cTesting%2cTFS%2cMVC%2cAJAX%2c"&gt;&lt;/div&gt;
&lt;script type='text/javascript' language='Javascript' src='http://s1.lqcdn.com/m.min.js?dt=2.3.110104.1'&gt;&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10748614-9005318907901573812?l=frazzleddad.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Frazzleddad/~4/IP8FAGOBTLQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://frazzleddad.blogspot.com/feeds/9005318907901573812/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=10748614&amp;postID=9005318907901573812" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/9005318907901573812" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/9005318907901573812" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Frazzleddad/~3/IP8FAGOBTLQ/31-days-of-testingday-18-baseline.html" title="31 Days of Testing—Day 18: Baseline Datasets" /><author><name>Jim Holmes</name><uri>http://www.blogger.com/profile/05869146736565695900</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://3.bp.blogspot.com/_LOCL59IyTaA/Spykv6_NRpI/AAAAAAAAAAM/S2ZeQAzrL5o/s1600-R/3853180914_14858ae15f_o.jpg" /></author><thr:total>1</thr:total><feedburner:origLink>http://frazzleddad.blogspot.com/2011/12/31-days-of-testingday-18-baseline.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10748614.post-3388491467524649415</id><published>2011-12-20T23:47:00.001-05:00</published><updated>2011-12-21T00:27:28.272-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="#31DaysOfTesting" /><title type="text">31 Days of Testing—Day 17: Rules for Effective Data-Driven Tests</title><content type="html">&lt;p&gt;&lt;strong&gt;Updated:&lt;/strong&gt; &lt;a href="http://frazzleddad.blogspot.com/2011/12/31-days-of-testing-kickoff.html"&gt;Index to all posts in this series is here&lt;/a&gt;!&lt;/p&gt;  &lt;p&gt;Today’s post comes from guest author &lt;a href="http://twitter.com/spetryjohnson"&gt;Seth Petry-Johnson&lt;/a&gt; who’s &lt;a href="http://www.petry-johnson.com/blog/"&gt;posted it at his blog&lt;/a&gt;. Seth contacted me when I started this series and expressed interest in writing about keeping database tests in good shape. That topic is music to my ears, so I took him up on his offer.&lt;/p&gt;  &lt;p&gt;Seth will be following up his post with more at his site, so I encourage you to keep an eye on his blog too!&lt;/p&gt;  &lt;hr /&gt;  &lt;p&gt;Tests that hit the database are slow, fragile, and difficult to automate. All the cool kids are using mocking and stubbing and in-memory databases to keep their unit tests isolated and fast, and that's awesome. I do that too (though my &amp;quot;cool kid&amp;quot; status is debatable). &lt;/p&gt;  &lt;p&gt;However, there are times when talking to a real database is necessary. Maybe you're testing actual data access logic, or maybe you're writing some high end integration/acceptance tests, or maybe you're just working in an architecture that doesn't &lt;em&gt;let &lt;/em&gt;you mock/stub/inject your way to isolated bliss. &lt;strong&gt;If any of that sounds familiar, then this post is for you&lt;/strong&gt;!&lt;/p&gt;  &lt;p&gt;Below is a list of strategies and suggestions for effective data testing that I've collected from years of experience testing large, &amp;quot;enterprisey&amp;quot;, data-driven applications. Data tests will never be painless, but following these rules makes it suck less.&lt;/p&gt;  &lt;h4&gt;Rules for good data tests&lt;/h4&gt;  &lt;ol&gt;   &lt;li&gt;Tests should create their own scenario data; never assume it already exists. &lt;em&gt;Magic row IDs kill kittens!&lt;/em&gt; &lt;/li&gt;    &lt;li&gt;Make liberal use of data helper and scenario setup classes. &lt;/li&gt;    &lt;li&gt;Don't use your data access layer to test your data access layer. &lt;/li&gt;    &lt;li&gt;Tests should make no permanent changes to the database - leave no data behind! &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;Rule 1: Tests should create their own data&lt;/p&gt;  &lt;p&gt;One of the worst things you can do in a data test is to assume that some record (a customer, an order, etc) exists that fulfills your scenario requirements. This is a cardinal sin for many reasons:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;It's extremely fragile; databases change over time, and tests that rely on pre-existing data often break (causing false-negative test failures). &lt;/li&gt;    &lt;li&gt;It obscures the test's purpose. A test's setup communicates the data context in which our assertions are valid. If you omit that setup logic, you make it hard for other programmers to understand the scenario that you are testing. &lt;/li&gt;    &lt;li&gt;It's not maintainable; other programmers won't know what makes customer ID 5 appropriate for one test and customer ID 7 appropriate for another. Once a test like this breaks, it tends to stay broken or get deleted. &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;In other words: relying on pre-existing data means your tests will break often, are painful to maintain when they do break, and don't clearly justify why another program should spend time fixing them.&lt;/p&gt;  &lt;p&gt;The solution is simple: &lt;strong&gt;each test should create each and every test record it will rely on&lt;/strong&gt;. If that sounds like a lot of work, it can be.... but keep reading to see how to keep it manageable.&lt;/p&gt;  &lt;h4&gt;Rule 2: Liberal use of data helper and scenario setup classes&lt;/h4&gt;  &lt;p&gt;Setting up the supporting data for a test sucks. It's time consuming and generally results in a lot of duplication, which then reduces test maintainability and readability. &lt;strong&gt;Test code is real code and should be kept DRY like anything else!&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;I've found it useful to create two different types of helper classes to attack this problem:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;strong&gt;Data helpers&lt;/strong&gt; are utility classes that expose methods for quickly creating entities in the system. These classes:       &lt;ul&gt;       &lt;li&gt;Are generally static, for convenience. &lt;/li&gt;        &lt;li&gt;Exist in the test project, not the main data access project. &lt;/li&gt;        &lt;li&gt;Create a single object (or object graph), such as a Customer or an Order with its OrderItem children. &lt;/li&gt;        &lt;li&gt;Create data with meaningful defaults, but allow the important fields to be explicitly specified where needed. (&lt;em&gt;Optional parameters in .NET 4 FTW!&lt;/em&gt;) &lt;/li&gt;     &lt;/ul&gt;   &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Scenario objects &lt;/strong&gt;(aka &amp;quot;fixtures&amp;quot;) represent specific data scenarios that might apply to multiple tests, such as the scenario in which a Customer has placed an Order and one of the items is backordered. These classes:       &lt;ul&gt;       &lt;li&gt;Exist in the test project. &lt;/li&gt;        &lt;li&gt;Have public properties that identify key data in the scenario (e.g. the Customer ID, Order ID, and backordered Item ID). &lt;/li&gt;        &lt;li&gt;Are instantiated by a test, at which time the scenario data is created. &lt;/li&gt;     &lt;/ul&gt;   &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;In short, data helpers are low-level utilities for creating a specific data record in a specific state, while scenario classes represent larger contexts consisting of multiple entities. I have found that while the time needed to create these objects is not trivial, &lt;strong&gt;it quickly pays off as new tests are easier and easier to write. &lt;/strong&gt;&lt;strong&gt;     &lt;br /&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;h4&gt;&lt;strong&gt;Rule 3: Don't use your DAL to test your DAL&lt;/strong&gt;&lt;/h4&gt;  &lt;p&gt;Tests for DAL code generally set up some data in the database, invoke the DAL, and then verify that the database was properly modified. I've generally found it difficult to use the primary DAL to quickly and concisely verify those assertions.&lt;/p&gt;  &lt;p&gt;In some cases, the primary DAL may not expose a suitable API for doing record-level verification. For example, when there are significant differences between the &lt;em&gt;logical schema &lt;/em&gt;exposed through the domain layer and the &lt;em&gt;physical schema &lt;/em&gt;of the database, it may be impossible (or at least difficult) to write low-level data assertions.&lt;/p&gt;  &lt;p&gt;In other cases, especially early in development, using the DAL to test the DAL creates dependency issues. For instance, many tests involve a sequence of events like &amp;quot;get entity by ID, save changes to it, then verify it was changed&amp;quot;. If &lt;em&gt;both &lt;/em&gt;the GetById() and Save() methods are currently under development then your test will give you inconclusive results until both methods are implemented. &lt;/p&gt;  &lt;p&gt;In all of these cases I've found it valuable to &lt;strong&gt;verify data assertions using a&lt;/strong&gt; &lt;strong&gt;LINQ to SQL data context&lt;/strong&gt;. This provides a convenient, object-based representation of the data schema that is perfectly suited for verifying row-level operations were performed properly. This data context lives in the test project and is automatically regenerated (using &lt;a href="http://msdn.microsoft.com/en-us/library/bb386987.aspx"&gt;SQLMetal.exe&lt;/a&gt;) whenever the schema changes, so it's a close-to-zero-effort solution.&lt;/p&gt;  &lt;p&gt;You could also use a micro ORM like &lt;a href="https://github.com/robconery/massive"&gt;Massive&lt;/a&gt;, or anything else that makes it quick and easy to interact directly with the database.&lt;/p&gt;  &lt;h4&gt;&lt;strong&gt;Rule 4: Tests make no permanent changes to the database&lt;/strong&gt;&lt;/h4&gt;  &lt;p&gt;Tests should be run often, and if you follow Rule #1 your tests create a lot of new data when they run. If you don't clean up after them, your test database will quickly grow in size. Also, if you point your test suite at the same database you use to run your app, you'll quickly get tired of seeing that test data accumulate in your views.&lt;/p&gt;  &lt;p&gt;The easiest way to prevent this is to wrap each test in a database transaction, and then rollback that transaction at the end of the test. This performs the desired cleanup and also isolates tests running in parallel from interfering with each other's data.&lt;/p&gt;  &lt;p&gt;There are a few different ways to approach this. Depending on your needs, check out &lt;a href="http://stackoverflow.com/questions/321180/how-do-i-test-database-related-code-with-nunit"&gt;this&lt;/a&gt; or &lt;a href="http://haacked.com/archive/2005/06/10/4580.aspx"&gt;this&lt;/a&gt;.&lt;/p&gt;  &lt;h4&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/h4&gt;  &lt;p&gt;None of these techniques are particularly clever or game changing, but when used together they can significantly improve your data tests:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;When tests create their own scenario data, you don't need to run them against a particular known state. This reduces maintenance costs significantly. &lt;/li&gt;    &lt;li&gt;Investing in data helpers and scenario classes makes it easy to add new tests. The easier it is to write tests, the more likely that developers will actually do it. &lt;/li&gt;    &lt;li&gt;&amp;quot;Close to the metal&amp;quot; abstractions like LINQ to SQL make it easy to write row- and field-level assertions against the database. &lt;/li&gt;    &lt;li&gt;Adding some &amp;quot;auto rollback&amp;quot; behavior to your data tests keeps your database trim and tidy, no matter how many times you run your test suite. &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Happy data testing!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;div class="lqm_ad" lqm_publisher="lqm.frazzleddad.site" lqm_zone="ron" lqm_format="125x125" lqm_tgs="HTML5%2cWeb%2cAgile%2cALM%2cVS2010%2cSharePoint%2cWPF%2cXML%2cSilverlight%2cTesting%2cTFS%2cMVC%2cAJAX%2c"&gt;&lt;/div&gt;
&lt;script type='text/javascript' language='Javascript' src='http://s1.lqcdn.com/m.min.js?dt=2.3.110104.1'&gt;&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10748614-3388491467524649415?l=frazzleddad.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Frazzleddad/~4/SN_95hfRjsE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://frazzleddad.blogspot.com/feeds/3388491467524649415/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=10748614&amp;postID=3388491467524649415" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/3388491467524649415" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/3388491467524649415" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Frazzleddad/~3/SN_95hfRjsE/31-days-of-testingday-17-rules-for.html" title="31 Days of Testing—Day 17: Rules for Effective Data-Driven Tests" /><author><name>Jim Holmes</name><uri>http://www.blogger.com/profile/05869146736565695900</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://3.bp.blogspot.com/_LOCL59IyTaA/Spykv6_NRpI/AAAAAAAAAAM/S2ZeQAzrL5o/s1600-R/3853180914_14858ae15f_o.jpg" /></author><thr:total>2</thr:total><feedburner:origLink>http://frazzleddad.blogspot.com/2011/12/31-days-of-testingday-17-rules-for.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10748614.post-1333366851188028101</id><published>2011-12-19T13:08:00.001-05:00</published><updated>2011-12-21T00:27:06.959-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="#31DaysOfTesting" /><title type="text">31 Days of Testing-Day 16: Testing Web Services</title><content type="html">&lt;p&gt;&lt;strong&gt;Updated:&lt;/strong&gt; &lt;a href="http://frazzleddad.blogspot.com/2011/12/31-days-of-testing-kickoff.html"&gt;Index to all posts in this series is here&lt;/a&gt;!&lt;/p&gt;  &lt;p&gt;Today’s post is from long-time pal and one-time colleague &lt;a href="http://twitter.com/danhounshell"&gt;Dan Hounshell&lt;/a&gt;. Dan’s been around the Heartland developer community for quite some time, although it’s been awhile since he’s gotten up at a conference or user group to share his great knowledge. (Yes, Dan, that’s a public Dope Slap for you to get presenting again!)&lt;/p&gt;  &lt;p&gt;Dan is the Development lead for the Core at Telligent, makers of Telligent Enterprise and Community platforms for social networking. He’s spent several years building out the extensibility services layer for those platforms and helped build up several thousand integration tests hitting all aspects of testing the services. Dan’s post really speaks to the lessons he’s learned through this journey, which is why I was so happy when he agreed to write this post. He’s been there and done that.&lt;/p&gt;  &lt;p&gt;In other words, he knows of what he speaks. (Well, or writes in this case.)&lt;/p&gt;  &lt;h1&gt;Testing Web Services&lt;/h1&gt;  &lt;p&gt;Let’s kick this off with a big dose of honesty. Creating tests for your web service layer is a lot of hard work. It involves working with a lot of moving parts that can be finicky and stubborn: HTTP, data, XML, maybe JSON, etc. At some point you may get very frustrated. At some point you may consider “skipping it just this once” or maybe even quitting entirely. Do not give up – the payoff is worth it. Once you have a good test suite in place new tests will be easy to add, you will get on a roll and eventually realize you have thousands of tests covering your application. You will be amazed and proud as you watch your test count grow from 100 to 500 and ultimately into the thousands.&lt;/p&gt;  &lt;p&gt;The following tips and advice are based on knowledge that my team and I have gained over the last few years building out a hefty integration test suite for the web service layer of our rather large product. &lt;/p&gt;  &lt;p&gt;&lt;b&gt;Types of tests&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;I have seen a several different methods used for testing web services including unit tests, “light” integration tests, and “full” integration tests. The approach you choose will depend on your experience, how testable your application is, and your goals for your tests. &lt;/p&gt;  &lt;p&gt;Unit testing is valuable if your web service layer has been built in a way that allows it to be tested in isolation. If you already have a thorough test suite of integration tests and functionality tests then unit tests may be all that you need. In this scenario you could unit test common methods, serializers, any mappers or converters, etc. Adding unit tests should prove that your web services work properly when given the right data but they will not guarantee that they are getting it. &lt;/p&gt;  &lt;p&gt;By “light” integration tests I am referring to only testing a layer or so beneath the web services along with them rather than the full stack. Perhaps you have a model layer directly beneath your web services that you can disconnect from the data/persistence layer. Mocking or faking the data will allow you to fully test your web services along with the logic beneath them. This approach is a good option if you already have a full set of integration tests and do not want to spend unnecessary time duplicating testing of the full stack.&lt;/p&gt;  &lt;p&gt;The approach that we adopted is to do “full” integration testing with our web services. Think of where web services live… on top of everything else. Testing the full stack in this manner is akin to doing a functional test but without having to worry about the UI. We continue unit testing pieces of concern but spend the bulk of our budget testing the entire stack. Most of the rest of this article relates to integration testing web services.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Every journey begins with one small step&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;Before you can have a great test suite you have to write the first test. Do it. Make it simple. Do it wrong, it doesn’t matter. Don’t set out to create a grand testing framework, write just one test in one test fixture on one page. New up a web client, make a request to an endpoint on your local development site that will return some data, and write a couple of asserts to make sure the response contains something expected. Now run your test(s). Rinse and repeat. Congratulations, you are on your way! &lt;/p&gt;  &lt;p&gt;&lt;b&gt;Refactor, refactor, refactor&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;Now that you have some tests in place start looking for duplicate/similar code. Make note of every time you cut and paste the same setup code from one test or test fixture to another. Start refactoring those sorts of things out into helpers or into base classes. There is no reason to have the same web client setup code in every test, refactor it so you’ll have something more like this:&lt;/p&gt;  &lt;div id="codeSnippetWrapper"&gt;   &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;     &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;var response = MakeGetRequest(baseUrl, “/api/users.xml”);&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
I am compelled to issue a warning at this point because this is a hole I’ve fallen into myself. Be careful about refactoring too far. Avoid so many layers of abstraction that you hinder test readability. Jim also mentioned this in a previous post, &lt;a href="http://frazzleddad.blogspot.com/2011/12/31-days-of-testingday-8-pay-attention.html"&gt;“Day 8: Pay Attention to Your Tests’ Setup!”&lt;/a&gt;.&lt;/div&gt;

&lt;p&gt;In addition to refactoring the tests themselves, look at the code you are using to setup data for your tests and refactor that as well.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Standing up data&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;Build a library for standing up data that is easy to use with as little input as possible.&lt;/p&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;var user = test_data.CreateUser(“bob”);&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
In the above example passwords, email addresses and other things may be required to create a user in your application but you ought to be able to supply default values for those. &lt;/div&gt;

&lt;p&gt;Add fluid interface support to your test data creation methods to make it easy and fast to create complex sets of related data.&lt;/p&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;var blog = test_data.CreateBlog().WithPosts(3).EachWithComments(3);&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Compare that to the tediousness of the example below:&lt;/p&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;var blog = test_data.CreateBlog(); &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;var post1 = test_data.CreateBlogPost(blog); &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;var post2 = test_data.CreateBlogPost(blog); &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;var post3 = test_data.CreateBlogPost(blog); &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;var comment1_1 = test_data.CreateBlogPostComment(post1); &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;var comment1_2 = test_data.CreateBlogPostComment(post1); &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;… 5 more lines of the same go here … &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;var comment3_3 = test_data.CreateBlogPostComment(post3);&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Consider adding a session monitor to your test data creation library that will keep track of the objects that you create in order to delete them after your tests finish. This allows your developers to concentrate on testing the functionality and not concerning themselves with cleaning up test data when they are finished with it.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Providing data&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;In order to perform a full integration test of your web services you will more than likely need some data store to provide known test data or allow for creation of data on the fly. If your web services only serve up mostly static or known data, you are not allowing creation or updating of data, then you may be able to use your existing local development database for your test suite. Another option is to build a separate database with known data for use by tests, which will work fine for mostly static data or a small number of tests. However, the best option for dynamic data is to use a separate data store from your development/staging database(s). Creating a new database on the fly, filling it with data as-needed for each test, and destroying it when testing is completed is the cleanest method for testing but it is expensive and will increase the time it takes for your test suite to run. &lt;/p&gt;

&lt;p&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Specifications&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;Do yourself and your team a favor by writing your tests using specification syntax. I’m not saying that you need to adopt TDD, BDD or the Talking Mister Ed but just to make your tests readable. Use an existing specification tool or develop your own wrapper around your chosen test framework’s Assert method. You will appreciate the naming standards when you have hundreds of files and thousands of tests in your suite. The below code is a simplified example of something you might see in our web services test suite:&lt;/p&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;Folder: Users&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;Filename: when_making_a_get_user_request_with_invalid_username&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;[TestFixture] &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;class&lt;/span&gt; when_making_a_get_user_request_with_invalid_username () &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;{ &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #008000"&gt;// setup stuff for http request goes here &lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    [Test] &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; response_code_should_be_404()&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    { &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;        response.Code.ShouldBe(404); &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    [Test] &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; response_should_have_errors()&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    { &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;        response.Errors.ShouldNotBeNull(); &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    [Test] &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; response_should_have_user_not_found_error()&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    { &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;        response.Errors&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;                .Where(x =&amp;gt; x.Message.Contains(“User not found”)) &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;                .FirstOrDefault()&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;                .ShouldNotBeNull(); &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;}&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;The above is very readable and is a benefit to your developers, testers and product/project managers. Adopting a specification syntax allows your test suite to describe exactly what your web services deliver.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Foster the process&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;Building a good/thorough/dependable test suite is as much about making testing a first class citizen of the development process as it is writing and running tests. You must evangelize writing tests. To make sure that your test suite gets built and maintained you have to require tests with each code change and require that checks for those tests are part of the dev/code review process.&lt;/p&gt;

&lt;p&gt;Be strict about feature development – updated code should include updated tests and updated documentation. For web services work I recommend adopting a workflow approach of &lt;/p&gt;

&lt;p&gt;1. Development&lt;/p&gt;

&lt;p&gt;2. Create tests (can be swapped with #1 if you TDD)&lt;/p&gt;

&lt;p&gt;3. Create/Update documentation&lt;/p&gt;

&lt;p&gt;4. Dev review / Code review (make sure tests and documentation are in place)&lt;/p&gt;

&lt;p&gt;5. QA review&lt;/p&gt;

&lt;p&gt;6. Build proof of concept, possibly throwaway app, that makes use of your new web service functionality&lt;/p&gt;

&lt;p&gt;If you have good tests in place your QA review may be as simple as explaining the tests that were created and showing that they work. &lt;/p&gt;

&lt;p&gt;Get everyone involved. Let everyone know “this is how we do things when we work on web services”. Empower team members to make wholesale changes, refactor, and become part of the solution.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Other tips&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;Test the edges. Make sure your web services are returning exceptions/errors when they should be.&lt;/p&gt;

&lt;p&gt;Always ask “how can/should/do I test this” when creating/updating code. As Jim has mentioned in many of his previous posts in this series, testing should be part of the development cycle not something optional or tacked onto the end. How can you know that something works without testing? &lt;/p&gt;

&lt;p&gt;Make writing tests fun. Try stuff that you would not/cannot use in production code.&lt;/p&gt;

&lt;p&gt;Always look for new things to test and new ways to test.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://frazzleddad.blogspot.com/2011/12/31-days-of-testing-day-5-choosing-what.html"&gt;“You can’t test everything. Get over it.”&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Run the section of tests in the area you are working regularly, as in several times a coding session. Run the complete set (if possible) before committing your changes. &lt;/p&gt;

&lt;p&gt;&lt;b&gt;Enjoy!&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;Remember that building and maintaining a good test suite is a journey and not a destination. At some point you will realize that you have succeeded in building something great and that all the hard work is worth it. It is a pleasure watching your test suite grow over time and notify you of bugs introduced to your code base. Your customers will appreciate it too!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;div class="lqm_ad" lqm_publisher="lqm.frazzleddad.site" lqm_zone="ron" lqm_format="125x125" lqm_tgs="HTML5%2cWeb%2cAgile%2cALM%2cVS2010%2cSharePoint%2cWPF%2cXML%2cSilverlight%2cTesting%2cTFS%2cMVC%2cAJAX%2c"&gt;&lt;/div&gt;
&lt;script type='text/javascript' language='Javascript' src='http://s1.lqcdn.com/m.min.js?dt=2.3.110104.1'&gt;&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10748614-1333366851188028101?l=frazzleddad.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Frazzleddad/~4/DwCK_v1e7Ag" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://frazzleddad.blogspot.com/feeds/1333366851188028101/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=10748614&amp;postID=1333366851188028101" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/1333366851188028101" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/1333366851188028101" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Frazzleddad/~3/DwCK_v1e7Ag/31-days-of-testing-day-16-testing-web.html" title="31 Days of Testing-Day 16: Testing Web Services" /><author><name>Jim Holmes</name><uri>http://www.blogger.com/profile/05869146736565695900</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://3.bp.blogspot.com/_LOCL59IyTaA/Spykv6_NRpI/AAAAAAAAAAM/S2ZeQAzrL5o/s1600-R/3853180914_14858ae15f_o.jpg" /></author><thr:total>3</thr:total><feedburner:origLink>http://frazzleddad.blogspot.com/2011/12/31-days-of-testing-day-16-testing-web.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10748614.post-5897275617960648121</id><published>2011-12-16T11:40:00.001-05:00</published><updated>2011-12-21T00:26:48.859-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="#31DaysOfTesting" /><title type="text">31 Days of Testing—Day 15: Cucumber is Not A QA Tool</title><content type="html">&lt;p&gt;&lt;strong&gt;Updated:&lt;/strong&gt; &lt;a href="http://frazzleddad.blogspot.com/2011/12/31-days-of-testing-kickoff.html"&gt;Index to all posts in this series is here&lt;/a&gt;!&lt;/p&gt;  &lt;p&gt;Today’s post is written by &lt;a href="http://twitter.com/timwingfield"&gt;Tim Wingfield&lt;/a&gt; and cross-posted from &lt;a href="http://blog.timwingfield.com/"&gt;his own blog&lt;/a&gt;. Tim’s a great pal who I had the pleasure (really!) of working with several years ago. Tim’s walked the walk around hard work getting great communication flowing with customers, and I’ve seen him succeed putting sensible, tailored workflows in to client environments.&lt;/p&gt;  &lt;p&gt;Tim’s had some great experience figuring out what tools can help facilitate good communication, and when I shared my thoughts about this blog series he offered to write up some of his experiences and thinking around Cucumber. Without further ado, Tim’s nicely done post on why Cucumber isn’t just for QA.&lt;/p&gt;  &lt;h1&gt;Cucumber is Not A QA Tool&lt;/h1&gt;  &lt;p&gt;In my recent travels, and they have been plenty, I have come across some interesting views about Cucumber.&lt;/p&gt;  &lt;p&gt;In one conversation someone was looking for &amp;quot;QA people with Cucumber experience.&amp;quot; I thought that was pretty cool, they want some testing folks familiar with a rather popular test framework. Then came the second sentence, &amp;quot;I really don't know what cucumber does, but I know that QA people need to do it.&amp;quot; (Yes, this person was a recruiter, if you couldn't tell by now.)&lt;/p&gt;  &lt;p&gt;Contrast that with conversations with management and leadership types who don't know what cucumber is, but as you explain it you can see the words scrolling across their foreheads, &amp;quot;If we use this, I can lay off half my QA people! Cost savings!!&amp;quot;&lt;/p&gt;  &lt;p&gt;Now, I can not totally discount both points, there is a little merit of truth in each one of them. In the first case, a QA group that knows and understands Cucumber can be a great asset to your product team and the long term quality of your product. Additionally, once Cucumber is in place it will free up some QA departments from doing repetitive manual tests and allow them to focus on more exploratory testing.&lt;/p&gt;  &lt;p&gt;However, if you consider Cucumber to be only a QA tool, you are missing a good portion of what this and other Acceptance Test Driven Development (ATDD) or Behavior Driven Development (BDD) frameworks can provide.&lt;/p&gt;  &lt;p&gt;&lt;em&gt;Note: From this point forward I will use BDD in the discussion. BDD and ATDD are very closely related, almost indistinguishable in practice. I learned the phrase BDD first, so I tend to stick with that. And for writing purposes, it is one character shorter, thus promoting my natural laziness.&lt;/em&gt;&lt;/p&gt;  &lt;h5&gt;Collaboration&lt;/h5&gt;  &lt;p&gt;The biggest benefit to employing Cucumber, or any BDD framework, is that it tears down a lot of the communication walls to which we have become accustomed. Specifically the Gherkin syntax of &lt;code&gt;Given/When/Then&lt;/code&gt; helps bring down those walls by writing acceptance criteria in a plain text format that everybody can agree on.&lt;/p&gt;  &lt;p&gt;From the business's standpoint, finally being able to communicate to the development and QA teams in a clear manner has to be beyond welcome. Being able to work with multiple team members and arrive at something for the delivery team that says:&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;Given the customer has an item in their shopping cart
When the click the checkout button
Then I will have more money in my bank account
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;No more reams of documentation around a simple feature with wire frames, UML diagrams, feature documents, and other items buried in a wiki somewhere that gets ignored. Plain text is easy, and in my experience the easier something is to use, the better chance you have of it getting used.&lt;/p&gt;

&lt;p&gt;Now from the QA person's standpoint, we have actual acceptance criteria that let us know if a feature is complete or not. No more vague language that allows room for interpretation where I can inadvertently add more functionality to a feature because it is what I think the user might want someday. Maybe.&lt;/p&gt;

&lt;p&gt;Finally from the developer's standpoint we have features with an end point and a better description than, &amp;quot;Customer checkout feature.&amp;quot; And not only do we have more clear, concise feature descriptions, when we are done writing code for the features we have a set of tests that will execute against the plain text features.&lt;/p&gt;

&lt;h5&gt;Build What We Want&lt;/h5&gt;

&lt;p&gt;Cucumber benefits all aspects of the business, but possibly its greatest benefit is that it allows us to easily agree to what &amp;quot;done&amp;quot; means for any given feature. The definition of done for a feature won't change over the life of the project, or if it does change we will have a broken Cucumber spec to alert us that we have changed our mind somewhere. But by having that agreement of &amp;quot;done&amp;quot; in place up front it frees up the developers and testers to focus on specifics of implementation and verification of the feature without ambiguity.&lt;/p&gt;

&lt;p&gt;Developers of many languages keep a set of unit tests handy that will break if they make a change to existing code. It may or may not be an intentional break, but the point is they have a safety net to catch a logic change. A suite of Cucumber tests has that same watchful eye over your application as a whole, rather than just at the class or method level. If the business changes how a feature behaves and a cucumber test goes red, then the collaboration can kick back in across the whole team and the team can decide how to handle the original intent of the feature with this new feature request.&lt;/p&gt;

&lt;p&gt;In short, we have a living record of what &amp;quot;done&amp;quot; has meant to every feature we have developed over the life of the project. By executing tests against those acceptance criteria, we also have a constant check that &amp;quot;done&amp;quot; for existing features remains done.&lt;/p&gt;

&lt;h5&gt;Cucumber Helps With Quality&lt;/h5&gt;

&lt;p&gt;So while I don't think Cucumber is a QA specific tool, it helps with overall product quality. By providing a good place for collaboration and by helping the team define &amp;quot;done&amp;quot; early in the feature life cycle, quality has become a first class citizen to your team. Quality is a whole team goal, not just members of the QA team. (By definition, people on the QA team &lt;em&gt;assure&lt;/em&gt; quality, they don't build it.)&lt;/p&gt;

&lt;p&gt;If you are not using Cucumber or another BDD framework, I would highly recommend looking into using one. However, look into those tools for the benefits they can bring your whole team not simply as a QA tool. And while they will help alleviate some manual work for the QA folks on your team, Cucumber is no replacement for a human being who can dive into the application in ways you never thought of when laying out the acceptance criteria.&lt;/p&gt;

&lt;p&gt;After all who would you rather have exploring your app and poking around looking for things to go wrong?&lt;/p&gt;

&lt;p&gt;Your QA team?&lt;/p&gt;

&lt;p&gt;Or your users?&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;div class="lqm_ad" lqm_publisher="lqm.frazzleddad.site" lqm_zone="ron" lqm_format="125x125" lqm_tgs="HTML5%2cWeb%2cAgile%2cALM%2cVS2010%2cSharePoint%2cWPF%2cXML%2cSilverlight%2cTesting%2cTFS%2cMVC%2cAJAX%2c"&gt;&lt;/div&gt;
&lt;script type='text/javascript' language='Javascript' src='http://s1.lqcdn.com/m.min.js?dt=2.3.110104.1'&gt;&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10748614-5897275617960648121?l=frazzleddad.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Frazzleddad/~4/v14lFfh5md4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://frazzleddad.blogspot.com/feeds/5897275617960648121/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=10748614&amp;postID=5897275617960648121" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/5897275617960648121" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/5897275617960648121" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Frazzleddad/~3/v14lFfh5md4/31-days-of-testingday-15-cucumber-is.html" title="31 Days of Testing—Day 15: Cucumber is Not A QA Tool" /><author><name>Jim Holmes</name><uri>http://www.blogger.com/profile/05869146736565695900</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://3.bp.blogspot.com/_LOCL59IyTaA/Spykv6_NRpI/AAAAAAAAAAM/S2ZeQAzrL5o/s1600-R/3853180914_14858ae15f_o.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://frazzleddad.blogspot.com/2011/12/31-days-of-testingday-15-cucumber-is.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10748614.post-7651966710705491839</id><published>2011-12-15T23:34:00.001-05:00</published><updated>2011-12-21T00:26:26.506-05:00</updated><title type="text">31 Days of Testing—Day 14: Tests as Specifications</title><content type="html">&lt;p&gt;&lt;strong&gt;Updated:&lt;/strong&gt; &lt;a href="http://frazzleddad.blogspot.com/2011/12/31-days-of-testing-kickoff.html"&gt;Index to all posts in this series is here&lt;/a&gt;!&lt;/p&gt;  &lt;p&gt;Your automated tests, be they unit, integration, or functional, are amazing things. They validate parts of your system are working as expected; they free you from tedious, repetitive manual testing so you can focus on high-value exploratory testing; etc., etc. etc. Automated tests are great.&lt;/p&gt;  &lt;p&gt;What they aren’t always good at, in their basic form, is providing a good communication channel between the technical folks, testers, and business owners/stakeholders/folks who are writing the checks for your work. Take a look at any of the Selenium examples I’ve used in previous posts. Can you really expect your stakeholders to understand what’s going on? Should you? Likely not, but isn’t this a missed opportunity to get some great communication around what you, your devs, and your stakeholder actually want the system to do?&lt;/p&gt;  &lt;p&gt;To steal a phrase from the mobile world, there’s an app for that. Well, not an app, specifically, but a broad set of tools, frameworks, and nifty software that can help you greatly facilitate those conversations.&lt;/p&gt;  &lt;p&gt;Your non-dev folks on your team likely can’t understand something like the cascading drop down menu test I wrote up the other day:&lt;/p&gt;  &lt;div id="codeSnippetWrapper"&gt;   &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;     &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;[Test]&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; Working_with_no_content()&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;{&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; make = &lt;span style="color: #006080"&gt;&amp;quot;Acura&amp;quot;&lt;/span&gt;;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; model = &lt;span style="color: #006080"&gt;&amp;quot;Integra&amp;quot;&lt;/span&gt;;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; color = &lt;span style="color: #006080"&gt;&amp;quot;Sea Green&amp;quot;&lt;/span&gt;;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    browser.Navigate().GoToUrl(&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #006080"&gt;&amp;quot;http://www.asp.net/ajaxLibrary/AjaxControlToolkitSampleSite/CascadingDropDown/CascadingDropDown.aspx&amp;quot;&lt;/span&gt;);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    var listOfMakes = browser.FindElement(By.Id(&lt;span style="color: #006080"&gt;&amp;quot;ctl00_SampleContent_DropDownList1&amp;quot;&lt;/span&gt;));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    WebDriverWait wait = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; WebDriverWait(browser, TimeSpan.FromSeconds(10));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    wait.Until&amp;lt;IWebElement&amp;gt;((d) =&amp;gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; d.FindElement(By.XPath(&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;        &lt;span style="color: #006080"&gt;&amp;quot;id('ctl00_SampleContent_DropDownList1')/option[text()='&amp;quot;&lt;/span&gt;+make+&lt;span style="color: #006080"&gt;&amp;quot;']&amp;quot;&lt;/span&gt;));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    });&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    var makeOptions = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; SelectElement(listOfMakes);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    makeOptions.SelectByText(make);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    var listOfModels = browser.FindElement(By.Id(&lt;span style="color: #006080"&gt;&amp;quot;ctl00_SampleContent_DropDownList2&amp;quot;&lt;/span&gt;));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    wait.Until&amp;lt;IWebElement&amp;gt;((d) =&amp;gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; d.FindElement(By.XPath(&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;        &lt;span style="color: #006080"&gt;&amp;quot;id('ctl00_SampleContent_DropDownList2')/option[text()='&amp;quot;&lt;/span&gt;+model+&lt;span style="color: #006080"&gt;&amp;quot;']&amp;quot;&lt;/span&gt;));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    });&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    var modelOptions = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; SelectElement(listOfModels);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    modelOptions.SelectByText(model);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    var listOfColors = browser.FindElement(By.Id(&lt;span style="color: #006080"&gt;&amp;quot;ctl00_SampleContent_DropDownList3&amp;quot;&lt;/span&gt;));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    wait.Until&amp;lt;IWebElement&amp;gt;((d) =&amp;gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; d.FindElement(By.XPath(&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;        &lt;span style="color: #006080"&gt;&amp;quot;id('ctl00_SampleContent_DropDownList3')/option[text()='&amp;quot;&lt;/span&gt;+color+&lt;span style="color: #006080"&gt;&amp;quot;']&amp;quot;&lt;/span&gt;));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    });&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    var colorOptions = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; SelectElement(listOfColors);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    colorOptions.SelectByText(color);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    browser.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(10));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    Assert.IsTrue(browser.FindElement(By.Id(&lt;span style="color: #006080"&gt;&amp;quot;ctl00_SampleContent_Label1&amp;quot;&lt;/span&gt;)).Displayed);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;}&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;but what they &lt;em&gt;can&lt;/em&gt; understand is something like this:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Given I am at the main page&lt;/p&gt;

  &lt;p&gt;When I select Acura for Model&lt;/p&gt;

  &lt;p&gt;And I select Integra for Make&lt;/p&gt;

  &lt;p&gt;And I select Sea Green for Color&lt;/p&gt;

  &lt;p&gt;Then the result message shows ‘You selected a Sea Green Acura Integra!’&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This idea of being able to specify your system’s behavior in plain English, or something close to it, is the fundamental concept of tools like Cucumber, JBehave, MSpec, and a host of other tools. Rather than losing behavior in the depths of C#, .NET, Ruby, or some other language we can bring it up to something in plain English like Cucumber, or a set of clearly understood tables in the case of Fitnesse.&lt;/p&gt;

&lt;p&gt;Instead of me spending a lot of bandwidth trying to rehash all this, I am going to give you some reading assignments.&lt;/p&gt;

&lt;p&gt;First, go grab a copy of Gojko Adzic’s &lt;em&gt;&lt;a href="http://www.amazon.com/gp/product/1617290084/ref=as_li_tf_tl?ie=UTF8&amp;amp;tag=frazzleddadco-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=1617290084"&gt;Specifications By Example&lt;/a&gt;.&lt;/em&gt; He does an amazing job of taking the technology out of the picture and laying out a wonderful case for why you should look to specifications. He’s backed up his position with a number of great case studies. Perhaps one of the best books I’ve ever read on testing.&lt;/p&gt;

&lt;p&gt;Secondly, go read Elisabeth Hendrickson’s awesome post on &lt;a href="http://testobsessed.com/blog/2011/12/01/selecting-test-automation-tools/"&gt;selecting test automation tools&lt;/a&gt;. She does a wonderful job of laying out how you might approach investigating and selecting some tools to help you bridge these communication gaps. The only additions I’d make to her great article is to say&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;I don’t think you necessarily need to write tests in the same language as your system. Figure out what tool works best for your team. &lt;/li&gt;

  &lt;li&gt;Think about your customer’s skills and teams if you’re handing off your system and tests. Will your customer be able to deal with all those Fitnesse or Cucumber fixtures? Just because you as a consultant think it’s cool and useful doesn’t mean it’s going to be great for your customer. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Finally, do your own trolling around for topics like Behavior Driven Development, or Acceptance Test Driven Development. There’s some wonderful stuff being written by wicked smart folks.&lt;/p&gt;

&lt;p&gt;Tomorrow I have a post on using Cucumber in the real world written by my good pal Tim Wingfield. I think that will be a great practical follow on to this post!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;div class="lqm_ad" lqm_publisher="lqm.frazzleddad.site" lqm_zone="ron" lqm_format="125x125" lqm_tgs="HTML5%2cWeb%2cAgile%2cALM%2cVS2010%2cSharePoint%2cWPF%2cXML%2cSilverlight%2cTesting%2cTFS%2cMVC%2cAJAX%2c"&gt;&lt;/div&gt;
&lt;script type='text/javascript' language='Javascript' src='http://s1.lqcdn.com/m.min.js?dt=2.3.110104.1'&gt;&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10748614-7651966710705491839?l=frazzleddad.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Frazzleddad/~4/DcoIoWEI4GM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://frazzleddad.blogspot.com/feeds/7651966710705491839/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=10748614&amp;postID=7651966710705491839" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/7651966710705491839" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/7651966710705491839" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Frazzleddad/~3/DcoIoWEI4GM/31-days-of-testingday-14-tests-as.html" title="31 Days of Testing—Day 14: Tests as Specifications" /><author><name>Jim Holmes</name><uri>http://www.blogger.com/profile/05869146736565695900</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://3.bp.blogspot.com/_LOCL59IyTaA/Spykv6_NRpI/AAAAAAAAAAM/S2ZeQAzrL5o/s1600-R/3853180914_14858ae15f_o.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://frazzleddad.blogspot.com/2011/12/31-days-of-testingday-14-tests-as.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10748614.post-910496131823593429</id><published>2011-12-14T14:41:00.001-05:00</published><updated>2011-12-14T14:41:53.124-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="#31DaysOfTesting" /><title type="text">Test Studio Webinar on Functional Test 201 (Common Problems)</title><content type="html">&lt;p&gt;If you liked &lt;a href="http://frazzleddad.blogspot.com/2011/12/31-days-of-testingday-13-functional.html"&gt;today’s post on how to get through common problems in web UI automation&lt;/a&gt;, you may be interested in attending a Telerik Test Studio webinar I’m giving tomorrow.&lt;/p&gt;  &lt;p&gt;This webinar is one of our regular Thursday “intro to Test Studio” webinars, but I’m going to morph its content a bit and focus on walking through the three cases I laid out in the other post. Along the way you’ll get some exposure to other neat features of Test Studio. If there’s time I’ll also walk through performance and manual testing features we have.&lt;/p&gt;  &lt;p&gt;In case it’s not obvious, I won’t be walking through much Selenium code, although I’ll speak to commonalities and differences.&lt;/p&gt;  &lt;p&gt;I’d love to have you join the webinar. &lt;a href="https://www1.gotomeeting.com/register/420871649"&gt;Go register&lt;/a&gt; if you’re interested!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;div class="lqm_ad" lqm_publisher="lqm.frazzleddad.site" lqm_zone="ron" lqm_format="125x125" lqm_tgs="HTML5%2cWeb%2cAgile%2cALM%2cVS2010%2cSharePoint%2cWPF%2cXML%2cSilverlight%2cTesting%2cTFS%2cMVC%2cAJAX%2c"&gt;&lt;/div&gt;
&lt;script type='text/javascript' language='Javascript' src='http://s1.lqcdn.com/m.min.js?dt=2.3.110104.1'&gt;&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10748614-910496131823593429?l=frazzleddad.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Frazzleddad/~4/fS2JlQR3IsY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://frazzleddad.blogspot.com/feeds/910496131823593429/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=10748614&amp;postID=910496131823593429" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/910496131823593429" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/910496131823593429" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Frazzleddad/~3/fS2JlQR3IsY/test-studio-webinar-on-functional-test.html" title="Test Studio Webinar on Functional Test 201 (Common Problems)" /><author><name>Jim Holmes</name><uri>http://www.blogger.com/profile/05869146736565695900</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://3.bp.blogspot.com/_LOCL59IyTaA/Spykv6_NRpI/AAAAAAAAAAM/S2ZeQAzrL5o/s1600-R/3853180914_14858ae15f_o.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://frazzleddad.blogspot.com/2011/12/test-studio-webinar-on-functional-test.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10748614.post-2167095352276341353</id><published>2011-12-14T08:29:00.001-05:00</published><updated>2011-12-21T00:26:09.351-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="#31DaysOfTesting" /><title type="text">31 Days of Testing—Day 13: Functional Test 201 (Common Problems)</title><content type="html">&lt;p&gt;&lt;strong&gt;Updated:&lt;/strong&gt; &lt;a href="http://frazzleddad.blogspot.com/2011/12/31-days-of-testing-kickoff.html"&gt;Index to all posts in this series is here&lt;/a&gt;!&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Updated:&lt;/strong&gt; (Updated Case 2. &lt;a href="http://twitter.com/adamgoucher"&gt;Adam Goucher&lt;/a&gt; pointed out I referenced the Wait class which is for explicit waits, yet this case is about &lt;strong&gt;&lt;em&gt;implicit&lt;/em&gt;&lt;/strong&gt; waits. Yeesh. Major writing failure. Thankfully I have good pals who keep me straight.)&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; I missed posting this yesterday due to a long trip back and forth to visit the amazing new expansion at the Kalahari in preparation for &lt;a href="http://codemash.org"&gt;CodeMash&lt;/a&gt;. That coupled with a sick kid left me behind the power curve. Sorry!&lt;/p&gt;  &lt;p&gt;Functional testing at the UI, particularly for web tests, can be a pile of convoluted, confusing poo. Headaches from AJAX dynamic calls, oddball JavaScript events interfering with fields you’re trying to work with, horrific DOM models, the list goes on and on. &lt;/p&gt;  &lt;p&gt;There’s a nice opener for you, but never fear, I’m going to help you through a few of those—because I’ve suffered through them and would prefer you didn’t have to.&lt;/p&gt;  &lt;p&gt;Testing a web application means you need to get down and jinky with exactly how your page is loading. You’ll need to work closely with your developers to understand what sorts of JavaScript you have on your page. You’ll need to look closely at your testing framework to understand how it interacts with the DOM and how it handles dynamic content situations.&lt;/p&gt;  &lt;p&gt;Let’s look at three common situations you’ll likely run in to on web applications:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;JavaScript is bound to an input field and is disrupting your test’s input to that field. &lt;/li&gt;    &lt;li&gt;You’re working with an AJAX-ified page where the &lt;em&gt;element&lt;/em&gt; you need isn’t yet loaded. &lt;/li&gt;    &lt;li&gt;You’re working with an AJAX-ified page where the &lt;em&gt;content&lt;/em&gt; you need isn’t yet loaded. &lt;/li&gt; &lt;/ol&gt;  &lt;h2&gt;Case 1: JavaScript is Messing With My Fields!&lt;/h2&gt;  &lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: You’ve got a JavaScript validation attached to one or more fields you’re trying to interact with. For example, on the &lt;a href="http://growing-planet-634.herokuapp.com/welcome"&gt;Test Studio demo app&lt;/a&gt; we use JavaScript to check that the password and username fields &lt;em&gt;both&lt;/em&gt; have content before we enable the login button. This JavaScript is causing your test framework some grief and the validation isn’t working to enable the button.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Answer:&lt;/strong&gt; Varies by framework. When using Selenium, the field’s &lt;em&gt;onchange&lt;/em&gt; event only fires when focus leaves that field. You’ll need to click to another field to get the validation to work properly. That might look something like this:&lt;/p&gt;  &lt;div id="codeSnippetWrapper"&gt;   &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;     &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;browser.FindElement(By.Id(&lt;span style="color: #006080"&gt;&amp;quot;username&amp;quot;&lt;/span&gt;)).SendKeys(&lt;span style="color: #006080"&gt;&amp;quot;testuser&amp;quot;&lt;/span&gt;);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;browser.FindElement(By.Id(&lt;span style="color: #006080"&gt;&amp;quot;password&amp;quot;&lt;/span&gt;)).SendKeys(&lt;span style="color: #006080"&gt;&amp;quot;abc123&amp;quot;&lt;/span&gt;);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;browser.FindElement(By.Id(&lt;span style="color: #006080"&gt;&amp;quot;username&amp;quot;&lt;/span&gt;)).Click();&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;In &lt;a href="http://www.telerik.com/automated-testing-tools.aspx"&gt;Test Studio&lt;/a&gt;, we inject text directly to the DOM, so no events at all get fired on fields. You’ll need to use the “SimulateRealTyping” property to push the input text through the browser itself. This is a property on that specific test step:&lt;/p&gt;
&lt;a title="SimTyping by Jim Holmes OH, on Flickr" href="http://www.flickr.com/photos/jimholmes/6508993283/"&gt;&lt;img alt="SimTyping" src="http://farm8.staticflickr.com/7004/6508993283_2e97cccb00.jpg" width="500" height="170" /&gt;&lt;/a&gt; 

&lt;p&gt;Other testing tools/frameworks will have their own approaches to solving this problem. Point being, spend time understanding your app and your tools.&lt;/p&gt;

&lt;h2&gt;Case 2: AJAX (or some dynamic system) hasn’t yet loaded the element I need!&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; The elements you need to work with haven’t yet been loaded on the page. &lt;/p&gt;

&lt;p&gt;Have a look at the &lt;a href="http://www.asp.net/ajaxLibrary/AjaxControlToolkitSampleSite/DropDown/DropDown.aspx"&gt;ASP.NET AJAX demo page for a drop down menu&lt;/a&gt;. Use some tool like Firebug to inspect the current DOM and you’ll see the menu elements aren’t actually loaded on the page yet. The menu elements (the ones you want to interact with!) don’t get loaded until you click on the pull-down list. You can’t validate or interact with elements that aren’t yet there!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Answer:&lt;/strong&gt; Use Selenium’s implicit wait feature&amp;#160; to help out with this. You set a default timeout for the browser session you’re currently using, after which Selenium will wait for the configured amount of time for the elements to appear. If the elements don’t show up in the DOM, you’ll see an Exception, which is what you want – the elements aren’t appearing, so your test is failing. In Selenium this looks like:&lt;/p&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;[Test]&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; Validate_correct_choices_generate_correct_answer()&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;{&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    browser.Navigate().GoToUrl(&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;        &lt;span style="color: #006080"&gt;&amp;quot;http://www.asp.net/ajaxLibrary/AjaxControlToolkitSampleSite/DropDown/DropDown.aspx&amp;quot;&lt;/span&gt;);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    browser.FindElement(By.Id(&lt;span style="color: #006080"&gt;&amp;quot;ctl00_SampleContent_TextLabel&amp;quot;&lt;/span&gt;)).Click();&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    browser.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(10));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    browser.FindElement(By.Id(&lt;span style="color: #006080"&gt;&amp;quot;ctl00_SampleContent_Option1&amp;quot;&lt;/span&gt;)).Click();&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    Assert.IsTrue(browser.FindElement(By.Id(&lt;span style="color: #006080"&gt;&amp;quot;ctl00_SampleContent_lblSelection&amp;quot;&lt;/span&gt;)).Displayed);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;}&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Note we’re setting the ImplicitWait property to ten seconds. The test will hang around that long waiting for the service call to finish and the DOM to update with the element we need to work with.&lt;/p&gt;

&lt;p&gt;In Test Studio we already handle this sort of implicit wait without any extra steps.&lt;/p&gt;

&lt;p&gt;Not sure how Watir handles these sorts of situations, but I would assume (yes, bad word!) that it’s very similar.&lt;/p&gt;

&lt;h2&gt;Case 3: AJAX (or some dynamic system) hasn’t yet loaded the ContentI need!&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; The content you need to work with hasn’t yet been loaded on the page. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Answer:&lt;/strong&gt; Use explicit waits to delay your test’s execution until the content is loaded.&lt;/p&gt;

&lt;p&gt;Have a look at the &lt;a href="http://www.asp.net/ajaxLibrary/AjaxControlToolkitSampleSite/CascadingDropDown/CascadingDropDown.aspx"&gt;ASP.NET AJAX demo page for cascading menus&lt;/a&gt;. &lt;/p&gt;
&lt;a title="cascading by Jim Holmes OH, on Flickr" href="http://www.flickr.com/photos/jimholmes/6510194967/"&gt;&lt;img alt="cascading" src="http://farm8.staticflickr.com/7143/6510194967_9b59d9c5d5.jpg" width="500" height="447" /&gt;&lt;/a&gt; 

&lt;p&gt;Use some tool like Firebug to inspect the current DOM and you’ll see the menu elements are there, but not their content. Make some choices for the menus, look at the DOM. Now you’ll see the content exists after having been loaded by an AJAX callback. Because the elements exist, but not their content, we need to handle this with an explicit wait. (See &lt;a href="http://seleniumhq.org/docs/04_webdriver_advanced.html#explicit-and-implicit-waits-reference"&gt;the awesome Selenium article on explicit and implicit waits&lt;/a&gt;. You &lt;strong&gt;&lt;em&gt;need&lt;/em&gt;&lt;/strong&gt; to read this!) &lt;/p&gt;

&lt;p&gt;In the example above we need to navigate to the page, then make a series of selctions. In each dropdown selection list, we’ll need to get that selection element, then wait for the specific item we want to appear. That content is loaded by the previous selection, or in the case of the Make selection, by the actual page loading. &lt;/p&gt;

&lt;p&gt;Here’s how we write the first section of code to wait and select our option in the Make pulldown. After that, we use WebDriverWait to explicitly wait until our target element (the make of “Acura” in this case) appears. Once the content is loaded in to the options we can then select the specific item from the option list.&lt;/p&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;[Test]&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; Working_with_no_content()&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;{&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; make = &lt;span style="color: #006080"&gt;&amp;quot;Acura&amp;quot;&lt;/span&gt;;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; model = &lt;span style="color: #006080"&gt;&amp;quot;Integra&amp;quot;&lt;/span&gt;;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; color = &lt;span style="color: #006080"&gt;&amp;quot;Sea Green&amp;quot;&lt;/span&gt;;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    browser.Navigate().GoToUrl(&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;        &lt;span style="color: #006080"&gt;&amp;quot;http://www.asp.net/ajaxLibrary/AjaxControlToolkitSampleSite/CascadingDropDown/CascadingDropDown.aspx&amp;quot;&lt;/span&gt;);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    var listOfMakes = browser.FindElement(By.Id(&lt;span style="color: #006080"&gt;&amp;quot;ctl00_SampleContent_DropDownList1&amp;quot;&lt;/span&gt;));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    WebDriverWait wait = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; WebDriverWait(browser, TimeSpan.FromSeconds(10));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    wait.Until&amp;lt;IWebElement&amp;gt;((d) =&amp;gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;        &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; d.FindElement(By.XPath(&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;            &lt;span style="color: #006080"&gt;&amp;quot;id('ctl00_SampleContent_DropDownList1')/option[text()='&amp;quot;&lt;/span&gt;+make+&lt;span style="color: #006080"&gt;&amp;quot;']&amp;quot;&lt;/span&gt;));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    });&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    var makeOptions = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; SelectElement(listOfMakes);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    makeOptions.SelectByText(make);&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Actions on the remaining two lists are the same. For the final confirmation message, also AJAX-ified, we can go back to our implicit wait pattern shown earlier—that message is in an element which isn’t loaded on the page. (&lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt; The test for the confirmation message isn’t a great one. A better one would be to ensure the proper combination of make+model+color is rendered as well as the general message – I took this shortcut for brevity’s sake because this example’s already somewhat lengthy.)&lt;/p&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;browser.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(10));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;Assert.IsTrue(browser.FindElement(By.Id(&lt;span style="color: #006080"&gt;&amp;quot;ctl00_SampleContent_Label1&amp;quot;&lt;/span&gt;)).Displayed);&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div&gt;The entire test looks like this:&lt;/div&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;[Test]&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; Working_with_no_content()&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;{&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; make = &lt;span style="color: #006080"&gt;&amp;quot;Acura&amp;quot;&lt;/span&gt;;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; model = &lt;span style="color: #006080"&gt;&amp;quot;Integra&amp;quot;&lt;/span&gt;;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; color = &lt;span style="color: #006080"&gt;&amp;quot;Sea Green&amp;quot;&lt;/span&gt;;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    browser.Navigate().GoToUrl(&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;        &lt;span style="color: #006080"&gt;&amp;quot;http://www.asp.net/ajaxLibrary/AjaxControlToolkitSampleSite/CascadingDropDown/CascadingDropDown.aspx&amp;quot;&lt;/span&gt;);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    var listOfMakes = browser.FindElement(By.Id(&lt;span style="color: #006080"&gt;&amp;quot;ctl00_SampleContent_DropDownList1&amp;quot;&lt;/span&gt;));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    WebDriverWait wait = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; WebDriverWait(browser, TimeSpan.FromSeconds(10));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    wait.Until&amp;lt;IWebElement&amp;gt;((d) =&amp;gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;        &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; d.FindElement(By.XPath(&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;            &lt;span style="color: #006080"&gt;&amp;quot;id('ctl00_SampleContent_DropDownList1')/option[text()='&amp;quot;&lt;/span&gt;+make+&lt;span style="color: #006080"&gt;&amp;quot;']&amp;quot;&lt;/span&gt;));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    });&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    var makeOptions = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; SelectElement(listOfMakes);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    makeOptions.SelectByText(make);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    var listOfModels = browser.FindElement(By.Id(&lt;span style="color: #006080"&gt;&amp;quot;ctl00_SampleContent_DropDownList2&amp;quot;&lt;/span&gt;));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    wait.Until&amp;lt;IWebElement&amp;gt;((d) =&amp;gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;        &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; d.FindElement(By.XPath(&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;            &lt;span style="color: #006080"&gt;&amp;quot;id('ctl00_SampleContent_DropDownList2')/option[text()='&amp;quot;&lt;/span&gt;+model+&lt;span style="color: #006080"&gt;&amp;quot;']&amp;quot;&lt;/span&gt;));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    });&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    var modelOptions = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; SelectElement(listOfModels);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    modelOptions.SelectByText(model);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    var listOfColors = browser.FindElement(By.Id(&lt;span style="color: #006080"&gt;&amp;quot;ctl00_SampleContent_DropDownList3&amp;quot;&lt;/span&gt;));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    wait.Until&amp;lt;IWebElement&amp;gt;((d) =&amp;gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;        &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; d.FindElement(By.XPath(&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;            &lt;span style="color: #006080"&gt;&amp;quot;id('ctl00_SampleContent_DropDownList3')/option[text()='&amp;quot;&lt;/span&gt;+color+&lt;span style="color: #006080"&gt;&amp;quot;']&amp;quot;&lt;/span&gt;));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    });&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    var colorOptions = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; SelectElement(listOfColors);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    colorOptions.SelectByText(color);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    browser.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(10));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    Assert.IsTrue(browser.FindElement(By.Id(&lt;span style="color: #006080"&gt;&amp;quot;ctl00_SampleContent_Label1&amp;quot;&lt;/span&gt;)).Displayed);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;}&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div&gt;Test Studio handles this in a similar fashion: wait for the item you need to work with to appear, then interact with it. If you’re working straight in code with the Telerik Testing framework, then it’s not overly different from above in concept. If you’re working with the Test Studio standalone edition the test would look like this:&lt;/div&gt;
&lt;a title="studio_ajax_cascade by Jim Holmes OH, on Flickr" href="http://www.flickr.com/photos/jimholmes/6510478749/"&gt;&lt;img alt="studio_ajax_cascade" src="http://farm8.staticflickr.com/7008/6510478749_a89c5391c9.jpg" width="500" height="224" /&gt;&lt;/a&gt; 

&lt;h3&gt;Wrapping Up&lt;/h3&gt;

&lt;p&gt;There you have it. A quick walk-through of three common situations on web pages that can give automation folks grief.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;div class="lqm_ad" lqm_publisher="lqm.frazzleddad.site" lqm_zone="ron" lqm_format="125x125" lqm_tgs="HTML5%2cWeb%2cAgile%2cALM%2cVS2010%2cSharePoint%2cWPF%2cXML%2cSilverlight%2cTesting%2cTFS%2cMVC%2cAJAX%2c"&gt;&lt;/div&gt;
&lt;script type='text/javascript' language='Javascript' src='http://s1.lqcdn.com/m.min.js?dt=2.3.110104.1'&gt;&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10748614-2167095352276341353?l=frazzleddad.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Frazzleddad/~4/I2Nn90mZ0cI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://frazzleddad.blogspot.com/feeds/2167095352276341353/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=10748614&amp;postID=2167095352276341353" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/2167095352276341353" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/2167095352276341353" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Frazzleddad/~3/I2Nn90mZ0cI/31-days-of-testingday-13-functional.html" title="31 Days of Testing—Day 13: Functional Test 201 (Common Problems)" /><author><name>Jim Holmes</name><uri>http://www.blogger.com/profile/05869146736565695900</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://3.bp.blogspot.com/_LOCL59IyTaA/Spykv6_NRpI/AAAAAAAAAAM/S2ZeQAzrL5o/s1600-R/3853180914_14858ae15f_o.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://frazzleddad.blogspot.com/2011/12/31-days-of-testingday-13-functional.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10748614.post-6750698437443245044</id><published>2011-12-12T23:15:00.000-05:00</published><updated>2011-12-21T00:24:02.913-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="#31DaysOfTesting" /><title type="text">31 Days of Testing—Day 12: Functional Test 101</title><content type="html">&lt;p&gt;&lt;strong&gt;Updated:&lt;/strong&gt; &lt;a href="http://frazzleddad.blogspot.com/2011/12/31-days-of-testing-kickoff.html"&gt;Index to all posts in this series is here&lt;/a&gt;!&lt;/p&gt;  &lt;p&gt;In my last post I gave you a bit of a drive-by spewing of some of the things I think are critical to keep your automation maintainable, particularly in the functional test world.&lt;/p&gt;  &lt;p&gt;Now let’s take a look at what a functional UI test looks like. Again, there’s a huge difference in the specifics between platforms and frameworks/tools. I’m going to focus on a very simple web UI test to kick off here. I’m using Selenium 2.15. &lt;a href="http://code.google.com/p/selenium/"&gt;Selenium’s&lt;/a&gt; an open source web testing framework available on nearly every platform you could care to write code for. &lt;a href="http://watir.com/"&gt;Watir&lt;/a&gt; for Ruby is another example of an open source web testing framework. &lt;a href="http://www.telerik.com/automated-testing-tools.aspx"&gt;Test Studio&lt;/a&gt;, the commercial product I’m involved with, has a &lt;a href="http://www.telerik.com/automated-testing-tools/free-testing-framework.aspx"&gt;free testing framework&lt;/a&gt; as its underpinnings.&lt;/p&gt;  &lt;p&gt;These tools are used in stand alone tests, but they’re also used extensively as backing for other specification-style testing tools such as Cucumber, Fitness, Capybara, and others.&lt;/p&gt;  &lt;p&gt;All these frameworks use roughly the same approach to testing your web applications:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Start up a browser &lt;/li&gt;    &lt;li&gt;Navigate somewhere &lt;/li&gt;    &lt;li&gt;Find things of interest on the page to interact with      &lt;ul&gt;       &lt;li&gt;Inspect text &lt;/li&gt;        &lt;li&gt;Input text &lt;/li&gt;        &lt;li&gt;Click &lt;/li&gt;        &lt;li&gt;Scroll &lt;/li&gt;        &lt;li&gt;Blow up &lt;/li&gt;     &lt;/ul&gt;   &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;(I may be exaggerating with the Blow Up part.)&lt;/p&gt;  &lt;p&gt;Without further ado, let’s take a look at how a functional test with Selenium is built. For simplicity’s sake and readability I’m leaving this test a bit more linear than I normally would. I’ll show you some potential refactorings/optimizations in later posts—including some discussion of the Page Object Pattern I’ve mentioned previously.&lt;/p&gt;  &lt;p&gt;You may need a separate test framework to actually execute your functional framework inside of. Selenium doesn’t come with any form of test runner, reporting engine, or even Assert support. The examples below use NUnit to drive the tests around&lt;/p&gt;  &lt;p&gt;The app we’ll be testing is &lt;a href="http://growing-planet-634.herokuapp.com/"&gt;the Test Studio demo app hosted at Heroku&lt;/a&gt;. We’ll use two simple tests: check for the login link on the home page, and validate that we can log on to the system.&lt;/p&gt;  &lt;p&gt;First test first. Skipping over setting up the project and references, the first thing you’ll need to do is declare a test fixture, plus a variable for the browser we’ll be working with. Selenium supports many different browser drivers, all of which implement the IWebDriver interface.&lt;/p&gt;  &lt;div id="codeSnippetWrapper"&gt;   &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;     &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;[TestFixture]&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;class&lt;/span&gt; Home_page_displays_correctly&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;{&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    IWebDriver browser;&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Now we can use a fixture setup method to instantiate our browser. I’m using the FireFox driver, so there are a few profile and binary executable file things I need to deal with. &lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt; In a true implementation, I’d have this instantiation handled by some sort of provider so I could flexibly stand up Firefox, Chrome, IE, or whatever browser I wanted this run to be with.&lt;/p&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;[TestFixtureSetUp]&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; Run_once_before_anything()&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;{&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    var profile = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; FirefoxProfile();&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    var exe = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; FirefoxBinary();&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    browser = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; FirefoxDriver(exe, profile);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    browser.Navigate().GoToUrl(&lt;span style="color: #006080"&gt;&amp;quot;http://growing-planet-634.herokuapp.com/welcome&amp;quot;&lt;/span&gt;)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;}&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;The last step is to actually navigate to the URL/page we’re going to work with. You need to understand that the navigation will cause WebDriver to wait for the page’s “onload” event to fire. This is a handy blocking step, meaning you don’t need to worry about adding in code to pause and wait for the page to load. You also need to understand this does &lt;strong&gt;&lt;em&gt;not&lt;/em&gt;&lt;/strong&gt; have anything to do with dynamic content. You’ll need to handle that yourself. More on that later.&lt;/p&gt;

&lt;p&gt;We’ve navigated to the home page, and now it’s time to find the elements we want to test for. Each framework is subtly different, but the general idea is the same: specify an HTML ID, CSS selector, XPath, or some other form of find logic so the framework can locate the element you want. In our WebDriver test we do the following:&lt;/p&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;[Test]&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; Login_link_is_correct()&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;{&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    IWebElement loginLink = browser.FindElement(By.Id(&lt;span style="color: #006080"&gt;&amp;quot;login_link&amp;quot;&lt;/span&gt;));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    Assert.IsTrue(loginLink.Text.Equals(&lt;span style="color: #006080"&gt;&amp;quot;Login&amp;quot;&lt;/span&gt;));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    Assert.IsTrue(loginLink.GetAttribute(&lt;span style="color: #006080"&gt;&amp;quot;href&amp;quot;&lt;/span&gt;).Contains(&lt;span style="color: #006080"&gt;&amp;quot;/login&amp;quot;&lt;/span&gt;));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;}&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;We’re grabbing the element with the ID of “login_link”, then we’re checking text and attribute values on that element.&amp;#160; We’re using NUnit Asserts to make the actual test.&lt;/p&gt;

&lt;p&gt;How do we know which IDs to work with? First, best answer: talk with the developers who created that UI. Second, nearly as good an answer: use a tool like Firebug, IE Developer Toolbar, or the DOM Explorer in Test Studio to help you walk through the page’s DOM.&lt;/p&gt;

&lt;p&gt;We can also interact with elements on the page in the same fashion. If I want to log in, I’ll use these steps right after the initial navigation step:&lt;/p&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;browser.FindElement(By.Id(&lt;span style="color: #006080"&gt;&amp;quot;login_link&amp;quot;&lt;/span&gt;)).Click();&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;browser.FindElement(By.Id(&lt;span style="color: #006080"&gt;&amp;quot;username&amp;quot;&lt;/span&gt;)).SendKeys(&lt;span style="color: #006080"&gt;&amp;quot;testuser&amp;quot;&lt;/span&gt;);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;browser.FindElement(By.Id(&lt;span style="color: #006080"&gt;&amp;quot;password&amp;quot;&lt;/span&gt;)).SendKeys(&lt;span style="color: #006080"&gt;&amp;quot;abc123&amp;quot;&lt;/span&gt;);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;browser.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(10));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;browser.FindElement(By.Id(&lt;span style="color: #006080"&gt;&amp;quot;login_button&amp;quot;&lt;/span&gt;)).Click();&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;I can interact with elements by first finding them, then using sensible commands like Click() for buttons/links, or SendKeys for input fields.&lt;/p&gt;

&lt;p&gt;The fourth statement in this group sets an implicit wait for the navigation after the final Click() event&amp;#160; on the login button. While WebDriver nicely waited for the page to completely load after the Navigate call above, it doesn’t know to wait for a page load after clicking a link. We’re putting in an implicit wait to have Selenium pause before failing subsequent actions after that navigation.&lt;/p&gt;

&lt;p&gt;In this case, that subsequent action is another test validating the presence of the log&lt;strong&gt;&lt;em&gt;off&lt;/em&gt;&lt;/strong&gt;&amp;#160; link.&lt;/p&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;[Test]&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; Logout_link_displays()&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;{&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    Assert.IsTrue(browser.FindElement(By.LinkText(&lt;span style="color: #006080"&gt;&amp;quot;Logout&amp;quot;&lt;/span&gt;)).Displayed);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;}&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Without the implicit wait, WebDriver would blow past this assert, failing it because the page hadn’t properly loaded.&lt;/p&gt;

&lt;p&gt;Implicit and explicit waits are one of the most critical things an automation developer needs to understand. The Selenium documentation has &lt;a href="http://seleniumhq.org/docs/04_webdriver_advanced.html#explicit-and-implicit-waits-reference"&gt;a great article&lt;/a&gt; on it which you need to read and understand—you’ll save yourself a lot of pain!&lt;/p&gt;

&lt;p&gt;This brief overview showed some common themes regardless of the automation framework you’re using. You need to create a browser, have it navigate somewhere. That navigation may or may not handle delays necessary for your testing. Once you’re at a page you can find and interact with elements on that page.&lt;/p&gt;

&lt;p&gt;I’ll follow this with a post on dealing with some common troublesome issues like AJAX or other dynamic content, pop up windows, file system dialogs, and others.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;div class="lqm_ad" lqm_publisher="lqm.frazzleddad.site" lqm_zone="ron" lqm_format="125x125" lqm_tgs="HTML5%2cWeb%2cAgile%2cALM%2cVS2010%2cSharePoint%2cWPF%2cXML%2cSilverlight%2cTesting%2cTFS%2cMVC%2cAJAX%2c"&gt;&lt;/div&gt;
&lt;script type='text/javascript' language='Javascript' src='http://s1.lqcdn.com/m.min.js?dt=2.3.110104.1'&gt;&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10748614-6750698437443245044?l=frazzleddad.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Frazzleddad/~4/JGnjPoxI18w" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://frazzleddad.blogspot.com/feeds/6750698437443245044/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=10748614&amp;postID=6750698437443245044" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/6750698437443245044" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/6750698437443245044" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Frazzleddad/~3/JGnjPoxI18w/31-days-of-testingday-12-functional.html" title="31 Days of Testing—Day 12: Functional Test 101" /><author><name>Jim Holmes</name><uri>http://www.blogger.com/profile/05869146736565695900</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://3.bp.blogspot.com/_LOCL59IyTaA/Spykv6_NRpI/AAAAAAAAAAM/S2ZeQAzrL5o/s1600-R/3853180914_14858ae15f_o.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://frazzleddad.blogspot.com/2011/12/31-days-of-testingday-12-functional.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10748614.post-2302210426869233877</id><published>2011-12-11T23:22:00.001-05:00</published><updated>2011-12-21T00:23:23.452-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="#31DaysOfTesting" /><title type="text">31 Days of Testing—Day 11: Maintainable Functional Automation</title><content type="html">&lt;p&gt;&lt;strong&gt;Updated:&lt;/strong&gt; &lt;a href="http://frazzleddad.blogspot.com/2011/12/31-days-of-testing-kickoff.html"&gt;Index to all posts in this series is here&lt;/a&gt;!&lt;/p&gt;  &lt;p&gt;I wanted to write a blog post on “Introduction to Functional Automation Testing” but I realized I firmly believe there are a number of things to know before you ever start down the road of functional automation so that you don’t get bushwhacked a few weeks or months in to your effort.&lt;/p&gt;  &lt;p&gt;In my view, functional tests via a browser or desktop automation tool, are by far the slowest, most brittle, most curmudgeonly type of automated test. Maybe that’s why I like them so much…&lt;/p&gt;  &lt;p&gt;I’ve been fortunate to have spent a few trips over the last month working with customers to train them up on &lt;a href="http://www.telerik.com/automated-testing-tools.aspx"&gt;Telerik’s Test Studio&lt;/a&gt;, the great automation tool I’ve joined Telerik to help promote. I think the customers are a little taken aback when I get very frank and animated about the difficulties around functional test automation. I also make it very clear that everything you do around functional test automation is closely tied to how well your tests will be running in four weeks or four months.&lt;/p&gt;  &lt;p&gt;I’ve also been spending a LOT of time wandering around to various conferences and user groups giving my talk on &lt;a href="http://speakerdeck.com/u/jimholmes/p/automation-isnt-all-shiny-toys"&gt;Automation Isn’t Shiny Toys&lt;/a&gt;. That talk really drives home the point that you’ve got to carefully focus on your automation strategy if you want to succeed and stay sane.&lt;/p&gt;  &lt;p&gt;In this post I want to highlight a few of the things I discuss in that talk. I’ll dive in to greater detail on a few of those points in later posts, but the post you’re reading now will give you the broad view.&lt;/p&gt;  &lt;p&gt;I’ve seen automation fail, or cause tremendous stress, in a number of situations: Projects I’ve been on, projects I’ve seen colleagues on, projects I’ve talked about with friends and other conference attendees, and projects I’ve seen at customer sites. I’ve come to see three common threads among these conversations: long-running tests, brittle tests which break too frequently, and tests which take too much time to maintain over the life of the project. (I also admit that items two and three are likely the same root cause.)&lt;/p&gt;  &lt;h1&gt;Long Running Tests&lt;/h1&gt;  &lt;p&gt;Long-running test suites can suck the trust out of your project. Functional tests at the UI level will always be much slower than other sorts of tests, but you can’t have a productive, trustworthy automation environment if your tests can only be run once a month over a weekend. You need a moderately short execution time so you can have your UI tests running several times a day.&lt;/p&gt;  &lt;p&gt;Keep in mind the scale of time in this context. 800 tests taking 30 seconds per test will take you nearly seven hours to execute. Small changes across many tests will net you huge gains.&lt;/p&gt;  &lt;p&gt;Here are a few causes I’ve seen for overly long execution times:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;strong&gt;Poor infrastructure.&lt;/strong&gt; You shouldn’t be running your UI tests in series. Scale out to some form of a grid and parallelize your execution. Get reasonable hardware to host your app and execution agents on. Get this all in place &lt;em&gt;early&lt;/em&gt; in your project so you don’t have to sweat it when the pressure’s on. &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Using the browser for setup.&lt;/strong&gt; Browser actions are SLLLLLOOOOOOOOW. They’re brittle, too. Never use the browser for setting up prerequisite data or configuration, always look to build a backing API to handle this sort of stuff. You can cut huge amounts of time out of your tests by pushing setup out of the browser and to an API. &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Testing too much.&lt;/strong&gt; Just because you can automate something doesn’t mean you should. Focus on high-value tests around your most critical business needs, or the highest-risk areas. Keep other tests for your eyeballs and manual exploratory testing. &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Navigating where you don’t need to.&lt;/strong&gt; Navigation is expensive. Wherever possible, bypass logons, navigation, and other unnecessary steps. You can even look to modify your system to be able to bypass these actions. Test these steps elsewhere, of course, but not every time! &lt;/li&gt; &lt;/ul&gt;  &lt;h1&gt;Brittle Tests&lt;/h1&gt;  &lt;p&gt;Brittle tests break for odd reasons. Brittle tests work in the developers’ environments, but fail in the QA or staging environments. Brittle tests fail when run in your suite, but pass when run individually.&lt;/p&gt;  &lt;p&gt;Here are a few causes of brittle tests I’ve run in to (or shot myself in the foot with!):&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;strong&gt;Side effects.&lt;/strong&gt; Every test absolutely must be self-sufficient and granular. A test needs to set up its own prerequisites and, where possible, clean up after itself. (Database transaction rollback, FTW!) Tests may not &lt;strong&gt;&lt;em&gt;ever&lt;/em&gt;&lt;/strong&gt; rely on state set by other tests! This doesn’t mean you can’t use a baseline dataset to configure broad sets of prerequisites, but you darn well better make sure to clean up after yourself. &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Bad environments.&lt;/strong&gt; You can’t expect horrible environments to be stable. I once worked on a project where our functional tests were expected to run on our CI server. The server was continually slammed by builds from 20 devs. Moreover, the CI server had a database on it used by other servers. And it was a virtual machine on a poor host. Priceless. Get realistic environments. You’ll be happy you did. &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Complex, badly designed tests.&lt;/strong&gt; This dovetails in with the side effects mentioned above. It also ties in to maintainability which I’ll address shortly. Mixed concerns, too many responsibilities, tight coupling to the UI or other components—this sounds just like problems we see when building systems. You know what? That impacts our tests, too! Pay attention to how you’re building your tests. &lt;/li&gt; &lt;/ul&gt;  &lt;h1&gt;High-Maintenance Tests&lt;/h1&gt;  &lt;p&gt;If you’re not careful, you can easily end up spending more time maintaining your existing automated tests than you spend creating new ones or doing your exploratory testing. Roy Osherove, in his amazing book &lt;em&gt;&lt;a href="http://www.amazon.com/gp/product/1933988274/ref=as_li_tf_tl?ie=UTF8&amp;amp;tag=frazzleddadco-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=193398827"&gt;The Art of Unit Testing&lt;/a&gt;&lt;/em&gt; talks about a project that failed because his team spent too much time maintaining tests. Note they didn’t abandon testing on their project—&lt;em&gt;the entire project failed!&lt;/em&gt; Ouch.&lt;/p&gt;  &lt;p&gt;In my experience, for functional/UI tests, the number one cause of high-maintenance tests is poor element locator management. Side effects and poor test design contribute, but locators is the number one PITA around. Locators are how your testing framework/tool finds elements on the page or in the application. (I’ll have much more on locators in a following post.) If you aren’t careful, you’ll end up spending huge amounts of time fixing up your tests’ locators every time the UI changes even a small amount.&lt;/p&gt;  &lt;p&gt;Two things can greatly help you cut down pain around your locator management.&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;&lt;strong&gt;Centralize your locator definition.&lt;/strong&gt; This is one place to learn, live, and love the DRY principle. Having locators defined in more than once place in your codebase is a recipe for misery. If you’re writing purely code-based tests then look to something like the Page Object Pattern, or perhaps even a simple dictionary or set of read-only fields. Tools like QTP or &lt;a href="http://www.telerik.com/automated-testing-tools.aspx"&gt;Test Studio&lt;/a&gt; help you by creating either a database (QTP) or element repository (Test Studio) which centralizes locator storage and maintenance. With a centralized locator storage you only have to go to one place to update your locators. Every test refers back to that repository to actually read the locator’s definition. It’s a thing of beauty. &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Prefer good locator types where possible.&lt;/strong&gt; Locators are generally based on HTML attributes, CSS, or the Document Object Model (DOM) structure. Not every tool or framework will support every locator type. More on a few of the most common locator types:       &lt;ul&gt;       &lt;li&gt;HTML ID values are the number one, mostest bestest way to specify your locators. IDs are unique on the page, so you’re never ambiguous. IDs don’t change based on their position on the page, so if a field is moved elsewhere on the page your tests &lt;em&gt;should&lt;/em&gt; work fine.&amp;#160; IDs are also the fastest to resolve. &lt;/li&gt;        &lt;li&gt;Text or link. Some frameworks will let you locate an element/target by its link text or text in the field. This is handy; however, may not be unique on the page. &lt;/li&gt;        &lt;li&gt;CSS selectors are things like CSS class names. These are fairly fast to resolve, but you’re not guaranteed uniqueness on the page, so they may not work for you. &lt;/li&gt;        &lt;li&gt;XPath. XPath works with the DOM structure based on paths and functions. I have a love/hate relationship with XPath. The geeky part of me that loves regular expressions (I’m taking counseling for it) also loves XPath. I can solve some very tricky problems with XPath, which is neat; however, XPath is hard to grok, and it’s extraordinarily brittle. If anything moves the slightest bit in the page’s DOM then your XPath is totally borked. Moreover, XPath is extremely slow in Internet Explorer. Finally, XPath under the .NET framework support isn’t fully implemented, so XPaths which work fine in tools like the XPather for Firefox may not work in testing tools built on the .NET Framework. &lt;/li&gt;     &lt;/ul&gt;   &lt;/li&gt; &lt;/ol&gt;  &lt;h1&gt;&lt;/h1&gt;  &lt;h1&gt;Wrapping Up&lt;/h1&gt;  &lt;p&gt;I’ll be doing some follow on posts introducing you to functional test automation; however, past experience has taught me that you’ll see the most success when you keep your automation suite’s maintainability in mind from the start. Gee, isn’t that sort of the same approach great software developers use? Maybe you should treat your testing codebase like production code—&lt;strong&gt;&lt;em&gt;because it is production code!&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;I hope you’re enjoying this series so far and are finding it useful. I’m enjoying writing it!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;div class="lqm_ad" lqm_publisher="lqm.frazzleddad.site" lqm_zone="ron" lqm_format="125x125" lqm_tgs="HTML5%2cWeb%2cAgile%2cALM%2cVS2010%2cSharePoint%2cWPF%2cXML%2cSilverlight%2cTesting%2cTFS%2cMVC%2cAJAX%2c"&gt;&lt;/div&gt;
&lt;script type='text/javascript' language='Javascript' src='http://s1.lqcdn.com/m.min.js?dt=2.3.110104.1'&gt;&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10748614-2302210426869233877?l=frazzleddad.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Frazzleddad/~4/ZXSlJQqXTZo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://frazzleddad.blogspot.com/feeds/2302210426869233877/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=10748614&amp;postID=2302210426869233877" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/2302210426869233877" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/2302210426869233877" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Frazzleddad/~3/ZXSlJQqXTZo/31-days-of-testingday-11-maintainable.html" title="31 Days of Testing—Day 11: Maintainable Functional Automation" /><author><name>Jim Holmes</name><uri>http://www.blogger.com/profile/05869146736565695900</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://3.bp.blogspot.com/_LOCL59IyTaA/Spykv6_NRpI/AAAAAAAAAAM/S2ZeQAzrL5o/s1600-R/3853180914_14858ae15f_o.jpg" /></author><thr:total>2</thr:total><feedburner:origLink>http://frazzleddad.blogspot.com/2011/12/31-days-of-testingday-11-maintainable.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10748614.post-7004810117697226740</id><published>2011-12-10T12:16:00.001-05:00</published><updated>2011-12-21T00:22:51.164-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="#31DaysOfTesting" /><title type="text">31 Days of Testing—Day 10: Mocking Out Dependencies</title><content type="html">&lt;p&gt;&lt;strong&gt;Updated:&lt;/strong&gt; &lt;a href="http://frazzleddad.blogspot.com/2011/12/31-days-of-testing-kickoff.html"&gt;Index to all posts in this series is here&lt;/a&gt;!&lt;/p&gt;  &lt;p&gt;Frankly, I’ve dreaded writing this post. Mocking is something that’s difficult to clearly get across, and the approaches and tooling for mocking vary perhaps even more widely than those for testing frameworks. There’s also an extraordinary range of terms people use to describe subtly different aspects of mocking—because we software folks don’t have enough confusing terminology in our lives already.&lt;/p&gt;  &lt;p&gt;While I’m being specific with a mocking implementation in this post, it’s important that you keep focused at the higher level of the concepts I’m trying to get across. Look to the broad concepts and go do more exploration in the mocking tools and approaches specific to your platform.&lt;/p&gt;  &lt;p&gt;Enough disclaimers. Let’s roll.&lt;/p&gt;  &lt;h1&gt;&lt;/h1&gt;  &lt;h1&gt;Why Mock?&lt;/h1&gt;  &lt;p&gt;In testing, mocking is a technique to let you cheat your system to isolate away dependencies external to the area of code you're trying to test. This is particularly important in unit testing where you want to focus on one small portion of your system—and you can’t do that if you’re having to deal with calls to external services, databases, security providers, etc.&lt;/p&gt;  &lt;p&gt;Let’s have a look at one example. Below is some code for updating an employee’s information. This sort of action can be quite sensitive, especially since there is privacy and salary information involved. Ergo, you want to ensure there’s very solid security around this use case. My example below uses a security provider service which checks the current user and sees whether that user is permitted to do the specific actions on the target employee.&lt;/p&gt;  &lt;div id="codeSnippetWrapper"&gt;   &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;     &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;class&lt;/span&gt; EmployeeUpdater&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;{&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff"&gt;readonly&lt;/span&gt; IUpdateEmployeePermissible _updateEmployeeSecurityProvider;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; EmployeeUpdater(&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;        IUpdateEmployeePermissible updateEmployeeSecurityProvider)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;        _updateEmployeeSecurityProvider = updateEmployeeSecurityProvider;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; Employee UpdateEmployeeRate(Employee targetEmployee, &lt;span style="color: #0000ff"&gt;float&lt;/span&gt; newRate)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;        Employee updatedEmployee;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;        &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (_updateEmployeeSecurityProvider.CanUpdateEmployeeRate(targetEmployee.EmployeeId))&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;        {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;            updatedEmployee = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; Employee(targetEmployee.EmployeeId, targetEmployee.FirstName&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;                                           targetEmployee.LastName,&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;                                           targetEmployee.Address, newRate);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;        }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;        &lt;span style="color: #0000ff"&gt;else&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;        {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;            &lt;span style="color: #0000ff"&gt;throw&lt;/span&gt; &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; SecurityException(&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;                &lt;span style="color: #006080"&gt;&amp;quot;Invoking user doesn't have permissions to update target target employee. &amp;quot;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;                + targetEmployee);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;        }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;        &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; updatedEmployee;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;}&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;I’m leaving out the implementation details for the security provider in an attempt to keep this as readable as possible. What’s important is an external (to this class) service figures out whether or not the current user can modify the target employee.&lt;/p&gt;

&lt;h1&gt;Design Impacts&lt;/h1&gt;

&lt;p&gt;Depending on your tool and platform, mocking may impact how you design your system. Note the security provider is passed in to the EmployeeUpdater’s constructor. Injecting dependencies this way, or through some form of setter, is an extremely preferred design approach for many reasons. &lt;/p&gt;

&lt;p&gt;First, the EmployeeUpdater shouldn’t have to know how to create anything. Single Responsibility Principle says this class should know how to do its job really well, and not worry about other things. If the manner of creating a new security provider changed, then we'd have to modify the EmployeeUpdater, too. That’s nuts. Let things get built elsewhere and simply passed in to objects that need them.&lt;/p&gt;

&lt;p&gt;Secondly, injecting the dependency to the class gives us a seam where we can substitute the real item with a fake one. We can use mocking tools, or just flat code, to create that faked out item and pass it to the portion of the system we’re trying to test.&lt;/p&gt;

&lt;p&gt;One more consideration for mocking which can impact your design: different mocking toolsets on different platforms have limitations around what sorts of objects/classes they’re able to work with. In the .NET world, for example, many mocking tools are unable to deal with static or concrete classes. You’ll need to provide interfaces or abstract implementations for the classes you’ll want to mock out. Some tools will deal with static classes, some will deal with concretes, some won’t at all, and then some platforms lend themselves to completely different approaches.&lt;/p&gt;

&lt;h1&gt;Simple Stub Mocks&lt;/h1&gt;

&lt;p&gt;There are a number of different types of mocked objects. Mocked objects may have some behavior defined in them, or they may just be empty shells which do nothing more than satisfy your test’s need to have something in the “shape” of the real object. &lt;/p&gt;

&lt;p&gt;In my examples below I’m using RhinoMocks for .NET. RhinoMocks uses the term “stub” to refer to a mock with no behavior, and “mock” for a faked object which has some behavior defined for it. The first test I’ll show you simply checks that an Employee object is returned with the correct updated pay rate. I don’t want to deal with the external security provider on this; the test is solely focused on whether or not the update happens properly. Ergo, I want to decouple the system and ignore the real security provider—I just want that to tell the system, “Yes, the current user is authorized.”&lt;/p&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;[Test]&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; Updated_employee_has_correct_rate()&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;{&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;int&lt;/span&gt; userId = 1;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;int&lt;/span&gt; oldRate = 5;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;int&lt;/span&gt; newRate = 10;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    var stubProvider = MockRepository.GenerateStub&amp;lt;IUpdateEmployeePermissible&amp;gt;();&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    stubProvider.Stub(x =&amp;gt; x.CanUpdateEmployeeRate(userId)).Return(&lt;span style="color: #0000ff"&gt;true&lt;/span&gt;);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    Employee current = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; Employee(userId, &lt;span style="color: #006080"&gt;&amp;quot;Jim&amp;quot;&lt;/span&gt;, &lt;span style="color: #006080"&gt;&amp;quot;Holmes&amp;quot;&lt;/span&gt;, &lt;span style="color: #006080"&gt;&amp;quot;Doghouse&amp;quot;&lt;/span&gt;, oldRate);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    EmployeeUpdater updater = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; EmployeeUpdater(stubProvider);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    Employee updated = updater.UpdateEmployeeRate(current, newRate);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    Assert.AreEqual(updated.HourlyRate, newRate);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;}&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;div&gt;The first statement in this test creates a stub of the security provider as defined by the interface IUpdateEMployeePermissible. (Dear .NET folks: can we please lose the last vestige of the silly Hungarian notation and just drop the “I” from interface names?).&amp;#160; The second statement defines an implementation that says “Whenever your method CanUpdateEmployeeRate is called, return true.”&lt;/div&gt;

&lt;p&gt;Next we create an Employee object as test data and an EmployeeUpdater to actually test. Note that we pass in our stubbed out provider to the constructor. The EmployeeUpdater will now use the fake provider we created earlier.&lt;/p&gt;

&lt;p&gt;The rest of the test is straight forward: invoke the method, check the results.&lt;/p&gt;

&lt;p&gt;Because we’re using a stub, the fake provider won’t even care if we invoke CanUpdateEmployeeRate with a bogus ID. It will simply return true regardless of when or how it’s invoked.&lt;/p&gt;

&lt;h1&gt;Mocks With Behaviors&lt;/h1&gt;

&lt;p&gt;Now for the next test. We want to ensure that our security provider’s CanUpdateEmployeeRate is always invoked—this is a critical part of our overall business rules to ensure proper security for updating employees, after all. &lt;/p&gt;

&lt;p&gt;What would happen if someone was working on the CanUpdateEmployeeRate method and commented out the security check for some troubleshooting, then mistakenly checked that in to source? You’d completely bypass the security check in production. Ick. Like that sort of thing never happens…&lt;/p&gt;

&lt;p&gt;Mocking frameworks generally provide you ways to deal with this sort of interaction. In RhinoMocks you can use a “mock” to handle this.&lt;/p&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;[Test]&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; Verify_security_provider_is_called()&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;{&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;int&lt;/span&gt; userId = 1;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;int&lt;/span&gt; oldRate = 5;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;int&lt;/span&gt; newRate = 10;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    var mockProvider = MockRepository.GenerateMock&amp;lt;IUpdateEmployeePermissible&amp;gt;();&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    mockProvider.Expect(x =&amp;gt; x.CanUpdateEmployeeRate(userId)).Return(&lt;span style="color: #0000ff"&gt;true&lt;/span&gt;);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    Employee current = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; Employee(userId, &lt;span style="color: #006080"&gt;&amp;quot;Jim&amp;quot;&lt;/span&gt;, &lt;span style="color: #006080"&gt;&amp;quot;Holmes&amp;quot;&lt;/span&gt;, &lt;span style="color: #006080"&gt;&amp;quot;Doghouse&amp;quot;&lt;/span&gt;, oldRate);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    EmployeeUpdater updater = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; EmployeeUpdater(mockProvider);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    Employee updated = updater.UpdateEmployeeRate(current, newRate);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    mockProvider.VerifyAllExpectations();&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;}&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Several major differences here: First, we’re generating a Mock, not a Stub in the first real statement. Secondly, we’re calling mockProvider.&lt;em&gt;&lt;strong&gt;Expect&lt;/strong&gt;&lt;/em&gt; instead of .&lt;em&gt;&lt;strong&gt;Stub&lt;/strong&gt;&lt;/em&gt;. The Expect call sets an &lt;em&gt;expectation &lt;/em&gt;that CanUpdateEmployeeRate will be called once with the specific value set in userId. The next several lines are all the same; however, the final statement in this method isn’t the usual Assert we associate with tests – it’s a call to the mocked out security provider’s VerifyAllExpectations method.&lt;/p&gt;

&lt;p&gt;This final statement asks the mocked object to check whether or not the system under test did the things we expected it to. In this case, we expected the system under test to invoke the CanUpdateEmployeeRate method with a value of 1.&lt;/p&gt;

&lt;p&gt;The verification will fail if the CanUpdateEmployeeRate wasn’t invoked at all, or if it was invoked with a value other than 1.&lt;/p&gt;

&lt;p&gt;Mocking frameworks can provide a wide range of expectation checks. You can look to ordered expectations (method A called before method D), constraints (input value between x and z), and many other validations.&lt;/p&gt;

&lt;h1&gt;Tools for Creating Mocks&lt;/h1&gt;

&lt;p&gt;You might start to see that creating a series of mocks could be quite complex. For example, if my security provider depended on something else which in turn depended on something else, then I’d have a nasty tree of objects I needed to deal with. Tools exist in every platform to help ease this. Factory Girl in Ruby is one, StructureMap and Ninject in the .NET world are others.&lt;/p&gt;

&lt;p&gt;Look up these approaches to help you ease the burden of dealing with complex object graphs both in your testing and production worlds.&lt;/p&gt;

&lt;h1&gt;Wrapping Up&lt;/h1&gt;

&lt;p&gt;This is an extremely simplistic, high-level view of mocking. My goal was to simply show where mocks might be used, and expose you to a few of the fundamental concepts around them. Mocks are an extremely powerful tool, but it’s extremely easy to use them too much, or in too confusing a manner. It’s also easy to end up testing the mock you just created instead of the system itself!&lt;/p&gt;

&lt;p&gt;Don’t stop with this post. Go read more around mocks and figure out how they can help you better test your systems!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;div class="lqm_ad" lqm_publisher="lqm.frazzleddad.site" lqm_zone="ron" lqm_format="125x125" lqm_tgs="HTML5%2cWeb%2cAgile%2cALM%2cVS2010%2cSharePoint%2cWPF%2cXML%2cSilverlight%2cTesting%2cTFS%2cMVC%2cAJAX%2c"&gt;&lt;/div&gt;
&lt;script type='text/javascript' language='Javascript' src='http://s1.lqcdn.com/m.min.js?dt=2.3.110104.1'&gt;&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10748614-7004810117697226740?l=frazzleddad.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Frazzleddad/~4/05lrRKqFcag" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://frazzleddad.blogspot.com/feeds/7004810117697226740/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=10748614&amp;postID=7004810117697226740" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/7004810117697226740" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/7004810117697226740" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Frazzleddad/~3/05lrRKqFcag/31-days-of-testingday-10-mocking-out.html" title="31 Days of Testing—Day 10: Mocking Out Dependencies" /><author><name>Jim Holmes</name><uri>http://www.blogger.com/profile/05869146736565695900</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://3.bp.blogspot.com/_LOCL59IyTaA/Spykv6_NRpI/AAAAAAAAAAM/S2ZeQAzrL5o/s1600-R/3853180914_14858ae15f_o.jpg" /></author><thr:total>3</thr:total><feedburner:origLink>http://frazzleddad.blogspot.com/2011/12/31-days-of-testingday-10-mocking-out.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10748614.post-1554271764430889091</id><published>2011-12-09T22:16:00.001-05:00</published><updated>2011-12-21T00:22:02.725-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="#31DaysOfTesting" /><title type="text">31 Days of Testing—Day 9: Readable Tests</title><content type="html">&lt;p&gt;&lt;strong&gt;Updated:&lt;/strong&gt; &lt;a href="http://frazzleddad.blogspot.com/2011/12/31-days-of-testing-kickoff.html"&gt;Index to all posts in this series is here&lt;/a&gt;!&lt;/p&gt;  &lt;p&gt;This post is an off-the-cuff tangential post based on some interesting comments to my previous posts on test fundamentals.&lt;/p&gt;  &lt;p&gt;A couple folks commented on my use of C#’s “var” keyword, pointing out that it made the tests less readable in their view. I think it raises an interesting point of discussion that is an extremely large debate on the value of strict typing. I’m going to forgo &lt;em&gt;that&lt;/em&gt; conversation, and instead try to focus down on readability of tests.&lt;/p&gt;  &lt;p&gt;In the following test, what’s most important?&lt;/p&gt;  &lt;div id="codeSnippetWrapper"&gt;   &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;     &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;[Test]&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; Computing_with_40_hours_at_rate_5_returns_200()&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;{&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    WageComputer computer;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; isHourlyWorker = &lt;span style="color: #0000ff"&gt;true&lt;/span&gt;;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    var computedWages = computer.ComputeWages(40, 5, isHourlyWorker);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    Assert.AreEqual(200, computedWages);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;}&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;This test is validating that the ComputeWages method is correctly working and returning the correct values for my inputs. This test &lt;em&gt;isn’t&lt;/em&gt; looking to validate the return type. Frankly, I don’t care what the return type is. I’m more concerned that the algorithm is functioning properly. &lt;/p&gt;

&lt;p&gt;If I’m only concerned about the value coming out of the method, then, quite frankly, anything extra at all on the left-hand side of that statement is pure noise to me. Look at these two different examples:&lt;/p&gt;

&lt;p style="font-family: &amp;#39;Courier New&amp;#39;"&gt;&lt;strong style="font-size: 16pt"&gt;double&lt;/strong&gt; computedWages = computer.ComputeWages(40, 5, isHourlyWorker);&lt;/p&gt;

&lt;p style="font-family: &amp;#39;Courier New&amp;#39;"&gt;var&lt;strong style="font-size: 16pt"&gt; computedWages&lt;/strong&gt; = computer.ComputeWages(40, 5, isHourlyWorker);&lt;/p&gt;

&lt;p&gt;That’s how my mind looks at these sorts of things. Frankly, I’d be happier if the compiler just got rid of the entire type declaration. The compiler knows what the right-hand side is returning. Even “var” is just noise to me. Something like this would be a thing of beauty:&lt;/p&gt;

&lt;p style="font-family: &amp;#39;Courier New&amp;#39;"&gt;&lt;strong style="font-size: 16pt"&gt;computedWages&lt;/strong&gt; = computer.ComputeWages(40, 5, isHourlyWorker);&lt;/p&gt;

&lt;p&gt;(By the way, that’s not my idea. My pal &lt;a href="http://twitter.com/rubybuddha"&gt;Leon Gersing&lt;/a&gt; put this idea in front of me during an interesting discussion several years back at a .NET training day we put on.)&lt;/p&gt;

&lt;p&gt;Moreover, explicitly using strong typing makes your tests more brittle. If you ever change the return type from ComputeWages you’ll need to go back and update every test using that class. (In this case that’s a somewhat contrived example. Please look at the larger point here.)&lt;/p&gt;

&lt;p&gt;If you feel the need to have a test specifically checking the return type, then write a separate test for that. &lt;/p&gt;

&lt;p&gt;Carry this over to everything else about your tests. What’s noise? What’s really important? Can you easily grok a test you wrote three weeks ago? Three months ago?&lt;/p&gt;

&lt;p&gt;No? Why not?&lt;/p&gt;

&lt;p&gt;Great software engineers, or the latest trend of labeling folks who care as “craftsmen,” understand the criticality of writing code that’s easily readable.&amp;#160; Tests need to have the same care, &lt;strong&gt;&lt;em&gt;because tests are production code.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Take a close look at what you really for your test and cut out everything else. It’s just noise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Updated:&lt;/strong&gt; By the way, in case you get put off my my frequently curmudgeonly sounding tone, I really do love this sort of feedback and questioning on my posts. I do not have all the answers. Your questions and points make me constantly re-evaluate how I’m doing things, and that’s a good thing! Please, keep up comments and questions. I love ‘em!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;div class="lqm_ad" lqm_publisher="lqm.frazzleddad.site" lqm_zone="ron" lqm_format="125x125" lqm_tgs="HTML5%2cWeb%2cAgile%2cALM%2cVS2010%2cSharePoint%2cWPF%2cXML%2cSilverlight%2cTesting%2cTFS%2cMVC%2cAJAX%2c"&gt;&lt;/div&gt;
&lt;script type='text/javascript' language='Javascript' src='http://s1.lqcdn.com/m.min.js?dt=2.3.110104.1'&gt;&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10748614-1554271764430889091?l=frazzleddad.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Frazzleddad/~4/fZRXVgUC8Ms" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://frazzleddad.blogspot.com/feeds/1554271764430889091/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=10748614&amp;postID=1554271764430889091" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/1554271764430889091" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/1554271764430889091" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Frazzleddad/~3/fZRXVgUC8Ms/31-days-of-testingday-9-readable-tests.html" title="31 Days of Testing—Day 9: Readable Tests" /><author><name>Jim Holmes</name><uri>http://www.blogger.com/profile/05869146736565695900</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://3.bp.blogspot.com/_LOCL59IyTaA/Spykv6_NRpI/AAAAAAAAAAM/S2ZeQAzrL5o/s1600-R/3853180914_14858ae15f_o.jpg" /></author><thr:total>4</thr:total><feedburner:origLink>http://frazzleddad.blogspot.com/2011/12/31-days-of-testingday-9-readable-tests.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10748614.post-6442061977973999934</id><published>2011-12-08T20:33:00.001-05:00</published><updated>2011-12-21T00:21:49.458-05:00</updated><title type="text">31 Days of Testing—Day 8: Pay Attention to Your Tests’ Setup!</title><content type="html">&lt;p&gt;&lt;strong&gt;Updated:&lt;/strong&gt; &lt;a href="http://frazzleddad.blogspot.com/2011/12/31-days-of-testing-kickoff.html"&gt;Index to all posts in this series is here&lt;/a&gt;!&lt;/p&gt;  &lt;p&gt;In yesterday’s post I laid out some fundamentals of what a test looks like in C# with NUnit, plus how you’d go about running it and getting results. Keep in mind that while I’m using NUnit and C# to illustrate fundamentals for these few columns, the basic tenets apply regardless of your platform or testing framework/toolset.&lt;/p&gt;  &lt;p&gt;Today I want to focus on handling setup conditions for your tests. In yesterday’s examples, every test created a new WageComputer instance. This sort of duplication gets tedious and can lead to more maintenance hassles since it violates &lt;a href="http://en.wikipedia.org/wiki/Don't_repeat_yourself"&gt;the DRY principle&lt;/a&gt; (Don’t Repeat Yourself). If you scatter creation of prerequisite classes, services, data, etc. across multiple tests, you’ll have to go to those same multiple tests to fix busted instantiations when those prerequisites change.&lt;/p&gt;  &lt;p&gt;Instead, look to push this sort of work off to a centralized location. In many cases, you can use your test framework’s features to do this. NUnit and many other frameworks support setup methods at the fixture and namespace scope. Moreover, you can have these setup methods run once per class/fixture, or once per test. This lets you have great control over where you create prerequisite objects, load/create data, etc.&lt;/p&gt;  &lt;p&gt;Here’s how this looks in NUnit using my examples from the previous post:&lt;/p&gt;  &lt;div id="codeSnippetWrapper"&gt;   &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;     &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;[TestFixture]&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;class&lt;/span&gt; When_working_with_an_hourly_worker&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;{&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;private&lt;/span&gt; WageComputer computer;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; isHourlyWorker;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    [TestFixtureSetUp]&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; Run_once_before_any_tests_run()&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;        isHourlyWorker = &lt;span style="color: #0000ff"&gt;true&lt;/span&gt;;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;        computer = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; WageComputer();&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    [Test]&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; Computing_with_40_hours_at_rate_5_returns_200()&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;        &lt;span style="color: #008000"&gt;//Arrange&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;        &lt;span style="color: #008000"&gt;//Act&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;        var computedWages = computer.ComputeWages(40, 5, isHourlyWorker);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;        &lt;span style="color: #008000"&gt;//Assert&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;        Assert.AreEqual(200, computedWages);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    [Test]&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; Computing_with_41_hours_at_rate_5_returns_207_5()&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt; &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #008000"&gt;//Arrange&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;     &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #008000"&gt;//Act &lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    var wages = computer.ComputeWages(41, 5, &lt;span style="color: #0000ff"&gt;true&lt;/span&gt;);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    Assert.AreEqual(207.50, wages);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;The TestFixtureSetup attribute will cause the NUnit runner to execute the method Run_once_before_any_tests_run when this class/fixture is first loaded. We’ll stand up a new instance of the WageComputer and set isHourlyWorker true. This is very similar to a class constructor, but the lifecycle is managed by the test framework, not the .NET internals. (Which I couldn’t explain to you. Go ask Jon Skeet or Bill Wagner.)&lt;/p&gt;

&lt;p&gt;There’s a similar attribute called SetUp which executes &lt;strong&gt;&lt;em&gt;before each test&lt;/em&gt;&lt;/strong&gt;. This is handy if you need to freshly initialize something before each test. (Remember, tests shouldn’t rely on &lt;strong&gt;&lt;em&gt;any&lt;/em&gt;&lt;/strong&gt; state set in another test.)&lt;/p&gt;

&lt;p&gt;What gets set up may need to get torn down. Frameworks and tools generally support some form of TestFixtureTeardown or Teardown approach to clean up after each fixture or each test, respectively. These are great places to stuff transaction rollbacks to clean up after database interactions, for example.&lt;/p&gt;

&lt;p&gt;This is a very simplistic, trivial example, but I’m sure you grok the general concept here. You can use fixture setup and teardown methods to deal with fixture-related prerequisites. Very handy.&lt;/p&gt;

&lt;p&gt;Now for some important caveats.&lt;/p&gt;

&lt;p&gt;While you want to avoid too much duplication, you can easily get carried away and overly clever with inheritance and abstraction of your setup actions—to the point where it gets extraordinarily difficult to understand what’s being set up where.&lt;/p&gt;

&lt;p&gt;In a previous life I worked with an internally created test framework based off a Behavior Driven Development framework. We let ourselves get perhaps a little overly clever with our inheritance and setup chain, and it became very difficult to learn and understand. (OK, there’s no “perhaps” about it. We did, and I was a major part of letting that happen. Bad Jim. Bad Jim!) &lt;/p&gt;

&lt;p&gt;Using some psuedo code, the inheritance and setup chain looked something roughly like this:&lt;/p&gt;

&lt;div id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;Integration_test  (Base)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;  &lt;span style="color: #008000"&gt;//sets up database&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;Functional_test (Inherits from Integration_test)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;  &lt;span style="color: #008000"&gt;//sets up browser, configures UI&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;Feature_test  (Inherits from Functional_test)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;  &lt;span style="color: #008000"&gt;//configures system&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;Module_test  (Inherits from Feature_test)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;  &lt;span style="color: #008000"&gt;//creates a module&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #008000"&gt;//Now we get to the actual tests!&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;When_creating_something_as_regular_user  (Inherits from Module_test)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;  &lt;span style="color: #008000"&gt;//creates new user&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;  &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;  &lt;span style="color: #008000"&gt;//FINALLY does some testing!&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Imagine you’ve got a failure in a test. You could potentially have to walk back up four layers of inheritance to understand exactly what’s going on where. This is an overly clever approach to setup which makes understanding of your tests extremely difficult. Remember that code is read and re-read many more times than it’s written. &lt;/p&gt;

&lt;p&gt;In these sorts of cases it’s OK to relax a bit on the Don’t Repeat Yourself principle. There’s an extremely applicable saying for this situation: “Keep your system DRY and your tests slightly moist.” What that means is, it’s OK to duplicate some setup steps in your tests if it makes the fixture/class/spec more understandable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Bottom Line&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Leverage your testing platform/framework’s features for helping you get prerequisites for your tests set up, just don’t get so convoluted that you can’t easily understand what’s going on in the test when you open it up a couple weeks or months after you’ve written it.&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;
  &lt;div class="lqm_ad" lqm_tgs="HTML5%2cWeb%2cAgile%2cALM%2cVS2010%2cSharePoint%2cWPF%2cXML%2cSilverlight%2cTesting%2cTFS%2cMVC%2cAJAX%2c" lqm_format="125x125" lqm_zone="ron" lqm_publisher="lqm.frazzleddad.site"&gt;&lt;/div&gt;
  &lt;script type="text/javascript" language="Javascript" src="http://s1.lqcdn.com/m.min.js?dt=2.3.110104.1"&gt;&lt;/script&gt;&lt;/div&gt;  &lt;div class="blogger-post-footer"&gt;&lt;div class="lqm_ad" lqm_publisher="lqm.frazzleddad.site" lqm_zone="ron" lqm_format="125x125" lqm_tgs="HTML5%2cWeb%2cAgile%2cALM%2cVS2010%2cSharePoint%2cWPF%2cXML%2cSilverlight%2cTesting%2cTFS%2cMVC%2cAJAX%2c"&gt;&lt;/div&gt;
&lt;script type='text/javascript' language='Javascript' src='http://s1.lqcdn.com/m.min.js?dt=2.3.110104.1'&gt;&lt;/script&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/10748614-6442061977973999934?l=frazzleddad.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/Frazzleddad/~4/iez8UDcRa04" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://frazzleddad.blogspot.com/feeds/6442061977973999934/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=10748614&amp;postID=6442061977973999934" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/6442061977973999934" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/10748614/posts/default/6442061977973999934" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Frazzleddad/~3/iez8UDcRa04/31-days-of-testingday-8-pay-attention.html" title="31 Days of Testing—Day 8: Pay Attention to Your Tests’ Setup!" /><author><name>Jim Holmes</name><uri>http://www.blogger.com/profile/05869146736565695900</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://3.bp.blogspot.com/_LOCL59IyTaA/Spykv6_NRpI/AAAAAAAAAAM/S2ZeQAzrL5o/s1600-R/3853180914_14858ae15f_o.jpg" /></author><thr:total>4</thr:total><feedburner:origLink>http://frazzleddad.blogspot.com/2011/12/31-days-of-testingday-8-pay-attention.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-10748614.post-4102914393385475774</id><published>2011-12-07T16:31:00.001-05:00</published><updated>2011-12-21T00:21:24.404-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="#31DaysOfTesting" /><title type="text">31 Days of Testing—Day 7: Automated Test Basics</title><content type="html">&lt;p&gt;&lt;strong&gt;Updated:&lt;/strong&gt; &lt;a href="http://frazzleddad.blogspot.com/2011/12/31-days-of-testing-kickoff.html"&gt;Index to all posts in this series is here&lt;/a&gt;!&lt;/p&gt;  &lt;p&gt;I thought today’s post would be a good opportunity to lay out some fundamentals of automated tests work. The mechanics of test automation varies extraordinarily based on what platform you’re working on, and even within platforms you’ll see vastly differing approaches based on whatever testing framework/toolset you’re using.&lt;/p&gt;  &lt;p&gt;Testing frameworks have a number of commonalities, regardless of platform and implementation. You’ll have a &lt;strong&gt;&lt;em&gt;test runner&lt;/em&gt;&lt;/strong&gt; which executes the tests. Test runners may live in the IDE you’re using to write your tests, they may be a separate GUI, or they may be a command line runner. Command line test runners make it possible to run your tests in a number of different environments such as a build or continuous integration server. The tests themselves will likely be organized, depending on the framework/tool, into classes, fixtures, scenarios, or something else. You generally point your test runner at these fixtures and the runner noodles out what tests are contained within those groups. The runner will execute all those tests and give you back a report on the pass/fail status.&lt;/p&gt;  &lt;p&gt;Today’s column shows unit tests, but the general concepts apply to integration and functional tests too.&lt;/p&gt;  &lt;p&gt;I’m going to be showing examples in C# with NUnit and RhinoMock, simply because that’s what I’m very comfy with. Yes, yes, we should all get out of our comfort zone and do things on other platforms, but the point of this series isn’t for you to watch me flail around while trying to learn new stuff… &lt;img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-smile" alt="Smile" src="http://lh3.ggpht.com/-VyillLgPsnY/Tt_bIYROp4I/AAAAAAAAADM/SZt-wemUx3o/wlEmoticon-smile%25255B2%25255D.png?imgmax=800" /&gt;&lt;/p&gt;  &lt;p&gt;Some things to keep in mind as you read posts of mine which contain code:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;I’m old school, although I try hard to keep up with new trends &lt;/li&gt;    &lt;li&gt;I haven’t written system code in a long time, just test code. There’s a bit of a difference. &lt;/li&gt;    &lt;li&gt;I am a starting point for your learning, not a destination. Look at my stuff, figure out what works and doesn’t, then go find other places to learn more. &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;The examples I’ll be using today are taken from the Unit Testing 101 talks I give. The examples aren’t *DD-ish because I want to focus on very fundamental pieces with out adding any methodology to the mix. Like I said, old school—but the foundational principles are critical and similar regardless.&lt;/p&gt;  &lt;p&gt;Without further ado, part one, unit test basics. The system we’ll be testing is a simple payroll wage calculator method. It’s purposely not optimized or concisely written because I use it as a starting point for some refactoring, etc. (If you’ve interviewed with me for a job you’ve likely seen a variant of this…) &lt;/p&gt;  &lt;p&gt;Here’s the method:&lt;/p&gt;  &lt;div id="codeSnippetWrapper"&gt;   &lt;div style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"&gt;     &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;float&lt;/span&gt; ComputeWages(&lt;span style="color: #0000ff"&gt;float&lt;/span&gt; hours, &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;                            &lt;span style="color: #0000ff"&gt;float&lt;/span&gt; rate, &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;                            &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; isHourlyWorker)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;{&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (hours &amp;lt; 0)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;        &lt;span style="color: #0000ff"&gt;throw&lt;/span&gt; &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; ArgumentException(&lt;span style="color: #006080"&gt;&amp;quot;Hours must be greater or equal to zero &amp;quot;&lt;/span&gt; &lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;            + hours);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;float&lt;/span&gt; wages = 0;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: white; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style="
