<?xml version="1.0" encoding="iso-8859-1"?><!-- generator="b2evolution/4.0.3" -->
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:admin="http://webns.net/mvcb/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>Web Developer - Author(s): Eli Weinstock-Herman (tarwn)</title>
		<link>http://blogs.lessthandot.com/index.php/WebDev/</link>
		<atom:link rel="self" type="application/rss+xml" href="http://blogs.lessthandot.com/index.php/WebDev/?tempskin=_rss2" />
		<description></description>
		<language>en-GB</language>
		<docs>http://blogs.law.harvard.edu/tech/rss</docs>
		<admin:generatorAgent rdf:resource="http://b2evolution.net/?v=4.0.3"/>
		<ttl>60</ttl>
				<item>
			<title>My Mobile-Friendly Site Redesign</title>
			<link>http://blogs.lessthandot.com/index.php/WebDev/WebDesignGraphicsStyling/my-mobile-friendly-site-redesign</link>
			<pubDate>Fri, 07 Sep 2012 13:01:00 +0000</pubDate>			<dc:creator>Eli Weinstock-Herman (tarwn)</dc:creator>
			<category domain="main">Web Design, Graphics &amp; Styling</category>
<category domain="alt">Javascript</category>
<category domain="alt">XHTML &amp; CSS</category>			<guid isPermaLink="false">1813@http://blogs.lessthandot.com/</guid>
						<description>&lt;p&gt;I built my first website for the NCSA Mosaic browser. A lot has changed since then, and the challenges we had with the original browser wars are upon us again as we try and build sites that work equally well on a variety of mobile phones, tablets, and the desktops that originally gave us so much trouble. Like the browser wars, the two main approaches have been server-side and client-side, with the server-side approach using the browsers User Agent to serve up a browser-specific response and the client-side method being an attempt to serve up one version of HTML that works on all potential browsers.&lt;/p&gt;

&lt;p&gt;While the server-side method has not gotten much more advanced, the client-side methods are generations beyond where we used to be. &lt;a href=&quot;http://www.alistapart.com/articles/responsive-web-design/&quot; alt=&quot;Responsive Web Design, A List Apart, Ethan Marcotte&quot;&gt;Responsive Web Design&lt;/a&gt; and &lt;a href=&quot;http://easy-readers.net/books/adaptive-web-design/&quot; alt=&quot;Adaptive Web Design, Aaron Gustafson&quot;&gt;Adaptive Web Design&lt;/a&gt; provide the methods to use CSS and javascript to produce site that scales and progressively enhances to meet the end users device capabilities.&lt;/p&gt;

&lt;p&gt;A couple months ago I decided to redesign my personal website again, with the goal of making it provide a good experience at mobile phone, tablet, and monitor sizes. Here&#039;s how I did it.&lt;/p&gt;

&lt;h2&gt;The Screen Sizes&lt;/h2&gt;
&lt;p&gt;I decided to focus on screen sizes starting at mobile phone (480px or less) all the way up to large browser (more than 1240px). Each resolution would be usable and contain a similar palette and graphical elements. I also attempted to minimize the amount of extra elements I load at the smaller sizes, assuming that these smaller sizes would have less (and more costly) bandwidth.&lt;/p&gt;

&lt;p&gt;These are the layouts I ended up with:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://tiernok.com/LTDBlog/website/SmallPhone.png&quot; alt=&quot;Small phone&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://tiernok.com/LTDBlog/website/LargePhone.png&quot; alt=&quot;Large phone&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://tiernok.com/LTDBlog/website/SmallBrowser.png&quot; alt=&quot;Small browser&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://tiernok.com/LTDBlog/website/LargeBrowser.png&quot; alt=&quot;Large browser&quot; /&gt;&lt;/p&gt;

&lt;p&gt;As the available screen real estate gets larger, the logo size increases, the number of social icons increase, and additional inner right sidebar appears, and then finally additional background and an outer right sidebar become available. The width of the available reading area scales smoothly from the smallest size to the largest size.&lt;/p&gt;

&lt;h2&gt;Implementation&lt;/h2&gt;
&lt;p&gt;While my site doesn&#039;t have a lot of functionality, achieving the layout changes and addition of features at the largest sizes was still a nice challenge. The key to the site is the CSS media queries that alter the visibility of elements and areas, combined with some javascript to load in larger images or additional elements when relevant. In the event that a browser doesn&#039;t support media queries and javascript, they will receive the smallest possible layout. Though there are workarounds, I purposefully left the IE8/IE7 layout in this mode, which would also be used for most mobile browsers.&lt;/p&gt;

&lt;p&gt;If you load the site in the secondary window and drag the width around, you will be able to see the transitions: &lt;a href=&quot;http://tiernok.com&quot; target=&quot;_blank&quot;&gt;http://tiernok.com&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;Images&lt;/h3&gt;
&lt;p&gt;I cheated a bit on images. Any image with the &quot;imglow&quot; CSS class will be hidden above 480 px screen width and replaced with ones that with a class &quot;imghi&quot;. Since many small browsers would load the large image even if it is hidden, I put the actual image source in a data attribute and use a bit of javascript to populate the src value.&lt;/p&gt;

&lt;p&gt;&lt;code lang=&quot;html&quot;&gt;&lt;br /&gt;
&amp;lt;img src=&quot;_n_images/logo_sm.png&quot; alt=&quot;Eli Weinstock-Herman | Tarwn&quot; border=&quot;0&quot; class=&quot;imglow&quot; /&amp;gt;&lt;br /&gt;
&amp;lt;img data-fullsrc=&quot;_n_images/logo.png&quot; alt=&quot;Eli Weinstock-Herman | Tarwn&quot; border=&quot;0&quot; class=&quot;imghi&quot; /&amp;gt;&lt;br /&gt;
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The social images at the top of the screen have classes that define whether they are available for small resolution or not and display at half resolution in smaller screens. These don&#039;t load dynamically at larger size, so all 4 will load on browsers that load hidden images.&lt;/p&gt;

&lt;p&gt;The background coffee spills start to show at the medium resolutions via positioned background images on the main content wrapper, so these only load when the screen is large enough to display them. This is also when the inner right side panel is displayed, along with the contact image and books. This content is loaded and not displayed at smaller resolutions, so there is some room to shrink that initial mobile load further.&lt;/p&gt;

&lt;h3&gt;CSS Media Queries&lt;/h3&gt;
&lt;p&gt;The main CSS logic is as follows:&lt;/p&gt;

&lt;p&gt;&lt;b&gt;&amp;gt; 480 pixels wide:&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Hide the small logo, show the large one at reduced size&lt;/li&gt;
&lt;li&gt;Increase the size of the 2 social icons in the header&lt;/li&gt;
&lt;li&gt;Switch navigation from bar to buttons, show more links&lt;/li&gt;
&lt;li&gt;Switch from full screen to fixed borders for main content&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;b&gt;&amp;gt; 650 pixels wide:&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Show the large logo at larger size&lt;/li&gt;
&lt;li&gt;Show all 4 social icons&lt;/li&gt;
&lt;li&gt;Add last two links to navigation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;b&gt;&amp;gt; 750 pixels wide:&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Show the large logo at largest size&lt;/li&gt;
&lt;li&gt;Show inner right sidebar&lt;/li&gt;
&lt;li&gt;Constrain main body to 800px wide&lt;/li&gt;
&lt;li&gt;Add coffee stain background images&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;b&gt;&amp;gt; 1000 pixels wide:&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add constraints to content wrapper and area to maintain smooth centering&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;b&gt;&amp;gt; 1240 pixels wide:&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Show the outer right sidebar (javascript used to load content asynchronously)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Minimizing the pages that were available at the smaller resolutions not only kept the content more focused, but also meant that some of the more complex pages (like the resume) would only be available at resolutions that would support them.&lt;/p&gt;

&lt;h2&gt;Am I Happy With It?&lt;/h2&gt;
&lt;p&gt;I started the redesign with the &lt;a href=&quot;http://html5boilerplate.com/&quot;&gt;Html5 boilerplate&lt;/a&gt; to give me a consistent start place and minimize how much time I had to deal with browser specific layout tweaks. I did tweak the media query resolutions quite a bit as I built out the pages, but part of this was my lack of designer skills and not having a clear enough picture of what I wanted the site to look like. &lt;/p&gt;

&lt;p&gt;Currently a mobile device without javascript will load about 83KB of content (obviously this changes based on what is on the front-page of the site). With Javascript this goes up to 160KB for mobile and 800x600 resolutions and 200KB for the largest screen size. I think the smaller resolutions could be reduced further by restricting the inner sidebar content and small icon loading, and the whole site could do with some minification and gzip. &lt;/p&gt;

&lt;p&gt;Overall I am very happy with it. The size is usable at every resolution I try and this method doesn&#039;t rely on the client-side user agent. Additional functionality would be easy to add, as I could use feature detection to add functionality only if the end user&#039;s browser supported it, regardless of the user agent string. I&#039;m very happy with the result.&lt;/p&gt;&lt;div class=&quot;item_footer&quot;&gt;&lt;p&gt;&lt;small&gt;&lt;a href=&quot;http://blogs.lessthandot.com/index.php/WebDev/WebDesignGraphicsStyling/my-mobile-friendly-site-redesign&quot;&gt;Original post&lt;/a&gt; blogged on &lt;a href=&quot;http://lessthandot.com/&quot;&gt;LessThanDot&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;</description>
			<content:encoded><![CDATA[<p>I built my first website for the NCSA Mosaic browser. A lot has changed since then, and the challenges we had with the original browser wars are upon us again as we try and build sites that work equally well on a variety of mobile phones, tablets, and the desktops that originally gave us so much trouble. Like the browser wars, the two main approaches have been server-side and client-side, with the server-side approach using the browsers User Agent to serve up a browser-specific response and the client-side method being an attempt to serve up one version of HTML that works on all potential browsers.</p>

<p>While the server-side method has not gotten much more advanced, the client-side methods are generations beyond where we used to be. <a href="http://www.alistapart.com/articles/responsive-web-design/" alt="Responsive Web Design, A List Apart, Ethan Marcotte">Responsive Web Design</a> and <a href="http://easy-readers.net/books/adaptive-web-design/" alt="Adaptive Web Design, Aaron Gustafson">Adaptive Web Design</a> provide the methods to use CSS and javascript to produce site that scales and progressively enhances to meet the end users device capabilities.</p>

<p>A couple months ago I decided to redesign my personal website again, with the goal of making it provide a good experience at mobile phone, tablet, and monitor sizes. Here's how I did it.</p>

<h2>The Screen Sizes</h2>
<p>I decided to focus on screen sizes starting at mobile phone (480px or less) all the way up to large browser (more than 1240px). Each resolution would be usable and contain a similar palette and graphical elements. I also attempted to minimize the amount of extra elements I load at the smaller sizes, assuming that these smaller sizes would have less (and more costly) bandwidth.</p>

<p>These are the layouts I ended up with:</p>

<p><img src="http://tiernok.com/LTDBlog/website/SmallPhone.png" alt="Small phone" /></p>

<p><img src="http://tiernok.com/LTDBlog/website/LargePhone.png" alt="Large phone" /></p>

<p><img src="http://tiernok.com/LTDBlog/website/SmallBrowser.png" alt="Small browser" /></p>

<p><img src="http://tiernok.com/LTDBlog/website/LargeBrowser.png" alt="Large browser" /></p>

<p>As the available screen real estate gets larger, the logo size increases, the number of social icons increase, and additional inner right sidebar appears, and then finally additional background and an outer right sidebar become available. The width of the available reading area scales smoothly from the smallest size to the largest size.</p>

<h2>Implementation</h2>
<p>While my site doesn't have a lot of functionality, achieving the layout changes and addition of features at the largest sizes was still a nice challenge. The key to the site is the CSS media queries that alter the visibility of elements and areas, combined with some javascript to load in larger images or additional elements when relevant. In the event that a browser doesn't support media queries and javascript, they will receive the smallest possible layout. Though there are workarounds, I purposefully left the IE8/IE7 layout in this mode, which would also be used for most mobile browsers.</p>

<p>If you load the site in the secondary window and drag the width around, you will be able to see the transitions: <a href="http://tiernok.com" target="_blank">http://tiernok.com</a></p>

<h3>Images</h3>
<p>I cheated a bit on images. Any image with the "imglow" CSS class will be hidden above 480 px screen width and replaced with ones that with a class "imghi". Since many small browsers would load the large image even if it is hidden, I put the actual image source in a data attribute and use a bit of javascript to populate the src value.</p>

<p><code lang="html"><br />
&lt;img src="_n_images/logo_sm.png" alt="Eli Weinstock-Herman | Tarwn" border="0" class="imglow" /&gt;<br />
&lt;img data-fullsrc="_n_images/logo.png" alt="Eli Weinstock-Herman | Tarwn" border="0" class="imghi" /&gt;<br />
</code></p>

<p>The social images at the top of the screen have classes that define whether they are available for small resolution or not and display at half resolution in smaller screens. These don't load dynamically at larger size, so all 4 will load on browsers that load hidden images.</p>

<p>The background coffee spills start to show at the medium resolutions via positioned background images on the main content wrapper, so these only load when the screen is large enough to display them. This is also when the inner right side panel is displayed, along with the contact image and books. This content is loaded and not displayed at smaller resolutions, so there is some room to shrink that initial mobile load further.</p>

<h3>CSS Media Queries</h3>
<p>The main CSS logic is as follows:</p>

<p><b>&gt; 480 pixels wide:</b></p>
<ul>
<li>Hide the small logo, show the large one at reduced size</li>
<li>Increase the size of the 2 social icons in the header</li>
<li>Switch navigation from bar to buttons, show more links</li>
<li>Switch from full screen to fixed borders for main content</li>
</ul>

<p><b>&gt; 650 pixels wide:</b></p>
<ul>
<li>Show the large logo at larger size</li>
<li>Show all 4 social icons</li>
<li>Add last two links to navigation</li>
</ul>

<p><b>&gt; 750 pixels wide:</b></p>
<ul>
<li>Show the large logo at largest size</li>
<li>Show inner right sidebar</li>
<li>Constrain main body to 800px wide</li>
<li>Add coffee stain background images</li>
</ul>

<p><b>&gt; 1000 pixels wide:</b></p>
<ul>
<li>Add constraints to content wrapper and area to maintain smooth centering</li>
</ul>

<p><b>&gt; 1240 pixels wide:</b></p>
<ul>
<li>Show the outer right sidebar (javascript used to load content asynchronously)</li>
</ul>

<p>Minimizing the pages that were available at the smaller resolutions not only kept the content more focused, but also meant that some of the more complex pages (like the resume) would only be available at resolutions that would support them.</p>

<h2>Am I Happy With It?</h2>
<p>I started the redesign with the <a href="http://html5boilerplate.com/">Html5 boilerplate</a> to give me a consistent start place and minimize how much time I had to deal with browser specific layout tweaks. I did tweak the media query resolutions quite a bit as I built out the pages, but part of this was my lack of designer skills and not having a clear enough picture of what I wanted the site to look like. </p>

<p>Currently a mobile device without javascript will load about 83KB of content (obviously this changes based on what is on the front-page of the site). With Javascript this goes up to 160KB for mobile and 800x600 resolutions and 200KB for the largest screen size. I think the smaller resolutions could be reduced further by restricting the inner sidebar content and small icon loading, and the whole site could do with some minification and gzip. </p>

<p>Overall I am very happy with it. The size is usable at every resolution I try and this method doesn't rely on the client-side user agent. Additional functionality would be easy to add, as I could use feature detection to add functionality only if the end user's browser supported it, regardless of the user agent string. I'm very happy with the result.</p><div class="item_footer"><p><small><a href="http://blogs.lessthandot.com/index.php/WebDev/WebDesignGraphicsStyling/my-mobile-friendly-site-redesign">Original post</a> blogged on <a href="http://lessthandot.com/">LessThanDot</a>.</small></p></div>]]></content:encoded>
								<comments>http://blogs.lessthandot.com/index.php/WebDev/WebDesignGraphicsStyling/my-mobile-friendly-site-redesign#comments</comments>
			<wfw:commentRss>http://blogs.lessthandot.com/index.php/WebDev/?tempskin=_rss2&#38;disp=comments&#38;p=1813</wfw:commentRss>
		</item>
				<item>
			<title>Using T4 templates for Centralized Javascript</title>
			<link>http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/Javascript/using-t4-templates-for-centralized</link>
			<pubDate>Fri, 02 Dec 2011 15:42:00 +0000</pubDate>			<dc:creator>Eli Weinstock-Herman (tarwn)</dc:creator>
			<category domain="alt">ASP.NET</category>
<category domain="main">Javascript</category>			<guid isPermaLink="false">1501@http://blogs.lessthandot.com/</guid>
						<description>&lt;p&gt;In my previous post I mentioned that I was looking for an answer to the age-old question of how to manage common CSS and JavaScript across multiple projects (specifically ASP.Net projects). Using T4 templates, I was able to not only create a common location for CSS files, but to take it a step farther and use Less in ordr to simplify that common CSS even further.&lt;/p&gt;

&lt;p&gt;But we left JavaScript out of the equation.&lt;/p&gt;

&lt;h2&gt;T4&#039;ing the JavaScript&lt;/h2&gt;
&lt;p&gt;As a reminder, I am looking for a clean way to store shared JavaScript and CSS in a single place so I share it amongst several web projects and also make changes to the files on the fly and see them in the browser without a costly solution rebuild.&lt;/p&gt;

&lt;p&gt;On the JavaScript side of things I decided to write my own little T4 template. The goal was to be able to pick up all of the *.js files in a central location, merge them, and save them out as a common file that I could then include in my local projects. &lt;/p&gt;

&lt;p&gt;My solution layout is similar to the one in the prior post:&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;text&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;text&quot; id=&quot;cb68&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;/SolutionName/Common/NN-*.js files&lt;br /&gt;/SolutionName/Project1/scripts/CommonJS.tt&lt;br /&gt;/SolutionName/Project2/scripts/CommonJS.tt&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb38295&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I prefix my common javascript files with a 2-digit number so I can include them in a planned order every time.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;CommonJS.tt&lt;/b&gt;&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;c#&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;c#&quot; id=&quot;cb67654&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&amp;lt;&lt;span style=&quot;color: #339933;&quot;&gt;#@ template language=&amp;quot;C#&amp;quot; hostspecific=&amp;quot;True&amp;quot; #&amp;gt;&lt;/span&gt;&lt;br /&gt;&amp;lt;&lt;span style=&quot;color: #339933;&quot;&gt;#@ import namespace=&amp;quot;System&amp;quot; #&amp;gt;&lt;/span&gt;&lt;br /&gt;&amp;lt;&lt;span style=&quot;color: #339933;&quot;&gt;#@ import namespace=&amp;quot;System.IO&amp;quot; #&amp;gt;&lt;/span&gt;&lt;br /&gt;&amp;lt;&lt;span style=&quot;color: #339933;&quot;&gt;#@ import namespace=&amp;quot;Microsoft.VisualStudio.TextTemplating&amp;quot; #&amp;gt;&lt;/span&gt;&lt;br /&gt;&amp;lt;&lt;span style=&quot;color: #339933;&quot;&gt;#@ Output Extension=&amp;quot;.js&amp;quot; #&amp;gt;&lt;/span&gt;&lt;br /&gt;&amp;lt;&lt;span style=&quot;color: #339933;&quot;&gt;#&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;/*-------------------------------------------------*/&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;// Settings &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;/*-------------------------------------------------*/&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #993333;&quot;&gt;string&lt;/span&gt; _targetDirectory = @&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;&lt;span style=&quot;color: #000099; font-weight: bold;&quot;&gt;\.&lt;/span&gt;.&lt;span style=&quot;color: #000099; font-weight: bold;&quot;&gt;\.&lt;/span&gt;.&lt;span style=&quot;color: #000099; font-weight: bold;&quot;&gt;\C&lt;/span&gt;ommon&amp;quot;&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;/*-------------------------------------------------*/&lt;/span&gt;&lt;br /&gt;Directory.&lt;span style=&quot;color: #202020;&quot;&gt;SetCurrentDirectory&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;Path.&lt;span style=&quot;color: #202020;&quot;&gt;GetDirectoryName&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;Host.&lt;span style=&quot;color: #202020;&quot;&gt;TemplateFile&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt; + _targetDirectory&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;var filespec = &lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;*.js&amp;quot;&lt;/span&gt;;&lt;br /&gt;var files = Directory.&lt;span style=&quot;color: #202020;&quot;&gt;GetFiles&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;.&amp;quot;&lt;/span&gt;,filespec&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #339933;&quot;&gt;#&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;/*&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;CommandJavascript.js&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;Converted at: &amp;lt;#= DateTime.Now.ToShortDateString() #&amp;gt; &amp;lt;#= DateTime.Now.ToShortTimeString() #&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;File List (&amp;lt;#= files.Length #&amp;gt; Found):&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;&amp;lt;#= &amp;quot;\t&amp;quot; + String.Join(&amp;quot;\n\t&amp;quot;, files) #&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;*/&lt;/span&gt;&lt;br /&gt;&amp;lt;&lt;span style=&quot;color: #339933;&quot;&gt;#&lt;/span&gt;&lt;br /&gt;foreach&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;var jsFile in files&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;br /&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; Write&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;/* ----------- &amp;quot;&lt;/span&gt; + jsFile + &lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot; ----------- */&lt;span style=&quot;color: #000099; font-weight: bold;&quot;&gt;\n&lt;/span&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; using&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;StreamReader sr = File.&lt;span style=&quot;color: #202020;&quot;&gt;OpenText&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;jsFile&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Write&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;sr.&lt;span style=&quot;color: #202020;&quot;&gt;ReadToEnd&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; sr.&lt;span style=&quot;color: #202020;&quot;&gt;Close&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; Write&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;&lt;span style=&quot;color: #000099; font-weight: bold;&quot;&gt;\n&lt;/span&gt;/* ----------- &amp;quot;&lt;/span&gt; + jsFile + &lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot; ----------- */&lt;span style=&quot;color: #000099; font-weight: bold;&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;color: #000099; font-weight: bold;&quot;&gt;\n&lt;/span&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #339933;&quot;&gt;#&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb70346&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Place this file in our project, update the _targetDirectory variable and we have a quick way to pull common javascript down to our projects.&lt;/p&gt;

&lt;p&gt;The output file is a merged version of all of the *.js files found in the common folder. I also output comments around each file block to make it easy to track back to the original file, if needed.&lt;/p&gt;

&lt;h2&gt;The Price of Hack and Slash Code&lt;/h2&gt;
&lt;p&gt;I put this script together very quickly and, as such, it is missing some features I think would be nice additions. The ability to execute a minification run at the end would be nice, as would the &lt;a href=&quot;http://blogs.msdn.com/davidebb/archive/2009/06/26/the-mvc-t4-template-is-now-up-on-codeplex-and-it-does-change-your-code-a-bit.aspx&quot; title=&quot;MVC T4 article that discusses the concept of saving a template file to force regeneration&quot;&gt;MarkDirty&lt;/a&gt; feature that was in the T4CSS.tt code. &lt;/p&gt;

&lt;p&gt;As with the previous post, the &lt;a href=&quot;https://bitbucket.org/tarwn/aspnet_sharedresourceswitht4/overview&quot; title=&quot;Sample project on BitBucket&quot;&gt;sample project is available&lt;/a&gt;, so you can look at (or download) a working example if you would like.&lt;/p&gt;

&lt;p&gt;And that is that. Shortest Eli post ever? Maybe &lt;img src=&quot;http://blogs.lessthandot.com/rsc/smilies/icon_smile.gif&quot; title=&quot;:)&quot; alt=&quot;:)&quot; class=&quot;middle&quot; width=&quot;15&quot; height=&quot;15&quot; /&gt;&lt;/p&gt;&lt;div class=&quot;item_footer&quot;&gt;&lt;p&gt;&lt;small&gt;&lt;a href=&quot;http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/Javascript/using-t4-templates-for-centralized&quot;&gt;Original post&lt;/a&gt; blogged on &lt;a href=&quot;http://lessthandot.com/&quot;&gt;LessThanDot&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;</description>
			<content:encoded><![CDATA[<p>In my previous post I mentioned that I was looking for an answer to the age-old question of how to manage common CSS and JavaScript across multiple projects (specifically ASP.Net projects). Using T4 templates, I was able to not only create a common location for CSS files, but to take it a step farther and use Less in ordr to simplify that common CSS even further.</p>

<p>But we left JavaScript out of the equation.</p>

<h2>T4'ing the JavaScript</h2>
<p>As a reminder, I am looking for a clean way to store shared JavaScript and CSS in a single place so I share it amongst several web projects and also make changes to the files on the fly and see them in the browser without a costly solution rebuild.</p>

<p>On the JavaScript side of things I decided to write my own little T4 template. The goal was to be able to pick up all of the *.js files in a central location, merge them, and save them out as a common file that I could then include in my local projects. </p>

<p>My solution layout is similar to the one in the prior post:</p>
<div class="codebox"><div class="codeheader"><span>text</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb42029'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb42029','cb35253'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="text" id="cb42029" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">/SolutionName/Common/NN-*.js files</li><li style="" class="li2">/SolutionName/Project1/scripts/CommonJS.tt</li><li style="" class="li1">/SolutionName/Project2/scripts/CommonJS.tt</li></ol></div><div id="cb35253" style="display: none; color: red;"></div></div></div>

<p>I prefix my common javascript files with a 2-digit number so I can include them in a planned order every time.</p>

<p><b>CommonJS.tt</b></p>
<div class="codebox"><div class="codeheader"><span>c#</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb20993'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb20993','cb44387'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="c#" id="cb20993" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">&lt;<span style="color: #339933;">#@ template language=&quot;C#&quot; hostspecific=&quot;True&quot; #&gt;</span></li><li style="" class="li2">&lt;<span style="color: #339933;">#@ import namespace=&quot;System&quot; #&gt;</span></li><li style="" class="li1">&lt;<span style="color: #339933;">#@ import namespace=&quot;System.IO&quot; #&gt;</span></li><li style="" class="li2">&lt;<span style="color: #339933;">#@ import namespace=&quot;Microsoft.VisualStudio.TextTemplating&quot; #&gt;</span></li><li style="" class="li1">&lt;<span style="color: #339933;">#@ Output Extension=&quot;.js&quot; #&gt;</span></li><li style="" class="li2">&lt;<span style="color: #339933;">#</span></li><li style="" class="li1"><span style="color: #808080; font-style: italic;">/*-------------------------------------------------*/</span></li><li style="" class="li2"><span style="color: #808080; font-style: italic;">// Settings &nbsp; &nbsp; &nbsp;</span></li><li style="" class="li1"><span style="color: #808080; font-style: italic;">/*-------------------------------------------------*/</span></li><li style="" class="li2"><span style="color: #993333;">string</span> _targetDirectory = @<span style="color: #ff0000;">&quot;<span style="color: #000099; font-weight: bold;">\.</span>.<span style="color: #000099; font-weight: bold;">\.</span>.<span style="color: #000099; font-weight: bold;">\C</span>ommon&quot;</span>;</li><li style="" class="li1"><span style="color: #808080; font-style: italic;">/*-------------------------------------------------*/</span></li><li style="" class="li2">Directory.<span style="color: #202020;">SetCurrentDirectory</span><span style="color: #66cc66;">&#40;</span>Path.<span style="color: #202020;">GetDirectoryName</span><span style="color: #66cc66;">&#40;</span>Host.<span style="color: #202020;">TemplateFile</span><span style="color: #66cc66;">&#41;</span> + _targetDirectory<span style="color: #66cc66;">&#41;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">var filespec = <span style="color: #ff0000;">&quot;*.js&quot;</span>;</li><li style="" class="li1">var files = Directory.<span style="color: #202020;">GetFiles</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;.&quot;</span>,filespec<span style="color: #66cc66;">&#41;</span>;</li><li style="" class="li2"><span style="color: #339933;">#&gt;</span></li><li style="" class="li1"><span style="color: #808080; font-style: italic;">/*</span></li><li style="" class="li2"><span style="color: #808080; font-style: italic;">CommandJavascript.js</span></li><li style="" class="li1"><span style="color: #808080; font-style: italic;">Converted at: &lt;#= DateTime.Now.ToShortDateString() #&gt; &lt;#= DateTime.Now.ToShortTimeString() #&gt;</span></li><li style="" class="li2"><span style="color: #808080; font-style: italic;">File List (&lt;#= files.Length #&gt; Found):</span></li><li style="" class="li1"><span style="color: #808080; font-style: italic;">&lt;#= &quot;\t&quot; + String.Join(&quot;\n\t&quot;, files) #&gt;</span></li><li style="" class="li2"><span style="color: #808080; font-style: italic;">*/</span></li><li style="" class="li1">&lt;<span style="color: #339933;">#</span></li><li style="" class="li2">foreach<span style="color: #66cc66;">&#40;</span>var jsFile in files<span style="color: #66cc66;">&#41;</span> </li><li style="" class="li1"><span style="color: #66cc66;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; Write<span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;/* ----------- &quot;</span> + jsFile + <span style="color: #ff0000;">&quot; ----------- */<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #66cc66;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; using<span style="color: #66cc66;">&#40;</span>StreamReader sr = File.<span style="color: #202020;">OpenText</span><span style="color: #66cc66;">&#40;</span>jsFile<span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #66cc66;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; Write<span style="color: #66cc66;">&#40;</span>sr.<span style="color: #202020;">ReadToEnd</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; sr.<span style="color: #202020;">Close</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #66cc66;">&#125;</span></li><li style="" class="li2">&nbsp; &nbsp; Write<span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;<span style="color: #000099; font-weight: bold;">\n</span>/* ----------- &quot;</span> + jsFile + <span style="color: #ff0000;">&quot; ----------- */<span style="color: #000099; font-weight: bold;">\n</span><span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #66cc66;">&#41;</span>;</li><li style="" class="li1"><span style="color: #66cc66;">&#125;</span></li><li style="" class="li2"><span style="color: #339933;">#&gt;</span></li></ol></div><div id="cb44387" style="display: none; color: red;"></div></div></div>

<p>Place this file in our project, update the _targetDirectory variable and we have a quick way to pull common javascript down to our projects.</p>

<p>The output file is a merged version of all of the *.js files found in the common folder. I also output comments around each file block to make it easy to track back to the original file, if needed.</p>

<h2>The Price of Hack and Slash Code</h2>
<p>I put this script together very quickly and, as such, it is missing some features I think would be nice additions. The ability to execute a minification run at the end would be nice, as would the <a href="http://blogs.msdn.com/davidebb/archive/2009/06/26/the-mvc-t4-template-is-now-up-on-codeplex-and-it-does-change-your-code-a-bit.aspx" title="MVC T4 article that discusses the concept of saving a template file to force regeneration">MarkDirty</a> feature that was in the T4CSS.tt code. </p>

<p>As with the previous post, the <a href="https://bitbucket.org/tarwn/aspnet_sharedresourceswitht4/overview" title="Sample project on BitBucket">sample project is available</a>, so you can look at (or download) a working example if you would like.</p>

<p>And that is that. Shortest Eli post ever? Maybe <img src="http://blogs.lessthandot.com/rsc/smilies/icon_smile.gif" title=":)" alt=":)" class="middle" width="15" height="15" /></p><div class="item_footer"><p><small><a href="http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/Javascript/using-t4-templates-for-centralized">Original post</a> blogged on <a href="http://lessthandot.com/">LessThanDot</a>.</small></p></div>]]></content:encoded>
								<comments>http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/Javascript/using-t4-templates-for-centralized#comments</comments>
			<wfw:commentRss>http://blogs.lessthandot.com/index.php/WebDev/?tempskin=_rss2&#38;disp=comments&#38;p=1501</wfw:commentRss>
		</item>
				<item>
			<title>CSS, Javascript, T4 Templates, and Less, Oh My</title>
			<link>http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/XHTMLCSS/css-javascript-t4-templates-and-less</link>
			<pubDate>Fri, 02 Dec 2011 13:02:00 +0000</pubDate>			<dc:creator>Eli Weinstock-Herman (tarwn)</dc:creator>
			<category domain="alt">ASP.NET</category>
<category domain="alt">Javascript</category>
<category domain="main">XHTML &amp; CSS</category>			<guid isPermaLink="false">1498@http://blogs.lessthandot.com/</guid>
						<description>&lt;p&gt;For the past few months, I have been looking for a way to define some JS and CSS files that would be shared between multiple projects in an ASP.Net solution. The intent is to define common scripts and CSS in one place instead of trying to keep multiple copies of it in sync or implementing an internal CDN with a versioning scheme. The challenge is finding a way to do this with a minimum of impact on the development, deployment, and production processes.&lt;/p&gt;

&lt;h2&gt;The Shared Project&lt;/h2&gt;
&lt;p&gt;Yesterday the best solution I was also the best one I had thought of on my own, which was to create a shared project and use pre- or post-build commands to copy the common files to the relevant web projects. Unfortunately this doesn&#039;t solve the &quot;let me edit a file without rebuilding&quot; unless I then edit the copied file, test, and remember to paste the changes back into the source without first building and wiping out my temporary changes.&lt;/p&gt;

&lt;p&gt;Yuck.&lt;/p&gt;

&lt;p&gt;This wasn&#039;t going to make life that much easier.&lt;/p&gt;

&lt;h2&gt;Start with just the CSS?&lt;/h2&gt;
&lt;p&gt;This morning, while looking into the potential of using LESS or SASS to reduce the repetitiveness of the files, I realized I had the potential for a much better solution. If I had a way to compile a LESS or SASS file on the fly into a CSS file, then I could still put my common CSS in a central location and just use an import statement in a template in each project to pull those common values in. &lt;/p&gt;

&lt;p&gt;I initially looked at using SASS with the &lt;a href=&quot;http://visualstudiogallery.msdn.microsoft.com/2b96d16a-c986-4501-8f97-8008f9db141a&quot; title=&quot;Mindscape Web Workbench extension on VisualStudioGallery&quot;&gt;Mindscape Web Workbench&lt;/a&gt; plugin. This seemed like a good solution, but something I read recently about design-time T4 templates led me to wonder if someone had created a T4 template that would transform LESS or SASS syntax into a nice clean CSS file. &lt;/p&gt;

&lt;p&gt;What did we do before search engines...&lt;/p&gt;

&lt;h2&gt;Implement T4CSS Template&lt;/h2&gt;
&lt;p&gt;Phil Haack (&lt;a href=&quot;http://haacked.com/&quot; title=&quot;Phil&#039;s blog&quot;&gt;blog&lt;/a&gt;|&lt;a href=&quot;https://twitter.com/#!/haacked&quot; title=&quot;@haacked on twitter&quot;&gt;twitter&lt;/a&gt;) posted a blog in 2009 on &lt;a href=&quot;http://haacked.com/archive/2009/12/02/t4-template-for-less-css.aspx&quot; title=&quot;T4CSS: A T4 Template for .Less CSS With Compression&quot;&gt;exactly this topic&lt;/a&gt;. He created a T4 template for Visual Studio 2008 that would use the dotless C# assembly to convert LESS files to static CSS files and provided to the &lt;a href=&quot;http://www.dotlesscss.org/&quot; title=&quot;Visit the .Less Site&quot;&gt;.Less site&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now we&#039;re cooking.&lt;/p&gt;

&lt;p&gt;First we need to download the t4css package from github: &lt;a href=&quot;https://github.com/dotless/dotless/downloads&quot;&gt;https://github.com/dotless/dotless/downloads&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are two files we are concerned with, the dotless.Core DLL and the T4CSS.tt template file. The template file is placed in our CSS folder in our site. &lt;/p&gt;

&lt;div style=&quot;font-size: .9em; background-color: #eeeeee; padding: .5em;&quot;&gt;
&lt;h3&gt;Referencing the Assembly&lt;/h3&gt;
&lt;p&gt;Unfortunately Visual Studio 2010&#039;s T4 implementation no longer accesses assemblies through the project references, but this still leaves us with &lt;a href=&quot;http://weblogs.asp.net/lhunt/archive/2010/05/04/t4-template-error-assembly-directive-cannot-locate-referenced-assembly-in-visual-studio-2010-project.aspx&quot; title=&quot;T4 Template error - Assembly Directive cannot locate referenced assembly in Visual Studio 2010 project&quot;&gt;a few options&lt;/a&gt;. Given that I want to share this among several projects, I put the dotless.Core DLL in a folder at my solution level and updated the path in the T4 template to use the solution path macro. &lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Next I created a sample file to play with, which I called test.less.css (fancy, I know). I also modified the settings section of the T4CSS.tt file, setting _runOnBuild and _useCssExtension to &quot;true&quot;. This will cause the template to run on each build, as well as when I trigger it, and it will look for files ending in &quot;.less.css&quot; instead of just &quot;.less&quot;. This gives us some CSS intellisense with minimal hassle, though Mindscape&#039;s Web Workbench apparently handles this out of the box and there is &lt;a href=&quot;http://visualstudiogallery.msdn.microsoft.com/dd5635b0-3c70-484f-abcb-cbdcabaa9923&quot; title=&quot;CSS Is Less&quot;&gt;an extension&lt;/a&gt; to make VS treat the less extension as a CSS format.&lt;/p&gt;

&lt;div style=&quot;font-size: .9em; background-color: #eeeeee; padding: .5em;&quot;&gt;
There is also a &lt;a href=&quot;http://visualstudiogallery.msdn.microsoft.com/e646c6ec-87a7-45ea-81e8-d655a3d3e73e?SRC=VSIDE&quot; title=&quot;LessExtension&quot;&gt;LessExtension&lt;/a&gt; in the gallery that seems to offer some of the functionality I already have with the T4 template, but I didn&#039;t have a chance to play with it.
&lt;/div&gt;

&lt;p&gt;If we keep the T4CSS file open, it will mark itself as unsaved each time it runs, so using Ctrl+Shift+S will save it and regenerate the output CSS. At least that&#039;s the theory. Unfortunately in my case, it seems that the template file was being saved prior to the css file, so I&#039;ve taken to pressing Ctrl+S and then Ctrl+Shift+S after making a quick change in my CSS (which is still way better than a Rebuild All would be).&lt;/p&gt;

&lt;p&gt;&lt;i&gt;Note: There is also  the &quot;Transform All Templates&quot; button on the top of the solution explorer if I don&#039;t feel like double-saving. I could also add a shortcut in  the keyboard commands list (Tools -&gt; Options -&gt; Environment -&gt; Keyboard) for &quot;TextTransformation.TransformAllTemplates&lt;/i&gt;&lt;/p&gt;

&lt;h2&gt;Working Across Projects&lt;/h2&gt;
&lt;p&gt;This solution hasn&#039;t quite given me the &quot;save the file and refresh the page&quot; ease of use of a static CSS file. This means if you are editing a less file that more than one template references, and you don&#039;t have all the templates open, you could get out of sync. To help keep things clean in source control, this means you should run a complete build (to let all the transforms run) or use. &lt;/p&gt;

&lt;p&gt;To make this work for multiple projects we can add a folder at the solution level with our less files and use the @import statement to pull them in. Except now we can&#039;t do the .less.css trick anymore because less doesn&#039;t process @import&#039;s ending in CSS, assuming they are intended to be regular css imports. At this point, it&#039;s probably time to stop fighting it, apply the &quot;Less is CSS&quot; extension I mentioned above, and change back to using .less instead of .less.css. Fun times.&lt;/p&gt;

&lt;p&gt;So what we end up with is a folder structure that looks like:&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;text&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;text&quot; id=&quot;cb70570&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;/SolutionName/Common/dotless.Core.dll&lt;br /&gt;/SolutionName/Common/common.less&lt;br /&gt;/SolutionName/Project1/css/T4CSS.tt&lt;br /&gt;/SolutionName/Project1/css/stylesheet.less&lt;br /&gt;/SolutionName/Project2/css/T4CSS.tt&lt;br /&gt;/SolutionName/Project2/css/stylesheet.less&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb99630&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And inside the project-specific .less files we have an import at the top, like so:&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;css&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;css&quot; id=&quot;cb76923&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #a1a100;&quot;&gt;@import &amp;quot;../../Common/common.less&amp;quot;;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;/* plus some project specific-stuff */&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb92776&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And there we have it, shared CSS. I posted a &lt;a href=&quot;https://bitbucket.org/tarwn/aspnet_sharedresourceswitht4/overview&quot; title=&quot;See project on BitBucket&quot;&gt;working sample project on BitBucket&lt;/a&gt; if you want to browse it in detail.&lt;/p&gt;

&lt;h2&gt;I Specifically Heard You Say JavaScript&lt;/h2&gt;
&lt;p&gt;I intend to solve the common javascript issue the same way, except in this case I will write my own T4 templates to directly copy the files from the common area that are needed in each project. This will provide me with an easy way to manage common scripts in a central location, the ability to edit and refresh my page to test changes without rebuilds, and can easily be extended to include minified versions of the files.&lt;/p&gt;&lt;div class=&quot;item_footer&quot;&gt;&lt;p&gt;&lt;small&gt;&lt;a href=&quot;http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/XHTMLCSS/css-javascript-t4-templates-and-less&quot;&gt;Original post&lt;/a&gt; blogged on &lt;a href=&quot;http://lessthandot.com/&quot;&gt;LessThanDot&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;</description>
			<content:encoded><![CDATA[<p>For the past few months, I have been looking for a way to define some JS and CSS files that would be shared between multiple projects in an ASP.Net solution. The intent is to define common scripts and CSS in one place instead of trying to keep multiple copies of it in sync or implementing an internal CDN with a versioning scheme. The challenge is finding a way to do this with a minimum of impact on the development, deployment, and production processes.</p>

<h2>The Shared Project</h2>
<p>Yesterday the best solution I was also the best one I had thought of on my own, which was to create a shared project and use pre- or post-build commands to copy the common files to the relevant web projects. Unfortunately this doesn't solve the "let me edit a file without rebuilding" unless I then edit the copied file, test, and remember to paste the changes back into the source without first building and wiping out my temporary changes.</p>

<p>Yuck.</p>

<p>This wasn't going to make life that much easier.</p>

<h2>Start with just the CSS?</h2>
<p>This morning, while looking into the potential of using LESS or SASS to reduce the repetitiveness of the files, I realized I had the potential for a much better solution. If I had a way to compile a LESS or SASS file on the fly into a CSS file, then I could still put my common CSS in a central location and just use an import statement in a template in each project to pull those common values in. </p>

<p>I initially looked at using SASS with the <a href="http://visualstudiogallery.msdn.microsoft.com/2b96d16a-c986-4501-8f97-8008f9db141a" title="Mindscape Web Workbench extension on VisualStudioGallery">Mindscape Web Workbench</a> plugin. This seemed like a good solution, but something I read recently about design-time T4 templates led me to wonder if someone had created a T4 template that would transform LESS or SASS syntax into a nice clean CSS file. </p>

<p>What did we do before search engines...</p>

<h2>Implement T4CSS Template</h2>
<p>Phil Haack (<a href="http://haacked.com/" title="Phil's blog">blog</a>|<a href="https://twitter.com/#!/haacked" title="@haacked on twitter">twitter</a>) posted a blog in 2009 on <a href="http://haacked.com/archive/2009/12/02/t4-template-for-less-css.aspx" title="T4CSS: A T4 Template for .Less CSS With Compression">exactly this topic</a>. He created a T4 template for Visual Studio 2008 that would use the dotless C# assembly to convert LESS files to static CSS files and provided to the <a href="http://www.dotlesscss.org/" title="Visit the .Less Site">.Less site</a>.</p>

<p>Now we're cooking.</p>

<p>First we need to download the t4css package from github: <a href="https://github.com/dotless/dotless/downloads">https://github.com/dotless/dotless/downloads</a></p>

<p>There are two files we are concerned with, the dotless.Core DLL and the T4CSS.tt template file. The template file is placed in our CSS folder in our site. </p>

<div style="font-size: .9em; background-color: #eeeeee; padding: .5em;">
<h3>Referencing the Assembly</h3>
<p>Unfortunately Visual Studio 2010's T4 implementation no longer accesses assemblies through the project references, but this still leaves us with <a href="http://weblogs.asp.net/lhunt/archive/2010/05/04/t4-template-error-assembly-directive-cannot-locate-referenced-assembly-in-visual-studio-2010-project.aspx" title="T4 Template error - Assembly Directive cannot locate referenced assembly in Visual Studio 2010 project">a few options</a>. Given that I want to share this among several projects, I put the dotless.Core DLL in a folder at my solution level and updated the path in the T4 template to use the solution path macro. </p>
</div>

<p>Next I created a sample file to play with, which I called test.less.css (fancy, I know). I also modified the settings section of the T4CSS.tt file, setting _runOnBuild and _useCssExtension to "true". This will cause the template to run on each build, as well as when I trigger it, and it will look for files ending in ".less.css" instead of just ".less". This gives us some CSS intellisense with minimal hassle, though Mindscape's Web Workbench apparently handles this out of the box and there is <a href="http://visualstudiogallery.msdn.microsoft.com/dd5635b0-3c70-484f-abcb-cbdcabaa9923" title="CSS Is Less">an extension</a> to make VS treat the less extension as a CSS format.</p>

<div style="font-size: .9em; background-color: #eeeeee; padding: .5em;">
There is also a <a href="http://visualstudiogallery.msdn.microsoft.com/e646c6ec-87a7-45ea-81e8-d655a3d3e73e?SRC=VSIDE" title="LessExtension">LessExtension</a> in the gallery that seems to offer some of the functionality I already have with the T4 template, but I didn't have a chance to play with it.
</div>

<p>If we keep the T4CSS file open, it will mark itself as unsaved each time it runs, so using Ctrl+Shift+S will save it and regenerate the output CSS. At least that's the theory. Unfortunately in my case, it seems that the template file was being saved prior to the css file, so I've taken to pressing Ctrl+S and then Ctrl+Shift+S after making a quick change in my CSS (which is still way better than a Rebuild All would be).</p>

<p><i>Note: There is also  the "Transform All Templates" button on the top of the solution explorer if I don't feel like double-saving. I could also add a shortcut in  the keyboard commands list (Tools -> Options -> Environment -> Keyboard) for "TextTransformation.TransformAllTemplates</i></p>

<h2>Working Across Projects</h2>
<p>This solution hasn't quite given me the "save the file and refresh the page" ease of use of a static CSS file. This means if you are editing a less file that more than one template references, and you don't have all the templates open, you could get out of sync. To help keep things clean in source control, this means you should run a complete build (to let all the transforms run) or use. </p>

<p>To make this work for multiple projects we can add a folder at the solution level with our less files and use the @import statement to pull them in. Except now we can't do the .less.css trick anymore because less doesn't process @import's ending in CSS, assuming they are intended to be regular css imports. At this point, it's probably time to stop fighting it, apply the "Less is CSS" extension I mentioned above, and change back to using .less instead of .less.css. Fun times.</p>

<p>So what we end up with is a folder structure that looks like:</p>
<div class="codebox"><div class="codeheader"><span>text</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb77442'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb77442','cb2808'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="text" id="cb77442" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">/SolutionName/Common/dotless.Core.dll</li><li style="" class="li2">/SolutionName/Common/common.less</li><li style="" class="li1">/SolutionName/Project1/css/T4CSS.tt</li><li style="" class="li2">/SolutionName/Project1/css/stylesheet.less</li><li style="" class="li1">/SolutionName/Project2/css/T4CSS.tt</li><li style="" class="li2">/SolutionName/Project2/css/stylesheet.less</li></ol></div><div id="cb2808" style="display: none; color: red;"></div></div></div>

<p>And inside the project-specific .less files we have an import at the top, like so:</p>
<div class="codebox"><div class="codeheader"><span>css</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb73104'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb73104','cb68581'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="css" id="cb73104" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #a1a100;">@import &quot;../../Common/common.less&quot;;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1"><span style="color: #808080; font-style: italic;">/* plus some project specific-stuff */</span></li></ol></div><div id="cb68581" style="display: none; color: red;"></div></div></div>

<p>And there we have it, shared CSS. I posted a <a href="https://bitbucket.org/tarwn/aspnet_sharedresourceswitht4/overview" title="See project on BitBucket">working sample project on BitBucket</a> if you want to browse it in detail.</p>

<h2>I Specifically Heard You Say JavaScript</h2>
<p>I intend to solve the common javascript issue the same way, except in this case I will write my own T4 templates to directly copy the files from the common area that are needed in each project. This will provide me with an easy way to manage common scripts in a central location, the ability to edit and refresh my page to test changes without rebuilds, and can easily be extended to include minified versions of the files.</p><div class="item_footer"><p><small><a href="http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/XHTMLCSS/css-javascript-t4-templates-and-less">Original post</a> blogged on <a href="http://lessthandot.com/">LessThanDot</a>.</small></p></div>]]></content:encoded>
								<comments>http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/XHTMLCSS/css-javascript-t4-templates-and-less#comments</comments>
			<wfw:commentRss>http://blogs.lessthandot.com/index.php/WebDev/?tempskin=_rss2&#38;disp=comments&#38;p=1498</wfw:commentRss>
		</item>
				<item>
			<title>Automated Web Testing with Selenium WebDriver</title>
			<link>http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/automated-web-testing-with-selenium-2</link>
			<pubDate>Wed, 02 Nov 2011 10:27:00 +0000</pubDate>			<dc:creator>Eli Weinstock-Herman (tarwn)</dc:creator>
			<category domain="main">UI Development</category>			<guid isPermaLink="false">1461@http://blogs.lessthandot.com/</guid>
						<description>&lt;p&gt;Last week we created a pair of smoke tests with the &lt;a href=&quot;http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/automated-web-testing-with-selenium&quot; title=&quot;Read last week&#039;s post&quot;&gt;Selenium IDE&lt;/a&gt; tool. Several people commented, with varying levels of politeness, about the downsides of Selenium IDE. There are even those that will tell you not to use it at all, to immediately bypass Selenium IDE and go straight to code. My opinion is that this is a matter of context. &lt;/p&gt;

&lt;p&gt;Today we are going to get into the nuts and bolts of coding automated tests against the WebDriver library. Before we get into that, however, let&#039;s discuss when it is appropriate to make this transition.&lt;/p&gt;

&lt;h2&gt;Context and When to Transition&lt;/h2&gt;
&lt;p&gt;Most projects that are using browser automation are large and require a fairly complex set of tests. The solutions they are using are at least at the level we will be working at today, if not further down the road to automated acceptance testing. A higher level of complexity across a much larger project is just not manageable with Selenium IDE, so many people are used to bypassing it immediately and going straight to a code solution.&lt;/p&gt;

&lt;p&gt;Context is king. For small teams or sites, there is value in being able to put together some quick tests that can be run regularly without making an investment in creating a custom framework. It can also be a useful tool when you have a legacy site and just want to make sure it hasn&#039;t fallen over.&lt;/p&gt;

&lt;p&gt;Not everyone has worked in the context of these larger projects or has worked extensively with Selenium. If you are getting started for the first time or currently using Selenium IDE, here are some signs to consider moving to a custom solution:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You often update a number of tests simultaneously to change element names&lt;/li&gt;
&lt;li&gt;Sacrificing your computer while the tests run is becoming a nuisance&lt;/li&gt;
&lt;li&gt;You want to share the tests with team members, others in your company, or your client&lt;/li&gt;
&lt;li&gt;Maintaining the test suite list in the IDE tasks longer than creating tests&lt;/li&gt;
&lt;li&gt;You are starting to build tests for every feature you add to the software&lt;/li&gt;
&lt;li&gt;You want to tie the tests into the automated build system for your software&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In these cases, you have likely scaled past the point where Selenium IDE is enabling you, into a range where it&#039;s costs outweigh it&#039;s gains.&lt;/p&gt;

&lt;h2&gt;The Next Step for Web Automation&lt;/h2&gt;
&lt;p&gt;The next step in automating web interface tests is to use the Selenium WebDriver library to code the tests. Writing our own framework has some inherent advantages and disadvantages.&lt;/p&gt;

&lt;p&gt;Pros: Free, Reduced Duplication, Easy to share, Relatively easy to automate&lt;br /&gt;
Cons: Increased cost, Increased risk due to code errors, Increased complexity&lt;br /&gt;
Neutral: Level of fragility depends on level of developer&lt;/p&gt;

&lt;p&gt;There are Selenium WebDriver libraries for Java, Python, Ruby, and .Net. By using an existing Unit testing framework we don&#039;t have to write our own Test Runner and it is likely to have documentation and/or plugins for integrating into most of the popular build engines.&lt;/p&gt;

&lt;p&gt;For the purpose of this post, we are again going to create tests against my contact page, but we&#039;ll be increasing the number of tests. We will be using the .Net driver, C#, and the MS Test framework.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: I would advise this unit test project be separate from the overall solution for your application. Keeping it separate means you can version it separately, build and run it separately, and easily run the real unit tests in your product solution without accidentally running the interface ones as well.&lt;br /&gt;
&lt;br /&gt;
Note 2: I had intended to do this in VB.Net originally, but the downside of switching easily between languages in the same IDE is that sometimes you find yourself writing in the wrong one without even realizing it.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;After creating our .Net solution to house the test project, we need to add a reference to the Selenium WebDriver library. This can be downloaded from the &lt;a href=&quot;http://seleniumhq.org/download/&quot; title=&quot;Selenium WebDriver download page&quot;&gt;Web Driver Download page&lt;/a&gt; or added via NuGet:&lt;/p&gt;
&lt;div class=&quot;commandWrapper&quot;&gt;
&lt;div class=&quot;commandPrompt&quot;&gt;
&lt;p class=&quot;command&quot;&gt;PM&amp;gt; Install-Package Selenium.WebDriver&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: If you don&#039;t have NuGet installed, I highly recommend you go check it out at &lt;a href=&quot;http://nuget.org/&quot; title=&quot;NuGet.org&quot;&gt;http://nuget.org/&lt;/a&gt; or read more about it in &lt;a href=&quot;http://msdn.microsoft.com/en-us/magazine/hh547106.aspx&quot; title=&quot;Manage Project Libraries with NuGet by Phil Haack&quot;&gt;this recent MSDN article&lt;/a&gt;. NuGet is a package manager that allows us to quickly download, install, and update 3rd party packages in Visual Studio. It not only makes it easier to keep 3rd party libraries up to date, but also makes a number of packages more accessible, since they generally install the appropriate settings and references to easily get us started.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Once we have all of that set up, we can create a single class file with a quick test function to verify we&#039;re ready to go:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb55496&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;using&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;System&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;using&lt;/span&gt; Microsoft.&lt;span style=&quot;color: #0000FF;&quot;&gt;VisualStudio&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;TestTools&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;UnitTesting&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;using&lt;/span&gt; OpenQA.&lt;span style=&quot;color: #0000FF;&quot;&gt;Selenium&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Firefox&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;namespace&lt;/span&gt; SampleWebDriverUnitTest &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;TestClass&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;class&lt;/span&gt; GettingStartedTest &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; GettingStartedTest&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;TestMethod&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; GettingStarted_BasicFireFoxGET_LoadsPage&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;using&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;FirefoxDriver driver = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; FirefoxDriver&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;Url&lt;/span&gt; = &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;http://tiernok.com&amp;quot;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;Navigate&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Assert.&lt;span style=&quot;color: #0000FF;&quot;&gt;AreEqual&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Eli Weinstock-Herman | Tarwn&amp;quot;&lt;/span&gt;, driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;Title&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb14483&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is all it takes to get our first test. Create the solution, add the reference, create the test class and method. Since it&#039;s MS Test, Ctrl+R, A will run all of our tests in the integrated test runner.&lt;/p&gt;

&lt;p&gt;Selenium WebDriver is written in a somewhat fluent syntax, so for the rest of the post we will combine the URL and navigation into a single statement: driver.Navigate().GoToUrl(&quot;http://tiernok.com&quot;);&lt;/p&gt;

&lt;h2&gt;The First Test Cases&lt;/h2&gt;
&lt;p&gt;Last time we started off by testing navigation to the home page and the presence of two books, then we tested submitting a form would correctly fail validation when it was missing a required field. Today we&#039;re going to extend that form validation check, a more complex example in Selenium IDE that will be handled fairly easily in our new framework.&lt;/p&gt;

&lt;h3&gt;The Email Form&lt;/h3&gt;
&lt;p&gt;These are the situations we want to test for the email form:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;	When I submit the form without an email address, it displays an error.&lt;br /&gt;
	When I submit the form without a name, it displays an error.&lt;br /&gt;
	When I submit the form without a message, it displays an error.&lt;br /&gt;
	When I submit the form with all of the fields filled in, it displays a success message.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The steps for these tests are going to be similar to the second test we created in Selenium IDE last week. &lt;/p&gt;

&lt;h3&gt;Exporting a Selenium IDE Test Case&lt;/h3&gt;
&lt;p&gt;Selenium IDE has a feature that allows us to export tests, so when we are getting started we can look at that export to get a feel for how the code version will function. To export a test, open Selenium IDE, go to File, Export Text Case As, and choose C# (WebDriver). A C# file will be generated with the test case converted to an NUnit Test. The file includes some test setup and teardown methods, as well as the following test method:&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb16903&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;Test&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; TheContactPageReturnsErrorWhenEmailFieldEmptyTest&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;Navigate&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;GoToUrl&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;LinkText&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Contact&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Click&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;itxtFromName&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Clear&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;itxtFromName&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;SendKeys&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Selenium Test&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;itxtBody&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Clear&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;itxtBody&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;SendKeys&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;This is my Selenium Message&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;CssSelector&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;input[type=&lt;span style=&quot;color: #008080; font-weight: bold;&quot;&gt;\&amp;quot;&lt;/span&gt;submit&lt;span style=&quot;color: #008080; font-weight: bold;&quot;&gt;\&amp;quot;&lt;/span&gt;]&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Click&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; Assert.&lt;span style=&quot;color: #0000FF;&quot;&gt;AreEqual&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Please fill in all three entries before sending the message.&amp;quot;&lt;/span&gt;, driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;CssSelector&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;.err&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Text&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb23384&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Initially this may look more complicated then the Selenium IDE command steps, but really the only difference is that we are explicitly finding the elements we want to act on before acting, which was was more implicit in Selenium IDE. &lt;/p&gt;

&lt;p&gt;Converting this to an MS Test is not difficult, but we will want to wrap the tests in a using statement so that driver gets properly disposed (MS Test doesn&#039;t run cleanup properly when an exception occurs).&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb4422&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;TestMethod&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; ContactPageReturnsErrorWhenEmailFieldEmpty&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;using&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;FirefoxDriver driver = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; FirefoxDriver&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;Navigate&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;GoToUrl&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;http://tiernok.com/&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;LinkText&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Contact&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Click&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;itxtFromName&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Clear&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;itxtFromName&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;SendKeys&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Selenium Test&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;itxtBody&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Clear&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;itxtBody&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;SendKeys&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;This is my Selenium Message&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;CssSelector&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;input[type=&lt;span style=&quot;color: #008080; font-weight: bold;&quot;&gt;\&amp;quot;&lt;/span&gt;submit&lt;span style=&quot;color: #008080; font-weight: bold;&quot;&gt;\&amp;quot;&lt;/span&gt;]&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Click&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Assert.&lt;span style=&quot;color: #0000FF;&quot;&gt;AreEqual&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Please fill in all three entries before sending the message.&amp;quot;&lt;/span&gt;, driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;CssSelector&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;.err&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Text&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb54326&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The only real changes have been the addition of the using statement and changing the decorated attribute from NUnit&#039;s Test to MS Test&#039;s TestMethod. &lt;/p&gt;

&lt;h3&gt;The Rest of the Test Cases&lt;/h3&gt;

&lt;p&gt;Based on this one test, we can easily create tests to satisfy the other conditions:&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb67249&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;TestMethod&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; ContactPageReturnsErrorWhenNameFieldEmpty&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;using&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;FirefoxDriver driver = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; FirefoxDriver&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;Navigate&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;GoToUrl&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;http://tiernok.com/&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;LinkText&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Contact&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Click&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;itxtFromEmail&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Clear&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;itxtFromEmail&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;SendKeys&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;EmailAddress&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;itxtBody&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Clear&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;itxtBody&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;SendKeys&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;This is my Selenium Message&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;CssSelector&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;input[type=&lt;span style=&quot;color: #008080; font-weight: bold;&quot;&gt;\&amp;quot;&lt;/span&gt;submit&lt;span style=&quot;color: #008080; font-weight: bold;&quot;&gt;\&amp;quot;&lt;/span&gt;]&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Click&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Assert.&lt;span style=&quot;color: #0000FF;&quot;&gt;AreEqual&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Please fill in all three entries before sending the message.&amp;quot;&lt;/span&gt;, driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;CssSelector&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;.err&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Text&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;TestMethod&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; ContactPageReturnsErrorWhenMessageFieldEmpty&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;using&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;FirefoxDriver driver = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; FirefoxDriver&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;Navigate&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;GoToUrl&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;http://tiernok.com/&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;LinkText&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Contact&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Click&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;itxtFromName&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Clear&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;itxtFromName&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;SendKeys&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Selenium Test&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;itxtFromEmail&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Clear&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;itxtFromEmail&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;SendKeys&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;EmailAddress&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;CssSelector&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;input[type=&lt;span style=&quot;color: #008080; font-weight: bold;&quot;&gt;\&amp;quot;&lt;/span&gt;submit&lt;span style=&quot;color: #008080; font-weight: bold;&quot;&gt;\&amp;quot;&lt;/span&gt;]&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Click&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Assert.&lt;span style=&quot;color: #0000FF;&quot;&gt;AreEqual&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Please fill in all three entries before sending the message.&amp;quot;&lt;/span&gt;, driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;CssSelector&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;.err&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Text&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;TestMethod&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; ContactPageReturnsSuccessWhenAllFieldsProvided&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;using&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;FirefoxDriver driver = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; FirefoxDriver&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;Navigate&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;GoToUrl&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;http://tiernok.com/&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;LinkText&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Contact&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Click&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;itxtFromName&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Clear&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;itxtFromName&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;SendKeys&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Selenium Test&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;itxtFromEmail&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Clear&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;itxtFromEmail&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;SendKeys&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;EmailAddress&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;itxtBody&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Clear&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;itxtBody&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;SendKeys&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;This is my Selenium Message&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;CssSelector&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;input[type=&lt;span style=&quot;color: #008080; font-weight: bold;&quot;&gt;\&amp;quot;&lt;/span&gt;submit&lt;span style=&quot;color: #008080; font-weight: bold;&quot;&gt;\&amp;quot;&lt;/span&gt;]&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Click&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Assert.&lt;span style=&quot;color: #0000FF;&quot;&gt;AreEqual&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Thanks, your message has been sent successfully.&amp;quot;&lt;/span&gt;, driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;FindElement&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;By.&lt;span style=&quot;color: #0000FF;&quot;&gt;CssSelector&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;.suc&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;Text&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb37807&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Theoretically we&#039;re done, if we had a few more cases we could keep copying and pasting our way to the finish line. If you recall, however, one of the goals of  moving from Selenium IDE method to a custom solution was to reduce the amount of duplication and the fragility that duplication causes. So far all we have done is transfer that fragility from a series of test cases in the IDE to a series of unit tests in C#.&lt;/p&gt;

&lt;h2&gt;Reducing Test Fragility&lt;/h2&gt;
&lt;p&gt;When we start writing more than basic smoke tests, we are going to be accessing items repetitively and duplicating ourselves more often. Switching to code has allowed us to create additional tests very quickly, but we haven&#039;t really solved the duplication issue yet.&lt;/p&gt;

&lt;p&gt;There&#039;s two approaches we can take at this point, abstracting the duplication on our own or using the Page Factory from the Support library to implement the &lt;a href=&quot;http://code.google.com/p/selenium/wiki/PageObjects&quot; title=&quot;More information on Page Object&quot;&gt;Page Object&lt;/a&gt; pattern. &lt;/p&gt;

&lt;p&gt;The idea of the Page Object pattern is to represent each page in our target application as a class in our test framework. This allows us to reflect functionality and changes in the real page with functionality or changes in our class. Rather than locating and interacting with individual controls in a browser, our tests than interact with these Page Object classes as representations of what the browser should be seeing. &lt;/p&gt;

&lt;p&gt;Given we are just getting started with the code approach, lets use the Page Object approach for the Contact page and use the PageFactory that is provided in the Support library to help with the wiring.&lt;/p&gt;

&lt;h3&gt;Creating the Contact PageObject&lt;/h3&gt;
&lt;p&gt;Before we start making code changes, we need to get the Support library. As before, we can either download it from the &lt;a href=&quot;http://seleniumhq.org/download/&quot; title=&quot;Selenium WebDriver download page&quot;&gt;Selenium website&lt;/a&gt; or install it via NuGet:&lt;/p&gt;
&lt;div class=&quot;commandWrapper&quot;&gt;
&lt;div class=&quot;commandPrompt&quot;&gt;
&lt;p class=&quot;command&quot;&gt;PM&amp;gt; Install-Package Selenium.Support&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Next we are going to start modeling our page. Since there is a common layout throughout the site, I&#039;ll first create a base page object to represent the common elements (like navigation menu items) we will be interacting with:&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb87781&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;namespace&lt;/span&gt; SampleWebDriverUnitTest.&lt;span style=&quot;color: #0000FF;&quot;&gt;Pages&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #FF0000;&quot;&gt;class&lt;/span&gt; PageBase &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;FindsBy&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;How = How.&lt;span style=&quot;color: #0000FF;&quot;&gt;LinkText&lt;/span&gt;, &lt;span style=&quot;color: #0600FF;&quot;&gt;Using&lt;/span&gt; = &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Contact&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; IWebElement ContactLink;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; NavigateContactLink&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ContactLink.&lt;span style=&quot;color: #0000FF;&quot;&gt;Click&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb3894&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then we can extend this class to define the elements that are specific to the Contact form:&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb14514&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;namespace&lt;/span&gt; SampleWebDriverUnitTest.&lt;span style=&quot;color: #0000FF;&quot;&gt;Pages&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #FF0000;&quot;&gt;class&lt;/span&gt; ContactPage : PageBase &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;FindsBy&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;How=How.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;, &lt;span style=&quot;color: #0600FF;&quot;&gt;Using&lt;/span&gt;=&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;itxtFromEmail&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; IWebElement EmailInput;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;FindsBy&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;How = How.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;, &lt;span style=&quot;color: #0600FF;&quot;&gt;Using&lt;/span&gt; = &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;itxtFromName&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; IWebElement NameInput;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;FindsBy&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;How = How.&lt;span style=&quot;color: #0000FF;&quot;&gt;Id&lt;/span&gt;, &lt;span style=&quot;color: #0600FF;&quot;&gt;Using&lt;/span&gt; = &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;itxtBody&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; IWebElement BodyInput;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;FindsBy&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;How = How.&lt;span style=&quot;color: #0000FF;&quot;&gt;CssSelector&lt;/span&gt;, &lt;span style=&quot;color: #0600FF;&quot;&gt;Using&lt;/span&gt; = &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;input[type=submit]&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; IWebElement SubmitInput;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;FindsBy&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;How = How.&lt;span style=&quot;color: #0000FF;&quot;&gt;CssSelector&lt;/span&gt;, &lt;span style=&quot;color: #0600FF;&quot;&gt;Using&lt;/span&gt; = &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;.err&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; IWebElement ErrorMessage;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;FindsBy&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;How = How.&lt;span style=&quot;color: #0000FF;&quot;&gt;CssSelector&lt;/span&gt;, &lt;span style=&quot;color: #0600FF;&quot;&gt;Using&lt;/span&gt; = &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;.suc&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; IWebElement SuccessMessage;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; SendEmail&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; name, &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; email, &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; body&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;if&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;!&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;IsNullOrEmpty&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;email&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; EmailInput.&lt;span style=&quot;color: #0000FF;&quot;&gt;Clear&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; EmailInput.&lt;span style=&quot;color: #0000FF;&quot;&gt;SendKeys&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;email&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;if&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;!&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;IsNullOrEmpty&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;name&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; NameInput.&lt;span style=&quot;color: #0000FF;&quot;&gt;Clear&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; NameInput.&lt;span style=&quot;color: #0000FF;&quot;&gt;SendKeys&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;name&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;if&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;!&lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;IsNullOrEmpty&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;body&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; BodyInput.&lt;span style=&quot;color: #0000FF;&quot;&gt;Clear&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; BodyInput.&lt;span style=&quot;color: #0000FF;&quot;&gt;SendKeys&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;body&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; SubmitInput.&lt;span style=&quot;color: #0000FF;&quot;&gt;Click&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #008080; font-style: italic;&quot;&gt;// try/catches below are due to selenium using exceptions to indicate search failures rather than an empty result or null&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;bool&lt;/span&gt; IsDisplayingValidationError&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; get &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;try&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; ErrorMessage.&lt;span style=&quot;color: #0000FF;&quot;&gt;Displayed&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;catch&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;false&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;bool&lt;/span&gt; IsDisplayingSuccessMessage &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; get &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;try&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; SuccessMessage.&lt;span style=&quot;color: #0000FF;&quot;&gt;Displayed&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;catch&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;false&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&amp;nbsp; &amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb12800&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The PageFactory object is responsible for wiring our page objects to the actual page displayed in the browser. It attempts to match each IWebElement in the class to an element in the web page. By default, if you do not decorate the field with the FindsBy decorator then it will search for an element who&#039;s ID matches the variable name. I prefer to explicitly specify the find criteria, though, as this lets me name my variables consistently and limits the impact of an HTML change to a single decorator instead of causing a property name change.&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb47360&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;TestMethod&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; ContactPageReturnsErrorWhenEmailFieldEmpty_PageObjectVersion&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;using&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;FirefoxDriver driver = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; FirefoxDriver&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;Navigate&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;GoToUrl&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;http://tiernok.com/&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Pages.&lt;span style=&quot;color: #0000FF;&quot;&gt;PageBase&lt;/span&gt; homepage = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; Pages.&lt;span style=&quot;color: #0000FF;&quot;&gt;PageBase&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; PageFactory.&lt;span style=&quot;color: #0000FF;&quot;&gt;InitElements&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;driver, homepage&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; homepage.&lt;span style=&quot;color: #0000FF;&quot;&gt;NavigateContactLink&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Pages.&lt;span style=&quot;color: #0000FF;&quot;&gt;ContactPage&lt;/span&gt; contactPage = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; Pages.&lt;span style=&quot;color: #0000FF;&quot;&gt;ContactPage&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; PageFactory.&lt;span style=&quot;color: #0000FF;&quot;&gt;InitElements&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;driver, contactPage&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; contactPage.&lt;span style=&quot;color: #0000FF;&quot;&gt;SendEmail&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Selenium Test&amp;quot;&lt;/span&gt;, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;This is my Selenium Test Message&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Assert.&lt;span style=&quot;color: #0000FF;&quot;&gt;IsTrue&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;contactPage.&lt;span style=&quot;color: #0000FF;&quot;&gt;IsDisplayingValidationError&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb53500&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The new code:&lt;/p&gt;
&lt;ul&gt;
&lt;ol&gt;navigates to the site&lt;/ol&gt;
&lt;ol&gt;creates an instance of the base page&lt;/ol&gt;
&lt;ol&gt;clicks the navigation link for the Contact Page&lt;/ol&gt;
&lt;ol&gt;assumes it is on the contact page and creates an instance of that object&lt;/ol&gt;
&lt;ol&gt;Sends an email with two of the three values filled in&lt;/ol&gt;
&lt;ol&gt;Asserts that the error message is displayed&lt;/ol&gt;
&lt;/ul&gt;

&lt;p&gt;However, this code still has problems. &lt;/p&gt;

&lt;h3&gt;Refining and Correcting the First Test&lt;/h3&gt;
&lt;p&gt;The current code, in my opinion, doesn&#039;t fail soon enough if it is on the wrong page. In addition, this first test we&#039;ve converted is not very readable and I can already tell there is going to be a lot of duplication in later tests. Let&#039;s do some refactoring.&lt;/p&gt;

&lt;p&gt;Rather than repetitively initializing page objects in each test and and adding title verification to each test, lets move that behavior to the PageBase so we can easily do it for every page we load.&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb67043&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;class&lt;/span&gt; PageBase &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; RemoteWebDriver Driver &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; get; set; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; ExpectedTitle &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; get;set; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #008080; font-style: italic;&quot;&gt;// ... unchanged code ...&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;static&lt;/span&gt; TPage GetInstance&amp;lt;TPage&amp;gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;RemoteWebDriver driver, &lt;span style=&quot;color: #FF0000;&quot;&gt;string&lt;/span&gt; expectedTitle&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; where TPage : PageBase, &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; TPage pageInstance = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; TPage&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt; &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ExpectedTitle = expectedTitle,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Driver = driver&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; PageFactory.&lt;span style=&quot;color: #0000FF;&quot;&gt;InitElements&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;driver, pageInstance&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Assert.&lt;span style=&quot;color: #0000FF;&quot;&gt;AreEqual&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;pageInstance.&lt;span style=&quot;color: #0000FF;&quot;&gt;ExpectedTitle&lt;/span&gt;, driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;Title&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; pageInstance;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb75545&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now we have a single generic call that can create a Page (provided it inherits from PageBase and has a constructor) and execute an assertion on it&#039;s title. Lets update the NavigateContactLink method to return an initialized ContactPage instance, since this will be a consistent next step each time we navigate to a new page. This reduces the amount of code in the test and adds an automatic check to ensure we have reached the page we were expecting.&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb64630&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #FF0000;&quot;&gt;class&lt;/span&gt; PageBase &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #008080; font-style: italic;&quot;&gt;// ... unchanged ...&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; ContactPage NavigateContactLink&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ContactLink.&lt;span style=&quot;color: #0000FF;&quot;&gt;Click&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;return&lt;/span&gt; GetInstance&amp;lt;ContactPage&amp;gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;Driver, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Eli Weinstock-Herman | Tarwn - Contact&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #008080; font-style: italic;&quot;&gt;// ... unchanged ...&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb18118&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Our refactored test now looks like this:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb76530&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;TestMethod&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; ContactPageReturnsErrorWhenEmailFieldEmpty_PageObjectVersion&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;using&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;FirefoxDriver driver = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; FirefoxDriver&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;Navigate&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;GoToUrl&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;http://tiernok.com/&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; PageBase homepage = PageBase.&lt;span style=&quot;color: #0000FF;&quot;&gt;GetInstance&lt;/span&gt;&amp;lt;PageBase&amp;gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;driver, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Eli Weinstock-Herman | Tarwn&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ContactPage contactPage = homepage.&lt;span style=&quot;color: #0000FF;&quot;&gt;NavigateContactLink&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; contactPage.&lt;span style=&quot;color: #0000FF;&quot;&gt;SendEmail&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Selenium Test&amp;quot;&lt;/span&gt;, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;This is my Selenium Test Message&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Assert.&lt;span style=&quot;color: #0000FF;&quot;&gt;IsTrue&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;contactPage.&lt;span style=&quot;color: #0000FF;&quot;&gt;IsDisplayingValidationError&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb46872&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There is still some additional refactoring we could do, but this is far more readable, has reduced the level of duplication, and added title checks.&lt;/p&gt;

&lt;h3&gt;Converting the Remaining Tests&lt;/h3&gt;
&lt;p&gt;Converting the remining tests is a straightforward exercise.&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;csharp&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;csharp&quot; id=&quot;cb3120&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;TestMethod&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; ContactPageReturnsErrorWhenEmailFieldEmpty&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;using&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;FirefoxDriver driver = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; FirefoxDriver&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;Navigate&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;GoToUrl&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;http://tiernok.com/&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; PageBase homepage = PageBase.&lt;span style=&quot;color: #0000FF;&quot;&gt;GetInstance&lt;/span&gt;&amp;lt;PageBase&amp;gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;driver, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Eli Weinstock-Herman | Tarwn&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ContactPage contactPage = homepage.&lt;span style=&quot;color: #0000FF;&quot;&gt;NavigateContactLink&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; contactPage.&lt;span style=&quot;color: #0000FF;&quot;&gt;SendEmail&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Selenium Test&amp;quot;&lt;/span&gt;, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;This is my Selenium Test Message&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Assert.&lt;span style=&quot;color: #0000FF;&quot;&gt;IsTrue&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;contactPage.&lt;span style=&quot;color: #0000FF;&quot;&gt;IsDisplayingValidationError&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;TestMethod&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; ContactPageReturnsErrorWhenNameFieldEmpty&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;using&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;FirefoxDriver driver = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; FirefoxDriver&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;Navigate&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;GoToUrl&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;http://tiernok.com/&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; PageBase homepage = PageBase.&lt;span style=&quot;color: #0000FF;&quot;&gt;GetInstance&lt;/span&gt;&amp;lt;PageBase&amp;gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;driver, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Eli Weinstock-Herman | Tarwn&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ContactPage contactPage = homepage.&lt;span style=&quot;color: #0000FF;&quot;&gt;NavigateContactLink&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; contactPage.&lt;span style=&quot;color: #0000FF;&quot;&gt;SendEmail&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;, EmailAddress, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;This is my Selenium Test Message&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Assert.&lt;span style=&quot;color: #0000FF;&quot;&gt;IsTrue&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;contactPage.&lt;span style=&quot;color: #0000FF;&quot;&gt;IsDisplayingValidationError&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;TestMethod&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; ContactPageReturnsErrorWhenMessageFieldEmpty&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;using&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;FirefoxDriver driver = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; FirefoxDriver&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;Navigate&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;GoToUrl&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;http://tiernok.com/&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; PageBase homepage = PageBase.&lt;span style=&quot;color: #0000FF;&quot;&gt;GetInstance&lt;/span&gt;&amp;lt;PageBase&amp;gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;driver, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Eli Weinstock-Herman | Tarwn&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ContactPage contactPage = homepage.&lt;span style=&quot;color: #0000FF;&quot;&gt;NavigateContactLink&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; contactPage.&lt;span style=&quot;color: #0000FF;&quot;&gt;SendEmail&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Selenium Test&amp;quot;&lt;/span&gt;, EmailAddress, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Assert.&lt;span style=&quot;color: #0000FF;&quot;&gt;IsTrue&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;contactPage.&lt;span style=&quot;color: #0000FF;&quot;&gt;IsDisplayingValidationError&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#91;&lt;/span&gt;TestMethod&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#93;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #0600FF;&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0600FF;&quot;&gt;void&lt;/span&gt; ContactPageReturnsSuccessWhenAllFieldsProvided&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #0600FF;&quot;&gt;using&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;FirefoxDriver driver = &lt;a href=&quot;http://www.google.com/search?q=new+msdn.microsoft.com&quot;&gt;&lt;span style=&quot;color: #008000;&quot;&gt;new&lt;/span&gt;&lt;/a&gt; FirefoxDriver&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#123;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; driver.&lt;span style=&quot;color: #0000FF;&quot;&gt;Navigate&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;.&lt;span style=&quot;color: #0000FF;&quot;&gt;GoToUrl&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;http://tiernok.com/&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; PageBase homepage = PageBase.&lt;span style=&quot;color: #0000FF;&quot;&gt;GetInstance&lt;/span&gt;&amp;lt;PageBase&amp;gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;driver, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Eli Weinstock-Herman | Tarwn&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ContactPage contactPage = homepage.&lt;span style=&quot;color: #0000FF;&quot;&gt;NavigateContactLink&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; contactPage.&lt;span style=&quot;color: #0000FF;&quot;&gt;SendEmail&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;Selenium Test&amp;quot;&lt;/span&gt;, EmailAddress, &lt;span style=&quot;color: #808080;&quot;&gt;&amp;quot;This is my Selenium Message&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Assert.&lt;span style=&quot;color: #0000FF;&quot;&gt;IsTrue&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#40;&lt;/span&gt;contactPage.&lt;span style=&quot;color: #0000FF;&quot;&gt;IsDisplayingSuccessMessage&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#41;&lt;/span&gt;;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;#125;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb72331&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There is additional refactoring we could do at this stage. The hardcoded page titles and repeated driver.Navigate() calls could be cleaned up to further reduce duplication and make the tests more readable. As we add more pages and more tests, we are going to find other common functionality to refactor and other necessary functionality to add. The framework I end up building through aggressive refactoring of these tests may not look like the one that you end up with, but we will be reaching in the same direction.&lt;/p&gt;

&lt;h2&gt;Review&lt;/h2&gt;
&lt;p&gt;We have created the beginning of a more extensive test framework that enables us to create more complex tests with less duplication and effort. Refactoring common elements improved maintainability and our ability to react to changes, while using the PageObject pattern has started to separate the subject of our tests from the mechanics of how we interact with the browser.&lt;/p&gt;

&lt;p&gt;As before, though, there are some points to return to.&lt;/p&gt;

&lt;h3&gt;Complexity, Investment, and Code Errors&lt;/h3&gt;
&lt;p&gt;Getting started with some Selenium IDE tests was pretty easy. A customer solution, by comparison, scales farther but requires more up front investment and introduces the potential for errors inside the framework that could adversely affect the test results. Despite using an existing Test Runner and the PageFactory object for wiring, I still managed to make errors along the way. In one case, I actually lost time chasing down something that I thought was a timing issue (which you will run into at some point) that was actually a simple error in my code.&lt;/p&gt;

&lt;p&gt;Experience helps a lot in this area, as does following good code craftsmanship standards, refactoring aggressively, using consistent naming, and trying to keep the bigger picture of the framework in mind. Along the way I made variable type selections to specifically set myself up for later additions I want (choosing RemoteWebDriver over IWebDriver in several cases, for instance) and despite needing only one NavigateClick function for this sample, I created a pattern I would intend to follow for the rest of the navigation menu. Experience will help tell us these things are available, but first we have to make the mistakes and experiments along the way to gain that experience.&lt;/p&gt;

&lt;h3&gt;Moving Forward&lt;/h3&gt;
&lt;p&gt;From automating basic smoke tests and HTML element interactions we have moved into the realm of interacting with web pages and actions on those web pages. We can abstract a step further, though, and start focusing on validating what the end user has asked for. The next level is a framework that abstracts the web page away, leaving us to interact with higher level functions and processes, validating whether the application fulfills the users acceptance criteria.&lt;/p&gt;

&lt;h3&gt;Finishing Up&lt;/h3&gt;
&lt;p&gt;The Selenium WebDriver library offers some great capabilities. Using a handy Unit Testing framework and Test Runner can help give us a solution that is halfway between automated acceptance testing and Selenium IDE in a fairly short period of time. There are still a lot of areas we haven&#039;t discussed, such as integrating with a build server or tying into multiple browser drivers, but this post has already run a bit longer than the prior one. If you are interested, the code for this post is in a &lt;a href=&quot;https://bitbucket.org/tarwn/seleniumwebdriversample/&quot; title=&quot;Source code for the post&quot;&gt;mercurial repository on bitbucket&lt;/a&gt; and as always we can continue discussing any of the points in the post in the comments here or in the forum area.&lt;/p&gt;&lt;div class=&quot;item_footer&quot;&gt;&lt;p&gt;&lt;small&gt;&lt;a href=&quot;http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/automated-web-testing-with-selenium-2&quot;&gt;Original post&lt;/a&gt; blogged on &lt;a href=&quot;http://lessthandot.com/&quot;&gt;LessThanDot&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;</description>
			<content:encoded><![CDATA[<p>Last week we created a pair of smoke tests with the <a href="http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/automated-web-testing-with-selenium" title="Read last week's post">Selenium IDE</a> tool. Several people commented, with varying levels of politeness, about the downsides of Selenium IDE. There are even those that will tell you not to use it at all, to immediately bypass Selenium IDE and go straight to code. My opinion is that this is a matter of context. </p>

<p>Today we are going to get into the nuts and bolts of coding automated tests against the WebDriver library. Before we get into that, however, let's discuss when it is appropriate to make this transition.</p>

<h2>Context and When to Transition</h2>
<p>Most projects that are using browser automation are large and require a fairly complex set of tests. The solutions they are using are at least at the level we will be working at today, if not further down the road to automated acceptance testing. A higher level of complexity across a much larger project is just not manageable with Selenium IDE, so many people are used to bypassing it immediately and going straight to a code solution.</p>

<p>Context is king. For small teams or sites, there is value in being able to put together some quick tests that can be run regularly without making an investment in creating a custom framework. It can also be a useful tool when you have a legacy site and just want to make sure it hasn't fallen over.</p>

<p>Not everyone has worked in the context of these larger projects or has worked extensively with Selenium. If you are getting started for the first time or currently using Selenium IDE, here are some signs to consider moving to a custom solution:</p>
<ul>
<li>You often update a number of tests simultaneously to change element names</li>
<li>Sacrificing your computer while the tests run is becoming a nuisance</li>
<li>You want to share the tests with team members, others in your company, or your client</li>
<li>Maintaining the test suite list in the IDE tasks longer than creating tests</li>
<li>You are starting to build tests for every feature you add to the software</li>
<li>You want to tie the tests into the automated build system for your software</li>
</ul>
<p>In these cases, you have likely scaled past the point where Selenium IDE is enabling you, into a range where it's costs outweigh it's gains.</p>

<h2>The Next Step for Web Automation</h2>
<p>The next step in automating web interface tests is to use the Selenium WebDriver library to code the tests. Writing our own framework has some inherent advantages and disadvantages.</p>

<p>Pros: Free, Reduced Duplication, Easy to share, Relatively easy to automate<br />
Cons: Increased cost, Increased risk due to code errors, Increased complexity<br />
Neutral: Level of fragility depends on level of developer</p>

<p>There are Selenium WebDriver libraries for Java, Python, Ruby, and .Net. By using an existing Unit testing framework we don't have to write our own Test Runner and it is likely to have documentation and/or plugins for integrating into most of the popular build engines.</p>

<p>For the purpose of this post, we are again going to create tests against my contact page, but we'll be increasing the number of tests. We will be using the .Net driver, C#, and the MS Test framework.</p>

<p><em>Note: I would advise this unit test project be separate from the overall solution for your application. Keeping it separate means you can version it separately, build and run it separately, and easily run the real unit tests in your product solution without accidentally running the interface ones as well.<br />
<br />
Note 2: I had intended to do this in VB.Net originally, but the downside of switching easily between languages in the same IDE is that sometimes you find yourself writing in the wrong one without even realizing it.</em></p>

<h2>Getting Started</h2>
<p>After creating our .Net solution to house the test project, we need to add a reference to the Selenium WebDriver library. This can be downloaded from the <a href="http://seleniumhq.org/download/" title="Selenium WebDriver download page">Web Driver Download page</a> or added via NuGet:</p>
<div class="commandWrapper">
<div class="commandPrompt">
<p class="command">PM&gt; Install-Package Selenium.WebDriver</p>
</div>
</div><p></p>

<p><em>Note: If you don't have NuGet installed, I highly recommend you go check it out at <a href="http://nuget.org/" title="NuGet.org">http://nuget.org/</a> or read more about it in <a href="http://msdn.microsoft.com/en-us/magazine/hh547106.aspx" title="Manage Project Libraries with NuGet by Phil Haack">this recent MSDN article</a>. NuGet is a package manager that allows us to quickly download, install, and update 3rd party packages in Visual Studio. It not only makes it easier to keep 3rd party libraries up to date, but also makes a number of packages more accessible, since they generally install the appropriate settings and references to easily get us started.</em></p>

<p>Once we have all of that set up, we can create a single class file with a quick test function to verify we're ready to go:</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb79606'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb79606','cb49774'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb79606" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #0600FF;">using</span> <span style="color: #000000;">System</span>;</li><li style="" class="li2"><span style="color: #0600FF;">using</span> Microsoft.<span style="color: #0000FF;">VisualStudio</span>.<span style="color: #0000FF;">TestTools</span>.<span style="color: #0000FF;">UnitTesting</span>;</li><li style="" class="li1"><span style="color: #0600FF;">using</span> OpenQA.<span style="color: #0000FF;">Selenium</span>.<span style="color: #0000FF;">Firefox</span>;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1"><span style="color: #0600FF;">namespace</span> SampleWebDriverUnitTest <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#91;</span>TestClass<span style="color: #000000;">&#93;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #FF0000;">class</span> GettingStartedTest <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">public</span> GettingStartedTest<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span> <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#91;</span>TestMethod<span style="color: #000000;">&#93;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #0600FF;">void</span> GettingStarted_BasicFireFoxGET_LoadsPage<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">using</span> <span style="color: #000000;">&#40;</span>FirefoxDriver driver = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> FirefoxDriver<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">Url</span> = <span style="color: #808080;">&quot;http://tiernok.com&quot;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">Navigate</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Assert.<span style="color: #0000FF;">AreEqual</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;Eli Weinstock-Herman | Tarwn&quot;</span>, driver.<span style="color: #0000FF;">Title</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb49774" style="display: none; color: red;"></div></div></div>

<p>This is all it takes to get our first test. Create the solution, add the reference, create the test class and method. Since it's MS Test, Ctrl+R, A will run all of our tests in the integrated test runner.</p>

<p>Selenium WebDriver is written in a somewhat fluent syntax, so for the rest of the post we will combine the URL and navigation into a single statement: driver.Navigate().GoToUrl("http://tiernok.com");</p>

<h2>The First Test Cases</h2>
<p>Last time we started off by testing navigation to the home page and the presence of two books, then we tested submitting a form would correctly fail validation when it was missing a required field. Today we're going to extend that form validation check, a more complex example in Selenium IDE that will be handled fairly easily in our new framework.</p>

<h3>The Email Form</h3>
<p>These are the situations we want to test for the email form:</p>
<blockquote>
<p>	When I submit the form without an email address, it displays an error.<br />
	When I submit the form without a name, it displays an error.<br />
	When I submit the form without a message, it displays an error.<br />
	When I submit the form with all of the fields filled in, it displays a success message.</p>
</blockquote>

<p>The steps for these tests are going to be similar to the second test we created in Selenium IDE last week. </p>

<h3>Exporting a Selenium IDE Test Case</h3>
<p>Selenium IDE has a feature that allows us to export tests, so when we are getting started we can look at that export to get a feel for how the code version will function. To export a test, open Selenium IDE, go to File, Export Text Case As, and choose C# (WebDriver). A C# file will be generated with the test case converted to an NUnit Test. The file includes some test setup and teardown methods, as well as the following test method:</p>
<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb71451'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb71451','cb2276'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb71451" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #000000;">&#91;</span>Test<span style="color: #000000;">&#93;</span></li><li style="" class="li2"><span style="color: #0600FF;">public</span> <span style="color: #0600FF;">void</span> TheContactPageReturnsErrorWhenEmailFieldEmptyTest<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span></li><li style="" class="li1"><span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; driver.<span style="color: #0000FF;">Navigate</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">GoToUrl</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;/&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">LinkText</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;Contact&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Click</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">Id</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;itxtFromName&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Clear</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">Id</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;itxtFromName&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">SendKeys</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;Selenium Test&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">Id</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;itxtBody&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Clear</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">Id</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;itxtBody&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">SendKeys</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;This is my Selenium Message&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">CssSelector</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;input[type=<span style="color: #008080; font-weight: bold;">\&quot;</span>submit<span style="color: #008080; font-weight: bold;">\&quot;</span>]&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Click</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; Assert.<span style="color: #0000FF;">AreEqual</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;Please fill in all three entries before sending the message.&quot;</span>, driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">CssSelector</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;.err&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Text</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb2276" style="display: none; color: red;"></div></div></div>

<p>Initially this may look more complicated then the Selenium IDE command steps, but really the only difference is that we are explicitly finding the elements we want to act on before acting, which was was more implicit in Selenium IDE. </p>

<p>Converting this to an MS Test is not difficult, but we will want to wrap the tests in a using statement so that driver gets properly disposed (MS Test doesn't run cleanup properly when an exception occurs).</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb371'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb371','cb63727'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb371" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #000000;">&#91;</span>TestMethod<span style="color: #000000;">&#93;</span></li><li style="" class="li2"><span style="color: #0600FF;">public</span> <span style="color: #0600FF;">void</span> ContactPageReturnsErrorWhenEmailFieldEmpty<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">using</span> <span style="color: #000000;">&#40;</span>FirefoxDriver driver = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> FirefoxDriver<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">Navigate</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">GoToUrl</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;http://tiernok.com/&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">LinkText</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;Contact&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Click</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">Id</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;itxtFromName&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Clear</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">Id</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;itxtFromName&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">SendKeys</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;Selenium Test&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">Id</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;itxtBody&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Clear</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">Id</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;itxtBody&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">SendKeys</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;This is my Selenium Message&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">CssSelector</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;input[type=<span style="color: #008080; font-weight: bold;">\&quot;</span>submit<span style="color: #008080; font-weight: bold;">\&quot;</span>]&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Click</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; Assert.<span style="color: #0000FF;">AreEqual</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;Please fill in all three entries before sending the message.&quot;</span>, driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">CssSelector</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;.err&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Text</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb63727" style="display: none; color: red;"></div></div></div>
<p>The only real changes have been the addition of the using statement and changing the decorated attribute from NUnit's Test to MS Test's TestMethod. </p>

<h3>The Rest of the Test Cases</h3>

<p>Based on this one test, we can easily create tests to satisfy the other conditions:</p>
<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb24588'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb24588','cb3417'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb24588" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #000000;">&#91;</span>TestMethod<span style="color: #000000;">&#93;</span></li><li style="" class="li2"><span style="color: #0600FF;">public</span> <span style="color: #0600FF;">void</span> ContactPageReturnsErrorWhenNameFieldEmpty<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">using</span> <span style="color: #000000;">&#40;</span>FirefoxDriver driver = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> FirefoxDriver<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">Navigate</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">GoToUrl</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;http://tiernok.com/&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">LinkText</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;Contact&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Click</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">Id</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;itxtFromEmail&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Clear</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">Id</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;itxtFromEmail&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">SendKeys</span><span style="color: #000000;">&#40;</span>EmailAddress<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">Id</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;itxtBody&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Clear</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">Id</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;itxtBody&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">SendKeys</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;This is my Selenium Message&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">CssSelector</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;input[type=<span style="color: #008080; font-weight: bold;">\&quot;</span>submit<span style="color: #008080; font-weight: bold;">\&quot;</span>]&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Click</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; Assert.<span style="color: #0000FF;">AreEqual</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;Please fill in all three entries before sending the message.&quot;</span>, driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">CssSelector</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;.err&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Text</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1"><span style="color: #000000;">&#91;</span>TestMethod<span style="color: #000000;">&#93;</span></li><li style="" class="li2"><span style="color: #0600FF;">public</span> <span style="color: #0600FF;">void</span> ContactPageReturnsErrorWhenMessageFieldEmpty<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">using</span> <span style="color: #000000;">&#40;</span>FirefoxDriver driver = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> FirefoxDriver<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">Navigate</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">GoToUrl</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;http://tiernok.com/&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">LinkText</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;Contact&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Click</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">Id</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;itxtFromName&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Clear</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">Id</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;itxtFromName&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">SendKeys</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;Selenium Test&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">Id</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;itxtFromEmail&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Clear</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">Id</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;itxtFromEmail&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">SendKeys</span><span style="color: #000000;">&#40;</span>EmailAddress<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">CssSelector</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;input[type=<span style="color: #008080; font-weight: bold;">\&quot;</span>submit<span style="color: #008080; font-weight: bold;">\&quot;</span>]&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Click</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; Assert.<span style="color: #0000FF;">AreEqual</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;Please fill in all three entries before sending the message.&quot;</span>, driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">CssSelector</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;.err&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Text</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1"><span style="color: #000000;">&#91;</span>TestMethod<span style="color: #000000;">&#93;</span></li><li style="" class="li2"><span style="color: #0600FF;">public</span> <span style="color: #0600FF;">void</span> ContactPageReturnsSuccessWhenAllFieldsProvided<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">using</span> <span style="color: #000000;">&#40;</span>FirefoxDriver driver = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> FirefoxDriver<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">Navigate</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">GoToUrl</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;http://tiernok.com/&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">LinkText</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;Contact&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Click</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">Id</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;itxtFromName&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Clear</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">Id</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;itxtFromName&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">SendKeys</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;Selenium Test&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">Id</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;itxtFromEmail&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Clear</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">Id</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;itxtFromEmail&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">SendKeys</span><span style="color: #000000;">&#40;</span>EmailAddress<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">Id</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;itxtBody&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Clear</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">Id</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;itxtBody&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">SendKeys</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;This is my Selenium Message&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">CssSelector</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;input[type=<span style="color: #008080; font-weight: bold;">\&quot;</span>submit<span style="color: #008080; font-weight: bold;">\&quot;</span>]&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Click</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; Assert.<span style="color: #0000FF;">AreEqual</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;Thanks, your message has been sent successfully.&quot;</span>, driver.<span style="color: #0000FF;">FindElement</span><span style="color: #000000;">&#40;</span>By.<span style="color: #0000FF;">CssSelector</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;.suc&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">Text</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb3417" style="display: none; color: red;"></div></div></div>

<p>Theoretically we're done, if we had a few more cases we could keep copying and pasting our way to the finish line. If you recall, however, one of the goals of  moving from Selenium IDE method to a custom solution was to reduce the amount of duplication and the fragility that duplication causes. So far all we have done is transfer that fragility from a series of test cases in the IDE to a series of unit tests in C#.</p>

<h2>Reducing Test Fragility</h2>
<p>When we start writing more than basic smoke tests, we are going to be accessing items repetitively and duplicating ourselves more often. Switching to code has allowed us to create additional tests very quickly, but we haven't really solved the duplication issue yet.</p>

<p>There's two approaches we can take at this point, abstracting the duplication on our own or using the Page Factory from the Support library to implement the <a href="http://code.google.com/p/selenium/wiki/PageObjects" title="More information on Page Object">Page Object</a> pattern. </p>

<p>The idea of the Page Object pattern is to represent each page in our target application as a class in our test framework. This allows us to reflect functionality and changes in the real page with functionality or changes in our class. Rather than locating and interacting with individual controls in a browser, our tests than interact with these Page Object classes as representations of what the browser should be seeing. </p>

<p>Given we are just getting started with the code approach, lets use the Page Object approach for the Contact page and use the PageFactory that is provided in the Support library to help with the wiring.</p>

<h3>Creating the Contact PageObject</h3>
<p>Before we start making code changes, we need to get the Support library. As before, we can either download it from the <a href="http://seleniumhq.org/download/" title="Selenium WebDriver download page">Selenium website</a> or install it via NuGet:</p>
<div class="commandWrapper">
<div class="commandPrompt">
<p class="command">PM&gt; Install-Package Selenium.Support</p>
</div>
</div><p></p>

<p>Next we are going to start modeling our page. Since there is a common layout throughout the site, I'll first create a base page object to represent the common elements (like navigation menu items) we will be interacting with:</p>
<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb2005'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb2005','cb13076'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb2005" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #0600FF;">namespace</span> SampleWebDriverUnitTest.<span style="color: #0000FF;">Pages</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #FF0000;">class</span> PageBase <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#91;</span>FindsBy<span style="color: #000000;">&#40;</span>How = How.<span style="color: #0000FF;">LinkText</span>, <span style="color: #0600FF;">Using</span> = <span style="color: #808080;">&quot;Contact&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#93;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; IWebElement ContactLink;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #0600FF;">void</span> NavigateContactLink<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ContactLink.<span style="color: #0000FF;">Click</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb13076" style="display: none; color: red;"></div></div></div>

<p>Then we can extend this class to define the elements that are specific to the Contact form:</p>
<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb73431'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb73431','cb55207'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb73431" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #0600FF;">namespace</span> SampleWebDriverUnitTest.<span style="color: #0000FF;">Pages</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #FF0000;">class</span> ContactPage : PageBase <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#91;</span>FindsBy<span style="color: #000000;">&#40;</span>How=How.<span style="color: #0000FF;">Id</span>, <span style="color: #0600FF;">Using</span>=<span style="color: #808080;">&quot;itxtFromEmail&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#93;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; IWebElement EmailInput;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#91;</span>FindsBy<span style="color: #000000;">&#40;</span>How = How.<span style="color: #0000FF;">Id</span>, <span style="color: #0600FF;">Using</span> = <span style="color: #808080;">&quot;itxtFromName&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#93;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; IWebElement NameInput;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#91;</span>FindsBy<span style="color: #000000;">&#40;</span>How = How.<span style="color: #0000FF;">Id</span>, <span style="color: #0600FF;">Using</span> = <span style="color: #808080;">&quot;itxtBody&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#93;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; IWebElement BodyInput;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#91;</span>FindsBy<span style="color: #000000;">&#40;</span>How = How.<span style="color: #0000FF;">CssSelector</span>, <span style="color: #0600FF;">Using</span> = <span style="color: #808080;">&quot;input[type=submit]&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#93;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; IWebElement SubmitInput;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#91;</span>FindsBy<span style="color: #000000;">&#40;</span>How = How.<span style="color: #0000FF;">CssSelector</span>, <span style="color: #0600FF;">Using</span> = <span style="color: #808080;">&quot;.err&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#93;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; IWebElement ErrorMessage;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#91;</span>FindsBy<span style="color: #000000;">&#40;</span>How = How.<span style="color: #0000FF;">CssSelector</span>, <span style="color: #0600FF;">Using</span> = <span style="color: #808080;">&quot;.suc&quot;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#93;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; IWebElement SuccessMessage;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #0600FF;">void</span> SendEmail<span style="color: #000000;">&#40;</span><span style="color: #FF0000;">string</span> name, <span style="color: #FF0000;">string</span> email, <span style="color: #FF0000;">string</span> body<span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">if</span> <span style="color: #000000;">&#40;</span>!<span style="color: #FF0000;">string</span>.<span style="color: #0000FF;">IsNullOrEmpty</span><span style="color: #000000;">&#40;</span>email<span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; EmailInput.<span style="color: #0000FF;">Clear</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; EmailInput.<span style="color: #0000FF;">SendKeys</span><span style="color: #000000;">&#40;</span>email<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">if</span> <span style="color: #000000;">&#40;</span>!<span style="color: #FF0000;">string</span>.<span style="color: #0000FF;">IsNullOrEmpty</span><span style="color: #000000;">&#40;</span>name<span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; NameInput.<span style="color: #0000FF;">Clear</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; NameInput.<span style="color: #0000FF;">SendKeys</span><span style="color: #000000;">&#40;</span>name<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">if</span> <span style="color: #000000;">&#40;</span>!<span style="color: #FF0000;">string</span>.<span style="color: #0000FF;">IsNullOrEmpty</span><span style="color: #000000;">&#40;</span>body<span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; BodyInput.<span style="color: #0000FF;">Clear</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; BodyInput.<span style="color: #0000FF;">SendKeys</span><span style="color: #000000;">&#40;</span>body<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; SubmitInput.<span style="color: #0000FF;">Click</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008080; font-style: italic;">// try/catches below are due to selenium using exceptions to indicate search failures rather than an empty result or null</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #FF0000;">bool</span> IsDisplayingValidationError<span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; get <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">try</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">return</span> ErrorMessage.<span style="color: #0000FF;">Displayed</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">catch</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">return</span> <span style="color: #0600FF;">false</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #FF0000;">bool</span> IsDisplayingSuccessMessage <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; get <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">try</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">return</span> SuccessMessage.<span style="color: #0000FF;">Displayed</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">catch</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">return</span> <span style="color: #0600FF;">false</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span>&nbsp; &nbsp;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb55207" style="display: none; color: red;"></div></div></div>

<p>The PageFactory object is responsible for wiring our page objects to the actual page displayed in the browser. It attempts to match each IWebElement in the class to an element in the web page. By default, if you do not decorate the field with the FindsBy decorator then it will search for an element who's ID matches the variable name. I prefer to explicitly specify the find criteria, though, as this lets me name my variables consistently and limits the impact of an HTML change to a single decorator instead of causing a property name change.</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb3331'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb3331','cb81283'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb3331" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #000000;">&#91;</span>TestMethod<span style="color: #000000;">&#93;</span></li><li style="" class="li2"><span style="color: #0600FF;">public</span> <span style="color: #0600FF;">void</span> ContactPageReturnsErrorWhenEmailFieldEmpty_PageObjectVersion<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">using</span> <span style="color: #000000;">&#40;</span>FirefoxDriver driver = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> FirefoxDriver<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">Navigate</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">GoToUrl</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;http://tiernok.com/&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; Pages.<span style="color: #0000FF;">PageBase</span> homepage = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> Pages.<span style="color: #0000FF;">PageBase</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; PageFactory.<span style="color: #0000FF;">InitElements</span><span style="color: #000000;">&#40;</span>driver, homepage<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; homepage.<span style="color: #0000FF;">NavigateContactLink</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; Pages.<span style="color: #0000FF;">ContactPage</span> contactPage = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> Pages.<span style="color: #0000FF;">ContactPage</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; PageFactory.<span style="color: #0000FF;">InitElements</span><span style="color: #000000;">&#40;</span>driver, contactPage<span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; contactPage.<span style="color: #0000FF;">SendEmail</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;Selenium Test&quot;</span>, <span style="color: #808080;">&quot;&quot;</span>, <span style="color: #808080;">&quot;This is my Selenium Test Message&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; Assert.<span style="color: #0000FF;">IsTrue</span><span style="color: #000000;">&#40;</span>contactPage.<span style="color: #0000FF;">IsDisplayingValidationError</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb81283" style="display: none; color: red;"></div></div></div>

<p>The new code:</p>
<ul>
<ol>navigates to the site</ol>
<ol>creates an instance of the base page</ol>
<ol>clicks the navigation link for the Contact Page</ol>
<ol>assumes it is on the contact page and creates an instance of that object</ol>
<ol>Sends an email with two of the three values filled in</ol>
<ol>Asserts that the error message is displayed</ol>
</ul>

<p>However, this code still has problems. </p>

<h3>Refining and Correcting the First Test</h3>
<p>The current code, in my opinion, doesn't fail soon enough if it is on the wrong page. In addition, this first test we've converted is not very readable and I can already tell there is going to be a lot of duplication in later tests. Let's do some refactoring.</p>

<p>Rather than repetitively initializing page objects in each test and and adding title verification to each test, lets move that behavior to the PageBase so we can easily do it for every page we load.</p>
<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb58897'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb58897','cb21984'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb58897" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #FF0000;">class</span> PageBase <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> RemoteWebDriver Driver <span style="color: #000000;">&#123;</span> get; set; <span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #FF0000;">string</span> ExpectedTitle <span style="color: #000000;">&#123;</span> get;set; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #008080; font-style: italic;">// ... unchanged code ...</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> <span style="color: #0600FF;">static</span> TPage GetInstance&lt;TPage&gt;<span style="color: #000000;">&#40;</span>RemoteWebDriver driver, <span style="color: #FF0000;">string</span> expectedTitle<span style="color: #000000;">&#41;</span> where TPage : PageBase, <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; TPage pageInstance = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> TPage<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span> </li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ExpectedTitle = expectedTitle,</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Driver = driver</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">&#125;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; PageFactory.<span style="color: #0000FF;">InitElements</span><span style="color: #000000;">&#40;</span>driver, pageInstance<span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; Assert.<span style="color: #0000FF;">AreEqual</span><span style="color: #000000;">&#40;</span>pageInstance.<span style="color: #0000FF;">ExpectedTitle</span>, driver.<span style="color: #0000FF;">Title</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">return</span> pageInstance;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb21984" style="display: none; color: red;"></div></div></div>

<p>Now we have a single generic call that can create a Page (provided it inherits from PageBase and has a constructor) and execute an assertion on it's title. Lets update the NavigateContactLink method to return an initialized ContactPage instance, since this will be a consistent next step each time we navigate to a new page. This reduces the amount of code in the test and adds an automatic check to ensure we have reached the page we were expecting.</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb2871'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb2871','cb36542'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb2871" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #FF0000;">class</span> PageBase <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #008080; font-style: italic;">// ... unchanged ...</span></li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">public</span> ContactPage NavigateContactLink<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; ContactLink.<span style="color: #0000FF;">Click</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0600FF;">return</span> GetInstance&lt;ContactPage&gt;<span style="color: #000000;">&#40;</span>Driver, <span style="color: #808080;">&quot;Eli Weinstock-Herman | Tarwn - Contact&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #008080; font-style: italic;">// ... unchanged ...</span></li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb36542" style="display: none; color: red;"></div></div></div>

<p>Our refactored test now looks like this:</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb22793'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb22793','cb85804'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb22793" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #000000;">&#91;</span>TestMethod<span style="color: #000000;">&#93;</span></li><li style="" class="li2"><span style="color: #0600FF;">public</span> <span style="color: #0600FF;">void</span> ContactPageReturnsErrorWhenEmailFieldEmpty_PageObjectVersion<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">using</span> <span style="color: #000000;">&#40;</span>FirefoxDriver driver = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> FirefoxDriver<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">Navigate</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">GoToUrl</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;http://tiernok.com/&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; PageBase homepage = PageBase.<span style="color: #0000FF;">GetInstance</span>&lt;PageBase&gt;<span style="color: #000000;">&#40;</span>driver, <span style="color: #808080;">&quot;Eli Weinstock-Herman | Tarwn&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; ContactPage contactPage = homepage.<span style="color: #0000FF;">NavigateContactLink</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; </li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; contactPage.<span style="color: #0000FF;">SendEmail</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;Selenium Test&quot;</span>, <span style="color: #808080;">&quot;&quot;</span>, <span style="color: #808080;">&quot;This is my Selenium Test Message&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; Assert.<span style="color: #0000FF;">IsTrue</span><span style="color: #000000;">&#40;</span>contactPage.<span style="color: #0000FF;">IsDisplayingValidationError</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb85804" style="display: none; color: red;"></div></div></div>

<p>There is still some additional refactoring we could do, but this is far more readable, has reduced the level of duplication, and added title checks.</p>

<h3>Converting the Remaining Tests</h3>
<p>Converting the remining tests is a straightforward exercise.</p>

<div class="codebox"><div class="codeheader"><span>csharp</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb67762'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb67762','cb80875'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="csharp" id="cb67762" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #000000;">&#91;</span>TestMethod<span style="color: #000000;">&#93;</span></li><li style="" class="li2"><span style="color: #0600FF;">public</span> <span style="color: #0600FF;">void</span> ContactPageReturnsErrorWhenEmailFieldEmpty<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">using</span> <span style="color: #000000;">&#40;</span>FirefoxDriver driver = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> FirefoxDriver<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">Navigate</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">GoToUrl</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;http://tiernok.com/&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; PageBase homepage = PageBase.<span style="color: #0000FF;">GetInstance</span>&lt;PageBase&gt;<span style="color: #000000;">&#40;</span>driver, <span style="color: #808080;">&quot;Eli Weinstock-Herman | Tarwn&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; ContactPage contactPage = homepage.<span style="color: #0000FF;">NavigateContactLink</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; contactPage.<span style="color: #0000FF;">SendEmail</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;Selenium Test&quot;</span>, <span style="color: #808080;">&quot;&quot;</span>, <span style="color: #808080;">&quot;This is my Selenium Test Message&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; Assert.<span style="color: #0000FF;">IsTrue</span><span style="color: #000000;">&#40;</span>contactPage.<span style="color: #0000FF;">IsDisplayingValidationError</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp;</li><li style="" class="li2"><span style="color: #000000;">&#91;</span>TestMethod<span style="color: #000000;">&#93;</span></li><li style="" class="li1"><span style="color: #0600FF;">public</span> <span style="color: #0600FF;">void</span> ContactPageReturnsErrorWhenNameFieldEmpty<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">using</span> <span style="color: #000000;">&#40;</span>FirefoxDriver driver = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> FirefoxDriver<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">Navigate</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">GoToUrl</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;http://tiernok.com/&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; PageBase homepage = PageBase.<span style="color: #0000FF;">GetInstance</span>&lt;PageBase&gt;<span style="color: #000000;">&#40;</span>driver, <span style="color: #808080;">&quot;Eli Weinstock-Herman | Tarwn&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; ContactPage contactPage = homepage.<span style="color: #0000FF;">NavigateContactLink</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; contactPage.<span style="color: #0000FF;">SendEmail</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;&quot;</span>, EmailAddress, <span style="color: #808080;">&quot;This is my Selenium Test Message&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; Assert.<span style="color: #0000FF;">IsTrue</span><span style="color: #000000;">&#40;</span>contactPage.<span style="color: #0000FF;">IsDisplayingValidationError</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1"><span style="color: #000000;">&#91;</span>TestMethod<span style="color: #000000;">&#93;</span></li><li style="" class="li2"><span style="color: #0600FF;">public</span> <span style="color: #0600FF;">void</span> ContactPageReturnsErrorWhenMessageFieldEmpty<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #0600FF;">using</span> <span style="color: #000000;">&#40;</span>FirefoxDriver driver = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> FirefoxDriver<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">Navigate</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">GoToUrl</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;http://tiernok.com/&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; PageBase homepage = PageBase.<span style="color: #0000FF;">GetInstance</span>&lt;PageBase&gt;<span style="color: #000000;">&#40;</span>driver, <span style="color: #808080;">&quot;Eli Weinstock-Herman | Tarwn&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; ContactPage contactPage = homepage.<span style="color: #0000FF;">NavigateContactLink</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; contactPage.<span style="color: #0000FF;">SendEmail</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;Selenium Test&quot;</span>, EmailAddress, <span style="color: #808080;">&quot;&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; Assert.<span style="color: #0000FF;">IsTrue</span><span style="color: #000000;">&#40;</span>contactPage.<span style="color: #0000FF;">IsDisplayingValidationError</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li2"><span style="color: #000000;">&#125;</span></li><li style="" class="li1">&nbsp;</li><li style="" class="li2"><span style="color: #000000;">&#91;</span>TestMethod<span style="color: #000000;">&#93;</span></li><li style="" class="li1"><span style="color: #0600FF;">public</span> <span style="color: #0600FF;">void</span> ContactPageReturnsSuccessWhenAllFieldsProvided<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #0600FF;">using</span> <span style="color: #000000;">&#40;</span>FirefoxDriver driver = <a href="http://www.google.com/search?q=new+msdn.microsoft.com"><span style="color: #008000;">new</span></a> FirefoxDriver<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><span style="color: #000000;">&#41;</span> <span style="color: #000000;">&#123;</span></li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; driver.<span style="color: #0000FF;">Navigate</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>.<span style="color: #0000FF;">GoToUrl</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;http://tiernok.com/&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; PageBase homepage = PageBase.<span style="color: #0000FF;">GetInstance</span>&lt;PageBase&gt;<span style="color: #000000;">&#40;</span>driver, <span style="color: #808080;">&quot;Eli Weinstock-Herman | Tarwn&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; ContactPage contactPage = homepage.<span style="color: #0000FF;">NavigateContactLink</span><span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; contactPage.<span style="color: #0000FF;">SendEmail</span><span style="color: #000000;">&#40;</span><span style="color: #808080;">&quot;Selenium Test&quot;</span>, EmailAddress, <span style="color: #808080;">&quot;This is my Selenium Message&quot;</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; Assert.<span style="color: #0000FF;">IsTrue</span><span style="color: #000000;">&#40;</span>contactPage.<span style="color: #0000FF;">IsDisplayingSuccessMessage</span><span style="color: #000000;">&#41;</span>;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #000000;">&#125;</span></li><li style="" class="li1"><span style="color: #000000;">&#125;</span></li></ol></div><div id="cb80875" style="display: none; color: red;"></div></div></div>

<p>There is additional refactoring we could do at this stage. The hardcoded page titles and repeated driver.Navigate() calls could be cleaned up to further reduce duplication and make the tests more readable. As we add more pages and more tests, we are going to find other common functionality to refactor and other necessary functionality to add. The framework I end up building through aggressive refactoring of these tests may not look like the one that you end up with, but we will be reaching in the same direction.</p>

<h2>Review</h2>
<p>We have created the beginning of a more extensive test framework that enables us to create more complex tests with less duplication and effort. Refactoring common elements improved maintainability and our ability to react to changes, while using the PageObject pattern has started to separate the subject of our tests from the mechanics of how we interact with the browser.</p>

<p>As before, though, there are some points to return to.</p>

<h3>Complexity, Investment, and Code Errors</h3>
<p>Getting started with some Selenium IDE tests was pretty easy. A customer solution, by comparison, scales farther but requires more up front investment and introduces the potential for errors inside the framework that could adversely affect the test results. Despite using an existing Test Runner and the PageFactory object for wiring, I still managed to make errors along the way. In one case, I actually lost time chasing down something that I thought was a timing issue (which you will run into at some point) that was actually a simple error in my code.</p>

<p>Experience helps a lot in this area, as does following good code craftsmanship standards, refactoring aggressively, using consistent naming, and trying to keep the bigger picture of the framework in mind. Along the way I made variable type selections to specifically set myself up for later additions I want (choosing RemoteWebDriver over IWebDriver in several cases, for instance) and despite needing only one NavigateClick function for this sample, I created a pattern I would intend to follow for the rest of the navigation menu. Experience will help tell us these things are available, but first we have to make the mistakes and experiments along the way to gain that experience.</p>

<h3>Moving Forward</h3>
<p>From automating basic smoke tests and HTML element interactions we have moved into the realm of interacting with web pages and actions on those web pages. We can abstract a step further, though, and start focusing on validating what the end user has asked for. The next level is a framework that abstracts the web page away, leaving us to interact with higher level functions and processes, validating whether the application fulfills the users acceptance criteria.</p>

<h3>Finishing Up</h3>
<p>The Selenium WebDriver library offers some great capabilities. Using a handy Unit Testing framework and Test Runner can help give us a solution that is halfway between automated acceptance testing and Selenium IDE in a fairly short period of time. There are still a lot of areas we haven't discussed, such as integrating with a build server or tying into multiple browser drivers, but this post has already run a bit longer than the prior one. If you are interested, the code for this post is in a <a href="https://bitbucket.org/tarwn/seleniumwebdriversample/" title="Source code for the post">mercurial repository on bitbucket</a> and as always we can continue discussing any of the points in the post in the comments here or in the forum area.</p><div class="item_footer"><p><small><a href="http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/automated-web-testing-with-selenium-2">Original post</a> blogged on <a href="http://lessthandot.com/">LessThanDot</a>.</small></p></div>]]></content:encoded>
								<comments>http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/automated-web-testing-with-selenium-2#comments</comments>
			<wfw:commentRss>http://blogs.lessthandot.com/index.php/WebDev/?tempskin=_rss2&#38;disp=comments&#38;p=1461</wfw:commentRss>
		</item>
				<item>
			<title>Automated Web Testing with Selenium IDE</title>
			<link>http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/automated-web-testing-with-selenium</link>
			<pubDate>Wed, 26 Oct 2011 11:18:00 +0000</pubDate>			<dc:creator>Eli Weinstock-Herman (tarwn)</dc:creator>
			<category domain="main">UI Development</category>			<guid isPermaLink="false">1455@http://blogs.lessthandot.com/</guid>
						<description>&lt;p&gt;How long does it take to browse through a website after each build and make sure none of the pages have mysteriously blown up? 5 Minutes? 20? No time at all? The time we invest in manual testing adds up. As we switch focus to newer parts of a site we may even stop testing the ones we &#039;finished&#039;, confident that they&#039;re stable and won&#039;t be affected by our newer changes (yeah right). And despite the time spent manually testing, we still eventually deploy issues that a 30s check in the right part of the application would have detected.&lt;/p&gt;

&lt;p&gt;Enter browser automation tools, such as Selenium. With browser automation tools we can invest some extra time up front to define those manual tests in code, then continue to run those tests long past the time we would have stopped doing it manually. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: This post is a walk-through for creating some automated browser test cases. To get the most from it you will probably want to download the tool below and launch it from a second browser so you can follow along. It assumes you have Firefox installed and are comfortable with HTML and css selectors.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;The Smoke Test&lt;/h2&gt;
&lt;p&gt;There are levels to automated testing, starting with &quot;none&quot; and ending with some fairly complex industry packages and/or in-house frameworks. Today we&#039;re going to look at the cheapest option, recording and rerunning a set of tests using &lt;a href=&quot;http://seleniumhq.org/projects/ide/&quot; title=&quot;Go to the Selenium IDE website&quot;&gt;Selenium IDE&lt;/a&gt;, a Firefox browser plugin. At this level we can replace many of our manual smoke tests with an automated set for a low initial investment.&lt;/p&gt;

&lt;p&gt;Selenium IDE is a browser plugin that can record and playback actions we make in the browser. Using Selenium we can record a set of paths through our website, add some checks, and be reasonably confidant that the site is still working. &lt;/p&gt;

&lt;p&gt;Pros: Free, Low up front time cost&lt;br /&gt;
Cons: Tests are fragile*, Potential for lots of duplication*, tests run on local browser&lt;br /&gt;
Neutral: There&#039;s a learning curve but good documentation and quick feedback*&lt;/p&gt;

&lt;p&gt;* I&#039;ll come back to these three points&lt;/p&gt;

&lt;p&gt;For the purposes of the post, we will use a couple pages from my personal website. We will create two tests to verify the main page loads properly and that the contact form submits and validates properly. In the even that I make a change to a shared file, these tests will ensure the pages continue to pass some basic tests.&lt;/p&gt;

&lt;h2&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;First we need to download the plugin (assuming you have Firefox installed): &lt;a href=&quot;http://seleniumhq.org/download/&quot; title=&quot;Download the plugin&quot;&gt;Download Selenium IDE&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After downloading the plugin and restarting, we should have a &quot;Selenium IDE&quot; option in the Tools menu (Windows users can press the &#039;Alt&#039; key to see the top menu). &lt;/p&gt;

&lt;div style=&quot;text-align: center; margin: 1em; color: #666666; font-size: 80%&quot;&gt;
&lt;img src=&quot;http://tiernok.com/LTDBlog/selenium/FF_Menu.jpg&quot; alt=&quot;Selenium IDE in Firefox&quot; /&gt;&lt;br /&gt;&lt;br /&gt;
Selenium IDE in the Firefox Menu
&lt;/div&gt;

&lt;p&gt;Pressing that Selenium IDE button will give us both a popup interface and a set of release notes that unfortunately are probably less than useful to a first time user.&lt;/p&gt;

&lt;div style=&quot;text-align: center; margin: 1em; color: #666666; font-size: 80%&quot;&gt;
&lt;img src=&quot;http://tiernok.com/LTDBlog/selenium/SeleniumIDE.jpg&quot; alt=&quot;Selenium IDE&quot; /&gt;&lt;br /&gt;&lt;br /&gt;
Selenium IDE
&lt;/div&gt;

&lt;p&gt;The main areas of the interface are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A test case list on the left which displays the list of &quot;Test Cases&quot; in the current &quot;Test Suite&quot;&lt;/li&gt;
&lt;li&gt;A command window in the main viewing area, which will display the individual commands we execute as part of selected test case&lt;/li&gt;
&lt;li&gt;A series of tabs at the bottom, of which we will only be interacting with the Log and Reference initially&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So now that we have an interface, lets jump into creating the first test case to give us something to explore.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: The FireBug Firefox extension is an invaluable tool when working with Selenium, I highly recommend you &lt;a href=&quot;http://getfirebug.com/&quot; title=&quot;Go get Firebug Extension&quot;&gt;download FireBug&lt;/a&gt; if you don&#039;t already have it.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;The First Test Case&lt;/h2&gt;
&lt;p&gt;Before we start recording our first test case, it&#039;s a good idea to create a saved Test Suite file. Pay careful attention to the title bar for the file dialog, as it will first ask you to save the Untitled test and then ask to save the Untitled Test Suite. Out of personal preference, I use a &quot;.testsuite&quot; and &quot;.test&quot; extension for my selenium IDE files.&lt;/p&gt;

&lt;dl&gt;
&lt;dt&gt;Test Suite &lt;/dt&gt;&lt;dd&gt;A group of test cases, including their name and filename&lt;/dd&gt;
&lt;dt&gt;Test Case&lt;/dt&gt;&lt;dd&gt;A series of commands that reports a single Pass/Fail result when they are run&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;We are now ready to start recording our first test. This test will click the &quot;home&quot; link in my website, verify we are on the right page, and verify two books are shown from my reading list.&lt;/p&gt;

&lt;h3&gt;Creating a Basic Recording&lt;/h3&gt;

&lt;p&gt;With our browser open, click the red circular icon on the right side of the Selenium IDE toolbar &lt;img src=&quot;http://tiernok.com/LTDBlog/selenium/SeleniumIDE_RecordButton.jpg&quot; alt=&quot;Record button&quot; /&gt;. This tells Selenium IDE to start recording our browser interactions. Returning to the browser window, enter &quot;http://tiernok.com&quot; in the address bar to navigate to the site. Click the &quot;home&quot; link in the navigation will load the index page. Once this is done, we can return to the IDE and stop the recording by pressing the record button again.&lt;/p&gt;

&lt;p&gt;At this point we see a few things in the interface. &lt;/p&gt;

&lt;div style=&quot;text-align: center; margin: 1em; color: #666666; font-size: 80%&quot;&gt;
&lt;img src=&quot;http://tiernok.com/LTDBlog/selenium/SeleniumIDE_Rec_01.jpg&quot; alt=&quot;Selenium IDE - Getting Started&quot; /&gt;&lt;br /&gt;&lt;br /&gt;
Selenium IDE - Getting Started
&lt;/div&gt;

&lt;p&gt;The Base URL field above the toolbar has populated with the URL of the site we are testing, the &quot;Untitled&quot; test case has a star next to it to indicate unsaved changes, and the command list in the right window has been populated with two commands. Let&#039;s save these changes (Ctrl + S) and test them. Navigate the browser to a different site, return to the Selenium IDE window, and press the &quot;Play Current Test Case&quot; button (the play symbol with a single horizontal line in focus). As expected, the browser opens the root URL (step 1), then presses the &quot;Home&quot; link. The results are displayed under the test case list as 1 Run and 0 Failures and we see the details of the commands echoed in the Log tab at the bottom. &lt;/p&gt;

&lt;p&gt;Now that we have the mechanics, lets add verifications. We are going to make some assertions about what should be displayed in the page and Selenium IDE will test them for us. The first thing we want to do is make sure we ended up on the correct page, which we can do by asserting the title of the page. &lt;/p&gt;

&lt;h3&gt;Adding a simple Assertion&lt;/h3&gt;

&lt;p&gt;To add an assertion command in the command list, select the empty row below the &quot;clickAndWait&quot; command.&lt;/p&gt;

&lt;div style=&quot;text-align: center; margin: 1em; color: #666666; font-size: 80%&quot;&gt;
&lt;img src=&quot;http://tiernok.com/LTDBlog/selenium/SeleniumIDE_Rec_02.jpg&quot; alt=&quot;Selenium IDE - Adding a Command&quot; /&gt;&lt;br /&gt;&lt;br /&gt;
Selenium IDE - Adding a Command
&lt;/div&gt;

&lt;p&gt;Under the command list, we have three inputs for the new command: &quot;Command&quot;, &quot;Target&quot;, and &quot;Value&quot;. Theoretically these are used to define what command Selenium should execute, the target it is acting on, and a value for the command. These are not always 100% accurate, as we are about to see.&lt;/p&gt;

&lt;p&gt;In this case we want the command &quot;assertTitle&quot; to indicate our expectation to Selenium that the page title will be a specific value. As we start typing it, the command dropdown will filter the list of available commands. Once the command is entered, Selenium IDE populates the reference tab with additional information about that command. Since &quot;assertTitle&quot; doesn&#039;t have a target, we can simply enter the value of the page title into the &quot;value&quot; field. &lt;/p&gt;

&lt;p&gt;Let&#039;s try an incorrect value first, just to see what happens when a test fails. Enter &quot;ice cream&quot; or something else nonsensical as the value and run the Test Case again.&lt;/p&gt;

&lt;p&gt;The result is different this time. Under the Test Case list we still have 1 run, but now we have 1 failure and a red bar. In the command list to the right, the &quot;assertTitle&quot; line has a red background (unless it currently has focus). In the log at the bottom we have some red text indicating the command that failed.&lt;/p&gt;

&lt;div style=&quot;text-align: center; margin: 1em; color: #666666; font-size: 80%&quot;&gt;
&lt;img src=&quot;http://tiernok.com/LTDBlog/selenium/SeleniumIDE_Rec_03.jpg&quot; alt=&quot;Selenium IDE - Results&quot; /&gt;&lt;br /&gt;&lt;br /&gt;
Selenium IDE - Results
&lt;/div&gt;

&lt;p&gt;We have actually learned one more thing. If we look closely at the error we will see that it tried to match the page title against an empty string and failed. The &quot;assertTitle&quot; command actually reads it&#039;s argument from the &quot;target&quot; input, not the &quot;value&quot; one. A better way to think of these inputs is as &quot;Argument 1&quot; and &quot;Argument 2&quot; rather than as &quot;Target&quot; and &quot;Value&quot;. If we put the value &quot;Eli Weinstock-Herman | Tarwn&quot; in the &quot;target&quot; input and run the tests again, it should Pass.&lt;/p&gt;

&lt;div style=&quot;text-align: center; margin: 1em; color: #666666; font-size: 80%&quot;&gt;
&lt;img src=&quot;http://tiernok.com/LTDBlog/selenium/SeleniumIDE_Rec_04.jpg&quot; alt=&quot;Selenium IDE - Results&quot; /&gt;&lt;br /&gt;&lt;br /&gt;
Selenium IDE - Results
&lt;/div&gt;

&lt;p&gt;Which it does. Now we need to move on to the slightly more complex case of verifying the presence of elements that change on every page load.&lt;/p&gt;

&lt;h3&gt;Asserting Something Is Present&lt;/h3&gt;
&lt;p&gt;There are a couple different commands we could use to create this test. For the purposes of the post, we will use the &quot;assertElementPresent&quot; command to verify the first and second books are available. Using Firebug (F12, Inspect button, hover over a book), we can see there is a &quot;book&quot; CSS class that wraps around the image and title text of each book. &lt;/p&gt;

&lt;p&gt;Add a new command to the Test Case, this time entering &quot;assertElementPresent&quot; in the &quot;Command&quot; input with a target of &quot;css=.book&quot;. There are several locators we can use here, for instance xpath= would allow us to use an xpath locator. To verify the target string, press the &quot;Find&quot; button tot he right of the input. If Selenium is able to find a match, the element will flash briefly in the browser window. Additionally, double clicking the command in the command list will test just that line and report the result.&lt;/p&gt;

&lt;p&gt;To test that there are two books, we are going to be sneaky and use the CSS selector &quot;css=.book + .book&quot;. Since this CSS path will only locate an element if it finds two book elements in a row, it is an easy way to verify that both are present. We can update the command with this new locator target and run the Test Case to see it passes successfully. &lt;/p&gt;

&lt;div style=&quot;text-align: center; margin: 1em; color: #666666; font-size: 80%&quot;&gt;
&lt;img src=&quot;http://tiernok.com/LTDBlog/selenium/SeleniumIDE_Rec_05.jpg&quot; alt=&quot;Selenium IDE - Test Case Commands&quot; /&gt;&lt;br /&gt;&lt;br /&gt;
Selenium IDE - Test Case Commands
&lt;/div&gt;

&lt;p&gt;The last thing we need to do is rename the Test Case to something useful. Right-Click the Test Case in the Test Case list on the left and edit the test name. I named my test &quot;HomeLinkDisplaysHomePageWithBooks&quot;, which may seem a bit clumsy but tells me exactly what I&#039;m tetsing without loking at the details.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: Attempting to test too many concepts in a single test makes the test more fragile (ie, increases the frequency we will need to update it as we make site changes) and requires more digging to determine what failed when the test doesn&#039;t Pass. A Descriptive name is not only useful but helps keep me from combining too many tests.&lt;br /&gt;
&lt;br /&gt;
Note #2: Another option for this test would have been the &lt;a href=&quot;http://release.seleniumhq.org/selenium-core/1.0/reference.html#storeXpathCount&quot; title=&quot;See more information in the Selenium IDE Reference&quot;&gt;&quot;assertXpathCount&quot; command&lt;/a&gt; that allows us to enter an xpath argument and verify the return count matches a specific number.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;The Second Test Case - Adding some Interaction&lt;/h2&gt;
&lt;p&gt;Now that we can create a test case that makes some basic assertions about the page, it&#039;s time to move on to a test that requires some interaction. In this case we are going to test that the contact form properly submits and returns an error when we enter less than the required number of inputs.&lt;/p&gt;

&lt;h3&gt;Recording the Test&lt;/h3&gt;
&lt;p&gt;To start, lets create a new test case by selecting &quot;New Test Case&quot; from the File menu in Selenium IDE. Like we did with the original test, lets save this one before we make any changes. I am going to call my test &quot;ContactPageReturnsErrorWhenEmailFieldEmpty.test&quot;. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: Something to consider when naming tests is how you want to organize them. Besides being descriptive, I also try to use a consistent pattern to make them easily sortable/searchable in the file system.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let&#039;s start another recording and open &quot;http://tiernok.com&quot; again. Click the &quot;Contact&quot; link in the top navigation, enter text in the Name and Message inputs in the form, then press the submit button. That&#039;s the whole workflow, so lets stop the recording and see what we have.&lt;/p&gt;

&lt;div style=&quot;text-align: center; margin: 1em; color: #666666; font-size: 80%&quot;&gt;
&lt;img src=&quot;http://tiernok.com/LTDBlog/selenium/SeleniumIDE_Rec_06.jpg&quot; alt=&quot;Selenium IDE - Test Recording&quot; /&gt;&lt;br /&gt;&lt;br /&gt;
Selenium IDE - Test Recording
&lt;/div&gt;

&lt;p&gt;This test has some new commands in it, but they are pretty self explanatory. We opened the root site, clicked on the &quot;Contact&quot; link and waited for the page to load, typed in two input boxes, and clicked the submit button, again waiting for the next page to load. To finish up the test, lets add an assertion for the error message.&lt;/p&gt;

&lt;h3&gt;Asserting the Error Message&lt;/h3&gt;
&lt;p&gt;Like the earlier Test Case, there are a few different ways we can write this assertion. In this case the two obvious methods are using either &quot;assertTextPresent&quot;, which tests that the text shows up somewhere on the page, &quot;assertElementPresent&quot;, which could look for an element with the &quot;.err&quot; CSS class, or &quot;assertText&quot;, which combines the two commands above to test that a specific element has specific text.&lt;/p&gt;

&lt;div style=&quot;text-align: center; margin: 1em; color: #666666; font-size: 80%&quot;&gt;
&lt;img src=&quot;http://tiernok.com/LTDBlog/selenium/SeleniumIDE_Rec_07.jpg&quot; alt=&quot;Selenium IDE - Error Assertion&quot; /&gt;&lt;br /&gt;&lt;br /&gt;
Selenium IDE - Error Assertion
&lt;/div&gt;

&lt;p&gt;I&#039;ve used the last option in my Test Case (as well as Firebug again to locate the element in the browser). If I save the Test Case and run it, I should see the browser run through the steps and Pass, just like our earlier test.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: You may have noticed the Fast-&amp;gt;Slow slider above the Test Case list on the left. This slider controls how fast or slow the individual commands are run when processing the tests. It can be helpful to run individual tests slowly, but the fast setting is useful when you want to run multiple tests or the whole set.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;Review&lt;/h2&gt;
&lt;p&gt;Through the course of the post we have created two basic tests. Neither took very long to create and it wouldn&#039;t take long to expand this suite to cover the entire site. Selenium IDE makes it easy to create basic test cases and tie them together into a suite. The tools, augmented with Firebug, make it fairly easy to build the series of commands for each Test Case and there is a fairly detailed &lt;a href=&quot;http://release.seleniumhq.org/selenium-core/1.0/reference.html&quot; title=&quot;View the command reference&quot;&gt;command reference&lt;/a&gt; available.&lt;/p&gt;

&lt;p&gt;However there were some points I mentioned above that I need to return to.&lt;/p&gt;

&lt;h3&gt;Test Fragility and Duplication&lt;/h3&gt;
&lt;p&gt;Tests in Selenium IDE tend to be somewhat fragile. In general, automated interface tests have a certain level of fragility because it&#039;s easy to break them simply by moving a few elements around or renaming some controls. The Selenium IDE is on the extreme side of this curve because these values are used at the individual command level rather than having a central list of search or match strings.&lt;/p&gt;

&lt;p&gt;We can reduce the effects of this fragility by keeping tests shorter, as shorter tests are easier to correct or update. Selenium IDE has the ability to define suite-level variables, so in some cases I created variables for common strings and put those declarations in a first &quot;test case&quot;.&lt;/p&gt;

&lt;h3&gt;Other Limitations&lt;/h3&gt;
&lt;p&gt;In addition to the fragility above, there are other limitations. Using a Test Suite to test two separate base URLs (for instance a Dev and a QA version or local and production) is difficult and requires some trickery. Scaling the tests can also be tricky, especially if you are used to the ability to run categories of unit tests or select subsets to run on the fly. There is also the issue of leaving your PC alone long enough to run the tests, since the browser will steal focus as it&#039;s running tests.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: There is also another oddity. When you have Selenium IDE open, links that open in new tabs or windows will be opened in a new window without the ability to scroll. This has gotten me more than once.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;And That&#039;s a Wrap&lt;/h3&gt;

&lt;p&gt;Selenium IDE offers an excellent first step for verifying everything is working from the interface level. It also provides a great introduction into the mindset of using a web automation framework for testing and has a lot of power for a very cheap price. While there are some limits to how far this approach will get you, the gains are fairly cheap and can be a real time saver. In a later post I will talk about the next step, interfacing directly with Selenium WebDriver from code to test a site.&lt;/p&gt;

&lt;p&gt;Next Post: &lt;a href=&quot;http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/automated-web-testing-with-selenium-2&quot; title=&quot;Automated Web Testing with Selenium WebDriver&quot;&gt;Automated Web Testing with Selenium WebDriver&lt;/a&gt;&lt;/p&gt;&lt;div class=&quot;item_footer&quot;&gt;&lt;p&gt;&lt;small&gt;&lt;a href=&quot;http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/automated-web-testing-with-selenium&quot;&gt;Original post&lt;/a&gt; blogged on &lt;a href=&quot;http://lessthandot.com/&quot;&gt;LessThanDot&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;</description>
			<content:encoded><![CDATA[<p>How long does it take to browse through a website after each build and make sure none of the pages have mysteriously blown up? 5 Minutes? 20? No time at all? The time we invest in manual testing adds up. As we switch focus to newer parts of a site we may even stop testing the ones we 'finished', confident that they're stable and won't be affected by our newer changes (yeah right). And despite the time spent manually testing, we still eventually deploy issues that a 30s check in the right part of the application would have detected.</p>

<p>Enter browser automation tools, such as Selenium. With browser automation tools we can invest some extra time up front to define those manual tests in code, then continue to run those tests long past the time we would have stopped doing it manually. </p>

<p><em>Note: This post is a walk-through for creating some automated browser test cases. To get the most from it you will probably want to download the tool below and launch it from a second browser so you can follow along. It assumes you have Firefox installed and are comfortable with HTML and css selectors.</em></p>

<h2>The Smoke Test</h2>
<p>There are levels to automated testing, starting with "none" and ending with some fairly complex industry packages and/or in-house frameworks. Today we're going to look at the cheapest option, recording and rerunning a set of tests using <a href="http://seleniumhq.org/projects/ide/" title="Go to the Selenium IDE website">Selenium IDE</a>, a Firefox browser plugin. At this level we can replace many of our manual smoke tests with an automated set for a low initial investment.</p>

<p>Selenium IDE is a browser plugin that can record and playback actions we make in the browser. Using Selenium we can record a set of paths through our website, add some checks, and be reasonably confidant that the site is still working. </p>

<p>Pros: Free, Low up front time cost<br />
Cons: Tests are fragile*, Potential for lots of duplication*, tests run on local browser<br />
Neutral: There's a learning curve but good documentation and quick feedback*</p>

<p>* I'll come back to these three points</p>

<p>For the purposes of the post, we will use a couple pages from my personal website. We will create two tests to verify the main page loads properly and that the contact form submits and validates properly. In the even that I make a change to a shared file, these tests will ensure the pages continue to pass some basic tests.</p>

<h2>Getting Started</h2>
<p>First we need to download the plugin (assuming you have Firefox installed): <a href="http://seleniumhq.org/download/" title="Download the plugin">Download Selenium IDE</a>.</p>

<p>After downloading the plugin and restarting, we should have a "Selenium IDE" option in the Tools menu (Windows users can press the 'Alt' key to see the top menu). </p>

<div style="text-align: center; margin: 1em; color: #666666; font-size: 80%">
<img src="http://tiernok.com/LTDBlog/selenium/FF_Menu.jpg" alt="Selenium IDE in Firefox" /><br /><br />
Selenium IDE in the Firefox Menu
</div>

<p>Pressing that Selenium IDE button will give us both a popup interface and a set of release notes that unfortunately are probably less than useful to a first time user.</p>

<div style="text-align: center; margin: 1em; color: #666666; font-size: 80%">
<img src="http://tiernok.com/LTDBlog/selenium/SeleniumIDE.jpg" alt="Selenium IDE" /><br /><br />
Selenium IDE
</div>

<p>The main areas of the interface are:</p>
<ul>
<li>A test case list on the left which displays the list of "Test Cases" in the current "Test Suite"</li>
<li>A command window in the main viewing area, which will display the individual commands we execute as part of selected test case</li>
<li>A series of tabs at the bottom, of which we will only be interacting with the Log and Reference initially</li>
</ul>

<p>So now that we have an interface, lets jump into creating the first test case to give us something to explore.</p>

<p><em>Note: The FireBug Firefox extension is an invaluable tool when working with Selenium, I highly recommend you <a href="http://getfirebug.com/" title="Go get Firebug Extension">download FireBug</a> if you don't already have it.</em></p>

<h2>The First Test Case</h2>
<p>Before we start recording our first test case, it's a good idea to create a saved Test Suite file. Pay careful attention to the title bar for the file dialog, as it will first ask you to save the Untitled test and then ask to save the Untitled Test Suite. Out of personal preference, I use a ".testsuite" and ".test" extension for my selenium IDE files.</p>

<dl>
<dt>Test Suite </dt><dd>A group of test cases, including their name and filename</dd>
<dt>Test Case</dt><dd>A series of commands that reports a single Pass/Fail result when they are run</dd>
</dl>

<p>We are now ready to start recording our first test. This test will click the "home" link in my website, verify we are on the right page, and verify two books are shown from my reading list.</p>

<h3>Creating a Basic Recording</h3>

<p>With our browser open, click the red circular icon on the right side of the Selenium IDE toolbar <img src="http://tiernok.com/LTDBlog/selenium/SeleniumIDE_RecordButton.jpg" alt="Record button" />. This tells Selenium IDE to start recording our browser interactions. Returning to the browser window, enter "http://tiernok.com" in the address bar to navigate to the site. Click the "home" link in the navigation will load the index page. Once this is done, we can return to the IDE and stop the recording by pressing the record button again.</p>

<p>At this point we see a few things in the interface. </p>

<div style="text-align: center; margin: 1em; color: #666666; font-size: 80%">
<img src="http://tiernok.com/LTDBlog/selenium/SeleniumIDE_Rec_01.jpg" alt="Selenium IDE - Getting Started" /><br /><br />
Selenium IDE - Getting Started
</div>

<p>The Base URL field above the toolbar has populated with the URL of the site we are testing, the "Untitled" test case has a star next to it to indicate unsaved changes, and the command list in the right window has been populated with two commands. Let's save these changes (Ctrl + S) and test them. Navigate the browser to a different site, return to the Selenium IDE window, and press the "Play Current Test Case" button (the play symbol with a single horizontal line in focus). As expected, the browser opens the root URL (step 1), then presses the "Home" link. The results are displayed under the test case list as 1 Run and 0 Failures and we see the details of the commands echoed in the Log tab at the bottom. </p>

<p>Now that we have the mechanics, lets add verifications. We are going to make some assertions about what should be displayed in the page and Selenium IDE will test them for us. The first thing we want to do is make sure we ended up on the correct page, which we can do by asserting the title of the page. </p>

<h3>Adding a simple Assertion</h3>

<p>To add an assertion command in the command list, select the empty row below the "clickAndWait" command.</p>

<div style="text-align: center; margin: 1em; color: #666666; font-size: 80%">
<img src="http://tiernok.com/LTDBlog/selenium/SeleniumIDE_Rec_02.jpg" alt="Selenium IDE - Adding a Command" /><br /><br />
Selenium IDE - Adding a Command
</div>

<p>Under the command list, we have three inputs for the new command: "Command", "Target", and "Value". Theoretically these are used to define what command Selenium should execute, the target it is acting on, and a value for the command. These are not always 100% accurate, as we are about to see.</p>

<p>In this case we want the command "assertTitle" to indicate our expectation to Selenium that the page title will be a specific value. As we start typing it, the command dropdown will filter the list of available commands. Once the command is entered, Selenium IDE populates the reference tab with additional information about that command. Since "assertTitle" doesn't have a target, we can simply enter the value of the page title into the "value" field. </p>

<p>Let's try an incorrect value first, just to see what happens when a test fails. Enter "ice cream" or something else nonsensical as the value and run the Test Case again.</p>

<p>The result is different this time. Under the Test Case list we still have 1 run, but now we have 1 failure and a red bar. In the command list to the right, the "assertTitle" line has a red background (unless it currently has focus). In the log at the bottom we have some red text indicating the command that failed.</p>

<div style="text-align: center; margin: 1em; color: #666666; font-size: 80%">
<img src="http://tiernok.com/LTDBlog/selenium/SeleniumIDE_Rec_03.jpg" alt="Selenium IDE - Results" /><br /><br />
Selenium IDE - Results
</div>

<p>We have actually learned one more thing. If we look closely at the error we will see that it tried to match the page title against an empty string and failed. The "assertTitle" command actually reads it's argument from the "target" input, not the "value" one. A better way to think of these inputs is as "Argument 1" and "Argument 2" rather than as "Target" and "Value". If we put the value "Eli Weinstock-Herman | Tarwn" in the "target" input and run the tests again, it should Pass.</p>

<div style="text-align: center; margin: 1em; color: #666666; font-size: 80%">
<img src="http://tiernok.com/LTDBlog/selenium/SeleniumIDE_Rec_04.jpg" alt="Selenium IDE - Results" /><br /><br />
Selenium IDE - Results
</div>

<p>Which it does. Now we need to move on to the slightly more complex case of verifying the presence of elements that change on every page load.</p>

<h3>Asserting Something Is Present</h3>
<p>There are a couple different commands we could use to create this test. For the purposes of the post, we will use the "assertElementPresent" command to verify the first and second books are available. Using Firebug (F12, Inspect button, hover over a book), we can see there is a "book" CSS class that wraps around the image and title text of each book. </p>

<p>Add a new command to the Test Case, this time entering "assertElementPresent" in the "Command" input with a target of "css=.book". There are several locators we can use here, for instance xpath= would allow us to use an xpath locator. To verify the target string, press the "Find" button tot he right of the input. If Selenium is able to find a match, the element will flash briefly in the browser window. Additionally, double clicking the command in the command list will test just that line and report the result.</p>

<p>To test that there are two books, we are going to be sneaky and use the CSS selector "css=.book + .book". Since this CSS path will only locate an element if it finds two book elements in a row, it is an easy way to verify that both are present. We can update the command with this new locator target and run the Test Case to see it passes successfully. </p>

<div style="text-align: center; margin: 1em; color: #666666; font-size: 80%">
<img src="http://tiernok.com/LTDBlog/selenium/SeleniumIDE_Rec_05.jpg" alt="Selenium IDE - Test Case Commands" /><br /><br />
Selenium IDE - Test Case Commands
</div>

<p>The last thing we need to do is rename the Test Case to something useful. Right-Click the Test Case in the Test Case list on the left and edit the test name. I named my test "HomeLinkDisplaysHomePageWithBooks", which may seem a bit clumsy but tells me exactly what I'm tetsing without loking at the details.</p>

<p><em>Note: Attempting to test too many concepts in a single test makes the test more fragile (ie, increases the frequency we will need to update it as we make site changes) and requires more digging to determine what failed when the test doesn't Pass. A Descriptive name is not only useful but helps keep me from combining too many tests.<br />
<br />
Note #2: Another option for this test would have been the <a href="http://release.seleniumhq.org/selenium-core/1.0/reference.html#storeXpathCount" title="See more information in the Selenium IDE Reference">"assertXpathCount" command</a> that allows us to enter an xpath argument and verify the return count matches a specific number.</em></p>

<h2>The Second Test Case - Adding some Interaction</h2>
<p>Now that we can create a test case that makes some basic assertions about the page, it's time to move on to a test that requires some interaction. In this case we are going to test that the contact form properly submits and returns an error when we enter less than the required number of inputs.</p>

<h3>Recording the Test</h3>
<p>To start, lets create a new test case by selecting "New Test Case" from the File menu in Selenium IDE. Like we did with the original test, lets save this one before we make any changes. I am going to call my test "ContactPageReturnsErrorWhenEmailFieldEmpty.test". </p>

<p><em>Note: Something to consider when naming tests is how you want to organize them. Besides being descriptive, I also try to use a consistent pattern to make them easily sortable/searchable in the file system.</em></p>

<p>Let's start another recording and open "http://tiernok.com" again. Click the "Contact" link in the top navigation, enter text in the Name and Message inputs in the form, then press the submit button. That's the whole workflow, so lets stop the recording and see what we have.</p>

<div style="text-align: center; margin: 1em; color: #666666; font-size: 80%">
<img src="http://tiernok.com/LTDBlog/selenium/SeleniumIDE_Rec_06.jpg" alt="Selenium IDE - Test Recording" /><br /><br />
Selenium IDE - Test Recording
</div>

<p>This test has some new commands in it, but they are pretty self explanatory. We opened the root site, clicked on the "Contact" link and waited for the page to load, typed in two input boxes, and clicked the submit button, again waiting for the next page to load. To finish up the test, lets add an assertion for the error message.</p>

<h3>Asserting the Error Message</h3>
<p>Like the earlier Test Case, there are a few different ways we can write this assertion. In this case the two obvious methods are using either "assertTextPresent", which tests that the text shows up somewhere on the page, "assertElementPresent", which could look for an element with the ".err" CSS class, or "assertText", which combines the two commands above to test that a specific element has specific text.</p>

<div style="text-align: center; margin: 1em; color: #666666; font-size: 80%">
<img src="http://tiernok.com/LTDBlog/selenium/SeleniumIDE_Rec_07.jpg" alt="Selenium IDE - Error Assertion" /><br /><br />
Selenium IDE - Error Assertion
</div>

<p>I've used the last option in my Test Case (as well as Firebug again to locate the element in the browser). If I save the Test Case and run it, I should see the browser run through the steps and Pass, just like our earlier test.</p>

<p><em>Note: You may have noticed the Fast-&gt;Slow slider above the Test Case list on the left. This slider controls how fast or slow the individual commands are run when processing the tests. It can be helpful to run individual tests slowly, but the fast setting is useful when you want to run multiple tests or the whole set.</em></p>

<h2>Review</h2>
<p>Through the course of the post we have created two basic tests. Neither took very long to create and it wouldn't take long to expand this suite to cover the entire site. Selenium IDE makes it easy to create basic test cases and tie them together into a suite. The tools, augmented with Firebug, make it fairly easy to build the series of commands for each Test Case and there is a fairly detailed <a href="http://release.seleniumhq.org/selenium-core/1.0/reference.html" title="View the command reference">command reference</a> available.</p>

<p>However there were some points I mentioned above that I need to return to.</p>

<h3>Test Fragility and Duplication</h3>
<p>Tests in Selenium IDE tend to be somewhat fragile. In general, automated interface tests have a certain level of fragility because it's easy to break them simply by moving a few elements around or renaming some controls. The Selenium IDE is on the extreme side of this curve because these values are used at the individual command level rather than having a central list of search or match strings.</p>

<p>We can reduce the effects of this fragility by keeping tests shorter, as shorter tests are easier to correct or update. Selenium IDE has the ability to define suite-level variables, so in some cases I created variables for common strings and put those declarations in a first "test case".</p>

<h3>Other Limitations</h3>
<p>In addition to the fragility above, there are other limitations. Using a Test Suite to test two separate base URLs (for instance a Dev and a QA version or local and production) is difficult and requires some trickery. Scaling the tests can also be tricky, especially if you are used to the ability to run categories of unit tests or select subsets to run on the fly. There is also the issue of leaving your PC alone long enough to run the tests, since the browser will steal focus as it's running tests.</p>

<p><em>Note: There is also another oddity. When you have Selenium IDE open, links that open in new tabs or windows will be opened in a new window without the ability to scroll. This has gotten me more than once.</em></p>

<h3>And That's a Wrap</h3>

<p>Selenium IDE offers an excellent first step for verifying everything is working from the interface level. It also provides a great introduction into the mindset of using a web automation framework for testing and has a lot of power for a very cheap price. While there are some limits to how far this approach will get you, the gains are fairly cheap and can be a real time saver. In a later post I will talk about the next step, interfacing directly with Selenium WebDriver from code to test a site.</p>

<p>Next Post: <a href="http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/automated-web-testing-with-selenium-2" title="Automated Web Testing with Selenium WebDriver">Automated Web Testing with Selenium WebDriver</a></p><div class="item_footer"><p><small><a href="http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/automated-web-testing-with-selenium">Original post</a> blogged on <a href="http://lessthandot.com/">LessThanDot</a>.</small></p></div>]]></content:encoded>
								<comments>http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/automated-web-testing-with-selenium#comments</comments>
			<wfw:commentRss>http://blogs.lessthandot.com/index.php/WebDev/?tempskin=_rss2&#38;disp=comments&#38;p=1455</wfw:commentRss>
		</item>
				<item>
			<title>Getting Started with JavaScript Unit Testing</title>
			<link>http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/Javascript/getting-started-with-javascript-unit</link>
			<pubDate>Mon, 26 Sep 2011 09:58:00 +0000</pubDate>			<dc:creator>Eli Weinstock-Herman (tarwn)</dc:creator>
			<category domain="main">Javascript</category>			<guid isPermaLink="false">1411@http://blogs.lessthandot.com/</guid>
						<description>&lt;p&gt;Recently I decided to start doing JavaScript &lt;a href=&quot;http://blogs.lessthandot.com/index.php/ITProfessionals/ProfessionalDevelopment/using-code-katas-to-improve&quot; title=&quot;Read Post: Using Code Katas to Improve Programming Skills&quot;&gt;code katas.&lt;/a&gt; I&#039;ve been using JavaScript for around ten years, but there there are still a lot of aspects I don&#039;t know well or that I could use more practice in. Case in point, I had never used a unit testing framework with javascript. Having never unit tested JavaScript before, I used a scientific tool to carefully select from amongst the numerous unit testing packages available.&lt;/p&gt;

&lt;p&gt;I typed &quot;javascript unit testing&quot; into Google and started reading.&lt;/p&gt;

&lt;h2&gt;jsTestDriver&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://code.google.com/p/js-test-driver/wiki/GettingStarted&quot; title=&quot;Visit the jsTestDriver wiki&quot;&gt;jsTestDriver&lt;/a&gt; was listed on a wide number of sites as one of the top JavaScript unit testing tools and fit an initial requirement I set myself of running outside of a browser. jsTestDriver runs as a client-server pair, the client sending tests to the server, which then runs them on a captured browser. The advantage of this method is it can easily be integrated with a code editor or as part of an automated build.&lt;/p&gt;

&lt;p&gt;The jsTestDriver site includes plugins for Eclipse, Maven, and IntelliJ. I also found an article on &lt;a href=&quot;http://slmoloch.blogspot.com/2009/08/how-to-run-jstestdriver-with-visual_02.html&quot; title=&quot;Read the Visual Studio post&quot;&gt;using it with Visual Studio&lt;/a&gt; and it was fairly easy setting it up as a user tool in EditPlus.&lt;/p&gt;

&lt;div style=&quot;text-align: center; margin: 1em; color: #666666; font-size: 80%&quot;&gt;
&lt;img src=&quot;http://tiernok.com/LTDBlog/jsunittest/editplus.png&quot; alt=&quot;Screenshot of EditPlus&quot; /&gt;&lt;br /&gt;&lt;br /&gt;
Screenshot of EditPlus Settings
&lt;/div&gt;

&lt;p&gt;Though I&#039;m not using the feature, jsTestDriver provides a flag to specify an output file for the results, enabling us to use it as part of a continuous build.&lt;/p&gt;

&lt;h3&gt;The Setup&lt;/h3&gt;
&lt;p&gt;Setting up jsTestDriver on my system was fairly straightforward.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download Java - Visit &lt;a href=&quot;http://java.com&quot; title=&quot;Java website&quot;&gt;java.com&lt;/a&gt; and download the appropriate installer, run the installer, remember to go into the control panel and fiddle with Java&#039;s update settings&lt;/li&gt;
&lt;li&gt;Download the jar file - Visit the &lt;a href=&quot;http://code.google.com/p/js-test-driver/downloads/list&quot; title=&quot;View the jsTestDriver downloads&quot;&gt;project on Google&lt;/a&gt; and download a copy of the jar file (I used the self-contained version)&lt;/li&gt;
&lt;li&gt;Create folders - Create a top level folder and two sub folders (for instance, src and src-test). Put the jar in the top level folder&lt;/li&gt;
&lt;li&gt;Create a conf file - I used the one in the &lt;a href=&quot;http://code.google.com/p/js-test-driver/wiki/GettingStarted#Writing_configuration_file&quot; title=&quot;Visit the Getting Started guide&quot;&gt;Getting Started Guide&lt;/a&gt; as a starting point&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At this point we should be able to fire up the server for the first time and verify everything is ready to go. I created a .cmd file on my system for the server so I could easily start it:&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;text&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;text&quot; id=&quot;cb89521&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&amp;quot;C:\Program Files (x86)\Java\jre6\bin\java&amp;quot; -jar JsTestDriver-1.3.2.jar --port 4224 --browser &amp;quot;C:\Documents and Settings\Tarwn\Local Settings\Application Data\Google\Chrome\Application\chrome.exe&amp;quot;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb3188&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;What this does is starts the jsTestDriver jar on port 4224 and also automatically starts up an instance of chrome that will be captured by the server to run tests. I was initially using Firefox but jsTestDriver can&#039;t intercept the console log the way it can with Chrome, so I wasn&#039;t getting very good output for failed or errored tests.&lt;/p&gt;

&lt;p&gt;Next I created a .cmd file to run all the tests in my folders:&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;text&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;text&quot; id=&quot;cb10627&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&amp;quot;C:\Program Files (x86)\Java\jre6\bin\java&amp;quot; -jar JsTestDriver-1.3.2.jar --tests all&lt;br /&gt;pause&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb57175&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This tells jsTestDriver to run all available tests (based on the settings in the conf) using a jsTestDriver server on port 4224. I ended up not using this cmd file very frequently, as it was much handier to be able to run them from a key command inside my editor.&lt;/p&gt;

&lt;h3&gt;Writing Tests&lt;/h3&gt;
&lt;p&gt;Once we have gotten this far, we can start writing some simple tests.&lt;/p&gt;

&lt;p&gt;In each directory (src and src-test), create a file named &quot;mystuff.js&quot;.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;src/mystuff.js&lt;/b&gt;&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;javascript&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;javascript&quot; id=&quot;cb73534&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;myAwesomeApp = {};&lt;br /&gt;&amp;nbsp;&lt;br /&gt;myAwesomeApp.MyAwesomeClass = function(){};&lt;br /&gt;&amp;nbsp;&lt;br /&gt;myAwesomeApp.MyAwesomeClass.prototype.add = function(num0, num1){&lt;br /&gt;&amp;nbsp; &amp;nbsp; return num0 + num1;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb81197&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;b&gt;src-test/mystuff.js&lt;/b&gt;&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;javascript&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;javascript&quot; id=&quot;cb56806&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;TestCase(&amp;quot;Sample Test Case&amp;quot;,{&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;quot;test Number plus Zero Equals Number&amp;quot;: function(){&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var adder = new myAwesomeApp.MyAwesomeClass();&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; assertEquals(5, adder.add(5,0));&lt;br /&gt;&amp;nbsp; &amp;nbsp; },&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;quot;test Number plus Number Equals Sum&amp;quot;: function(){&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var adder = new myAwesomeApp.MyAwesomeClass();&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; assertEquals(8, adder.add(5,3));&lt;br /&gt;&amp;nbsp; &amp;nbsp; },&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;quot;test Zero plus Number Equals Number&amp;quot;: function(){&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var adder = new myAwesomeApp.MyAwesomeClass();&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; assertEquals(5, adder.add(0,5));&lt;br /&gt;&amp;nbsp; &amp;nbsp; },&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;quot;test Number plus Negative of Number Equals Zero&amp;quot;: function(){&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var adder = new myAwesomeApp.MyAwesomeClass();&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; assertEquals(0, adder.add(5,-5));&lt;br /&gt;&amp;nbsp; &amp;nbsp; },&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;quot;test Fails miserably&amp;quot;: function(){&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; fail(&amp;quot;miserably&amp;quot;);&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb50457&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;JavaScript provides a number of different methods to define objects with functions, in the source class I used the prototype method and in the tests file I used an object literal. For jsTestDriver, the important part is that the tests in the object we pass to TestCase begin with the word test, and the object literal method seemed like a friendlier layout for a test file.&lt;/p&gt;

&lt;h3&gt;Running Tests&lt;/h3&gt;
&lt;p&gt;Once we have the two files in place, start the server by issuing the following command (or creating the cmd file like me):&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;text&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;text&quot; id=&quot;cb73974&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&amp;quot;C:\Program Files (x86)\Java\jre6\bin\java&amp;quot; -jar ../JsTestDriver-1.3.2.jar --port 4224 --browser &amp;quot;C:\Documents and Settings\Tarwn\Local Settings\Application Data\Google\Chrome\Application\chrome.exe&amp;quot;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb12303&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;You will need to update the browser and java paths to reflect your own.&lt;/p&gt;

&lt;p&gt;Once the browser has started and it has been captured by the server for testing, it will look like this:&lt;/p&gt;

&lt;div style=&quot;text-align: center; margin: 1em; color: #666666; font-size: 80%&quot;&gt;
&lt;img src=&quot;http://tiernok.com/LTDBlog/jsunittest/jsTestDriver.png&quot; alt=&quot;Chrome captured by jsTestDriver Server&quot; /&gt;&lt;br /&gt;&lt;br /&gt;
Chrome captured by jsTestDriver Server
&lt;/div&gt;

&lt;p&gt;Now we can run our tests by issuing the following command:&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;text&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;text&quot; id=&quot;cb64941&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&amp;quot;C:\Program Files (x86)\Java\jre6\bin\java&amp;quot; -jar ../JsTestDriver-1.3.2.jar --tests all&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb90878&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Again, you will need to change the java path to reflect your own (or remove it if you have added it to your PATH variable).&lt;/p&gt;

&lt;p&gt;The results should look something like this:&lt;/p&gt;
&lt;pre style=&quot;margin: 1em; border: 1px solid #999999; padding: 1em;&quot;&gt;
....F
Total 5 tests (Passed: 4; Fails: 1; Errors: 0) (0.00 ms)
  Chrome 13.0.782.220 Windows: Run 5 tests (Passed: 4; Fails: 1; Errors 0) (0.00 ms)
    Object Literal Test Case.test Fails miserably failed (0.00 ms): AssertError: miserably
      AssertError: miserably
          at Object.test Fails miserably (http://localhost:4224/test/src-test/mystuff.js:22:3)

Tests failed: Tests failed. See log for details.
&lt;/pre&gt;

&lt;p&gt;The top reflects the tests that have run at a glance with .s for passing tests, Fs for failed, and E for errored. Afterwards we get a summary of the total counts and then a section for the one browser we ran with. jsTestDriver allows you to capture multiple browsers, so we could configure this to run our tests across chrome, firefox, and IE simultaneously.&lt;/p&gt;

&lt;p&gt;jsTestDriver also supports &quot;setup&quot; and &quot;teardown&quot; functions to run before and after tests. &lt;/p&gt;

&lt;h2&gt;Qunit&lt;/h2&gt;
&lt;p&gt;Qunit is a browser-based solution that was built to unit test the jQuery framework. Qunit has fewer requirements to run, but because it runs directly in a browser it means we have to switch windows and refresh in order to get an updated test run.&lt;/p&gt;

&lt;h3&gt;The Setup&lt;/h3&gt;
&lt;p&gt;Because Qunit will run in our browser, there are relatively few requirements and unlike jsTestDriver, none of them are installations.&lt;/p&gt;

&lt;ol&gt;
   &lt;li&gt;We already made our folders, so nothing to do here&lt;/li&gt;
   &lt;li&gt;Download the necessary include files to the top level: &lt;a href=&quot;http://code.jquery.com/qunit/qunit-git.js&quot; title=&quot;Download qunit.js&quot;&gt;qunit.js&lt;/a&gt; and &lt;a href=&quot;http://code.jquery.com/qunit/qunit-git.css&quot; title=&quot;Download qunit.css&quot;&gt;qunit.css&lt;/a&gt; (I renamed mine without the -git)&lt;/li&gt;
   &lt;li&gt;Create an empty html file in the top level&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The empty file will be our test runner. Update the file to look like this:&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;html&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;html&quot; id=&quot;cb35688&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&amp;lt;DOCTYPE html&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;a href=&quot;http://december.com/html/4/element/html.html&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;html&amp;gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;a href=&quot;http://december.com/html/4/element/head.html&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;a href=&quot;http://december.com/html/4/element/script.html&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;script&lt;/span&gt;&lt;/a&gt; &lt;span style=&quot;color: #000066;&quot;&gt;src&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;http://code.jquery.com/jquery-1.6.4.min.js&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;type&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;a href=&quot;http://december.com/html/4/element/script.html&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;script&lt;/span&gt;&lt;/a&gt; &lt;span style=&quot;color: #000066;&quot;&gt;src&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;qunit.js&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;type&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;a href=&quot;http://december.com/html/4/element/link.html&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;link&lt;/span&gt;&lt;/a&gt; &lt;span style=&quot;color: #000066;&quot;&gt;rel&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;stylesheet&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;media&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;all&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;href&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;qunit.css&amp;quot;&lt;/span&gt; /&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;a href=&quot;http://december.com/html/4/element/script.html&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;script&lt;/span&gt;&lt;/a&gt; &lt;span style=&quot;color: #000066;&quot;&gt;src&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;src/mystuff.js&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;type&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;a href=&quot;http://december.com/html/4/element/script.html&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;script&lt;/span&gt;&lt;/a&gt; &lt;span style=&quot;color: #000066;&quot;&gt;src&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;src-test/mystuff_qunit.js&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;type&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;a href=&quot;http://december.com/html/4/element/body.html&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;a href=&quot;http://december.com/html/4/element/h1.html&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;h1&lt;/span&gt;&lt;/a&gt; &lt;span style=&quot;color: #000066;&quot;&gt;id&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;qunit-header&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;MyStuff&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;a href=&quot;http://december.com/html/4/element/h2.html&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;h2&lt;/span&gt;&lt;/a&gt; &lt;span style=&quot;color: #000066;&quot;&gt;id&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;qunit-banner&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;a href=&quot;http://december.com/html/4/element/h2.html&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;h2&lt;/span&gt;&lt;/a&gt; &lt;span style=&quot;color: #000066;&quot;&gt;id&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;qunit-userAgent&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;a href=&quot;http://december.com/html/4/element/ol.html&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;ol&lt;/span&gt;&lt;/a&gt; &lt;span style=&quot;color: #000066;&quot;&gt;id&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;qunit-tests&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;/ol&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb69363&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can see, we are referencing a CDNed version of jQuery, the local qunit files we downloaded, our source file, and a test js file we haven&#039;t created yet. The remainder of the HTML will be used by Qunit to display the results.&lt;/p&gt;

&lt;h3&gt;Writing Tests&lt;/h3&gt;
&lt;p&gt;Writing test in Qunit is pretty straightforward. Since we already have the src/mystuff.js file from above, we can jump right in and create a qunit version of our test cases.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;src-test/mystuff_qunit.js&lt;/b&gt;&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;javascript&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;javascript&quot; id=&quot;cb45204&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;module(&amp;quot;Sample Test Case&amp;quot;);&lt;br /&gt;&amp;nbsp;&lt;br /&gt;test(&amp;quot;Number plus Zero Equals Number&amp;quot;, function(){&lt;br /&gt;&amp;nbsp; &amp;nbsp; var adder = new myAwesomeApp.MyAwesomeClass();&lt;br /&gt;&amp;nbsp; &amp;nbsp; equals( adder.add(5,0),5);&lt;br /&gt;});&lt;br /&gt;&amp;nbsp;&lt;br /&gt;test(&amp;quot;Number plus Number Equals Sum&amp;quot;, function(){&lt;br /&gt;&amp;nbsp; &amp;nbsp; var adder = new myAwesomeApp.MyAwesomeClass();&lt;br /&gt;&amp;nbsp; &amp;nbsp; equals(adder.add(5,3),8);&lt;br /&gt;});&lt;br /&gt;&amp;nbsp;&lt;br /&gt;test(&amp;quot;Zero plus Number Equals Number&amp;quot;, function(){&lt;br /&gt;&amp;nbsp; &amp;nbsp; var adder = new myAwesomeApp.MyAwesomeClass();&lt;br /&gt;&amp;nbsp; &amp;nbsp; equals(adder.add(0,5),5);&lt;br /&gt;});&lt;br /&gt;&amp;nbsp;&lt;br /&gt;test(&amp;quot;Number plus Negative of Number Equals Zero&amp;quot;, function(){&lt;br /&gt;&amp;nbsp; &amp;nbsp; var adder = new myAwesomeApp.MyAwesomeClass();&lt;br /&gt;&amp;nbsp; &amp;nbsp; equals(adder.add(5,-5),0);&lt;br /&gt;});&lt;br /&gt;&amp;nbsp;&lt;br /&gt;test(&amp;quot;Fails miserably&amp;quot;, function(){&lt;br /&gt;&amp;nbsp; &amp;nbsp; ok(false,&amp;quot;miserably&amp;quot;);&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb2937&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Qunit&#039;s &lt;i&gt;equals&lt;/i&gt; method has it&#039;s actual and expected arguments reversed from jsTestDriver, instead expecting them in this order: &lt;i&gt;Qunit.equals(actual, expected)&lt;/i&gt;. I didn&#039;t originally notice this and had to update both the jsTestDriver test mapping script and the sample above (here and in bitbucket).&lt;/p&gt;

&lt;h3&gt;Running Tests&lt;/h3&gt;
&lt;p&gt;Opening the testrunner html file, we should now see it display a block for each test that we have defined above.&lt;/p&gt;

&lt;div style=&quot;text-align: center; margin: 1em; color: #666666; font-size: 80%&quot;&gt;
&lt;img src=&quot;http://tiernok.com/LTDBlog/jsunittest/qunit.png&quot; alt=&quot;QUnit Results&quot; /&gt;&lt;br /&gt;&lt;br /&gt;
QUnit Results
&lt;/div&gt;

&lt;p&gt;Failed tests automatically display details. Any test can be toggled open/closed by clicking it&#039;s name, and a handy &quot;rerun&quot; button lets us re-run a single test.&lt;/p&gt;

&lt;h2&gt;Combining Them&lt;/h2&gt;
&lt;p&gt;By writing a small amount of glue script, I was able to re-use my jsTestDriver tests in Qunit. Since I am currently only using a small subset of assertions and using the object literal method, the glue script is limited to only exactly what I needed.&lt;/p&gt;

&lt;p&gt;Add this file to the top level folder:&lt;br /&gt;
&lt;b&gt;jsTestDriverInQunit.js&lt;/b&gt;&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;javascript&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;javascript&quot; id=&quot;cb7171&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;/* bare minimum to run jsTestDriver tests as Qunit tests */&lt;br /&gt;function TestCase(name, tests){&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if(tests != null)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; module(name);&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; for(var key in tests){&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if(tests[key] instanceof Function &amp;amp;&amp;amp; key.indexOf(&amp;quot;test&amp;quot;) == 0){&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; test(key,tests[key]);&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return function(){};&lt;br /&gt;}&lt;br /&gt;&amp;nbsp;&lt;br /&gt;function assertEquals(arg0,arg1){&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; equals(arg1,arg0);&lt;br /&gt;}&lt;br /&gt;function fail(msg){&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ok(false,msg);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb32986&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And update the testrunner HTML file we created to look like this:&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;html&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;html&quot; id=&quot;cb6831&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&amp;lt;DOCTYPE html&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;a href=&quot;http://december.com/html/4/element/html.html&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;html&amp;gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;a href=&quot;http://december.com/html/4/element/head.html&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;a href=&quot;http://december.com/html/4/element/script.html&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;script&lt;/span&gt;&lt;/a&gt; &lt;span style=&quot;color: #000066;&quot;&gt;src&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;http://code.jquery.com/jquery-1.6.4.min.js&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;type&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;a href=&quot;http://december.com/html/4/element/script.html&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;script&lt;/span&gt;&lt;/a&gt; &lt;span style=&quot;color: #000066;&quot;&gt;src&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;qunit.js&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;type&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;a href=&quot;http://december.com/html/4/element/script.html&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;script&lt;/span&gt;&lt;/a&gt; &lt;span style=&quot;color: #000066;&quot;&gt;src&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;jsTestDriverInQunit.js&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;type&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;a href=&quot;http://december.com/html/4/element/link.html&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;link&lt;/span&gt;&lt;/a&gt; &lt;span style=&quot;color: #000066;&quot;&gt;rel&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;stylesheet&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;media&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;all&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;href&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;qunit.css&amp;quot;&lt;/span&gt; /&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;a href=&quot;http://december.com/html/4/element/script.html&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;script&lt;/span&gt;&lt;/a&gt; &lt;span style=&quot;color: #000066;&quot;&gt;src&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;src/mystuff.js&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;type&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;a href=&quot;http://december.com/html/4/element/script.html&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;script&lt;/span&gt;&lt;/a&gt; &lt;span style=&quot;color: #000066;&quot;&gt;src&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;src-test/mystuff.js&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;type&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;a href=&quot;http://december.com/html/4/element/body.html&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;a href=&quot;http://december.com/html/4/element/h1.html&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;h1&lt;/span&gt;&lt;/a&gt; &lt;span style=&quot;color: #000066;&quot;&gt;id&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;qunit-header&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;MyStuff&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;a href=&quot;http://december.com/html/4/element/h2.html&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;h2&lt;/span&gt;&lt;/a&gt; &lt;span style=&quot;color: #000066;&quot;&gt;id&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;qunit-banner&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;a href=&quot;http://december.com/html/4/element/h2.html&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;h2&lt;/span&gt;&lt;/a&gt; &lt;span style=&quot;color: #000066;&quot;&gt;id&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;qunit-userAgent&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;a href=&quot;http://december.com/html/4/element/ol.html&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;ol&lt;/span&gt;&lt;/a&gt; &lt;span style=&quot;color: #000066;&quot;&gt;id&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;qunit-tests&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;/ol&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb21685&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And now whether we run the jsTestDriver client/server or open the Qunit file, we will be running the same exact set of tests.&lt;/p&gt;

&lt;p&gt;There is also a &lt;a href=&quot;http://code.google.com/p/js-test-driver/wiki/QUnitAdapter&quot; title=&quot;Go to the QUnitAdapter project&quot;&gt;project&lt;/a&gt; that translates Qunit tests into tests that can be run with jsTestDriver.&lt;/p&gt;

&lt;p&gt;All of the source code for this post (as well as the content of a couple programming katas) can be found in my &lt;a href=&quot;https://bitbucket.org/tarwn/katas.javascript/src&quot; title=&quot;Go to the source for the post&quot;&gt;javascript repository&lt;/a&gt; on BitBucket. The folder structure is slightly different to cut down on duplication of resources.&lt;/p&gt;&lt;div class=&quot;item_footer&quot;&gt;&lt;p&gt;&lt;small&gt;&lt;a href=&quot;http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/Javascript/getting-started-with-javascript-unit&quot;&gt;Original post&lt;/a&gt; blogged on &lt;a href=&quot;http://lessthandot.com/&quot;&gt;LessThanDot&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;</description>
			<content:encoded><![CDATA[<p>Recently I decided to start doing JavaScript <a href="http://blogs.lessthandot.com/index.php/ITProfessionals/ProfessionalDevelopment/using-code-katas-to-improve" title="Read Post: Using Code Katas to Improve Programming Skills">code katas.</a> I've been using JavaScript for around ten years, but there there are still a lot of aspects I don't know well or that I could use more practice in. Case in point, I had never used a unit testing framework with javascript. Having never unit tested JavaScript before, I used a scientific tool to carefully select from amongst the numerous unit testing packages available.</p>

<p>I typed "javascript unit testing" into Google and started reading.</p>

<h2>jsTestDriver</h2>
<p><a href="http://code.google.com/p/js-test-driver/wiki/GettingStarted" title="Visit the jsTestDriver wiki">jsTestDriver</a> was listed on a wide number of sites as one of the top JavaScript unit testing tools and fit an initial requirement I set myself of running outside of a browser. jsTestDriver runs as a client-server pair, the client sending tests to the server, which then runs them on a captured browser. The advantage of this method is it can easily be integrated with a code editor or as part of an automated build.</p>

<p>The jsTestDriver site includes plugins for Eclipse, Maven, and IntelliJ. I also found an article on <a href="http://slmoloch.blogspot.com/2009/08/how-to-run-jstestdriver-with-visual_02.html" title="Read the Visual Studio post">using it with Visual Studio</a> and it was fairly easy setting it up as a user tool in EditPlus.</p>

<div style="text-align: center; margin: 1em; color: #666666; font-size: 80%">
<img src="http://tiernok.com/LTDBlog/jsunittest/editplus.png" alt="Screenshot of EditPlus" /><br /><br />
Screenshot of EditPlus Settings
</div>

<p>Though I'm not using the feature, jsTestDriver provides a flag to specify an output file for the results, enabling us to use it as part of a continuous build.</p>

<h3>The Setup</h3>
<p>Setting up jsTestDriver on my system was fairly straightforward.</p>

<ol>
<li>Download Java - Visit <a href="http://java.com" title="Java website">java.com</a> and download the appropriate installer, run the installer, remember to go into the control panel and fiddle with Java's update settings</li>
<li>Download the jar file - Visit the <a href="http://code.google.com/p/js-test-driver/downloads/list" title="View the jsTestDriver downloads">project on Google</a> and download a copy of the jar file (I used the self-contained version)</li>
<li>Create folders - Create a top level folder and two sub folders (for instance, src and src-test). Put the jar in the top level folder</li>
<li>Create a conf file - I used the one in the <a href="http://code.google.com/p/js-test-driver/wiki/GettingStarted#Writing_configuration_file" title="Visit the Getting Started guide">Getting Started Guide</a> as a starting point</li>
</ol>

<p>At this point we should be able to fire up the server for the first time and verify everything is ready to go. I created a .cmd file on my system for the server so I could easily start it:</p>
<div class="codebox"><div class="codeheader"><span>text</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb99930'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb99930','cb9792'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="text" id="cb99930" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">&quot;C:\Program Files (x86)\Java\jre6\bin\java&quot; -jar JsTestDriver-1.3.2.jar --port 4224 --browser &quot;C:\Documents and Settings\Tarwn\Local Settings\Application Data\Google\Chrome\Application\chrome.exe&quot;</li></ol></div><div id="cb9792" style="display: none; color: red;"></div></div></div>
<p>What this does is starts the jsTestDriver jar on port 4224 and also automatically starts up an instance of chrome that will be captured by the server to run tests. I was initially using Firefox but jsTestDriver can't intercept the console log the way it can with Chrome, so I wasn't getting very good output for failed or errored tests.</p>

<p>Next I created a .cmd file to run all the tests in my folders:</p>
<div class="codebox"><div class="codeheader"><span>text</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb16128'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb16128','cb20923'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="text" id="cb16128" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">&quot;C:\Program Files (x86)\Java\jre6\bin\java&quot; -jar JsTestDriver-1.3.2.jar --tests all</li><li style="" class="li2">pause</li></ol></div><div id="cb20923" style="display: none; color: red;"></div></div></div>
<p>This tells jsTestDriver to run all available tests (based on the settings in the conf) using a jsTestDriver server on port 4224. I ended up not using this cmd file very frequently, as it was much handier to be able to run them from a key command inside my editor.</p>

<h3>Writing Tests</h3>
<p>Once we have gotten this far, we can start writing some simple tests.</p>

<p>In each directory (src and src-test), create a file named "mystuff.js".</p>

<p><b>src/mystuff.js</b></p>
<div class="codebox"><div class="codeheader"><span>javascript</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb54179'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb54179','cb93571'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="javascript" id="cb54179" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">myAwesomeApp = {};</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">myAwesomeApp.MyAwesomeClass = function(){};</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">myAwesomeApp.MyAwesomeClass.prototype.add = function(num0, num1){</li><li style="" class="li2">&nbsp; &nbsp; return num0 + num1;</li><li style="" class="li1">};</li></ol></div><div id="cb93571" style="display: none; color: red;"></div></div></div>

<p><b>src-test/mystuff.js</b></p>
<div class="codebox"><div class="codeheader"><span>javascript</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb23732'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb23732','cb27284'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="javascript" id="cb23732" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">TestCase(&quot;Sample Test Case&quot;,{</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; &quot;test Number plus Zero Equals Number&quot;: function(){</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; var adder = new myAwesomeApp.MyAwesomeClass();</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; assertEquals(5, adder.add(5,0));</li><li style="" class="li2">&nbsp; &nbsp; },</li><li style="" class="li1">&nbsp; &nbsp; &quot;test Number plus Number Equals Sum&quot;: function(){</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; var adder = new myAwesomeApp.MyAwesomeClass();</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; assertEquals(8, adder.add(5,3));</li><li style="" class="li2">&nbsp; &nbsp; },</li><li style="" class="li1">&nbsp; &nbsp; &quot;test Zero plus Number Equals Number&quot;: function(){</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; var adder = new myAwesomeApp.MyAwesomeClass();</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; assertEquals(5, adder.add(0,5));</li><li style="" class="li2">&nbsp; &nbsp; },</li><li style="" class="li1">&nbsp; &nbsp; &quot;test Number plus Negative of Number Equals Zero&quot;: function(){</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; var adder = new myAwesomeApp.MyAwesomeClass();</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; assertEquals(0, adder.add(5,-5));</li><li style="" class="li2">&nbsp; &nbsp; },</li><li style="" class="li1">&nbsp; &nbsp; &quot;test Fails miserably&quot;: function(){</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; fail(&quot;miserably&quot;);</li><li style="" class="li1">&nbsp; &nbsp; }</li><li style="" class="li2">});</li></ol></div><div id="cb27284" style="display: none; color: red;"></div></div></div>

<p>JavaScript provides a number of different methods to define objects with functions, in the source class I used the prototype method and in the tests file I used an object literal. For jsTestDriver, the important part is that the tests in the object we pass to TestCase begin with the word test, and the object literal method seemed like a friendlier layout for a test file.</p>

<h3>Running Tests</h3>
<p>Once we have the two files in place, start the server by issuing the following command (or creating the cmd file like me):</p>
<div class="codebox"><div class="codeheader"><span>text</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb62152'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb62152','cb3339'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="text" id="cb62152" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">&quot;C:\Program Files (x86)\Java\jre6\bin\java&quot; -jar ../JsTestDriver-1.3.2.jar --port 4224 --browser &quot;C:\Documents and Settings\Tarwn\Local Settings\Application Data\Google\Chrome\Application\chrome.exe&quot;</li></ol></div><div id="cb3339" style="display: none; color: red;"></div></div></div>
<p>You will need to update the browser and java paths to reflect your own.</p>

<p>Once the browser has started and it has been captured by the server for testing, it will look like this:</p>

<div style="text-align: center; margin: 1em; color: #666666; font-size: 80%">
<img src="http://tiernok.com/LTDBlog/jsunittest/jsTestDriver.png" alt="Chrome captured by jsTestDriver Server" /><br /><br />
Chrome captured by jsTestDriver Server
</div>

<p>Now we can run our tests by issuing the following command:</p>
<div class="codebox"><div class="codeheader"><span>text</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb77058'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb77058','cb33603'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="text" id="cb77058" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">&quot;C:\Program Files (x86)\Java\jre6\bin\java&quot; -jar ../JsTestDriver-1.3.2.jar --tests all</li></ol></div><div id="cb33603" style="display: none; color: red;"></div></div></div>
<p>Again, you will need to change the java path to reflect your own (or remove it if you have added it to your PATH variable).</p>

<p>The results should look something like this:</p>
<pre style="margin: 1em; border: 1px solid #999999; padding: 1em;">
....F
Total 5 tests (Passed: 4; Fails: 1; Errors: 0) (0.00 ms)
  Chrome 13.0.782.220 Windows: Run 5 tests (Passed: 4; Fails: 1; Errors 0) (0.00 ms)
    Object Literal Test Case.test Fails miserably failed (0.00 ms): AssertError: miserably
      AssertError: miserably
          at Object.test Fails miserably (http://localhost:4224/test/src-test/mystuff.js:22:3)

Tests failed: Tests failed. See log for details.
</pre>

<p>The top reflects the tests that have run at a glance with .s for passing tests, Fs for failed, and E for errored. Afterwards we get a summary of the total counts and then a section for the one browser we ran with. jsTestDriver allows you to capture multiple browsers, so we could configure this to run our tests across chrome, firefox, and IE simultaneously.</p>

<p>jsTestDriver also supports "setup" and "teardown" functions to run before and after tests. </p>

<h2>Qunit</h2>
<p>Qunit is a browser-based solution that was built to unit test the jQuery framework. Qunit has fewer requirements to run, but because it runs directly in a browser it means we have to switch windows and refresh in order to get an updated test run.</p>

<h3>The Setup</h3>
<p>Because Qunit will run in our browser, there are relatively few requirements and unlike jsTestDriver, none of them are installations.</p>

<ol>
   <li>We already made our folders, so nothing to do here</li>
   <li>Download the necessary include files to the top level: <a href="http://code.jquery.com/qunit/qunit-git.js" title="Download qunit.js">qunit.js</a> and <a href="http://code.jquery.com/qunit/qunit-git.css" title="Download qunit.css">qunit.css</a> (I renamed mine without the -git)</li>
   <li>Create an empty html file in the top level</li>
</ol>

<p>The empty file will be our test runner. Update the file to look like this:</p>
<div class="codebox"><div class="codeheader"><span>html</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb5615'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb5615','cb77429'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="html" id="cb5615" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #009900;">&lt;DOCTYPE html&gt;</span></li><li style="" class="li2"><span style="color: #009900;"><a href="http://december.com/html/4/element/html.html"><span style="color: #000000; font-weight: bold;">&lt;html&gt;</span></a></span></li><li style="" class="li1"><span style="color: #009900;"><a href="http://december.com/html/4/element/head.html"><span style="color: #000000; font-weight: bold;">&lt;head&gt;</span></a></span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #009900;"><a href="http://december.com/html/4/element/script.html"><span style="color: #000000; font-weight: bold;">&lt;script</span></a> <span style="color: #000066;">src</span>=<span style="color: #ff0000;">&quot;http://code.jquery.com/jquery-1.6.4.min.js&quot;</span> <span style="color: #000066;">type</span>=<span style="color: #ff0000;">&quot;text/javascript&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/script&gt;</span></span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #009900;"><a href="http://december.com/html/4/element/script.html"><span style="color: #000000; font-weight: bold;">&lt;script</span></a> <span style="color: #000066;">src</span>=<span style="color: #ff0000;">&quot;qunit.js&quot;</span> <span style="color: #000066;">type</span>=<span style="color: #ff0000;">&quot;text/javascript&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/script&gt;</span></span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #009900;"><a href="http://december.com/html/4/element/link.html"><span style="color: #000000; font-weight: bold;">&lt;link</span></a> <span style="color: #000066;">rel</span>=<span style="color: #ff0000;">&quot;stylesheet&quot;</span> <span style="color: #000066;">media</span>=<span style="color: #ff0000;">&quot;all&quot;</span> <span style="color: #000066;">href</span>=<span style="color: #ff0000;">&quot;qunit.css&quot;</span> /<span style="color: #000000; font-weight: bold;">&gt;</span></span></li><li style="" class="li1">&nbsp;</li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #009900;"><a href="http://december.com/html/4/element/script.html"><span style="color: #000000; font-weight: bold;">&lt;script</span></a> <span style="color: #000066;">src</span>=<span style="color: #ff0000;">&quot;src/mystuff.js&quot;</span> <span style="color: #000066;">type</span>=<span style="color: #ff0000;">&quot;text/javascript&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/script&gt;</span></span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #009900;"><a href="http://december.com/html/4/element/script.html"><span style="color: #000000; font-weight: bold;">&lt;script</span></a> <span style="color: #000066;">src</span>=<span style="color: #ff0000;">&quot;src-test/mystuff_qunit.js&quot;</span> <span style="color: #000066;">type</span>=<span style="color: #ff0000;">&quot;text/javascript&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/script&gt;</span></span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/head&gt;</span></span></li><li style="" class="li2"><span style="color: #009900;"><a href="http://december.com/html/4/element/body.html"><span style="color: #000000; font-weight: bold;">&lt;body&gt;</span></a></span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #009900;"><a href="http://december.com/html/4/element/h1.html"><span style="color: #000000; font-weight: bold;">&lt;h1</span></a> <span style="color: #000066;">id</span>=<span style="color: #ff0000;">&quot;qunit-header&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>MyStuff<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/h1&gt;</span></span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #009900;"><a href="http://december.com/html/4/element/h2.html"><span style="color: #000000; font-weight: bold;">&lt;h2</span></a> <span style="color: #000066;">id</span>=<span style="color: #ff0000;">&quot;qunit-banner&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/h2&gt;</span></span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #009900;"><a href="http://december.com/html/4/element/h2.html"><span style="color: #000000; font-weight: bold;">&lt;h2</span></a> <span style="color: #000066;">id</span>=<span style="color: #ff0000;">&quot;qunit-userAgent&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/h2&gt;</span></span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #009900;"><a href="http://december.com/html/4/element/ol.html"><span style="color: #000000; font-weight: bold;">&lt;ol</span></a> <span style="color: #000066;">id</span>=<span style="color: #ff0000;">&quot;qunit-tests&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/ol&gt;</span></span></li><li style="" class="li1"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/body&gt;</span></span></li><li style="" class="li2"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/html&gt;</span></span></li></ol></div><div id="cb77429" style="display: none; color: red;"></div></div></div>

<p>As you can see, we are referencing a CDNed version of jQuery, the local qunit files we downloaded, our source file, and a test js file we haven't created yet. The remainder of the HTML will be used by Qunit to display the results.</p>

<h3>Writing Tests</h3>
<p>Writing test in Qunit is pretty straightforward. Since we already have the src/mystuff.js file from above, we can jump right in and create a qunit version of our test cases.</p>

<p><b>src-test/mystuff_qunit.js</b></p>
<div class="codebox"><div class="codeheader"><span>javascript</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb97331'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb97331','cb30203'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="javascript" id="cb97331" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">module(&quot;Sample Test Case&quot;);</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">test(&quot;Number plus Zero Equals Number&quot;, function(){</li><li style="" class="li2">&nbsp; &nbsp; var adder = new myAwesomeApp.MyAwesomeClass();</li><li style="" class="li1">&nbsp; &nbsp; equals( adder.add(5,0),5);</li><li style="" class="li2">});</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">test(&quot;Number plus Number Equals Sum&quot;, function(){</li><li style="" class="li1">&nbsp; &nbsp; var adder = new myAwesomeApp.MyAwesomeClass();</li><li style="" class="li2">&nbsp; &nbsp; equals(adder.add(5,3),8);</li><li style="" class="li1">});</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">test(&quot;Zero plus Number Equals Number&quot;, function(){</li><li style="" class="li2">&nbsp; &nbsp; var adder = new myAwesomeApp.MyAwesomeClass();</li><li style="" class="li1">&nbsp; &nbsp; equals(adder.add(0,5),5);</li><li style="" class="li2">});</li><li style="" class="li1">&nbsp;</li><li style="" class="li2">test(&quot;Number plus Negative of Number Equals Zero&quot;, function(){</li><li style="" class="li1">&nbsp; &nbsp; var adder = new myAwesomeApp.MyAwesomeClass();</li><li style="" class="li2">&nbsp; &nbsp; equals(adder.add(5,-5),0);</li><li style="" class="li1">});</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">test(&quot;Fails miserably&quot;, function(){</li><li style="" class="li2">&nbsp; &nbsp; ok(false,&quot;miserably&quot;);</li><li style="" class="li1">});</li></ol></div><div id="cb30203" style="display: none; color: red;"></div></div></div>

<p>Qunit's <i>equals</i> method has it's actual and expected arguments reversed from jsTestDriver, instead expecting them in this order: <i>Qunit.equals(actual, expected)</i>. I didn't originally notice this and had to update both the jsTestDriver test mapping script and the sample above (here and in bitbucket).</p>

<h3>Running Tests</h3>
<p>Opening the testrunner html file, we should now see it display a block for each test that we have defined above.</p>

<div style="text-align: center; margin: 1em; color: #666666; font-size: 80%">
<img src="http://tiernok.com/LTDBlog/jsunittest/qunit.png" alt="QUnit Results" /><br /><br />
QUnit Results
</div>

<p>Failed tests automatically display details. Any test can be toggled open/closed by clicking it's name, and a handy "rerun" button lets us re-run a single test.</p>

<h2>Combining Them</h2>
<p>By writing a small amount of glue script, I was able to re-use my jsTestDriver tests in Qunit. Since I am currently only using a small subset of assertions and using the object literal method, the glue script is limited to only exactly what I needed.</p>

<p>Add this file to the top level folder:<br />
<b>jsTestDriverInQunit.js</b></p>
<div class="codebox"><div class="codeheader"><span>javascript</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb80847'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb80847','cb99337'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="javascript" id="cb80847" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">/* bare minimum to run jsTestDriver tests as Qunit tests */</li><li style="" class="li2">function TestCase(name, tests){</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; if(tests != null)</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; module(name);</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; for(var key in tests){</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if(tests[key] instanceof Function &amp;&amp; key.indexOf(&quot;test&quot;) == 0){</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; test(key,tests[key]);</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; }</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; return function(){};</li><li style="" class="li1">}</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">function assertEquals(arg0,arg1){</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; equals(arg1,arg0);</li><li style="" class="li1">}</li><li style="" class="li2">function fail(msg){</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; ok(false,msg);</li><li style="" class="li2">}</li></ol></div><div id="cb99337" style="display: none; color: red;"></div></div></div>

<p>And update the testrunner HTML file we created to look like this:</p>
<div class="codebox"><div class="codeheader"><span>html</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb43279'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb43279','cb54279'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="html" id="cb43279" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #009900;">&lt;DOCTYPE html&gt;</span></li><li style="" class="li2"><span style="color: #009900;"><a href="http://december.com/html/4/element/html.html"><span style="color: #000000; font-weight: bold;">&lt;html&gt;</span></a></span></li><li style="" class="li1"><span style="color: #009900;"><a href="http://december.com/html/4/element/head.html"><span style="color: #000000; font-weight: bold;">&lt;head&gt;</span></a></span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #009900;"><a href="http://december.com/html/4/element/script.html"><span style="color: #000000; font-weight: bold;">&lt;script</span></a> <span style="color: #000066;">src</span>=<span style="color: #ff0000;">&quot;http://code.jquery.com/jquery-1.6.4.min.js&quot;</span> <span style="color: #000066;">type</span>=<span style="color: #ff0000;">&quot;text/javascript&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/script&gt;</span></span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #009900;"><a href="http://december.com/html/4/element/script.html"><span style="color: #000000; font-weight: bold;">&lt;script</span></a> <span style="color: #000066;">src</span>=<span style="color: #ff0000;">&quot;qunit.js&quot;</span> <span style="color: #000066;">type</span>=<span style="color: #ff0000;">&quot;text/javascript&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/script&gt;</span></span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #009900;"><a href="http://december.com/html/4/element/script.html"><span style="color: #000000; font-weight: bold;">&lt;script</span></a> <span style="color: #000066;">src</span>=<span style="color: #ff0000;">&quot;jsTestDriverInQunit.js&quot;</span> <span style="color: #000066;">type</span>=<span style="color: #ff0000;">&quot;text/javascript&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/script&gt;</span></span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #009900;"><a href="http://december.com/html/4/element/link.html"><span style="color: #000000; font-weight: bold;">&lt;link</span></a> <span style="color: #000066;">rel</span>=<span style="color: #ff0000;">&quot;stylesheet&quot;</span> <span style="color: #000066;">media</span>=<span style="color: #ff0000;">&quot;all&quot;</span> <span style="color: #000066;">href</span>=<span style="color: #ff0000;">&quot;qunit.css&quot;</span> /<span style="color: #000000; font-weight: bold;">&gt;</span></span></li><li style="" class="li2">&nbsp;</li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #009900;"><a href="http://december.com/html/4/element/script.html"><span style="color: #000000; font-weight: bold;">&lt;script</span></a> <span style="color: #000066;">src</span>=<span style="color: #ff0000;">&quot;src/mystuff.js&quot;</span> <span style="color: #000066;">type</span>=<span style="color: #ff0000;">&quot;text/javascript&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/script&gt;</span></span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #009900;"><a href="http://december.com/html/4/element/script.html"><span style="color: #000000; font-weight: bold;">&lt;script</span></a> <span style="color: #000066;">src</span>=<span style="color: #ff0000;">&quot;src-test/mystuff.js&quot;</span> <span style="color: #000066;">type</span>=<span style="color: #ff0000;">&quot;text/javascript&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/script&gt;</span></span></li><li style="" class="li1"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/head&gt;</span></span></li><li style="" class="li2"><span style="color: #009900;"><a href="http://december.com/html/4/element/body.html"><span style="color: #000000; font-weight: bold;">&lt;body&gt;</span></a></span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #009900;"><a href="http://december.com/html/4/element/h1.html"><span style="color: #000000; font-weight: bold;">&lt;h1</span></a> <span style="color: #000066;">id</span>=<span style="color: #ff0000;">&quot;qunit-header&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>MyStuff<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/h1&gt;</span></span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #009900;"><a href="http://december.com/html/4/element/h2.html"><span style="color: #000000; font-weight: bold;">&lt;h2</span></a> <span style="color: #000066;">id</span>=<span style="color: #ff0000;">&quot;qunit-banner&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/h2&gt;</span></span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #009900;"><a href="http://december.com/html/4/element/h2.html"><span style="color: #000000; font-weight: bold;">&lt;h2</span></a> <span style="color: #000066;">id</span>=<span style="color: #ff0000;">&quot;qunit-userAgent&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/h2&gt;</span></span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #009900;"><a href="http://december.com/html/4/element/ol.html"><span style="color: #000000; font-weight: bold;">&lt;ol</span></a> <span style="color: #000066;">id</span>=<span style="color: #ff0000;">&quot;qunit-tests&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/ol&gt;</span></span></li><li style="" class="li1"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/body&gt;</span></span></li><li style="" class="li2"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/html&gt;</span></span></li></ol></div><div id="cb54279" style="display: none; color: red;"></div></div></div>

<p>And now whether we run the jsTestDriver client/server or open the Qunit file, we will be running the same exact set of tests.</p>

<p>There is also a <a href="http://code.google.com/p/js-test-driver/wiki/QUnitAdapter" title="Go to the QUnitAdapter project">project</a> that translates Qunit tests into tests that can be run with jsTestDriver.</p>

<p>All of the source code for this post (as well as the content of a couple programming katas) can be found in my <a href="https://bitbucket.org/tarwn/katas.javascript/src" title="Go to the source for the post">javascript repository</a> on BitBucket. The folder structure is slightly different to cut down on duplication of resources.</p><div class="item_footer"><p><small><a href="http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/Javascript/getting-started-with-javascript-unit">Original post</a> blogged on <a href="http://lessthandot.com/">LessThanDot</a>.</small></p></div>]]></content:encoded>
								<comments>http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/Javascript/getting-started-with-javascript-unit#comments</comments>
			<wfw:commentRss>http://blogs.lessthandot.com/index.php/WebDev/?tempskin=_rss2&#38;disp=comments&#38;p=1411</wfw:commentRss>
		</item>
				<item>
			<title>'LocalSqlServer' Error Deploying WebSecurity in WebMatrix/Web Pages</title>
			<link>http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/localsqlserver-error-deploying-websecurity</link>
			<pubDate>Tue, 09 Aug 2011 10:52:00 +0000</pubDate>			<dc:creator>Eli Weinstock-Herman (tarwn)</dc:creator>
			<category domain="main">Server Programming</category>			<guid isPermaLink="false">1369@http://blogs.lessthandot.com/</guid>
						<description>&lt;p&gt;Several weeks ago I was working on a sample site in Web Matrix and ran into a brick wall when I attempted to implement WebSecurity against my website. It seemed no matter what I tried I was getting errors about my database, errors about a database I didn&#039;t know about, errors trying to deploy the config file at all. After hours of debugging and random attempts at web.config hackery, I finally tried the basic starter sites and found out that those, too, suffered from problems when you attempt to deploy them.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Configuration Error&lt;/strong&gt;&lt;br /&gt;
&lt;strong&gt;Description:&lt;/strong&gt; An error occurred during the processing of a configuration file required to service this request. Please review the specific error details below and modify your configuration file appropriately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parser Error Message:&lt;/strong&gt; The connection name &#039;LocalSqlServer&#039; was not found in the applications configuration or the connection string is empty.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Today I managed to solve it based on an &lt;a href=&quot;http://forum.winhost.com/archive/index.php/t-8697.html&quot;&gt;archived forum post&lt;/a&gt; I found through google. &lt;/p&gt;

&lt;p&gt;The solution is to add the following section to your web.config:&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;xml&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;xml&quot; id=&quot;cb45786&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;&amp;lt;!-- Added in an attempt to make simple security work --&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;connectionStrings&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;remove&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;name&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;LocalSqlServer&amp;quot;&lt;/span&gt; &lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;name&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;LocalSqlServer&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;connectionString&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;Data Source=.\App_Data\MyJunk.sdf&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;providerName&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;System.Data.SqlServerCe.4.0&amp;quot;&lt;/span&gt; &lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/connectionStrings&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;appSettings&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;key&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;enableSimpleMembership&amp;quot;&lt;/span&gt; &lt;span style=&quot;color: #000066;&quot;&gt;value&lt;/span&gt;=&lt;span style=&quot;color: #ff0000;&quot;&gt;&amp;quot;true&amp;quot;&lt;/span&gt; &lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #009900;&quot;&gt;&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;lt;/appSettings&lt;span style=&quot;font-weight: bold; color: black;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb54192&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Basically what is happening is that my web host has a machine.config with a SQL Connection string called &quot;LocalSqlServer&quot; (it&#039;s created by default by the .Net framework). For some reason when we use anything Membership related, it attempts to access the configured connection string for Membership, even though WebSecurity is provided it&#039;s own connection string on initialization.&lt;/p&gt;

&lt;p&gt;So the solution is to remove the settings that has been applied to our site from the host&#039;s machine.config and apply a new one. From what I can tell, it doesn&#039;t seem to matter if the new connection string is accurate, only that it exists.&lt;/p&gt;

&lt;p&gt;Yep, this means I don&#039;t actually have a SQLCE database named, MyJunk. I can tell you&#039;re disappointed. Also I don&#039;t know if ./ would work in this path, so please don&#039;t use this as a how-to guide on how to create a working SQLCE connection string.&lt;/p&gt;&lt;div class=&quot;item_footer&quot;&gt;&lt;p&gt;&lt;small&gt;&lt;a href=&quot;http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/localsqlserver-error-deploying-websecurity&quot;&gt;Original post&lt;/a&gt; blogged on &lt;a href=&quot;http://lessthandot.com/&quot;&gt;LessThanDot&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;</description>
			<content:encoded><![CDATA[<p>Several weeks ago I was working on a sample site in Web Matrix and ran into a brick wall when I attempted to implement WebSecurity against my website. It seemed no matter what I tried I was getting errors about my database, errors about a database I didn't know about, errors trying to deploy the config file at all. After hours of debugging and random attempts at web.config hackery, I finally tried the basic starter sites and found out that those, too, suffered from problems when you attempt to deploy them.</p>

<blockquote>
<p><strong>Configuration Error</strong><br />
<strong>Description:</strong> An error occurred during the processing of a configuration file required to service this request. Please review the specific error details below and modify your configuration file appropriately.</p>

<p><strong>Parser Error Message:</strong> The connection name 'LocalSqlServer' was not found in the applications configuration or the connection string is empty.</p>
</blockquote>

<p>Today I managed to solve it based on an <a href="http://forum.winhost.com/archive/index.php/t-8697.html">archived forum post</a> I found through google. </p>

<p>The solution is to add the following section to your web.config:</p>
<div class="codebox"><div class="codeheader"><span>xml</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb54545'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb54545','cb46611'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="xml" id="cb54545" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1"><span style="color: #009900;"><span style="color: #808080; font-style: italic;">&lt;!-- Added in an attempt to make simple security work --&gt;</span></span></li><li style="" class="li2"><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;connectionStrings<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;remove</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;LocalSqlServer&quot;</span> <span style="font-weight: bold; color: black;">/&gt;</span></span></li><li style="" class="li2">&nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;add</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;LocalSqlServer&quot;</span> <span style="color: #000066;">connectionString</span>=<span style="color: #ff0000;">&quot;Data Source=.\App_Data\MyJunk.sdf&quot;</span> <span style="color: #000066;">providerName</span>=<span style="color: #ff0000;">&quot;System.Data.SqlServerCe.4.0&quot;</span> <span style="font-weight: bold; color: black;">/&gt;</span></span></li><li style="" class="li1"><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/connectionStrings<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li2"><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;appSettings<span style="font-weight: bold; color: black;">&gt;</span></span></span></li><li style="" class="li1">&nbsp; &nbsp; <span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;add</span> <span style="color: #000066;">key</span>=<span style="color: #ff0000;">&quot;enableSimpleMembership&quot;</span> <span style="color: #000066;">value</span>=<span style="color: #ff0000;">&quot;true&quot;</span> <span style="font-weight: bold; color: black;">/&gt;</span></span></li><li style="" class="li2"><span style="color: #009900;"><span style="font-weight: bold; color: black;">&lt;/appSettings<span style="font-weight: bold; color: black;">&gt;</span></span></span></li></ol></div><div id="cb46611" style="display: none; color: red;"></div></div></div>

<p>Basically what is happening is that my web host has a machine.config with a SQL Connection string called "LocalSqlServer" (it's created by default by the .Net framework). For some reason when we use anything Membership related, it attempts to access the configured connection string for Membership, even though WebSecurity is provided it's own connection string on initialization.</p>

<p>So the solution is to remove the settings that has been applied to our site from the host's machine.config and apply a new one. From what I can tell, it doesn't seem to matter if the new connection string is accurate, only that it exists.</p>

<p>Yep, this means I don't actually have a SQLCE database named, MyJunk. I can tell you're disappointed. Also I don't know if ./ would work in this path, so please don't use this as a how-to guide on how to create a working SQLCE connection string.</p><div class="item_footer"><p><small><a href="http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/localsqlserver-error-deploying-websecurity">Original post</a> blogged on <a href="http://lessthandot.com/">LessThanDot</a>.</small></p></div>]]></content:encoded>
								<comments>http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/localsqlserver-error-deploying-websecurity#comments</comments>
			<wfw:commentRss>http://blogs.lessthandot.com/index.php/WebDev/?tempskin=_rss2&#38;disp=comments&#38;p=1369</wfw:commentRss>
		</item>
				<item>
			<title>The History of HTML Table Layouts</title>
			<link>http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/XHTMLCSS/history-of-html-table-layouts</link>
			<pubDate>Tue, 02 Aug 2011 10:01:00 +0000</pubDate>			<dc:creator>Eli Weinstock-Herman (tarwn)</dc:creator>
			<category domain="main">XHTML &amp; CSS</category>			<guid isPermaLink="false">1317@http://blogs.lessthandot.com/</guid>
						<description>&lt;div style=&quot;float: left; margin: .5em 1em .5em .5em;&quot;&gt;&lt;img src=&quot;http://tiernok.com/LTDBlog/rant.png&quot; alt=&quot;Ranting Guy&quot; style=&quot;height: 75px; &quot; /&gt;&lt;/div&gt;&lt;p&gt;The argument over table vs non-table layouts has been going on for years. I remember seeing online conversations as far back as ten years ago on the topic, and given my notoriously bad memory that should be taken as a minimum. Along the way we have dealt with partial CSS implementations, inconsistent rendering behavior, slow implementation of standards, competing proprietary implementations...it&#039;s been a long road.&lt;/p&gt;

&lt;p&gt;Table layouts are still in use and in some places they are still the default. They are easy to implement and the browser and standards wars of the late 90&#039;s and early 00&#039;s have left behind a lot of people who remember how impossible it sometimes felt to make a non-tabular layout. However, while there are still browser inconsistencies and delays in standards adoptions, sometimes I wonder if we have forgotten just how long it has been since we started down this non-tabular path.&lt;/p&gt;

&lt;h2&gt;Table-free Layout is &quot;New&quot;&lt;/h2&gt;
&lt;p&gt;Tables have existed as part of web standards since the &lt;a href=&quot;http://www.w3.org/TR/REC-html32&quot; title=&quot;HTML 3.2 specification at W3C&quot;&gt;HTML 3.2 standard&lt;/a&gt; (January 1997). The standard referenced an earlier RFC and was intended to be compliant with the table tags Netscape had already added to their browser, but this was their official addition to the HTML standard.&lt;/p&gt;

&lt;p&gt;The standard pointed out that tables could be used for tabular data or layout purposes, but cautioned that using them for layout would impact accessibility.&lt;/p&gt;

&lt;div style=&quot;margin: 0px 2em; font-style: italic; background-color: #eeeeee; padding: 4px;&quot;&gt;Browsers, 1996-1997: MS IE3/4, NCSA Mosaic 2.1/3, Netscape 4, Opera 2/3&lt;/div&gt;

&lt;p&gt;In &lt;a href=&quot;http://www.w3.org/TR/1998/REC-html40-19980424/cover.html&quot; title=&quot;HTML 4 specification at W3C&quot;&gt;HTML 4&lt;/a&gt; (April 1998), this warning was strengthened to &quot;Tables should not be used purely as a means to layout document content&quot;, and we were pointed to the addition of CSS1 to help accommodate this. It was noted, however, that using deprecated features was expected to continue for a little while in order to support older browsers (browser listed above).&lt;/p&gt;

&lt;p&gt;In &lt;a href=&quot;http://www.w3.org/TR/WD-html40/&quot; title=&quot;HTML 4.01 specification at W3C&quot;&gt;HTML 4.01&lt;/a&gt; (Dec 1999), this warning was strengthened to the following directive: &quot;Tables should not be used purely as a means to layout document content&quot;.&lt;/p&gt;

&lt;div style=&quot;margin: 0px 2em; font-style: italic; background-color: #eeeeee; padding: 4px;&quot;&gt;Browsers, 1998-1999: MS IE5, Netscape 4.5/4.6, Opera 3.5/3.6&lt;/div&gt;

&lt;p&gt;Tables have existed as part of the HTML standard since 1997, we&#039;ve been warned not to use them for layout, and CSS1 was added to the standard in 1998 to provide layout flexibility without having to resort to tables. While implementation of CSS1 was slow, the inconsistencies we saw between IE4, IE5, and NS 5.4 are a long time back.&lt;/p&gt;

&lt;p&gt;If you are interested in the CSS side of the history:&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;a href=&quot;http://www.w3.org/TR/CSS1/&quot; title=&quot;CSS1 Recommendation&quot;&gt;CSS1 Recommendation&lt;/a&gt;&lt;/dt&gt;&lt;dd&gt;was published December, 1996&lt;/dd&gt;
&lt;dt&gt;&lt;a href=&quot;http://www.w3.org/TR/2008/REC-CSS2-20080411/&quot; title=&quot;CSS2 Recommendation&quot;&gt;CSS2 Recommendation&lt;/a&gt;&lt;/dt&gt;&lt;dd&gt; was published May, 1998&lt;/dd&gt;
&lt;dt&gt;&lt;a href=&quot;http://www.w3.org/Style/CSS/specs#css21&quot; title=&quot;CSS2.1 Recommendation&quot;&gt;CSS2.1 Recommendation&lt;/a&gt;&lt;/dt&gt;&lt;dd&gt; began in 2002 and was published in 2011 &lt;/dd&gt;
&lt;dt&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Cascading_Style_Sheets#CSS_3&quot; title=&quot;CSS3 at Wikipedia&quot;&gt;CSS3 at Wikipedia&lt;/a&gt;&lt;/dt&gt;&lt;dd&gt; began in 1999 and is a collection of modular standards being published independantly&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;&lt;em&gt;Out of curiosity I pulled up my personal website from 2002 and checked the source. It was cross-browser compatible and the layout was table-free.&lt;/em&gt;&lt;/p&gt;
&lt;div style=&quot;margin: 0px 2em; font-style: italic; background-color: #eeeeee; padding: 4px;&quot;&gt;
Browsers, 2002: MS IE6, NS 7, Mozilla 1, Opera 5.1.4
&lt;/div&gt;
&lt;p&gt;Fast-forward:&lt;/p&gt;
&lt;div style=&quot;margin: 0px 2em; font-style: italic; background-color: #eeeeee; padding: 4px;&quot;&gt;Browsers, 2006: MS IE7, NS 8.1, Firefox 2, Safari 2&lt;/div&gt;
&lt;p&gt;Fast-forward:&lt;/p&gt;
&lt;div style=&quot;margin: 0px 2em; font-style: italic; background-color: #eeeeee; padding: 4px;&quot;&gt;Browsers, 2011: MS IE9, Firefox 4/5, Chrome 9/10/11/12, Safari 5&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h2&gt;CSS Takes Longer&lt;/h2&gt;
&lt;p&gt;There was a point in time when you could spend days trying to get the screen to layout properly. Oddly enough, with the slower internet connections available at the time, it was probably worth it to remove all the extra markup table layouts tend to incorporate.&lt;/p&gt;

&lt;p&gt;The amount of time it takes to produce a layout in CSS versus tables has gotten fairly close. With the consistency shown by today&#039;s browsers, the major gap in time is generally learning how to do it. That&#039;s not to say that all is perfect in the world of browserdom, but it&#039;s rare (unknown?) to be forced to use the table option given the number of available examples, prebuilt frameworks, and tweaks available from javascript packages.&lt;/p&gt;

&lt;p&gt;Building a house is hard. Can the house builder cut corners to get your house up faster? Sure. But at the end of the day, if the builder is professional they won&#039;t cut corners because those cut corners reduce the value of the house, reduce the satisfaction of the buyer, and increase the ongoing maintenance costs.&lt;/p&gt;

&lt;h2&gt;The Exceptions&lt;/h2&gt;
&lt;p&gt;There were exceptions and they got fewer every year. As IE 5.5 and NS 4.6 faded from view, this grew to very few exceptions. Add things like jQuery to the mix and I would be hard-pressed to find an example that required the negative baggage table-layout brings with it. Initially we created fixed width layouts, then along came fluid layouts, then elastic layouts. The latest entry in the game is &lt;a href=&quot;http://www.alistapart.com/articles/responsive-web-design/&quot;&gt;responsive layouts&lt;/a&gt;, where we not only change the overall size of the page to accommodate browser sizes, but also element sizes and flow.&lt;/p&gt;

&lt;p&gt;Resorting to table layout immediately means you aren&#039;t treating it as an exception. It&#039;s more likely you need to sit down with your manager or business and help them understand the ongoing costs involved with table layouts, some of the benefits available with newer techniques (ie, 2000 forward), and why it&#039;s important to start making the switch. Luckily there are &lt;a href=&quot;http://www.hotdesign.com/seybold/everything.html&quot; title=&quot;Why tables for layout is stupid: problems defined, solutions offered&quot;&gt;resources&lt;/a&gt; out there to help.&lt;/p&gt;

&lt;h2&gt;There&#039;s &quot;No Time&quot;&lt;/h2&gt;
&lt;p&gt;I understand deadlines, the pressure from the business to deliver, and the fact that many companies simply don&#039;t bother to grow their people. As we mentioned before, it can be difficult to convince our managers and business to understand why we need to go the, initially at least, slower route of non-tabular layout.&lt;/p&gt;

&lt;p&gt;But you need to go get started. Yesterday.&lt;/p&gt;

&lt;p&gt;If your company is hesitant to let you spend the time, point out the costs involved with supporting new browsers as they come out, the advantages of good searchability, the wave of mobile devices that are flooding the market...clean markup can help all of these and there&#039;s any number of sites out there that will provide longer, better articulated lists of costs and benefits (we&#039;ve had a lot of time to think about it). &lt;/p&gt;

&lt;p&gt;And don&#039;t forget to point out that since table layouts are contrary to the standard, what you are actually providing to your customer is a defective product.&lt;/p&gt;

&lt;h2&gt;Moving Forward&lt;/h2&gt;
&lt;p&gt;When we follow standards, the browser developers can spend more time on new and interesting advancements. When we violate standards, we force them to spend time making sense out of the random, invalid junk we&#039;ve sent to the browser.&lt;/p&gt;

&lt;p&gt;Poorly structured, non-standard HTML (like table layouts) takes more bandwidth, takes longer for browsers to render, is more fragile, is harder for experienced web developers to work with, basically forces a rewrite for mobile devices, wastes the time and efforts of browser developers ... it&#039;s bad code. &lt;/p&gt;

&lt;p&gt;It might be acceptable for a beginner to use tables for layout, by definition a beginner is just learning the ropes. But part of being a beginner is learning and growing past that stage. With non-tabular layouts being around for 13 years, it&#039;s long past the time we, as professionals, start requiring ourselves to know how to use them.&lt;/p&gt;&lt;div class=&quot;item_footer&quot;&gt;&lt;p&gt;&lt;small&gt;&lt;a href=&quot;http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/XHTMLCSS/history-of-html-table-layouts&quot;&gt;Original post&lt;/a&gt; blogged on &lt;a href=&quot;http://lessthandot.com/&quot;&gt;LessThanDot&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;</description>
			<content:encoded><![CDATA[<div style="float: left; margin: .5em 1em .5em .5em;"><img src="http://tiernok.com/LTDBlog/rant.png" alt="Ranting Guy" style="height: 75px; " /></div><p>The argument over table vs non-table layouts has been going on for years. I remember seeing online conversations as far back as ten years ago on the topic, and given my notoriously bad memory that should be taken as a minimum. Along the way we have dealt with partial CSS implementations, inconsistent rendering behavior, slow implementation of standards, competing proprietary implementations...it's been a long road.</p>

<p>Table layouts are still in use and in some places they are still the default. They are easy to implement and the browser and standards wars of the late 90's and early 00's have left behind a lot of people who remember how impossible it sometimes felt to make a non-tabular layout. However, while there are still browser inconsistencies and delays in standards adoptions, sometimes I wonder if we have forgotten just how long it has been since we started down this non-tabular path.</p>

<h2>Table-free Layout is "New"</h2>
<p>Tables have existed as part of web standards since the <a href="http://www.w3.org/TR/REC-html32" title="HTML 3.2 specification at W3C">HTML 3.2 standard</a> (January 1997). The standard referenced an earlier RFC and was intended to be compliant with the table tags Netscape had already added to their browser, but this was their official addition to the HTML standard.</p>

<p>The standard pointed out that tables could be used for tabular data or layout purposes, but cautioned that using them for layout would impact accessibility.</p>

<div style="margin: 0px 2em; font-style: italic; background-color: #eeeeee; padding: 4px;">Browsers, 1996-1997: MS IE3/4, NCSA Mosaic 2.1/3, Netscape 4, Opera 2/3</div>

<p>In <a href="http://www.w3.org/TR/1998/REC-html40-19980424/cover.html" title="HTML 4 specification at W3C">HTML 4</a> (April 1998), this warning was strengthened to "Tables should not be used purely as a means to layout document content", and we were pointed to the addition of CSS1 to help accommodate this. It was noted, however, that using deprecated features was expected to continue for a little while in order to support older browsers (browser listed above).</p>

<p>In <a href="http://www.w3.org/TR/WD-html40/" title="HTML 4.01 specification at W3C">HTML 4.01</a> (Dec 1999), this warning was strengthened to the following directive: "Tables should not be used purely as a means to layout document content".</p>

<div style="margin: 0px 2em; font-style: italic; background-color: #eeeeee; padding: 4px;">Browsers, 1998-1999: MS IE5, Netscape 4.5/4.6, Opera 3.5/3.6</div>

<p>Tables have existed as part of the HTML standard since 1997, we've been warned not to use them for layout, and CSS1 was added to the standard in 1998 to provide layout flexibility without having to resort to tables. While implementation of CSS1 was slow, the inconsistencies we saw between IE4, IE5, and NS 5.4 are a long time back.</p>

<p>If you are interested in the CSS side of the history:</p>
<dl>
<dt><a href="http://www.w3.org/TR/CSS1/" title="CSS1 Recommendation">CSS1 Recommendation</a></dt><dd>was published December, 1996</dd>
<dt><a href="http://www.w3.org/TR/2008/REC-CSS2-20080411/" title="CSS2 Recommendation">CSS2 Recommendation</a></dt><dd> was published May, 1998</dd>
<dt><a href="http://www.w3.org/Style/CSS/specs#css21" title="CSS2.1 Recommendation">CSS2.1 Recommendation</a></dt><dd> began in 2002 and was published in 2011 </dd>
<dt><a href="http://en.wikipedia.org/wiki/Cascading_Style_Sheets#CSS_3" title="CSS3 at Wikipedia">CSS3 at Wikipedia</a></dt><dd> began in 1999 and is a collection of modular standards being published independantly</dd>
</dl>

<p><em>Out of curiosity I pulled up my personal website from 2002 and checked the source. It was cross-browser compatible and the layout was table-free.</em></p>
<div style="margin: 0px 2em; font-style: italic; background-color: #eeeeee; padding: 4px;">
Browsers, 2002: MS IE6, NS 7, Mozilla 1, Opera 5.1.4
</div>
<p>Fast-forward:</p>
<div style="margin: 0px 2em; font-style: italic; background-color: #eeeeee; padding: 4px;">Browsers, 2006: MS IE7, NS 8.1, Firefox 2, Safari 2</div>
<p>Fast-forward:</p>
<div style="margin: 0px 2em; font-style: italic; background-color: #eeeeee; padding: 4px;">Browsers, 2011: MS IE9, Firefox 4/5, Chrome 9/10/11/12, Safari 5</div>
<p><br /></p>
<h2>CSS Takes Longer</h2>
<p>There was a point in time when you could spend days trying to get the screen to layout properly. Oddly enough, with the slower internet connections available at the time, it was probably worth it to remove all the extra markup table layouts tend to incorporate.</p>

<p>The amount of time it takes to produce a layout in CSS versus tables has gotten fairly close. With the consistency shown by today's browsers, the major gap in time is generally learning how to do it. That's not to say that all is perfect in the world of browserdom, but it's rare (unknown?) to be forced to use the table option given the number of available examples, prebuilt frameworks, and tweaks available from javascript packages.</p>

<p>Building a house is hard. Can the house builder cut corners to get your house up faster? Sure. But at the end of the day, if the builder is professional they won't cut corners because those cut corners reduce the value of the house, reduce the satisfaction of the buyer, and increase the ongoing maintenance costs.</p>

<h2>The Exceptions</h2>
<p>There were exceptions and they got fewer every year. As IE 5.5 and NS 4.6 faded from view, this grew to very few exceptions. Add things like jQuery to the mix and I would be hard-pressed to find an example that required the negative baggage table-layout brings with it. Initially we created fixed width layouts, then along came fluid layouts, then elastic layouts. The latest entry in the game is <a href="http://www.alistapart.com/articles/responsive-web-design/">responsive layouts</a>, where we not only change the overall size of the page to accommodate browser sizes, but also element sizes and flow.</p>

<p>Resorting to table layout immediately means you aren't treating it as an exception. It's more likely you need to sit down with your manager or business and help them understand the ongoing costs involved with table layouts, some of the benefits available with newer techniques (ie, 2000 forward), and why it's important to start making the switch. Luckily there are <a href="http://www.hotdesign.com/seybold/everything.html" title="Why tables for layout is stupid: problems defined, solutions offered">resources</a> out there to help.</p>

<h2>There's "No Time"</h2>
<p>I understand deadlines, the pressure from the business to deliver, and the fact that many companies simply don't bother to grow their people. As we mentioned before, it can be difficult to convince our managers and business to understand why we need to go the, initially at least, slower route of non-tabular layout.</p>

<p>But you need to go get started. Yesterday.</p>

<p>If your company is hesitant to let you spend the time, point out the costs involved with supporting new browsers as they come out, the advantages of good searchability, the wave of mobile devices that are flooding the market...clean markup can help all of these and there's any number of sites out there that will provide longer, better articulated lists of costs and benefits (we've had a lot of time to think about it). </p>

<p>And don't forget to point out that since table layouts are contrary to the standard, what you are actually providing to your customer is a defective product.</p>

<h2>Moving Forward</h2>
<p>When we follow standards, the browser developers can spend more time on new and interesting advancements. When we violate standards, we force them to spend time making sense out of the random, invalid junk we've sent to the browser.</p>

<p>Poorly structured, non-standard HTML (like table layouts) takes more bandwidth, takes longer for browsers to render, is more fragile, is harder for experienced web developers to work with, basically forces a rewrite for mobile devices, wastes the time and efforts of browser developers ... it's bad code. </p>

<p>It might be acceptable for a beginner to use tables for layout, by definition a beginner is just learning the ropes. But part of being a beginner is learning and growing past that stage. With non-tabular layouts being around for 13 years, it's long past the time we, as professionals, start requiring ourselves to know how to use them.</p><div class="item_footer"><p><small><a href="http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/XHTMLCSS/history-of-html-table-layouts">Original post</a> blogged on <a href="http://lessthandot.com/">LessThanDot</a>.</small></p></div>]]></content:encoded>
								<comments>http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/XHTMLCSS/history-of-html-table-layouts#comments</comments>
			<wfw:commentRss>http://blogs.lessthandot.com/index.php/WebDev/?tempskin=_rss2&#38;disp=comments&#38;p=1317</wfw:commentRss>
		</item>
				<item>
			<title>WebMatrix - Routing and Magic Pages</title>
			<link>http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/ASPNET/webmatrix-routing-and-magic-pages</link>
			<pubDate>Thu, 07 Jul 2011 09:42:00 +0000</pubDate>			<dc:creator>Eli Weinstock-Herman (tarwn)</dc:creator>
			<category domain="main">ASP.NET</category>			<guid isPermaLink="false">1327@http://blogs.lessthandot.com/</guid>
						<description>&lt;p&gt;So I&#039;ve been working on a practice site using WebMatrix. The basic premise was that I would create a site that had some similar functionality to Delicious to help me track the various articles, podcasts, books, and so on. This would also give me something practical to work on as I try to get a handle on this whole WebMatrix thing.&lt;/p&gt;

&lt;p&gt;So here&#039;s a couple tips, tricks, and the occasional slip in the mud I picked up along the way.&lt;/p&gt;

&lt;p&gt;Chrissie started us off with &lt;a href=&quot;http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/AJAX/trying-out-webmatrix-and-razor&quot; title=&quot;Chissie on Trying Out Webmatrix and Razor&quot;&gt;this post&lt;/a&gt;, so I&#039;m skipping right past the making of files and digging into the first layer of how things work (and/or how I broke them).&lt;/p&gt;

&lt;p&gt;&lt;i&gt;Note: I know in my &lt;a href=&quot;http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/the-many-functions-of-webmatrix&quot; title=&quot;Read the Webmatrix Overview post&quot;&gt;last post&lt;/a&gt; I said the application was WebMatrix and the programming framework was WebPages, but no one calls it WebPages (apparently that name is hard to search for or something), so for the remainder of this post I&#039;ll call it WebMatrix. Feel free to call me names later.&lt;/i&gt;&lt;/p&gt;

&lt;h2&gt;Magic Routing&lt;/h2&gt;
&lt;p&gt;The first problem I had when I deployed my site to a live host was getting the darn pages to work. No matter how I typed the paths, the cshtml pages wouldn&#039;t stop reporting a 404. So of course I started digging into the IIS settings. And fixed the meta settings. And fixed the extension. And fixed something else. And then found out I wasn&#039;t supposed to fix it and the real problem was the guy sitting behind my keyboard typing instead of reading the manual. Oops.&lt;/p&gt;

&lt;p&gt;When we build a razor site (or is it a WebPages site? WebMatrix site? argh...), it takes advantage of additions in the 4.0 Framework to provide routing automatically. Every cshtml file we create automatically becomes part of the route check when a request comes into IIS. This is similar to the ASP.Net MVC routing, but instead requires no up front work, running 100% on pixie dust.&lt;/p&gt;

&lt;p&gt;Say I ask for &lt;a href=&quot;http://MyAwesomeSite.com/blueberries/and/cream&quot;&gt;http://MyAwesomeSite.com/blueberries/and/cream&lt;/a&gt;. Note that we don&#039;t include the .cshtml extension. Behind the scenes, my webserver basically has this conversation with itself:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Do I have a file named /blueberries/and/cream.cshtml?&lt;br /&gt;
No.&lt;br /&gt;
How about /blueberries/and.cshtml?&lt;br /&gt;
Nope.&lt;br /&gt;
How about /blueberries.cshtml?&lt;br /&gt;
Nope.&lt;br /&gt;
Maybe there was a default file at the root level?&lt;br /&gt;
Ah, ok.&lt;br /&gt;
Are you busy tomorrow night...&lt;br /&gt;
Yep.&lt;br /&gt;
Oh...er, ok, well here&#039;s your file then.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The most specific file match wins, without the need for setting up routing ahead of time. Simply drop an appropriately named file in a folder and point an href at it. &lt;/p&gt;

&lt;p&gt;Any trailing portion of the URL is then stored in an array for easy access (which we will see later). What this allows us to do is create very easy, semantic URLs. &lt;/p&gt;

&lt;p&gt;Example: my application will show me the list of all activities when I go to &lt;a href=&quot;http://notmyrealurl.com/Activities&quot;&gt;http://notmyrealurl.com/Activities&lt;/a&gt; but will filter the list for the &quot;razor&quot; tag when I go to &lt;a href=&quot;http://notmyrealurl.com/Activities/razor&quot;&gt;http://notmyrealurl.com/Activities/razor&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The exception is files that begin with underscores. Underscored files are not accessible, as they are used to protect our Magic Files in the next section.&lt;/p&gt;

&lt;h2&gt;Magic Pages&lt;/h2&gt;
&lt;p&gt;Ok, so this part I&#039;m less enamored of, but there are some good parts. There are two classes of special files in WebMatrix, layout files and lifecycle pages. &lt;/p&gt;

&lt;h3&gt;Layout Files&lt;/h3&gt;
&lt;p&gt;Layout files allow us to define a common layout that we want to apply to our website, basically a template or master file. Inside the layout file we can define where we want the main body to be rendered (the file that was requested), as well as additional required or optional sections the original page needs to provide. A minimal layout file would look something like this:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;cshtml&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;cshtml&quot; id=&quot;cb52915&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;&amp;lt;html&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;head&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;lt;title&amp;gt; @PageData[&amp;quot;Title&amp;quot;] &amp;lt;/title&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;/head&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;body&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; @RenderPage(&amp;quot;~/Shared/_Header.cshtml&amp;quot;)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;lt;div id=&amp;quot;sidepane&amp;quot;&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; @RenderSection(&amp;quot;SidePane&amp;quot;, required: false)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;lt;div id=&amp;quot;main&amp;quot;&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; @RenderBody()&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; @RenderPage(&amp;quot;~/Shared/_Footer.cshtml&amp;quot;)&lt;br /&gt;&amp;lt;/body&amp;gt;&lt;br /&gt;&amp;lt;/html&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb55785&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A basic page that includes a header file, an optional section named &quot;SidePane&quot;, and body of the original page, and finally an included footer file. In this sample I am keeping my _Header, _Footer, and _MainLayout files in a subfolder called Shared. To use this layout, I could make a sample page like this:&lt;/p&gt;

&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;cshtml&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;cshtml&quot; id=&quot;cb27487&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;@{&lt;br /&gt;Layout = &amp;quot;~/Shared/_MainLayout.cshtml&amp;quot;;&lt;br /&gt;PageData[&amp;quot;Title&amp;quot;] = &amp;quot;My Awesome Hello World&amp;quot;;&lt;br /&gt;}&lt;br /&gt;&amp;lt;b&amp;gt;&amp;lt;blink&amp;gt;This stuff is in my main body, Hello!&amp;lt;/blink&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb18003&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And if I wanted to both show off the presence of any extra URL data you put in the URL as well as the optional sidepane?&lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;cshtml&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;cshtml&quot; id=&quot;cb66970&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;@{&lt;br /&gt;Layout = &amp;quot;~/Shared/_MainLayout.cshtml&amp;quot;;&lt;br /&gt;PageData[&amp;quot;Title&amp;quot;] = &amp;quot;My Awesome Hello World&amp;quot;;&lt;br /&gt;}&lt;br /&gt;&amp;lt;b&amp;gt;&amp;lt;blink&amp;gt;This stuff is in my main body, Hello!&amp;lt;/blink&amp;gt;&amp;lt;/b&amp;gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;@section SidePane{&lt;br /&gt;&amp;nbsp; &amp;nbsp; @if(UrlData.Count &amp;gt; 0){&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;text&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;marquee&amp;gt;Bam!&amp;lt;/marquee&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; First one: @UrlData[0]&amp;lt;br/&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; All of them: @(String.Join(&amp;quot;,&amp;quot;,UrlData))&amp;lt;br/&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/text&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb94571&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Yeah, I just used a blink and a marquee, that&#039;s how exciting this part is. You&#039;ll notice I also put a value in the PageData dictionary at the top of the page, which was then regurgitated by the layout and used as the title. When files are being rendered or included, they receive a copy of the PageData and can access the values we squirreled away. Handy.&lt;/p&gt;

&lt;p&gt;There&#039;s also a Page dynamic property in WebBasePage that we can assign stuff to. So far that hasn&#039;t blown up in my face.&lt;/p&gt;

&lt;p&gt;If I think back to the classic ASP days I can remember a time when we desperately wanted this type of functionality. A clean, straightforward way to define some sitewide layout templates separately from our actual page, and here it is. It&#039;s like ASP 4.0.&lt;/p&gt;

&lt;p&gt;And I only just now noticed that interesting conjunction in ASP and ASP.Net numbering schemes, hmm.&lt;/p&gt;

&lt;h3&gt;Lifecycle Pages&lt;/h3&gt;
&lt;p&gt;Then there are the lifecycle pages. Where layout files give us a handy way to do templating, these provide us some basic global capabilities for the site. There are three main files that we are concerned with:&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;~/web.config&lt;/dt&gt;&lt;dd&gt;This holds configurations for the site, like any ASP.Net site&lt;/dd&gt;
&lt;dt&gt;~/_AppStart.cshtml&lt;/dt&gt;&lt;dd&gt;Contains logic to run the first time any page is requested from the site&lt;/dd&gt;
&lt;dt&gt;~/_PageStart.cshtml&lt;/dt&gt;&lt;dd&gt;Contains logic to run when a page in the current or child directory is requested&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;AppStart is handy for setting values or creating objects that will have global application scope. For example, when using the SimpleSecurity helpers, you will want to put an initialization call here to initialize the authentication system. &lt;/p&gt;

&lt;p&gt;&lt;i&gt;And if you are me you will comment them out when you find out your webhosts machine.config fubars settings relevant to ASP.Net membership.&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;PageStart is trickier in a couple ways. First, PageStart is a bit of a misnomer. It does execute at the start of the page, but it can also be convinced  to encapsulate the execution of the requested page, running both before and after the main page is rendered. &lt;/p&gt;
&lt;div class=&quot;codebox&quot;&gt;&lt;div class=&quot;codeheader&quot;&gt;Code: &lt;span&gt;cshtml&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;codeholder&quot;&gt;&lt;div class=&quot;cshtml&quot; id=&quot;cb10774&quot; style=&quot;display: block; color: rgb(0, 0, 0);&quot;&gt;@{&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;lt;b&amp;gt;I&#039;m Before&amp;lt;/b&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; RunPage();&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;lt;text&amp;gt;I&#039;m after&amp;lt;/text&amp;gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div id=&quot;cb55281&quot; style=&quot;display: none; color: red;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you specifically include the RunPage() line, then your page will be run in between the code above and below it. If you don&#039;t include this directive then the entire PageStart file will be run before calling the requested page.&lt;/p&gt;

&lt;p&gt;The next trick is that _PageStart runs in a nested sequence from the root level to the level of the file that has been requested. Basically the engine traverses each directory from the root to the folder your requested file is in and, if a _PageStart file is present, executes it. It&#039;s similar to those nesting dolls (or recursion, oohh).&lt;/p&gt;

&lt;p&gt;But the magic is not done yet, and this was one of those pits of mud I fell into.&lt;/p&gt;

&lt;p&gt;Even though you can wrap the execution of the page inside a _PageStart, the execution of the page and PageStart finish before the Layout is applied and executed.&lt;/p&gt;

&lt;p&gt;Lets try that again (I lost 2 hours of my life to this feature).&lt;/p&gt;

&lt;p&gt;Say we have a _PageStart that wraps around the RunPage call, a Main page, and a referenced _LayoutMain file and we request &lt;a href=&quot;http://myevilpitofmud.com/Main&quot;&gt;http://myevilpitofmud.com/Main&lt;/a&gt;. The execution would look like:&lt;/p&gt;
&lt;ol&gt;
   &lt;li&gt;Top part of _PageStart&lt;/li&gt;
   &lt;li&gt;Main file&lt;/li&gt;
   &lt;li&gt;Bottom part of _PageStart&lt;/li&gt;
   &lt;li&gt;Layout file&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Yeah.&lt;/p&gt;

&lt;h2&gt;But what else did you do?&lt;/h2&gt;
&lt;p&gt;There&#039;s more. I&#039;ve spent 17 hours on my sample site so far, which includes implementing a SQL Compact database, implementing and removing WebSecurity authentication, and a score of other things. I&#039;ve found out the hard way that getting fancy can bite you and that it is actually possible to install a 10 inch tailpipe on the WebMatrix hatchback (which I just realized could refer to two different things I&#039;ve done, hmmm, tailpipe and a &lt;strike&gt;shopping cart handle&lt;/strike&gt; spoiler then). &lt;/p&gt;

&lt;p&gt;Unfortunately because I had issues with the authentication I can&#039;t post a live link yet, the next post will have to cover all the fun that has gone into trying to get a certified host for WebMatrix to run correctly (hint, not all &lt;a href=&quot;http://www.microsoft.com/web/hosting/home&quot; title=&quot;See hosting options at Microsoft&quot;&gt;&#039;SpotLight&#039; hosts&lt;/a&gt; can run the included, basic example sites).&lt;/p&gt;&lt;div class=&quot;item_footer&quot;&gt;&lt;p&gt;&lt;small&gt;&lt;a href=&quot;http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/ASPNET/webmatrix-routing-and-magic-pages&quot;&gt;Original post&lt;/a&gt; blogged on &lt;a href=&quot;http://lessthandot.com/&quot;&gt;LessThanDot&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;</description>
			<content:encoded><![CDATA[<p>So I've been working on a practice site using WebMatrix. The basic premise was that I would create a site that had some similar functionality to Delicious to help me track the various articles, podcasts, books, and so on. This would also give me something practical to work on as I try to get a handle on this whole WebMatrix thing.</p>

<p>So here's a couple tips, tricks, and the occasional slip in the mud I picked up along the way.</p>

<p>Chrissie started us off with <a href="http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/AJAX/trying-out-webmatrix-and-razor" title="Chissie on Trying Out Webmatrix and Razor">this post</a>, so I'm skipping right past the making of files and digging into the first layer of how things work (and/or how I broke them).</p>

<p><i>Note: I know in my <a href="http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/the-many-functions-of-webmatrix" title="Read the Webmatrix Overview post">last post</a> I said the application was WebMatrix and the programming framework was WebPages, but no one calls it WebPages (apparently that name is hard to search for or something), so for the remainder of this post I'll call it WebMatrix. Feel free to call me names later.</i></p>

<h2>Magic Routing</h2>
<p>The first problem I had when I deployed my site to a live host was getting the darn pages to work. No matter how I typed the paths, the cshtml pages wouldn't stop reporting a 404. So of course I started digging into the IIS settings. And fixed the meta settings. And fixed the extension. And fixed something else. And then found out I wasn't supposed to fix it and the real problem was the guy sitting behind my keyboard typing instead of reading the manual. Oops.</p>

<p>When we build a razor site (or is it a WebPages site? WebMatrix site? argh...), it takes advantage of additions in the 4.0 Framework to provide routing automatically. Every cshtml file we create automatically becomes part of the route check when a request comes into IIS. This is similar to the ASP.Net MVC routing, but instead requires no up front work, running 100% on pixie dust.</p>

<p>Say I ask for <a href="http://MyAwesomeSite.com/blueberries/and/cream">http://MyAwesomeSite.com/blueberries/and/cream</a>. Note that we don't include the .cshtml extension. Behind the scenes, my webserver basically has this conversation with itself:</p>
<blockquote>
<p>Do I have a file named /blueberries/and/cream.cshtml?<br />
No.<br />
How about /blueberries/and.cshtml?<br />
Nope.<br />
How about /blueberries.cshtml?<br />
Nope.<br />
Maybe there was a default file at the root level?<br />
Ah, ok.<br />
Are you busy tomorrow night...<br />
Yep.<br />
Oh...er, ok, well here's your file then.</p>
</blockquote>

<p>The most specific file match wins, without the need for setting up routing ahead of time. Simply drop an appropriately named file in a folder and point an href at it. </p>

<p>Any trailing portion of the URL is then stored in an array for easy access (which we will see later). What this allows us to do is create very easy, semantic URLs. </p>

<p>Example: my application will show me the list of all activities when I go to <a href="http://notmyrealurl.com/Activities">http://notmyrealurl.com/Activities</a> but will filter the list for the "razor" tag when I go to <a href="http://notmyrealurl.com/Activities/razor">http://notmyrealurl.com/Activities/razor</a>.</p>

<p>The exception is files that begin with underscores. Underscored files are not accessible, as they are used to protect our Magic Files in the next section.</p>

<h2>Magic Pages</h2>
<p>Ok, so this part I'm less enamored of, but there are some good parts. There are two classes of special files in WebMatrix, layout files and lifecycle pages. </p>

<h3>Layout Files</h3>
<p>Layout files allow us to define a common layout that we want to apply to our website, basically a template or master file. Inside the layout file we can define where we want the main body to be rendered (the file that was requested), as well as additional required or optional sections the original page needs to provide. A minimal layout file would look something like this:</p>

<div class="codebox"><div class="codeheader"><span>cshtml</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb45337'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb45337','cb60631'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="cshtml" id="cb45337" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">&lt;!DOCTYPE html&gt;</li><li style="" class="li2">&lt;html&gt;</li><li style="" class="li1">&nbsp; &lt;head&gt;</li><li style="" class="li2">&nbsp; &nbsp; &lt;title&gt; @PageData[&quot;Title&quot;] &lt;/title&gt;</li><li style="" class="li1">&nbsp; &lt;/head&gt;</li><li style="" class="li2">&nbsp; &lt;body&gt;</li><li style="" class="li1">&nbsp; &nbsp; @RenderPage(&quot;~/Shared/_Header.cshtml&quot;)</li><li style="" class="li2">&nbsp; &nbsp; &lt;div id=&quot;sidepane&quot;&gt;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; @RenderSection(&quot;SidePane&quot;, required: false)</li><li style="" class="li2">&nbsp; &nbsp; &lt;/div&gt;</li><li style="" class="li1">&nbsp; &nbsp; &lt;div id=&quot;main&quot;&gt;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; @RenderBody()</li><li style="" class="li1">&nbsp; &nbsp; &lt;/div&gt;</li><li style="" class="li2">&nbsp; &nbsp; &lt;/div&gt;</li><li style="" class="li1">&nbsp; &nbsp; @RenderPage(&quot;~/Shared/_Footer.cshtml&quot;)</li><li style="" class="li2">&lt;/body&gt;</li><li style="" class="li1">&lt;/html&gt;</li></ol></div><div id="cb60631" style="display: none; color: red;"></div></div></div>

<p>A basic page that includes a header file, an optional section named "SidePane", and body of the original page, and finally an included footer file. In this sample I am keeping my _Header, _Footer, and _MainLayout files in a subfolder called Shared. To use this layout, I could make a sample page like this:</p>

<div class="codebox"><div class="codeheader"><span>cshtml</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb45952'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb45952','cb29388'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="cshtml" id="cb45952" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">@{</li><li style="" class="li2">Layout = &quot;~/Shared/_MainLayout.cshtml&quot;;</li><li style="" class="li1">PageData[&quot;Title&quot;] = &quot;My Awesome Hello World&quot;;</li><li style="" class="li2">}</li><li style="" class="li1">&lt;b&gt;&lt;blink&gt;This stuff is in my main body, Hello!&lt;/blink&gt;&lt;/b&gt;</li></ol></div><div id="cb29388" style="display: none; color: red;"></div></div></div>

<p>And if I wanted to both show off the presence of any extra URL data you put in the URL as well as the optional sidepane?</p>
<div class="codebox"><div class="codeheader"><span>cshtml</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb37290'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb37290','cb76701'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="cshtml" id="cb37290" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">@{</li><li style="" class="li2">Layout = &quot;~/Shared/_MainLayout.cshtml&quot;;</li><li style="" class="li1">PageData[&quot;Title&quot;] = &quot;My Awesome Hello World&quot;;</li><li style="" class="li2">}</li><li style="" class="li1">&lt;b&gt;&lt;blink&gt;This stuff is in my main body, Hello!&lt;/blink&gt;&lt;/b&gt;</li><li style="" class="li2">&nbsp;</li><li style="" class="li1">@section SidePane{</li><li style="" class="li2">&nbsp; &nbsp; @if(UrlData.Count &gt; 0){</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &lt;text&gt;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; &lt;marquee&gt;Bam!&lt;/marquee&gt;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; First one: @UrlData[0]&lt;br/&gt;</li><li style="" class="li2">&nbsp; &nbsp; &nbsp; &nbsp; All of them: @(String.Join(&quot;,&quot;,UrlData))&lt;br/&gt;</li><li style="" class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &lt;/text&gt;</li><li style="" class="li2">&nbsp; &nbsp; }</li><li style="" class="li1">}</li></ol></div><div id="cb76701" style="display: none; color: red;"></div></div></div>

<p>Yeah, I just used a blink and a marquee, that's how exciting this part is. You'll notice I also put a value in the PageData dictionary at the top of the page, which was then regurgitated by the layout and used as the title. When files are being rendered or included, they receive a copy of the PageData and can access the values we squirreled away. Handy.</p>

<p>There's also a Page dynamic property in WebBasePage that we can assign stuff to. So far that hasn't blown up in my face.</p>

<p>If I think back to the classic ASP days I can remember a time when we desperately wanted this type of functionality. A clean, straightforward way to define some sitewide layout templates separately from our actual page, and here it is. It's like ASP 4.0.</p>

<p>And I only just now noticed that interesting conjunction in ASP and ASP.Net numbering schemes, hmm.</p>

<h3>Lifecycle Pages</h3>
<p>Then there are the lifecycle pages. Where layout files give us a handy way to do templating, these provide us some basic global capabilities for the site. There are three main files that we are concerned with:</p>
<dl>
<dt>~/web.config</dt><dd>This holds configurations for the site, like any ASP.Net site</dd>
<dt>~/_AppStart.cshtml</dt><dd>Contains logic to run the first time any page is requested from the site</dd>
<dt>~/_PageStart.cshtml</dt><dd>Contains logic to run when a page in the current or child directory is requested</dd>
</dl>

<p>AppStart is handy for setting values or creating objects that will have global application scope. For example, when using the SimpleSecurity helpers, you will want to put an initialization call here to initialize the authentication system. </p>

<p><i>And if you are me you will comment them out when you find out your webhosts machine.config fubars settings relevant to ASP.Net membership.</i></p>

<p>PageStart is trickier in a couple ways. First, PageStart is a bit of a misnomer. It does execute at the start of the page, but it can also be convinced  to encapsulate the execution of the requested page, running both before and after the main page is rendered. </p>
<div class="codebox"><div class="codeheader"><span>cshtml</span><div class="codebox_javascript_links"><a href="http://blogs.lessthandot.com" onclick="linenumberOnOff('cb57482'); return false;">Line number Off</a> | <a href="http://blogs.lessthandot.com#" onclick="expandCode('cb57482','cb14011'); return false;">Hide</a> | <a href="http://blogs.lessthandot.com#" onclick="selectCode(this); return false;">Select all</a></div></div><!-- we need this dummy div to fix a firefox bug when selecting code lines --><div class="codeholder"><div class="cshtml" id="cb57482" style="display: block; color: rgb(0, 0, 0);"><ol><li style="" class="li1">@{</li><li style="" class="li2">&nbsp; &nbsp; &lt;b&gt;I'm Before&lt;/b&gt;</li><li style="" class="li1">&nbsp; &nbsp; RunPage();</li><li style="" class="li2">&nbsp; &nbsp; &lt;text&gt;I'm after&lt;/text&gt;</li><li style="" class="li1">}</li></ol></div><div id="cb14011" style="display: none; color: red;"></div></div></div>

<p>If you specifically include the RunPage() line, then your page will be run in between the code above and below it. If you don't include this directive then the entire PageStart file will be run before calling the requested page.</p>

<p>The next trick is that _PageStart runs in a nested sequence from the root level to the level of the file that has been requested. Basically the engine traverses each directory from the root to the folder your requested file is in and, if a _PageStart file is present, executes it. It's similar to those nesting dolls (or recursion, oohh).</p>

<p>But the magic is not done yet, and this was one of those pits of mud I fell into.</p>

<p>Even though you can wrap the execution of the page inside a _PageStart, the execution of the page and PageStart finish before the Layout is applied and executed.</p>

<p>Lets try that again (I lost 2 hours of my life to this feature).</p>

<p>Say we have a _PageStart that wraps around the RunPage call, a Main page, and a referenced _LayoutMain file and we request <a href="http://myevilpitofmud.com/Main">http://myevilpitofmud.com/Main</a>. The execution would look like:</p>
<ol>
   <li>Top part of _PageStart</li>
   <li>Main file</li>
   <li>Bottom part of _PageStart</li>
   <li>Layout file</li>
</ol>

<p>Yeah.</p>

<h2>But what else did you do?</h2>
<p>There's more. I've spent 17 hours on my sample site so far, which includes implementing a SQL Compact database, implementing and removing WebSecurity authentication, and a score of other things. I've found out the hard way that getting fancy can bite you and that it is actually possible to install a 10 inch tailpipe on the WebMatrix hatchback (which I just realized could refer to two different things I've done, hmmm, tailpipe and a <strike>shopping cart handle</strike> spoiler then). </p>

<p>Unfortunately because I had issues with the authentication I can't post a live link yet, the next post will have to cover all the fun that has gone into trying to get a certified host for WebMatrix to run correctly (hint, not all <a href="http://www.microsoft.com/web/hosting/home" title="See hosting options at Microsoft">'SpotLight' hosts</a> can run the included, basic example sites).</p><div class="item_footer"><p><small><a href="http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/ASPNET/webmatrix-routing-and-magic-pages">Original post</a> blogged on <a href="http://lessthandot.com/">LessThanDot</a>.</small></p></div>]]></content:encoded>
								<comments>http://blogs.lessthandot.com/index.php/WebDev/ServerProgramming/ASPNET/webmatrix-routing-and-magic-pages#comments</comments>
			<wfw:commentRss>http://blogs.lessthandot.com/index.php/WebDev/?tempskin=_rss2&#38;disp=comments&#38;p=1327</wfw:commentRss>
		</item>
				<item>
			<title>What Does a Web Developer Need To Know</title>
			<link>http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/what-does-a-web-developer</link>
			<pubDate>Tue, 28 Jun 2011 09:54:00 +0000</pubDate>			<dc:creator>Eli Weinstock-Herman (tarwn)</dc:creator>
			<category domain="main">UI Development</category>
<category domain="alt">Server Programming</category>
<category domain="alt">Javascript</category>
<category domain="alt">AJAX</category>
<category domain="alt">XHTML &amp; CSS</category>			<guid isPermaLink="false">1314@http://blogs.lessthandot.com/</guid>
						<description>&lt;p&gt;What is a web developer? Recently I was involved in a discussion about the skills a web developer is expected to have and how interesting it has been to watch common bad practices (like table layout and SQL injection) continue to spread. So in the nature of Denis&#039;s &lt;a href=&quot;http://blogs.lessthandot.com/index.php/DataMgmt/DBProgramming/MSSQLServer/what-does-a-sql-server&quot; title=&quot;Read Denis&#039;s post&quot;&gt;What does a SQL Server developer need to know?&lt;/a&gt; post, I present the web developer breakdown.&lt;/p&gt;

&lt;h2&gt;What Level Am I?&lt;/h2&gt;
&lt;p&gt;Rather than try and attach years of experience to this scale, I&#039;m going to present a list of major areas and skills, questions, or topics in each area. I&#039;ve tried to capture whether I feel a skill is required to be an Intermediate or Advanced web developer. &lt;/p&gt;

&lt;p&gt;I put this list together based on my own experience, 20-30 web developer interview lists, and some feedback from others.&lt;/p&gt;

&lt;p&gt;Beginner: Knows some of the items on the list&lt;br /&gt;
Intermediate: Knows most intermediate items and some advanced&lt;br /&gt;
Advanced: Knows almost everything on the list&lt;/p&gt;

&lt;p&gt;&lt;style&gt;
.oneshottable { border: 1px solid #444444; border-collapse: collapse; width: 100%; margin-bottom: 12px; }
.oneshottable th { background-color: #444444; color: white; text-transform: uppercase; font-weight: bold; padding: 4px;font-size: 90%; }
.oneshottable td{ padding: 1px 2px; min-width: 20px; border-bottom: 1px solid #999999; font-size: 80%;}
.oneshottable td.summary:first-child{ text-align: left; font-weight: normal; padding: 3px;}
.oneshottable td:first-child{ text-align: center; font-weight: bold }
&lt;/style&gt;&lt;/p&gt;

&lt;table class=&quot;oneshottable&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;th colspan=&quot;2&quot;&gt;HTML&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;summary&quot;&gt;The ability to produce clean, valid, maintainable HTML is critical for a web developer. Current standards include HTML 4.01 (1999), XHTML 1 (2000), and the beginning of HTML 5 (2011)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;The effect of doctypes, can code a mostly valid site without looking up the rules&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;The effect of content-types and how to set or debug them&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;Knows (and use) all of these without looking them up:  html, head, title, meta, script, style, link, body, a, strong, em, ul, ol, li, table, tr, th, td, h1-5, input, select, option, img, p, span, div, pre, textarea&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;And most of these: dl/dt/dd, thead, tbody, tfoot, abbr, acronym, applet, object, blockquote, iframe, label, map, optgroup, sup, sub, base, fieldset&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;Can name/explain at least 15 of the new HTML5 tags&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;Knows the escapes for &amp;amp;, &amp;lt;, &amp;gt;, &amp;copy;, ASCII characters (though you may have to look up the character code)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;Can explain how HTTP works (headers, redirects, header requests, clientside caching)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;Knows how to link to a position in a page&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;table class=&quot;oneshottable&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;th colspan=&quot;2&quot;&gt;CSS&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;summary&quot;&gt;Cascading Style Sheets are used to describe the way an HTML documented should be presented. CSS 1 was released in 1996 and the latest published recommendation is CSS 3. &lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;Difference between display inline, block, and inline-block and what the default value is for div, span, a, b&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;Difference between position static, relative, fixed, absolute&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;What z-index is and how to use it&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;How z-index is calculated by individual browsers&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;Difference between padding and margin&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;Difference between display: none and visibility: hidden&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;Purpose and use of &quot;media&quot;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;Able to describe and calculate specificity&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;Knows at least a few attribute selectors&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;Can describe pseudo-classes and know many without looking them up&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;table class=&quot;oneshottable&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;th colspan=&quot;2&quot;&gt;Javascript&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;summary&quot;&gt;Javascript has become the defacto scripting language for web browsers, one of only a few ways to create client-side interactions consistently across available browsers. JavaScript was first shipped in 1995 as part of the Netscape browser.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;How to write a basic function, inline event handler, and a non-inline event handler (raw or w/ a package)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;Can write one or or more methods to make an AJAX call with reference material&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;Can write one or or more methods to make an AJAX call without reference material&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;Knows at least one framework (jQuery, Prototype, YUI, Dojo, MooTools, &amp;#8230;)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;Can create classes (function or literal)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;Knows and can use prototype (little &#039;p&#039;, not the framework)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;Can dynamically find and add elements to the page using DOM or a framework (without looking it up)&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;table class=&quot;oneshottable&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;th colspan=&quot;2&quot;&gt;Tools&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;summary&quot;&gt;Along with standard languages, there are numerous tools available to help validate, evaluate, and improve existing websites. Similar to a spell-checking tool in a word processing application, these tools can help us catch errors and oversights in our websites.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;Can name at least one site, application, or organization that has an HTML validation tool&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;Can name at least one site, application, or organization that has an CSS validation tool&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;Can name at least one site, application, or organization that has an SEO scoring tool&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;Can name at least one site, application, or organization that has an Accessibility evaluation tool&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;table class=&quot;oneshottable&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;th colspan=&quot;2&quot;&gt;Server-Side Programming&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;summary&quot;&gt;The shift to dynamic, server-generated websites in the mid-to-late 90&#039;s was responsible for shifting the focus of the web from document retrieval to interactivity, commerce, and services.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;Knows at least 2 server-side technologies - Perl, PHP, ASP 3, JSP, Java Servlets, CFM, ASP.Net (WebPages, Web Forms, MVC), Ruby on Rails, Python (WSGI, CGI, mod_python), etc&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;Knows more than 4 of the above list&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;Can explain difference between stateful and stateless development&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;Can explain how sessions work&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;Can explain how cookies work&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;Difference between GET and POST and how known languages present this information&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;Pros and cons of client-side vs server-side validation&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;Understands the basics of (SOAP) web services, how to implement them, and how they work&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;table class=&quot;oneshottable&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;th colspan=&quot;2&quot;&gt;Data&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;summary&quot;&gt;Most web applications have some form of data store behind them. Several forms of storage and caching mechanisms exist and being able to build a basic system often requires at least a basic level of knowledge in these areas.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;Can build databases, define queries, and debug on at least one major RDBMS&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;Knows basics for normalization and can create a basic data model to describe their database&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;Can work with at least one NoSQL database or data cache&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;Can write match and search regular expressions&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;Can write match and search regular expressions without a reference in more than one language&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;table class=&quot;oneshottable&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;th colspan=&quot;2&quot;&gt;Web Server&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;summary&quot;&gt;Using a server-side technology to generate HTML generally requires a web server. The two largest install bases are Microsoft IIS 7.5 and Apache 2.2.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;Understands the basics of how the web server (Apache or IIS) works and can create a new site&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;Can explain URL rewriting and implement on at least one system&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;table class=&quot;oneshottable&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;th colspan=&quot;2&quot;&gt;Performance&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;summary&quot;&gt;Tuning a website to run in an optimal fashion can sometimes be the difference between paying for a single server or multiple servers, between a responsive site and one that fails under load. &lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;Understands and can implement data caching&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;Understands and can implement page caching&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;Can use one or more page performance or load testing tools&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;Tools/methods and purpose of minification of JS and CSS&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;Understands and uses CDNs when possible&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;table class=&quot;oneshottable&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;th colspan=&quot;2&quot;&gt;Testing&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;summary&quot;&gt;As the technology behind the web has developed, tools to help automate some of the repetition involved in testing websites has likewise evolved.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;Can use one or more automated tools for interface testing&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;table class=&quot;oneshottable&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;th colspan=&quot;2&quot;&gt;Security&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;summary&quot;&gt;As recent news stories have reminded us, security is an ongoing requirement for websites. Understanding the basics of security can help us build security in from day one rather than trying to squeeze it in at the end.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;SQL Injection - what it is, how to prevent it&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;Cross site scripting - what it is, how to prevent it&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;Cross Site Request Forgery - what it is, how to prevent it&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;Understand hashing, salting, and importance of storing critical information in hashed or encrypted formats&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;Cookies - Understand session hijacking and dangers of storing user information or identification&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;table class=&quot;oneshottable&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;th colspan=&quot;2&quot;&gt;Design Principles&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;summary&quot;&gt;Being able to implement common design patterns without restoring to hacks is an important part of being a web developer (as opposed to a hobbiest). While it may prove impossible to build a site without some form of hack, hacks should not be the only tool in the toolbox.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;Table Layout - what it is, why it&#039;s bad (at least three reasons)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;Can implement multi-column fixed, fluid, and elastic layouts without reference material&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;Table Layout - and why using CSS for table layout (display: table, table-cell, etc) is not bad&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;Can explain and implement fixed, fluid (liquid), and elastic layouts&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;Can implement multi-column fixed, fluid, and elastic layouts without reference material&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;table class=&quot;oneshottable&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;th colspan=&quot;2&quot;&gt;Meta&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;summary&quot;&gt;Metadata (data about data) is critical to helping people and search engines find resources in our sites and applications.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;Knows purpose of description, keyword tags&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;Knows purpose of robots.txt file&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;Knows purpose and can implement sitemap.xml file&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;You know the basics for SEO&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;You&#039;ve implemented some form of browser analytics&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;Microformats &lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;You know what WCAG is and how to test conformance&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;You know what semantic markup is&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;table class=&quot;oneshottable&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;th colspan=&quot;2&quot;&gt;Disqualifiers/Limiters&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;summary&quot;&gt;These are topics I consider to be critical to advancement. They reflect common misunderstandings or bad practices that an experienced web developer has to grow past in order to advance their skillset.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;B&lt;/td&gt;&lt;td&gt;Backbutton doesn&#039;t work w/ your sites, would prefer to disable it altogether&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;B&lt;/td&gt;&lt;td&gt;You protect images from download by implementing right click intercepts, layering transparent images in top, etc&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;Implement performance tuning without before/after measurements&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;B&lt;/td&gt;&lt;td&gt;Table layout&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;em&gt;Note: Disqualifiers indicate that you won&#039;t progress past [B]eginner or [I]ntermediate levels until you stop doing them and understand why doing them was a bad idea&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;What&#039;s Missing&lt;/h2&gt;
&lt;p&gt;There are several things that were intentionally left out of this list. Some were hard to quantity in a general manner, others I was unsure how relevant they were. And I&#039;m sure people will be kind enough to add additional oversights and revisions below.&lt;/p&gt;

&lt;p&gt;Missing Categories:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Level of knowledge with server-side languages&lt;/li&gt;
&lt;li&gt;Project management skills&lt;/li&gt;
&lt;li&gt;Documentation and Modeling skills&lt;/li&gt;
&lt;li&gt;Design and Graphics skills&lt;/li&gt;
&lt;li&gt;Application Lifecycle Management - Source Control, Deployment, etc&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Extra items that didn&#039;t make it in:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Can implement RSS and ATOM feeds&lt;/li&gt;
&lt;li&gt;Can implement a web farm&lt;/li&gt;
&lt;li&gt;Implemented single-signon (openID, SAML, etc)&lt;/li&gt;
&lt;li&gt;Knows how to implement XML data islands (still relevant?)&lt;/li&gt;
&lt;li&gt;Knows how to create DTDs and/or XSDs&lt;/li&gt;
&lt;li&gt;Can write regular expressions without a reference&lt;/li&gt;
&lt;li&gt;Knows relevant tools for Yahoo (SiteExplorer), Google, Bing for managing site information&lt;/li&gt;
&lt;li&gt;Schema.org - it belongs in the Meta category but I didn&#039;t know what level of skill, knowledge, or adoption I should include considering it&#039;s newness and several other factors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is also the fact that we often get caught up maintaining or even adding on to a site that violates many of the skills and standards you see in this list. This doesn&#039;t prevent us from writing good, clean, semantic sites the rest of the time and being forced to write poor code does not make on inherently less skilled.&lt;/p&gt;

&lt;h2&gt;A Brief Digression, or Why an App Dev is Not a Web Dev&lt;/h2&gt;
&lt;p&gt;At one point web development was seen as less exciting and far less complex than application development. It is still seen as extremely similar to application development, so much so that it is quite normal to hire a good or great application developer and expect them to be a good or great web developer. &lt;/p&gt;

&lt;p&gt;Unfortunately it doesn&#039;t quite work that way, as web development has grown extremely complex since the first dynamic sites started showing up in the late 90&#039;s. Doubly unfortunately, web development is based on a stateless model instead of the stateful one most application developers are used to, uses a fluid layout instead of the grid layout, and sneaks a lot of extra communications and caching into the mix. This is a series of paradigm shifts not unlike the one between procedural, object oriented, and functional programming.&lt;/p&gt;&lt;div class=&quot;item_footer&quot;&gt;&lt;p&gt;&lt;small&gt;&lt;a href=&quot;http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/what-does-a-web-developer&quot;&gt;Original post&lt;/a&gt; blogged on &lt;a href=&quot;http://lessthandot.com/&quot;&gt;LessThanDot&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;</description>
			<content:encoded><![CDATA[<p>What is a web developer? Recently I was involved in a discussion about the skills a web developer is expected to have and how interesting it has been to watch common bad practices (like table layout and SQL injection) continue to spread. So in the nature of Denis's <a href="http://blogs.lessthandot.com/index.php/DataMgmt/DBProgramming/MSSQLServer/what-does-a-sql-server" title="Read Denis's post">What does a SQL Server developer need to know?</a> post, I present the web developer breakdown.</p>

<h2>What Level Am I?</h2>
<p>Rather than try and attach years of experience to this scale, I'm going to present a list of major areas and skills, questions, or topics in each area. I've tried to capture whether I feel a skill is required to be an Intermediate or Advanced web developer. </p>

<p>I put this list together based on my own experience, 20-30 web developer interview lists, and some feedback from others.</p>

<p>Beginner: Knows some of the items on the list<br />
Intermediate: Knows most intermediate items and some advanced<br />
Advanced: Knows almost everything on the list</p>

<p><style>
.oneshottable { border: 1px solid #444444; border-collapse: collapse; width: 100%; margin-bottom: 12px; }
.oneshottable th { background-color: #444444; color: white; text-transform: uppercase; font-weight: bold; padding: 4px;font-size: 90%; }
.oneshottable td{ padding: 1px 2px; min-width: 20px; border-bottom: 1px solid #999999; font-size: 80%;}
.oneshottable td.summary:first-child{ text-align: left; font-weight: normal; padding: 3px;}
.oneshottable td:first-child{ text-align: center; font-weight: bold }
</style></p>

<table class="oneshottable">
<tbody>
<tr><th colspan="2">HTML</th></tr>
<tr><td colspan="2" class="summary">The ability to produce clean, valid, maintainable HTML is critical for a web developer. Current standards include HTML 4.01 (1999), XHTML 1 (2000), and the beginning of HTML 5 (2011)</td></tr>
<tr><td>I</td><td>The effect of doctypes, can code a mostly valid site without looking up the rules</td></tr>
<tr><td>A</td><td>The effect of content-types and how to set or debug them</td></tr>
<tr><td>I</td><td>Knows (and use) all of these without looking them up:  html, head, title, meta, script, style, link, body, a, strong, em, ul, ol, li, table, tr, th, td, h1-5, input, select, option, img, p, span, div, pre, textarea</td></tr>
<tr><td>A</td><td>And most of these: dl/dt/dd, thead, tbody, tfoot, abbr, acronym, applet, object, blockquote, iframe, label, map, optgroup, sup, sub, base, fieldset</td></tr>
<tr><td>A</td><td>Can name/explain at least 15 of the new HTML5 tags</td></tr>
<tr><td>I</td><td>Knows the escapes for &amp;, &lt;, &gt;, &copy;, ASCII characters (though you may have to look up the character code)</td></tr>
<tr><td>A</td><td>Can explain how HTTP works (headers, redirects, header requests, clientside caching)</td></tr>
<tr><td>I</td><td>Knows how to link to a position in a page</td></tr>
</tbody>
</table>

<table class="oneshottable">
<tbody>
<tr><th colspan="2">CSS</th></tr>
<tr><td colspan="2" class="summary">Cascading Style Sheets are used to describe the way an HTML documented should be presented. CSS 1 was released in 1996 and the latest published recommendation is CSS 3. </td></tr>
<tr><td>I</td><td>Difference between display inline, block, and inline-block and what the default value is for div, span, a, b</td></tr>
<tr><td>I</td><td>Difference between position static, relative, fixed, absolute</td></tr>
<tr><td>I</td><td>What z-index is and how to use it</td></tr>
<tr><td>A</td><td>How z-index is calculated by individual browsers</td></tr>
<tr><td>I</td><td>Difference between padding and margin</td></tr>
<tr><td>I</td><td>Difference between display: none and visibility: hidden</td></tr>
<tr><td>I</td><td>Purpose and use of "media"</td></tr>
<tr><td>I</td><td>Able to describe and calculate specificity</td></tr>
<tr><td>I</td><td>Knows at least a few attribute selectors</td></tr>
<tr><td>A</td><td>Can describe pseudo-classes and know many without looking them up</td></tr>
</tbody>
</table>

<table class="oneshottable">
<tbody>
<tr><th colspan="2">Javascript</th></tr>
<tr><td colspan="2" class="summary">Javascript has become the defacto scripting language for web browsers, one of only a few ways to create client-side interactions consistently across available browsers. JavaScript was first shipped in 1995 as part of the Netscape browser.</td></tr>
<tr><td>I</td><td>How to write a basic function, inline event handler, and a non-inline event handler (raw or w/ a package)</td></tr>
<tr><td>I</td><td>Can write one or or more methods to make an AJAX call with reference material</td></tr>
<tr><td>A</td><td>Can write one or or more methods to make an AJAX call without reference material</td></tr>
<tr><td>I</td><td>Knows at least one framework (jQuery, Prototype, YUI, Dojo, MooTools, &#8230;)</td></tr>
<tr><td>I</td><td>Can create classes (function or literal)</td></tr>
<tr><td>A</td><td>Knows and can use prototype (little 'p', not the framework)</td></tr>
<tr><td>I</td><td>Can dynamically find and add elements to the page using DOM or a framework (without looking it up)</td></tr>
</tbody>
</table>

<table class="oneshottable">
<tbody>
<tr><th colspan="2">Tools</th></tr>
<tr><td colspan="2" class="summary">Along with standard languages, there are numerous tools available to help validate, evaluate, and improve existing websites. Similar to a spell-checking tool in a word processing application, these tools can help us catch errors and oversights in our websites.</td></tr>
<tr><td>A</td><td>Can name at least one site, application, or organization that has an HTML validation tool</td></tr>
<tr><td>A</td><td>Can name at least one site, application, or organization that has an CSS validation tool</td></tr>
<tr><td>A</td><td>Can name at least one site, application, or organization that has an SEO scoring tool</td></tr>
<tr><td>A</td><td>Can name at least one site, application, or organization that has an Accessibility evaluation tool</td></tr>
</tbody>
</table>

<table class="oneshottable">
<tbody>
<tr><th colspan="2">Server-Side Programming</th></tr>
<tr><td colspan="2" class="summary">The shift to dynamic, server-generated websites in the mid-to-late 90's was responsible for shifting the focus of the web from document retrieval to interactivity, commerce, and services.</td></tr>
<tr><td>I</td><td>Knows at least 2 server-side technologies - Perl, PHP, ASP 3, JSP, Java Servlets, CFM, ASP.Net (WebPages, Web Forms, MVC), Ruby on Rails, Python (WSGI, CGI, mod_python), etc</td></tr>
<tr><td>A</td><td>Knows more than 4 of the above list</td></tr>
<tr><td>I</td><td>Can explain difference between stateful and stateless development</td></tr>
<tr><td>I</td><td>Can explain how sessions work</td></tr>
<tr><td>I</td><td>Can explain how cookies work</td></tr>
<tr><td>I</td><td>Difference between GET and POST and how known languages present this information</td></tr>
<tr><td>I</td><td>Pros and cons of client-side vs server-side validation</td></tr>
<tr><td>A</td><td>Understands the basics of (SOAP) web services, how to implement them, and how they work</td></tr>
</tbody>
</table>

<table class="oneshottable">
<tbody>
<tr><th colspan="2">Data</th></tr>
<tr><td colspan="2" class="summary">Most web applications have some form of data store behind them. Several forms of storage and caching mechanisms exist and being able to build a basic system often requires at least a basic level of knowledge in these areas.</td></tr>
<tr><td>I</td><td>Can build databases, define queries, and debug on at least one major RDBMS</td></tr>
<tr><td>A</td><td>Knows basics for normalization and can create a basic data model to describe their database</td></tr>
<tr><td>I</td><td>Can work with at least one NoSQL database or data cache</td></tr>
<tr><td>I</td><td>Can write match and search regular expressions</td></tr>
<tr><td>A</td><td>Can write match and search regular expressions without a reference in more than one language</td></tr>
</tbody>
</table>

<table class="oneshottable">
<tbody>
<tr><th colspan="2">Web Server</th></tr>
<tr><td colspan="2" class="summary">Using a server-side technology to generate HTML generally requires a web server. The two largest install bases are Microsoft IIS 7.5 and Apache 2.2.</td></tr>
<tr><td>I</td><td>Understands the basics of how the web server (Apache or IIS) works and can create a new site</td></tr>
<tr><td>A</td><td>Can explain URL rewriting and implement on at least one system</td></tr>
</tbody>
</table>

<table class="oneshottable">
<tbody>
<tr><th colspan="2">Performance</th></tr>
<tr><td colspan="2" class="summary">Tuning a website to run in an optimal fashion can sometimes be the difference between paying for a single server or multiple servers, between a responsive site and one that fails under load. </td></tr>
<tr><td>A</td><td>Understands and can implement data caching</td></tr>
<tr><td>A</td><td>Understands and can implement page caching</td></tr>
<tr><td>A</td><td>Can use one or more page performance or load testing tools</td></tr>
<tr><td>I</td><td>Tools/methods and purpose of minification of JS and CSS</td></tr>
<tr><td>A</td><td>Understands and uses CDNs when possible</td></tr>
</tbody>
</table>

<table class="oneshottable">
<tbody>
<tr><th colspan="2">Testing</th></tr>
<tr><td colspan="2" class="summary">As the technology behind the web has developed, tools to help automate some of the repetition involved in testing websites has likewise evolved.</td></tr>
<tr><td>A</td><td>Can use one or more automated tools for interface testing</td></tr>
</tbody>
</table>

<table class="oneshottable">
<tbody>
<tr><th colspan="2">Security</th></tr>
<tr><td colspan="2" class="summary">As recent news stories have reminded us, security is an ongoing requirement for websites. Understanding the basics of security can help us build security in from day one rather than trying to squeeze it in at the end.</td></tr>
<tr><td>I</td><td>SQL Injection - what it is, how to prevent it</td></tr>
<tr><td>I</td><td>Cross site scripting - what it is, how to prevent it</td></tr>
<tr><td>A</td><td>Cross Site Request Forgery - what it is, how to prevent it</td></tr>
<tr><td>I</td><td>Understand hashing, salting, and importance of storing critical information in hashed or encrypted formats</td></tr>
<tr><td>I</td><td>Cookies - Understand session hijacking and dangers of storing user information or identification</td></tr>
</tbody>
</table>

<table class="oneshottable">
<tbody>
<tr><th colspan="2">Design Principles</th></tr>
<tr><td colspan="2" class="summary">Being able to implement common design patterns without restoring to hacks is an important part of being a web developer (as opposed to a hobbiest). While it may prove impossible to build a site without some form of hack, hacks should not be the only tool in the toolbox.</td></tr>
<tr><td>I</td><td>Table Layout - what it is, why it's bad (at least three reasons)</td></tr>
<tr><td>A</td><td>Can implement multi-column fixed, fluid, and elastic layouts without reference material</td></tr>
<tr><td>A</td><td>Table Layout - and why using CSS for table layout (display: table, table-cell, etc) is not bad</td></tr>
<tr><td>A</td><td>Can explain and implement fixed, fluid (liquid), and elastic layouts</td></tr>
<tr><td>A</td><td>Can implement multi-column fixed, fluid, and elastic layouts without reference material</td></tr>
</tbody>
</table>

<table class="oneshottable">
<tbody>
<tr><th colspan="2">Meta</th></tr>
<tr><td colspan="2" class="summary">Metadata (data about data) is critical to helping people and search engines find resources in our sites and applications.</td></tr>
<tr><td>I</td><td>Knows purpose of description, keyword tags</td></tr>
<tr><td>I</td><td>Knows purpose of robots.txt file</td></tr>
<tr><td>A</td><td>Knows purpose and can implement sitemap.xml file</td></tr>
<tr><td>A</td><td>You know the basics for SEO</td></tr>
<tr><td>A</td><td>You've implemented some form of browser analytics</td></tr>
<tr><td>A</td><td>Microformats </td></tr>
<tr><td>A</td><td>You know what WCAG is and how to test conformance</td></tr>
<tr><td>A</td><td>You know what semantic markup is</td></tr>
</tbody>
</table>

<table class="oneshottable">
<tbody>
<tr><th colspan="2">Disqualifiers/Limiters</th></tr>
<tr><td colspan="2" class="summary">These are topics I consider to be critical to advancement. They reflect common misunderstandings or bad practices that an experienced web developer has to grow past in order to advance their skillset.</td></tr>
<tr><td>B</td><td>Backbutton doesn't work w/ your sites, would prefer to disable it altogether</td></tr>
<tr><td>B</td><td>You protect images from download by implementing right click intercepts, layering transparent images in top, etc</td></tr>
<tr><td>I</td><td>Implement performance tuning without before/after measurements</td></tr>
<tr><td>B</td><td>Table layout</td></tr>
</tbody>
</table>

<p><em>Note: Disqualifiers indicate that you won't progress past [B]eginner or [I]ntermediate levels until you stop doing them and understand why doing them was a bad idea</em></p>

<h2>What's Missing</h2>
<p>There are several things that were intentionally left out of this list. Some were hard to quantity in a general manner, others I was unsure how relevant they were. And I'm sure people will be kind enough to add additional oversights and revisions below.</p>

<p>Missing Categories:</p>
<ul>
<li>Level of knowledge with server-side languages</li>
<li>Project management skills</li>
<li>Documentation and Modeling skills</li>
<li>Design and Graphics skills</li>
<li>Application Lifecycle Management - Source Control, Deployment, etc</li>
</ul>

<p>Extra items that didn't make it in:</p>
<ul>
<li>Can implement RSS and ATOM feeds</li>
<li>Can implement a web farm</li>
<li>Implemented single-signon (openID, SAML, etc)</li>
<li>Knows how to implement XML data islands (still relevant?)</li>
<li>Knows how to create DTDs and/or XSDs</li>
<li>Can write regular expressions without a reference</li>
<li>Knows relevant tools for Yahoo (SiteExplorer), Google, Bing for managing site information</li>
<li>Schema.org - it belongs in the Meta category but I didn't know what level of skill, knowledge, or adoption I should include considering it's newness and several other factors</li>
</ul>

<p>There is also the fact that we often get caught up maintaining or even adding on to a site that violates many of the skills and standards you see in this list. This doesn't prevent us from writing good, clean, semantic sites the rest of the time and being forced to write poor code does not make on inherently less skilled.</p>

<h2>A Brief Digression, or Why an App Dev is Not a Web Dev</h2>
<p>At one point web development was seen as less exciting and far less complex than application development. It is still seen as extremely similar to application development, so much so that it is quite normal to hire a good or great application developer and expect them to be a good or great web developer. </p>

<p>Unfortunately it doesn't quite work that way, as web development has grown extremely complex since the first dynamic sites started showing up in the late 90's. Doubly unfortunately, web development is based on a stateless model instead of the stateful one most application developers are used to, uses a fluid layout instead of the grid layout, and sneaks a lot of extra communications and caching into the mix. This is a series of paradigm shifts not unlike the one between procedural, object oriented, and functional programming.</p><div class="item_footer"><p><small><a href="http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/what-does-a-web-developer">Original post</a> blogged on <a href="http://lessthandot.com/">LessThanDot</a>.</small></p></div>]]></content:encoded>
								<comments>http://blogs.lessthandot.com/index.php/WebDev/UIDevelopment/what-does-a-web-developer#comments</comments>
			<wfw:commentRss>http://blogs.lessthandot.com/index.php/WebDev/?tempskin=_rss2&#38;disp=comments&#38;p=1314</wfw:commentRss>
		</item>
			</channel>
</rss>
