<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><title>Joshua Flanagan</title><link>http://www.lostechies.com/blogs/joshuaflanagan/default.aspx</link><description /><dc:language>en</dc:language><generator>CommunityServer 2008.5 (Build: 30929.2835)</generator><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/JoshuaFlanagan" type="application/rss+xml" /><item><title>Real World Refactoring</title><link>http://feedproxy.google.com/~r/JoshuaFlanagan/~3/WiQJ5sbwOfE/real-world-refactoring.aspx</link><pubDate>Mon, 29 Jun 2009 00:35:29 GMT</pubDate><guid isPermaLink="false">ded273ab-9e87-4979-8222-e4e2e46f1b46:22485</guid><dc:creator>Joshua Flanagan</dc:creator><slash:comments>5</slash:comments><wfw:commentRss>http://www.lostechies.com/blogs/joshuaflanagan/rsscomments.aspx?PostID=22485</wfw:commentRss><comments>http://www.lostechies.com/blogs/joshuaflanagan/archive/2009/06/28/real-world-refactoring.aspx#comments</comments><description>&lt;p&gt;If you’ve ever asked, or been asked, for an example to illustrate a software design principle, you know how frustrating it can be to work with a contrived example. There is rarely any depth to the example, and it lines up so neatly with the concept being explained that the student may have trouble recognizing similar situations in the wild. I’m going to try and relate a real world example of a recent refactoring effort, with the hope that the extra context and narrated walkthrough will help someone make a connection that has failed before.&lt;/p&gt;  &lt;h3&gt;A little background&lt;/h3&gt;  &lt;p&gt;My discussion revolves around &lt;a href="http://docu.jagregory.com/" target="_blank"&gt;Docu&lt;/a&gt;, which is an open source project for converting .NET XML comments into HTML documentation. (&lt;em&gt;Note: I want to make clear is that this is not a criticism of any of the Docu code. All code is written with specific goals and constraints in mind. As the goals evolve, some designs that worked perfectly in early iterations may start to create friction, and deserve to be reconsidered.&lt;/em&gt;) &lt;a href="http://msdn.microsoft.com/en-us/library/b2s063f7.aspx" target="_blank"&gt;.NET XML comments&lt;/a&gt; are constructed by applying a set of top-level tags to code elements such as classes, methods, or properties. Top-level tags are things like &lt;a href="http://msdn.microsoft.com/en-us/library/2d6dt3kf.aspx" target="_blank"&gt;&amp;lt;summary/&amp;gt;&lt;/a&gt;, &lt;a href="http://msdn.microsoft.com/en-us/library/3zw4z1ys.aspx" target="_blank"&gt;&amp;lt;remarks/&amp;gt;&lt;/a&gt;, or &lt;a href="http://msdn.microsoft.com/en-us/library/8cw818w8.aspx" target="_blank"&gt;&amp;lt;param/&amp;gt;&lt;/a&gt;. Within the contents of the top-level tags, you can use embedded tags to provide additional contextual information. Embedded tags are things like &lt;a href="http://msdn.microsoft.com/en-us/library/acd0tfbe.aspx" target="_blank"&gt;&amp;lt;see/&amp;gt;&lt;/a&gt;, &lt;a href="http://msdn.microsoft.com/en-us/library/x640hcd2.aspx" target="_blank"&gt;&amp;lt;para/&amp;gt;&lt;/a&gt;, or &lt;a href="http://msdn.microsoft.com/en-us/library/wb7x2fhw.aspx" target="_blank"&gt;&amp;lt;paramref/&amp;gt;&lt;/a&gt;. The following example shows a top level &amp;lt;remarks/&amp;gt; tag, with embedded &amp;lt;paramref/&amp;gt; and &amp;lt;para/&amp;gt; tags: &lt;/p&gt;  &lt;div style="padding-bottom:0px;margin:0px;padding-left:0px;padding-right:0px;display:inline;float:none;padding-top:0px;" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:d9db1802-6276-4d14-b945-b305ebd1578e" class="wlWriterEditableSmartContent"&gt;&lt;pre name="code" class="xml:nocontrols"&gt;///&amp;lt;remarks&amp;gt;Make sure the &amp;lt;paramref name=”maxValue” /&amp;gt; is a positive number.
///&amp;lt;para&amp;gt;Do not call more than once.&amp;lt;/para&amp;gt;&amp;lt;/remarks&amp;gt; &lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In Docu, the job of translating the XML contents of a single top-level tag into a semantic model is handled by the CommentParser. Let’s take a look at the &lt;a href="http://github.com/jagregory/docu/blob/e5569e48aea80084f1f8c1abc0c77e9d26906e37/src/Docu.Console/Parsing/Comments/CommentParser.cs" target="_blank"&gt;original implementation&lt;/a&gt;:&lt;/p&gt;

&lt;div style="padding-bottom:0px;margin:0px;padding-left:0px;padding-right:0px;display:inline;float:none;padding-top:0px;" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:8a832f5b-43a7-40fe-8c08-b28fce9e9c96" class="wlWriterEditableSmartContent"&gt;&lt;pre name="code" class="c#"&gt;public class CommentParser : ICommentParser
{
    private readonly IDictionary&amp;lt;Func&amp;lt;XmlNode, bool&amp;gt;, Func&amp;lt;XmlNode, bool, bool, IComment&amp;gt;&amp;gt; parsers =
        new Dictionary&amp;lt;Func&amp;lt;XmlNode, bool&amp;gt;, Func&amp;lt;XmlNode, bool, bool, IComment&amp;gt;&amp;gt;();

    private readonly InlineTextCommentParser InlineText = new InlineTextCommentParser();
    private readonly InlineCodeCommentParser InlineCode = new InlineCodeCommentParser();
    private readonly MultilineCodeCommentParser MultilineCode = new MultilineCodeCommentParser();
    private readonly SeeCodeCommentParser See = new SeeCodeCommentParser();
    private readonly ParagraphCommentParser Paragraph;
    private readonly ParameterReferenceParser ParameterReference = new ParameterReferenceParser();
    private readonly InlineListCommentParser InlineList;

    public CommentParser()
    {
        Paragraph = new ParagraphCommentParser(this);
        InlineList = new InlineListCommentParser(this);
        parsers.Add(node =&amp;gt; node is XmlText, InlineText.Parse);
        parsers.Add(node =&amp;gt; node.Name == &amp;quot;c&amp;quot;, InlineCode.Parse);
        parsers.Add(node =&amp;gt; node.Name == &amp;quot;code&amp;quot;, MultilineCode.Parse);
        parsers.Add(node =&amp;gt; node.Name == &amp;quot;see&amp;quot;, See.Parse);
        parsers.Add(node =&amp;gt; node.Name == &amp;quot;para&amp;quot;, Paragraph.Parse);
        parsers.Add(node =&amp;gt; node.Name == &amp;quot;paramref&amp;quot;, ParameterReference.Parse);
        parsers.Add(node =&amp;gt; node.Name == &amp;quot;list&amp;quot;, InlineList.Parse);
    }

    public IList&amp;lt;IComment&amp;gt; Parse(XmlNodeList nodes)
    {
        var blocks = new List&amp;lt;IComment&amp;gt;();

        int count = nodes.Count;
        for(int i = 0; i &amp;lt; count; i++)
        {
            XmlNode node = nodes;
            bool first = (i == 0);
            bool last = (i == (count - 1));

            foreach(var pair in parsers)
            {
                var isValid = pair.Key;
                var parser = pair.Value;

                if(!isValid(node))
                    continue;

                var block = parser(node, first, last);

                if(block != null)
                {
                    blocks.Add(block);
                    continue;
                }
            }
        }

        return blocks;
    }&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can see (lines 18-24 above) that in its constructor it builds up a collection of function (Func&amp;lt;&amp;gt;) pairs: one function that is used to identify a specific embedded tag, paired with another function that knows how to parse that tag into its model representation (an instance of IComment). The collection is used by the primary method in CommentParser, which iterates over all of the child nodes of a given chunk of XML, finds a parsing function that can handle that node, invokes the function, and collects the results. &lt;/p&gt;

&lt;p&gt;The functions that perform the parsing of individual embedded tags are implemented in separate classes (I’ll call them “node parsers”). The CommentParser creates an instance of each node parser and stores it in a field where it can be referenced by the collection of function pairs. The benefit of moving the embedded tag parsing into separate classes is that they can be developed and tested independent of CommentParser. Unfortunately, you need to make a number of changes to CommentParser every time you add a new node parser. Since there are still quite a few embedded tags that are not yet recognized by Docu, and each embedded tag (generally) requires a new node parser, the CommentParser class will be continuously modified and unstable. The primary goal of my refactoring effort is to create a more stable CommentParser that is open for extension (support for new embedded tags can be added) but closed for modification. &lt;/p&gt;

&lt;h3&gt;High Cohesion&lt;/h3&gt;

&lt;p&gt;As stated above, the logic for determining when a specific parsing function should be applied was inside of CommentParser, while the logic to implement that function was in the individual parsing classes. These two pieces of logic are tightly related to each other. You cannot safely apply an arbitrary node parser to any node – it only makes sense to apply a parser to the type of node it was designed for. We can make the node parsers and the CommentParser more cohesive by moving both pieces of logic into the node parsing classes. I’ll add a CanParse(XmlNode) method to each node parser. The method returns true if the parser knows how to parse a given comment node. I implement them by copying the logic for identifying specific tags from the Func&amp;lt;&amp;gt; pairs in CommentParser.&lt;/p&gt;

&lt;h3&gt;Low Coupling &lt;/h3&gt;

&lt;p&gt;We still have the problem that the CommentParser creates and stores an instance of each specific node parser class (lines 6-11 above). This high coupling between the CommentParser and the node parsing classes makes it impossible to execute in isolation. You cannot use a CommentParser without bringing all of the functionality of all node parsers along. You also have to modify the CommentParser every time a new type of node parser is added (as when adding support for a new type of comment tag).&lt;/p&gt;

&lt;p&gt;I’ll reduce coupling between CommentParser and individual node parsers by introducing an interface (ICommentNodeParser) to describe the functionality exposed by the node parsers. The CommentParser will only interact with this interface, which exposes the CanParse and Parse methods. I then change the node parsers so that they implement the new interface. Since they already had all of the needed functionality, it was just a matter of making sure the method signatures matched correctly. &lt;/p&gt;

&lt;h3&gt;Slight detour&lt;/h3&gt;

&lt;p&gt;When I started to add the interface to all of the node parsing classes, I noticed they all derived from CommentParserBase. However, none of the code in the application referred to these classes through this base class. That’s a pretty good indicator that inheritance is being used to share common functionality rather than for polymorphism. Using inheritance just to share some common functionality can lead to more inflexible designs and less cohesive classes. You are usually better off using composition instead of inheritance in these scenarios. In this case, the only shared functionality was a single helper method that had some special logic for string trimming. Since the method didn’t make use of any instance data, it was easy to move it to a separate class as an extension method on string. Now that CommentParserBase was empty, there was no reason to keep it around, so it was eliminated. The fact that no code broke when the base class was deleted (without using a refactoring tool) is a good indicator that it was the right decision.&lt;/p&gt;

&lt;h3&gt;Composition through dependency injection&lt;/h3&gt;

&lt;p&gt;At this point we have a bunch of node parsers that all implement a common interface. But the CommentParser is still tightly coupled to the various implementations because it has to create the instances. This is the perfect opportunity to use dependency injection to pass the node parser instances into the CommentParser. I just change the constructor of CommentParser to require an array of ICommentNodeParsers, and delete all the code that was creating the node parser instances. Whoever creates an instance of CommentParser will need to pass in the collection of node parser instances. CommentParser is no longer coupled to the specific node parsers and can be more easily used in isolation. You can see that the newer version is greatly simplified:&lt;/p&gt;

&lt;div style="padding-bottom:0px;margin:0px;padding-left:0px;padding-right:0px;display:inline;float:none;padding-top:0px;" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:40abf482-65bd-4279-842c-f0dc82396fef" class="wlWriterEditableSmartContent"&gt;&lt;pre name="code" class="c#"&gt;public class CommentParser : ICommentParser
   {
       private readonly ICommentNodeParser[] _parsers;

       public CommentParser(ICommentNodeParser[] parsers)
       {
           _parsers = parsers;
       }

       public IList&amp;lt;IComment&amp;gt; Parse(XmlNodeList nodes)
       {
           var blocks = new List&amp;lt;IComment&amp;gt;();

           var count = nodes.Count;
           for(var i = 0; i &amp;lt; count; i++)
           {
               var node = nodes;
               var first = (i == 0);
               var last = (i == (count - 1));

               var parser = _parsers.FirstOrDefault(p =&amp;gt; p.CanParse(node));
               if (parser == null) continue;

               var block = parser.Parse(this, node, first, last);
               if (block != null)
               {
                   blocks.Add(block);
               }
           }

           return blocks;
       }&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;At this point you may be thinking I just “passed the buck” for creating the individual node parser instances to another class up the stack. Someone still needs to create them, and will therefore be tightly coupled to the implementations. That is true. However, if we can pass that responsibility up the stack far enough, it can be handled by code that has no other responsibility than to configure and bootstrap our application. That type of code doesn’t typically have any logic that would be re-used and doesn’t have the same concerns about designing for maintainability. Luckily, in the case of Docu, we already use &lt;a href="http://structuremap.sourceforge.net/" target="_blank"&gt;StructureMap&lt;/a&gt; to compose our object instances so we get this functionality for free. I simply had to add a single line to the StructureMap configuration to tell it to make use of any implementation of ICommentNodeParser it finds in the Docu assembly.&lt;/p&gt;

&lt;div style="padding-bottom:0px;margin:0px;padding-left:0px;padding-right:0px;display:inline;float:none;padding-top:0px;" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:c4eff773-4ded-44d3-831b-39478edb3125" class="wlWriterEditableSmartContent"&gt;&lt;pre name="code" class="c#"&gt;public DefaultRegistry() {
  Scan(x =&amp;gt;  {
    x.AssemblyContainingType&amp;lt;DocumentationGenerator&amp;gt;();
    x.WithDefaultConventions();
    x.AddAllTypesOf&amp;lt;ICommentNodeParser&amp;gt;();&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Now when an instance of CommentParser is requested from StructureMap, it will automatically have the array of all node parsers injected to its constructor.&lt;/p&gt;

&lt;h3&gt;Wrap up&lt;/h3&gt;

&lt;p&gt;So what did we gain? Let’s compare the stories for adding the ability to parse a new XML documentation tag. In the original implementation, we had to:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Create a class with code that parses the new tag &lt;/li&gt;

  &lt;li&gt;Create a new field in CommentParser to hold an instance of the new parsing class &lt;/li&gt;

  &lt;li&gt;Modify CommentParser’s constructor to register the new class, along with a predicate that determines when it should be used &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And now we simply:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Create a class that implements ICommentNodeParser with code that recognizes and parses the new tag &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Notice that you didn’t have to touch CommentParser? We just gave it new functionality without having to change (and potentially destabilize) the code. That’s the open-closed principle in action. The increased cohesion of the node parsers makes them easier to understand and helps localize any future changes to their implementation. We also saw that the use of dependency injection and a composition tool (StructureMap in this case) made it painless for us to pull code apart into separate classes. The code was made easier to maintain by applying a few established object oriented design principles. Hopefully this example helped clarify the application of these principles. Your feedback is appreciated.&lt;/p&gt;

&lt;p&gt;For additional context, you can &lt;a href="http://github.com/jagregory/docu/commit/41bf6458ed4297a355400b0ea2c465a1511122b0" target="_blank"&gt;view the commit&lt;/a&gt; that contained the changes discussed above.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://www.lostechies.com/aggbug.aspx?PostID=22485" width="1" height="1"&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JoshuaFlanagan?a=WiQJ5sbwOfE:RqLY3lvu3yw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JoshuaFlanagan?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JoshuaFlanagan/~4/WiQJ5sbwOfE" height="1" width="1"/&gt;</description><category domain="http://www.lostechies.com/blogs/joshuaflanagan/archive/tags/structuremap/default.aspx">structuremap</category><category domain="http://www.lostechies.com/blogs/joshuaflanagan/archive/tags/docu/default.aspx">docu</category><category domain="http://www.lostechies.com/blogs/joshuaflanagan/archive/tags/composition/default.aspx">composition</category><category domain="http://www.lostechies.com/blogs/joshuaflanagan/archive/tags/design/default.aspx">design</category><feedburner:origLink>http://www.lostechies.com/blogs/joshuaflanagan/archive/2009/06/28/real-world-refactoring.aspx</feedburner:origLink></item><item><title>Integrating a custom test runner with TeamCity</title><link>http://feedproxy.google.com/~r/JoshuaFlanagan/~3/ZmntUyppLaY/integrating-a-custom-test-runner-with-teamcity.aspx</link><pubDate>Thu, 11 Jun 2009 00:45:15 GMT</pubDate><guid isPermaLink="false">ded273ab-9e87-4979-8222-e4e2e46f1b46:21752</guid><dc:creator>Joshua Flanagan</dc:creator><slash:comments>4</slash:comments><wfw:commentRss>http://www.lostechies.com/blogs/joshuaflanagan/rsscomments.aspx?PostID=21752</wfw:commentRss><comments>http://www.lostechies.com/blogs/joshuaflanagan/archive/2009/06/10/integrating-a-custom-test-runner-with-teamcity.aspx#comments</comments><description>&lt;p&gt;JetBrains’ TeamCity build server provides a wealth of information about the tests that are run as part of your build. If you use one of the test runners that they support out of the box, you automatically get real-time individual test results, detailed timing reports, and historical performance metrics.&lt;/p&gt;  &lt;p&gt;Up until recently, all of the tests for my team were run through NUnit – C# unit tests, QUnit JavaScript tests, and integration tests. Since NUnit is natively supported by TeamCity, we got used to the rich test reporting. However, we recently started to integrate our automated acceptance tests into the build. Our acceptance tests are written using StoryTeller, which are executed using its own test runner. The StoryTeller test runner has slightly different behavior than the NUnit runner. For one, a failed assertion does NOT stop execution of a test – it continues executing so that a single test can have multiple failed assertions. We also have the ability to mark individual tests with their state in the development lifecycle: they start as &amp;quot;acceptance” tests when they are first defined, and then are marked as “regression” once all supporting code has been correctly implemented. The idea is that you can drive your development with failing acceptance tests until they pass, at which point they become regression tests. This is relevant because a failed acceptance test does NOT fail the build – only a failed regression test will fail the build.&lt;/p&gt;  &lt;p&gt;The StoryTeller test runner is a console application that will return an error code if any regression tests fail. It also generates an HTML report which contains the details for each test. Our initial attempt at integrating it into our build allowed us to fail the build if any test failed, but we didn’t get much feedback about why it failed. We would have to go searching through the build log messages or HTML report files for details.&lt;/p&gt;  &lt;p&gt;I created a new StoryTeller ITestListener (&lt;a href="http://storyteller.tigris.org/source/browse/storyteller/trunk/source/StoryTeller/Engine/TeamCityTestListener.cs?revision=401&amp;amp;view=markup" target="_blank"&gt;TeamCityTestListener&lt;/a&gt;) which is used by the StoryTeller runner to log test results. It writes the test details to the console using TeamCity’s &lt;a href="http://www.jetbrains.net/confluence/display/TCD4/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-ReportingTests" target="_blank"&gt;Service Message API&lt;/a&gt; (which &lt;a href="http://www.lostechies.com/blogs/joshuaflanagan/archive/2008/09/10/monkey-patching-rake-for-use-with-teamcity.aspx" target="_blank"&gt;I mentioned previously&lt;/a&gt;). TeamCity picks up these messages, and creates the nice individual test reports, complete with timing and history:&lt;/p&gt;  &lt;p&gt;&lt;img title="StoryTeller test results in TeamCity" style="border-top-width:0px;display:inline;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;" height="378" alt="StoryTeller test results in TeamCity" src="http://www.lostechies.com/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/joshuaflanagan/image_5F00_4C7407A7.png" width="644" border="0" /&gt; &lt;/p&gt;  &lt;p&gt;Now we know exactly which tests failed, but we don’t know which assertions in the tests failed. As I mentioned, the complete details of the test execution are recorded in an HTML report. It’d be nice if we could view individual test reports right within TeamCity. Turns out, that is pretty easy to do as well. I followed the documentation for &lt;a href="http://www.jetbrains.net/confluence/display/TCD4/Including+Third-Party+Reports+in+the+Build+Results" target="_blank"&gt;Including Third-Party Reports in the Build Results&lt;/a&gt;, and added the following line to our &amp;lt;TEAMCITY&amp;gt;\.ServerConfig\.BuildServer\config\main-config.xml file:&lt;/p&gt;  &lt;div&gt;   &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#0000ff;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#800000;"&gt;report-tab&lt;/span&gt; &lt;span style="color:#ff0000;"&gt;title&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;=&amp;quot;StoryTeller&amp;quot;&lt;/span&gt; &lt;span style="color:#ff0000;"&gt;basePath&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;=&amp;quot;st-results&amp;quot;&lt;/span&gt; &lt;span style="color:#ff0000;"&gt;startPage&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;=&amp;quot;st-results.htm&amp;quot;&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;We now have a nice new StoryTeller tab next to the other build result tabs:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.lostechies.com/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/joshuaflanagan/image_5F00_2B14620B.png"&gt;&lt;img title="image" style="border-right:0px;border-top:0px;display:inline;border-left:0px;border-bottom:0px;" height="247" alt="image" src="http://www.lostechies.com/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/joshuaflanagan/image_5F00_thumb_5F00_2E4649F3.png" width="644" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;We configured the build to store all of the HTML reports as artifacts, and point our custom tab to the test results index HTML file generated by StoryTeller. Clicking the StoryTeller tab displays that index file with a color coded overview of all of the tests. Clicking a test brings up the report for that test so that I can see exactly which steps failed.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://www.lostechies.com/aggbug.aspx?PostID=21752" width="1" height="1"&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JoshuaFlanagan?a=ZmntUyppLaY:3U9vy-yIF2E:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JoshuaFlanagan?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JoshuaFlanagan/~4/ZmntUyppLaY" height="1" width="1"/&gt;</description><category domain="http://www.lostechies.com/blogs/joshuaflanagan/archive/tags/teamcity/default.aspx">teamcity</category><feedburner:origLink>http://www.lostechies.com/blogs/joshuaflanagan/archive/2009/06/10/integrating-a-custom-test-runner-with-teamcity.aspx</feedburner:origLink></item><item><title>Let your Inversion of Control tool work for you</title><link>http://feedproxy.google.com/~r/JoshuaFlanagan/~3/RDnD6UmUtN8/let-your-inversion-of-control-tool-work-for-you.aspx</link><pubDate>Wed, 10 Jun 2009 03:58:51 GMT</pubDate><guid isPermaLink="false">ded273ab-9e87-4979-8222-e4e2e46f1b46:21691</guid><dc:creator>Joshua Flanagan</dc:creator><slash:comments>5</slash:comments><wfw:commentRss>http://www.lostechies.com/blogs/joshuaflanagan/rsscomments.aspx?PostID=21691</wfw:commentRss><comments>http://www.lostechies.com/blogs/joshuaflanagan/archive/2009/06/09/let-your-inversion-of-control-tool-work-for-you.aspx#comments</comments><description>&lt;p&gt;If you are just starting out exploring use of an Inversion of Control tool (IoC), its very easy to go down the wrong path, and make things harder for yourself. Today I had a conversation with a bright developer who was feeling the pain because doing it all wrong. And just a month or so ago, I had the same conversation with another bright developer who was also doing it the same, wrong, way.&lt;/p&gt;  &lt;p&gt;The problem may be that people misinterpret the intent of an IoC tool. It is seen simply as a configuration tool – a way to swap out classes using some magical XML or a DSL. The thought is that as long as your code gets all of its dependencies out of the container, you can change the behavior just by configuring the container.&lt;/p&gt;  &lt;p&gt;And then you end up with code that looks like this:&lt;/p&gt;  &lt;div style="padding-bottom:0px;margin:0px;padding-left:0px;padding-right:0px;display:inline;float:none;padding-top:0px;" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:18f29b6a-df39-4f20-936e-f18674d4e5d4" class="wlWriterEditableSmartContent"&gt;&lt;pre name="code" class="c#"&gt;public class OrderService : IOrderService
{
    private IRepository _repository;
    private ISystemClock _systemClock;

    public OrderService()
    {
        _repository = Container.Resolve&amp;lt;IRepository&amp;gt;();
        _systemClock = Container.Resolve&amp;lt;ISystemClock&amp;gt;();
    }
}

public class OrderController
{
    private IOrderService _orderService;

    public OrderController()
    {
        _orderService = Container.Resolve&amp;lt;IOrderService&amp;gt;();
    }
}&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The OrderService needs an IRepository and an ISystemClock, so you pull them out of the container in the constructor. The OrderController needs an IOrderService, so you pull it out of the container in the constructor. You can now swap out actual implementations using some external configuration.&lt;/p&gt;

&lt;p&gt;Except now you’ve got this annoying dependency on the magical Container class. And maybe the Container has a dependency on an external XML file.&lt;/p&gt;

&lt;p&gt;And you discover that when you try to unit test your OrderService, you need to make sure you have a Container available, and that it has been configured to serve up whichever instances you want to use during testing.&lt;/p&gt;

&lt;p&gt;Your simple, plain-old CLR object (POCO) now has this deadweight Container dependency dragging it down. The Container requires extra care and feeding, in the form of configuration, and now it has infected your entire codebase.&lt;/p&gt;

&lt;p&gt;Hopefully, at this point you are feeling enough pain that you go seeking some advice, and hopefully you find out about auto-wiring.&lt;/p&gt;

&lt;p&gt;Any IoC tool worth using will support auto-wiring dependencies for objects retrieved from the container. This means that the tool will build up all of the necessary objects needed to satisfy a request. This allows you to rewrite the above code as:&lt;/p&gt;

&lt;div style="padding-bottom:0px;margin:0px;padding-left:0px;padding-right:0px;display:inline;float:none;padding-top:0px;" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:05c5a713-dba3-4dae-9581-fb0dbafcc1b4" class="wlWriterEditableSmartContent"&gt;&lt;pre name="code" class="c#"&gt;public class OrderService : IOrderService
{
    private readonly IRepository _repository;
    private readonly ISystemClock _systemClock;

    public OrderService(IRepository repository, ISystemClock systemClock)
    {
        _repository = repository;
        _systemClock = systemClock;
    }
}

public class OrderController
{
    private readonly IOrderService _orderService;

    public OrderController(IOrderService orderService)
    {
        _orderService = orderService;
    }
}&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;But where did the Container go? Exactly. The majority of your code should never have to deal with the container directly. Ideally, you make a single call to the container which builds up an object graph that implements your program. For a console application, you might make a call to the container once in your static Main method. For an MVC web application, you might make a call to the container at the beginning of each request to retrieve the appropriate Controller. The rest of your code is blissfully ignorant of the container. When something asks the container for an OrderController, the container will detect the dependency on the IOrderService. So it goes to build an OrderService, and detects the dependencies on the IRepository and ISystemClock. So it builds up those instances, and any dependencies they may have, and so on.&lt;/p&gt;

&lt;p&gt;By the way, my colleague &lt;a href="http://codebetter.com/blogs/jeremy.miller/" target="_blank"&gt;Jeremy Miller&lt;/a&gt; wrote this same post just a few months ago. But he assured me it was worth re-writing, because there are new people jumping on all the time, and they’re not likely to go back reading old blog posts. If you find yourself with the problem I describe above, go get the full details by reading his post about &lt;a href="http://codebetter.com/blogs/jeremy.miller/archive/2009/01/07/autowiring-in-structuremap-2-5.aspx" target="_blank"&gt;auto-wiring in StructureMap&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One thing that I’ll add is that I’ve heard a lot of talk lately that “container” is really a misnomer for what an IoC tool does, and that it should be more appropriately referred to as a “composer”. I think this argument has merit, and wonder if the name may be contributing to people going down the wrong path described above. A “container” sounds like something that stores a bunch of stuff for you, and it is your job to get stuff out of it, hence the explicit calls to the Container littered throughout your code. A “composer” sounds like something that takes little pieces of functionality in your application and puts them together into a usable whole. The bad code example above reflects “container” thinking. The better code example reflects “composer” thinking.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://www.lostechies.com/aggbug.aspx?PostID=21691" width="1" height="1"&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JoshuaFlanagan?a=RDnD6UmUtN8:JBTriHV_pHo:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JoshuaFlanagan?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JoshuaFlanagan/~4/RDnD6UmUtN8" height="1" width="1"/&gt;</description><category domain="http://www.lostechies.com/blogs/joshuaflanagan/archive/tags/structuremap/default.aspx">structuremap</category><category domain="http://www.lostechies.com/blogs/joshuaflanagan/archive/tags/composition/default.aspx">composition</category><feedburner:origLink>http://www.lostechies.com/blogs/joshuaflanagan/archive/2009/06/09/let-your-inversion-of-control-tool-work-for-you.aspx</feedburner:origLink></item><item><title>Latest Docu builds available</title><link>http://feedproxy.google.com/~r/JoshuaFlanagan/~3/vvPidFB_OAs/latest-docu-builds-available.aspx</link><pubDate>Wed, 20 May 2009 02:29:44 GMT</pubDate><guid isPermaLink="false">ded273ab-9e87-4979-8222-e4e2e46f1b46:21119</guid><dc:creator>Joshua Flanagan</dc:creator><slash:comments>2</slash:comments><wfw:commentRss>http://www.lostechies.com/blogs/joshuaflanagan/rsscomments.aspx?PostID=21119</wfw:commentRss><comments>http://www.lostechies.com/blogs/joshuaflanagan/archive/2009/05/19/latest-docu-builds-available.aspx#comments</comments><description>&lt;p&gt;&lt;a href="http://docu.jagregory.com/" target="_blank"&gt;Docu&lt;/a&gt; is a tool to create documentation out of your .NET XML comments, much in the same way we used to use NDoc. If legend has it right, it was created by the prolific &lt;a href="http://jagregory.lostechies.com" target="_blank"&gt;James Gregory&lt;/a&gt; with the sole goal of generating the documentation for &lt;a href="http://fluentnhibernate.org/" target="_blank"&gt;Fluent NHibernate&lt;/a&gt;. It won’t ever compete with &lt;a href="http://sandcastle.codeplex.com/" target="_blank"&gt;Sandcastle&lt;/a&gt; on features or functionality, nor should it. If you just need to create developer-focused, HTML documentation for your API, docu is the perfect tool. If you need to create higher level end-user documentation in different formats, you’ll probably always be better off with something like Sandcastle.&lt;/p&gt;  &lt;p&gt;Because of its original narrow focus, it doesn’t currently support all language elements completely, or all of the possible documentation tags. Fortunately, it has a solid codebase to build on, and there has been a lot of interest in it, as a simpler alternative to Sandcastle for simple tasks. Since it is under active development, and each change provides better and better support and stability, a single release can get outdated quickly. I thought it was important to make it easy for potential users to get the latest and greatest bits without having to get the code and compile it themselves.&lt;/p&gt;  &lt;p&gt;I’m happy to announce that you can now do just that by visiting the awesome &lt;a href="http://teamcity.codebetter.com" target="_blank"&gt;teamcity.codebetter.com&lt;/a&gt;. Login as guest (or create an account for yourself, if you’d like to monitor the build status of your favorite open source projects) and click on Docu in the list of projects. On the Docu page you will see a list of all recent builds, with the build number in the leftmost column. Click on the “View” link (or the down-arrow next to it) within the Artifacts column for the build you are interested in to see the list of files created by that build. Just grab the zip file and extract it to a folder and and you are ready to go.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;UPDATE: &lt;/strong&gt;As of build #35, you can download a zip file for each build which contains everything needed to run docu. You no longer need to download an earlier release from the project site first.&lt;/p&gt;  &lt;p&gt;Of course, you will be running from trunk, and so all the usual risks and disclaimers apply, but I think the project is still at an early enough stage that it actually gets more stable with each commit. That said, if you do run into problems, you can report issues to the &lt;a href="http://docu.lighthouseapp.com/projects/27685-docu/tickets" target="_blank"&gt;docu issue tracker&lt;/a&gt;, ask a question on the &lt;a href="http://groups.google.com/group/docu-group" target="_blank"&gt;mailing list&lt;/a&gt;, or just contact me through this blog and I’ll do my best to help you out.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://www.lostechies.com/aggbug.aspx?PostID=21119" width="1" height="1"&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JoshuaFlanagan?a=vvPidFB_OAs:6gbo9JpKDTA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JoshuaFlanagan?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JoshuaFlanagan/~4/vvPidFB_OAs" height="1" width="1"/&gt;</description><category domain="http://www.lostechies.com/blogs/joshuaflanagan/archive/tags/docu/default.aspx">docu</category><feedburner:origLink>http://www.lostechies.com/blogs/joshuaflanagan/archive/2009/05/19/latest-docu-builds-available.aspx</feedburner:origLink></item><item><title>ASP.NET 400 Bad Request with restricted characters</title><link>http://feedproxy.google.com/~r/JoshuaFlanagan/~3/QYjKifm6uq0/asp-net-400-bad-request-with-restricted-characters.aspx</link><pubDate>Tue, 28 Apr 2009 03:36:00 GMT</pubDate><guid isPermaLink="false">ded273ab-9e87-4979-8222-e4e2e46f1b46:20666</guid><dc:creator>Joshua Flanagan</dc:creator><slash:comments>5</slash:comments><wfw:commentRss>http://www.lostechies.com/blogs/joshuaflanagan/rsscomments.aspx?PostID=20666</wfw:commentRss><comments>http://www.lostechies.com/blogs/joshuaflanagan/archive/2009/04/27/asp-net-400-bad-request-with-restricted-characters.aspx#comments</comments><description>&lt;p&gt;Today I had to hunt down a reported defect that said our advanced search functionality was returning a Bad Request error. On initial inspection, I was unable to reproduce the issue. After talking to our product manager, I learned that he was trying to seed the search with the text &amp;ldquo;% %&amp;rdquo;. We have a quick search text box that lets you enter your criteria, and it has some built in rules about which fields it applies the criteria to. If you need more control over the criteria, you can enable the advanced search, and your quick search criteria will automatically be populated in the advanced search page. The way we were doing that was by passing the criteria in the URL, as in: http://localhost/dovetailcrm/contacts/query/&lt;strong&gt;yourbasiccriteria&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;To seed the advanced search for &amp;ldquo;% %&amp;rsquo;, it would load http://localhost/dovetailcrm/contacts/query/&lt;strong&gt;%25%20%25&lt;/strong&gt; (% URI encodes as %25, space encodes as %20)&lt;/p&gt;
&lt;p&gt;We were properly encoding the request, so what the problem? Attempting to load that URL would cause the error:&lt;/p&gt;
&lt;p&gt;&lt;span style="font-family:&amp;#39;Cordia New&amp;#39;;font-size:medium;"&gt;400 Bad Request      &lt;br /&gt;ASP.NET detected invalid characters in the URL.&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Proceed at your own risk&lt;/h2&gt;
&lt;p&gt;My first instinct was to suspect URLScan, or the IIS 7.0 equivalent. After a bit of googling, it became apparent that ASP.NET really didn&amp;rsquo;t like it when you tried to pass a %, &amp;amp;, *, or : in the URL. The various fixes were scattered around different forum posts, but &lt;a href="http://dirk.net/2008/06/09/ampersand-the-request-url-in-iis7/" target="_blank"&gt;summed up nicely at Dirk.Net&lt;/a&gt;. Unfortunately, the only answer seemed to be &amp;ldquo;make a registry change&amp;rdquo; or &amp;ldquo;don&amp;rsquo;t pass those characters in your URL&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;That didn&amp;rsquo;t sit well with me &amp;ndash; it didn&amp;rsquo;t seem right that there was no way to pass those characters in the URL without having to change your server configuration which could potentially expose your site to security risks.&lt;/p&gt;
&lt;h2&gt;Just because you can, doesn&amp;rsquo;t mean you should&lt;/h2&gt;
&lt;p&gt;After a little bit of experimentation, I discovered that you certainly CAN pass those characters in a URL: they just have to be passed in the query string (the part after the question mark). It suddenly started to make sense why there was not a whole lot of information on this error, and why the little information that was available seemed to be related to ASP.NET MVC. Up until ASP.NET MVC (or more accurately, System.Web.Routing), you would almost always send parameterized data in the URL as part of the query string. It wasn&amp;rsquo;t until we got Routing that we started putting parameters in the path portion of the URL.&lt;/p&gt;
&lt;p&gt;So after making a short story long, the solution was to simply pass the information the old fashioned way, in the query string: http://localhost/dovetailcrm/contacts/query?search&lt;strong&gt;=%25%20%25&lt;/strong&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://www.lostechies.com/aggbug.aspx?PostID=20666" width="1" height="1"&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JoshuaFlanagan?a=QYjKifm6uq0:LlcWadI0n2U:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JoshuaFlanagan?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JoshuaFlanagan/~4/QYjKifm6uq0" height="1" width="1"/&gt;</description><category domain="http://www.lostechies.com/blogs/joshuaflanagan/archive/tags/mvc/default.aspx">mvc</category><feedburner:origLink>http://www.lostechies.com/blogs/joshuaflanagan/archive/2009/04/27/asp-net-400-bad-request-with-restricted-characters.aspx</feedburner:origLink></item><item><title>But don’t worry, WebForms is not going away…</title><link>http://feedproxy.google.com/~r/JoshuaFlanagan/~3/2G0sZ52sNtY/but-don-t-worry-webforms-is-not-going-away.aspx</link><pubDate>Sun, 08 Mar 2009 22:23:40 GMT</pubDate><guid isPermaLink="false">ded273ab-9e87-4979-8222-e4e2e46f1b46:19688</guid><dc:creator>Joshua Flanagan</dc:creator><slash:comments>0</slash:comments><wfw:commentRss>http://www.lostechies.com/blogs/joshuaflanagan/rsscomments.aspx?PostID=19688</wfw:commentRss><comments>http://www.lostechies.com/blogs/joshuaflanagan/archive/2009/03/08/but-don-t-worry-webforms-is-not-going-away.aspx#comments</comments><description>&lt;p&gt;I got a kick out of &lt;a href="http://codebetter.com/blogs/jeremy.miller/archive/2009/03/08/why-i-prefer-the-mvc-model-2-style-over-webforms.aspx" target="_blank"&gt;Jeremy’s eye poke at the ASP.NET WebForms&lt;/a&gt; “simpler” approach to web development.&lt;/p&gt;  &lt;p&gt;In the same spirit, remember how convoluted it was to &lt;a href="http://msdn.microsoft.com/en-us/library/bb398998.aspx" target="_blank"&gt;call simple JSON services from WebForms&lt;/a&gt;? Well, my friend and former colleague &lt;a href="http://shashankshetty.wordpress.com/" target="_blank"&gt;Shashank Shetty&lt;/a&gt; shows how it is done with &lt;a href="http://shashankshetty.wordpress.com/2009/03/04/using-jsonresult-with-jquery-in-aspnet-mvc/" target="_blank"&gt;ASP.NET MVC and jQuery&lt;/a&gt; in his inaugural blog post.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://www.lostechies.com/aggbug.aspx?PostID=19688" width="1" height="1"&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JoshuaFlanagan?a=2G0sZ52sNtY:kEb1SbUygRw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JoshuaFlanagan?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JoshuaFlanagan/~4/2G0sZ52sNtY" height="1" width="1"/&gt;</description><category domain="http://www.lostechies.com/blogs/joshuaflanagan/archive/tags/jquery/default.aspx">jquery</category><category domain="http://www.lostechies.com/blogs/joshuaflanagan/archive/tags/mvc/default.aspx">mvc</category><feedburner:origLink>http://www.lostechies.com/blogs/joshuaflanagan/archive/2009/03/08/but-don-t-worry-webforms-is-not-going-away.aspx</feedburner:origLink></item><item><title>Auto mocking Explained</title><link>http://feedproxy.google.com/~r/JoshuaFlanagan/~3/zoQLntqVbzI/auto-mocking-explained.aspx</link><pubDate>Wed, 04 Feb 2009 01:25:32 GMT</pubDate><guid isPermaLink="false">ded273ab-9e87-4979-8222-e4e2e46f1b46:15466</guid><dc:creator>Joshua Flanagan</dc:creator><slash:comments>20</slash:comments><wfw:commentRss>http://www.lostechies.com/blogs/joshuaflanagan/rsscomments.aspx?PostID=15466</wfw:commentRss><comments>http://www.lostechies.com/blogs/joshuaflanagan/archive/2009/02/03/auto-mocking-explained.aspx#comments</comments><description>&lt;h2&gt;What does it do?&lt;/h2&gt;  &lt;p&gt;There is nothing fancy about an automocker. There is nothing scary about an automocker. An automocker is like one of those little classes we all write in order to quickly and easily create objects that we need for our tests. Nice, simple, helpful, non-threatening.&lt;/p&gt;  &lt;p&gt;You tell the automocker which class you want to test, and the automocker creates an instance of that class for you. If the constructor of your class requires you to pass in other objects, the automocker will automatically create a mock for each of the necessary types and pass them in. The automocker will keep track of all the mocks it creates, and allows you to retrieve them if you need to stub a specific behavior.&lt;/p&gt;  &lt;h2&gt;Why should I fear the automocker?&lt;/h2&gt;  &lt;p&gt;Where it gets scary (for some) is when you use the name most often used to refer to them: auto mocking &lt;em&gt;container&lt;/em&gt;. “Wait wait wait, you’re using an IOC container in your unit test?! Configuration! Brittle! No thank you!”&lt;/p&gt;  &lt;p&gt;The name comes from &lt;a href="http://blog.eleutian.com/CommentView,guid,762249da-e25a-4503-8f20-c6d59b1a69bc.aspx"&gt;AutoMockingContainer&lt;/a&gt;, which introduced the idea of an automocker (to me, at least) by using the Castle Windsor IOC tool to do the heavy lifting. The &lt;a href="http://structuremap.sourceforge.net" target="_blank"&gt;StructureMap&lt;/a&gt; IOC tool distribution also includes an automocker. But here’s the thing – the container is just an implementation detail. You, as the test writer, never have to know there is a container in play. Automocking is not an IOC container feature. Automocking is really a feature of a mocking framework, but just happens to be packaged and/or named in relation to a container.&lt;/p&gt;  &lt;p&gt;In other words, I suspect the idea of automocking would be a lot less scary to people if it were packaged with the mocking frameworks. Suppose Rhino.Mocks added the method: MockRepository.CreateWithMocks&amp;lt;ClassToTest&amp;gt;() – would automocking still be as distasteful? Maybe it would use Castle MicroKernel under the hood, but you wouldn’t ever be exposed to it (just as Rhino.Mocks currently uses Castle DynamicProxy under the hood).&lt;/p&gt;  &lt;h2&gt;Why should I use the automocker?&lt;/h2&gt;  &lt;p&gt;Ok, so you’re over the container part. But you’re not convinced. The automocker can save you some tedious keystrokes, but code snippets solve that problem. I think the big benefit of the automocker is that it helps keep the content of your tests focused on the functionality being tested. It also helps keep your tests less brittle as your objects evolve.&lt;/p&gt;  &lt;p&gt;Consider the following class that I need to test:&lt;/p&gt;  &lt;div style="border-right:gray 1px solid;padding-right:4px;border-top:gray 1px solid;padding-left:4px;font-size:8pt;padding-bottom:4px;margin:20px 0px 10px;overflow:auto;border-left:gray 1px solid;width:97.5%;cursor:text;max-height:200px;line-height:12pt;padding-top:4px;border-bottom:gray 1px solid;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;background-color:#f4f4f4;"&gt;   &lt;div style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;     &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   1:&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;class&lt;/span&gt; PhotoController&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   2:&lt;/span&gt; {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   3:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;private&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;readonly&lt;/span&gt; IPhotoCatalog photoCatalog;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   4:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   5:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; PhotoController(IPhotoCatalog photoCatalog)&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   6:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   7:&lt;/span&gt;         &lt;span style="color:#0000ff;"&gt;this&lt;/span&gt;.photoCatalog = photoCatalog;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   8:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   9:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  10:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; PhotoDetails[] List()&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  11:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  12:&lt;/span&gt;         &lt;span style="color:#0000ff;"&gt;return&lt;/span&gt; photoCatalog.FindAll();&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  13:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  14:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  15:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;void&lt;/span&gt; Save(PhotoDetails photo)&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  16:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  17:&lt;/span&gt;         photoCatalog.AddPhoto(photo);&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  18:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  19:&lt;/span&gt; }&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;A couple tests, without automocking, might look like this:&lt;/p&gt;

&lt;div style="border-right:gray 1px solid;padding-right:4px;border-top:gray 1px solid;padding-left:4px;font-size:8pt;padding-bottom:4px;margin:20px 0px 10px;overflow:auto;border-left:gray 1px solid;width:97.5%;cursor:text;max-height:200px;line-height:12pt;padding-top:4px;border-bottom:gray 1px solid;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;background-color:#f4f4f4;"&gt;
  &lt;div style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;
    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   1:&lt;/span&gt; [TestFixture]&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   2:&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;class&lt;/span&gt; when_visiting_the_list_page&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   3:&lt;/span&gt; {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   4:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;private&lt;/span&gt; PhotoDetails[] Output;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   5:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;private&lt;/span&gt; PhotoDetails[] AllPhotos;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   6:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   7:&lt;/span&gt;     [SetUp]&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   8:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;void&lt;/span&gt; Setup()&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   9:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  10:&lt;/span&gt;         AllPhotos = &lt;span style="color:#0000ff;"&gt;new&lt;/span&gt;[] { &lt;span style="color:#0000ff;"&gt;new&lt;/span&gt; PhotoDetails(1), &lt;span style="color:#0000ff;"&gt;new&lt;/span&gt; PhotoDetails(2) };&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  11:&lt;/span&gt;         IPhotoCatalog photoCatalog = MockRepository.GenerateStub&amp;lt;IPhotoCatalog&amp;gt;();&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  12:&lt;/span&gt;         photoCatalog.Stub(c =&amp;gt; c.FindAll()).Return(AllPhotos);&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  13:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  14:&lt;/span&gt;         var controller = &lt;span style="color:#0000ff;"&gt;new&lt;/span&gt; PhotoController(photoCatalog);&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  15:&lt;/span&gt;         Output = controller.List();&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  16:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  17:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  18:&lt;/span&gt;     [Test]&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  19:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;void&lt;/span&gt; should_display_all_of_the_photos_in_the_catalog()&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  20:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  21:&lt;/span&gt;         Assert.AreEqual(AllPhotos, Output);&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  22:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  23:&lt;/span&gt; }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  24:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  25:&lt;/span&gt; [TestFixture]&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  26:&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;class&lt;/span&gt; when_saving_a_photo&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  27:&lt;/span&gt; {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  28:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;private&lt;/span&gt; PhotoDetails thePhoto;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  29:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;private&lt;/span&gt; IPhotoCatalog photoCatalog;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  30:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  31:&lt;/span&gt;     [SetUp]&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  32:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;void&lt;/span&gt; Setup()&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  33:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  34:&lt;/span&gt;         photoCatalog = MockRepository.GenerateStub&amp;lt;IPhotoCatalog&amp;gt;();&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  35:&lt;/span&gt;         var controller = &lt;span style="color:#0000ff;"&gt;new&lt;/span&gt; PhotoController(photoCatalog);&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  36:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  37:&lt;/span&gt;         thePhoto = &lt;span style="color:#0000ff;"&gt;new&lt;/span&gt; PhotoDetails(4);&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  38:&lt;/span&gt;         controller.Save(thePhoto);&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  39:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  40:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  41:&lt;/span&gt;     [Test]&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  42:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;void&lt;/span&gt; should_store_the_photo_details_in_the_catalog()&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  43:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  44:&lt;/span&gt;         photoCatalog.AssertWasCalled(c =&amp;gt; c.AddPhoto(thePhoto));&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  45:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  46:&lt;/span&gt; }&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;The same tests, with automocking, would look like this:&lt;/p&gt;

&lt;div style="border-right:gray 1px solid;padding-right:4px;border-top:gray 1px solid;padding-left:4px;font-size:8pt;padding-bottom:4px;margin:20px 0px 10px;overflow:auto;border-left:gray 1px solid;width:97.5%;cursor:text;max-height:200px;line-height:12pt;padding-top:4px;border-bottom:gray 1px solid;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;background-color:#f4f4f4;"&gt;
  &lt;div style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;
    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   1:&lt;/span&gt; [TestFixture]&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   2:&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;class&lt;/span&gt; when_visiting_the_list_page&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   3:&lt;/span&gt; {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   4:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;private&lt;/span&gt; PhotoDetails[] Output;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   5:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;private&lt;/span&gt; PhotoDetails[] AllPhotos;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   6:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   7:&lt;/span&gt;     [SetUp]&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   8:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;void&lt;/span&gt; Setup()&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   9:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  10:&lt;/span&gt;         AllPhotos = &lt;span style="color:#0000ff;"&gt;new&lt;/span&gt;[] { &lt;span style="color:#0000ff;"&gt;new&lt;/span&gt; PhotoDetails(1), &lt;span style="color:#0000ff;"&gt;new&lt;/span&gt; PhotoDetails(2) };&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  11:&lt;/span&gt;         var mocks = &lt;span style="color:#0000ff;"&gt;new&lt;/span&gt; RhinoAutoMocker&amp;lt;PhotoController&amp;gt;();&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  12:&lt;/span&gt;         var controller = mocks.ClassUnderTest;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  13:&lt;/span&gt;         mocks.Get&amp;lt;IPhotoCatalog&amp;gt;().Stub(c =&amp;gt; c.FindAll()).Return(AllPhotos);&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  14:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  15:&lt;/span&gt;         Output = controller.List();&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  16:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  17:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  18:&lt;/span&gt;     [Test]&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  19:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;void&lt;/span&gt; should_display_all_of_the_photos_in_the_catalog()&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  20:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  21:&lt;/span&gt;         Assert.AreEqual(AllPhotos, Output);&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  22:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  23:&lt;/span&gt; }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  24:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  25:&lt;/span&gt; [TestFixture]&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  26:&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;class&lt;/span&gt; when_saving_a_photo&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  27:&lt;/span&gt; {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  28:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;private&lt;/span&gt; PhotoDetails thePhoto;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  29:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;private&lt;/span&gt; RhinoAutoMocker&amp;lt;PhotoController&amp;gt; mocks;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  30:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  31:&lt;/span&gt;     [SetUp]&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  32:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;void&lt;/span&gt; Setup()&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  33:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  34:&lt;/span&gt;         mocks = &lt;span style="color:#0000ff;"&gt;new&lt;/span&gt; RhinoAutoMocker&amp;lt;PhotoController&amp;gt;();&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  35:&lt;/span&gt;         var controller = mocks.ClassUnderTest;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  36:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  37:&lt;/span&gt;         thePhoto = &lt;span style="color:#0000ff;"&gt;new&lt;/span&gt; PhotoDetails(4);&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  38:&lt;/span&gt;         controller.Save(thePhoto);&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  39:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  40:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  41:&lt;/span&gt;     [Test]&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  42:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;void&lt;/span&gt; should_store_the_photo_details_in_the_catalog()&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  43:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  44:&lt;/span&gt;         mocks.Get&amp;lt;IPhotoCatalog&amp;gt;().AssertWasCalled(c =&amp;gt; c.AddPhoto(thePhoto));&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  45:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  46:&lt;/span&gt; }&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Not a huge difference, mostly because the class under test only has a single dependency, so you are only hiding a single call to MockRepository.GenerateMock. But what about when I need to change some functionality on one of the methods? We realize we need to save a copy of the uploaded photo to the file system. We will need to change the Save method on the controller, and add a new dependency on an IFileSystem service.&lt;/p&gt;

&lt;p&gt;We’ll update the automocked tests to include the new specification (no other changes to the tests required):&lt;/p&gt;

&lt;div style="border-right:gray 1px solid;padding-right:4px;border-top:gray 1px solid;padding-left:4px;font-size:8pt;padding-bottom:4px;margin:20px 0px 10px;overflow:auto;border-left:gray 1px solid;width:97.5%;cursor:text;max-height:200px;line-height:12pt;padding-top:4px;border-bottom:gray 1px solid;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;background-color:#f4f4f4;"&gt;
  &lt;div style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;
    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   1:&lt;/span&gt; [Test]&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   2:&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;void&lt;/span&gt; should_store_the_photo_in_the_file_system()&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   3:&lt;/span&gt; {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   4:&lt;/span&gt;     mocks.Get&amp;lt;IFileSystem&amp;gt;().AssertWasCalled(fs =&amp;gt; fs.WriteFile(thePhoto.Filename, thePhoto.Data));&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   5:&lt;/span&gt; }&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;And the corresponding code to:&lt;/p&gt;

&lt;div style="border-right:gray 1px solid;padding-right:4px;border-top:gray 1px solid;padding-left:4px;font-size:8pt;padding-bottom:4px;margin:20px 0px 10px;overflow:auto;border-left:gray 1px solid;width:97.5%;cursor:text;max-height:200px;line-height:12pt;padding-top:4px;border-bottom:gray 1px solid;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;background-color:#f4f4f4;"&gt;
  &lt;div style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;
    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   1:&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;class&lt;/span&gt; PhotoController&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   2:&lt;/span&gt; {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   3:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;private&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;readonly&lt;/span&gt; IPhotoCatalog photoCatalog;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   4:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;private&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;readonly&lt;/span&gt; IFileSystem fileSystem;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   5:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   6:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; PhotoController(IPhotoCatalog photoCatalog, IFileSystem fileSystem)&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   7:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   8:&lt;/span&gt;         &lt;span style="color:#0000ff;"&gt;this&lt;/span&gt;.photoCatalog = photoCatalog;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   9:&lt;/span&gt;         &lt;span style="color:#0000ff;"&gt;this&lt;/span&gt;.fileSystem = fileSystem;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  10:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  11:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  12:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; PhotoDetails[] List()&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  13:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  14:&lt;/span&gt;         &lt;span style="color:#0000ff;"&gt;return&lt;/span&gt; photoCatalog.FindAll();&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  15:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  16:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  17:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;void&lt;/span&gt; Save(PhotoDetails photo)&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  18:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  19:&lt;/span&gt;         photoCatalog.AddPhoto(photo);&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  20:&lt;/span&gt;         fileSystem.WriteFile(photo.Filename, photo.Data);&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  21:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  22:&lt;/span&gt; }&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Now I can run all of my tests (for both Save and List) – they all compile and pass. Notice the only change to my tests was adding a single method that asserted the new behavior.&lt;/p&gt;

&lt;p&gt;The tests that didn’t use the automocker? Not as lucky. I add the new method that has the new assertion,&lt;/p&gt;

&lt;div style="border-right:gray 1px solid;padding-right:4px;border-top:gray 1px solid;padding-left:4px;font-size:8pt;padding-bottom:4px;margin:20px 0px 10px;overflow:auto;border-left:gray 1px solid;width:97.5%;cursor:text;max-height:200px;line-height:12pt;padding-top:4px;border-bottom:gray 1px solid;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;background-color:#f4f4f4;"&gt;
  &lt;div style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;
    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   1:&lt;/span&gt; [Test]&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   2:&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;void&lt;/span&gt; should_store_the_photo_in_the_file_system()&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   3:&lt;/span&gt; {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   4:&lt;/span&gt;     fileSystem.AssertWasCalled(fs =&amp;gt; fs.WriteFile(thePhoto.Filename, thePhoto.Data));&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   5:&lt;/span&gt; }&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;and then I have to update the Setup to create the new mock. Not a big deal. &lt;/p&gt;

&lt;div style="border-right:gray 1px solid;padding-right:4px;border-top:gray 1px solid;padding-left:4px;font-size:8pt;padding-bottom:4px;margin:20px 0px 10px;overflow:auto;border-left:gray 1px solid;width:97.5%;cursor:text;max-height:200px;line-height:12pt;padding-top:4px;border-bottom:gray 1px solid;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;background-color:#f4f4f4;"&gt;
  &lt;div style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;
    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   1:&lt;/span&gt; fileSystem = MockRepository.GenerateStub&amp;lt;IFileSystem&amp;gt;();&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   2:&lt;/span&gt; var controller = &lt;span style="color:#0000ff;"&gt;new&lt;/span&gt; PhotoController(photoCatalog, fileSystem);&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;The IDE complains because I’m trying to pass two arguments to the controller constructor, but it only takes one, so I update the controller. Still not a big deal. Try and run my test – ugh, compiler error. Looks like I need to update the tests for the List method? Why? I’m not making any changes to the List method? So I update the Setup for that test fixture as well:&lt;/p&gt;

&lt;div style="border-right:gray 1px solid;padding-right:4px;border-top:gray 1px solid;padding-left:4px;font-size:8pt;padding-bottom:4px;margin:20px 0px 10px;overflow:auto;border-left:gray 1px solid;width:97.5%;cursor:text;max-height:200px;line-height:12pt;padding-top:4px;border-bottom:gray 1px solid;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;background-color:#f4f4f4;"&gt;
  &lt;div style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;
    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   1:&lt;/span&gt; var fileSystem = MockRepository.GenerateStub&amp;lt;IFileSystem&amp;gt;();&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   2:&lt;/span&gt; var controller = &lt;span style="color:#0000ff;"&gt;new&lt;/span&gt; PhotoController(photoCatalog, fileSystem);&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Is it horrible? No. Is the automocked version nicer? I think so. The evolution of my code had minimal impact on the tests, plus I was able to eliminate “noise” code that creates all of the mocks. What does the automocked version cost me? Just an Add Reference to an assembly that I already have in the&amp;#160; tools/lib section of my source tree.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Neither my code nor my tests have any dependency on an IOC container. There is no external dependency or configuration. Even if I don’t use StructureMap in my application code, I could still use the RhinoAutoMocker that it includes for my tests. If my code DOES use StructureMap as its IOC container, I could still use the original Windsor-based AutoMockingContainer for my tests without a problem. The key point being that the IOC container you use in your application has nothing to do with the automocking you do in your tests.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://www.lostechies.com/aggbug.aspx?PostID=15466" width="1" height="1"&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JoshuaFlanagan?a=zoQLntqVbzI:P7X7xGRZPDg:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JoshuaFlanagan?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JoshuaFlanagan/~4/zoQLntqVbzI" height="1" width="1"/&gt;</description><category domain="http://www.lostechies.com/blogs/joshuaflanagan/archive/tags/rhinomocks/default.aspx">rhinomocks</category><feedburner:origLink>http://www.lostechies.com/blogs/joshuaflanagan/archive/2009/02/03/auto-mocking-explained.aspx</feedburner:origLink></item><item><title>Solution for monitoring multiple TeamCity servers</title><link>http://feedproxy.google.com/~r/JoshuaFlanagan/~3/iDaXPxHRmiA/solution-for-monitoring-multiple-teamcity-servers.aspx</link><pubDate>Thu, 15 Jan 2009 15:23:51 GMT</pubDate><guid isPermaLink="false">ded273ab-9e87-4979-8222-e4e2e46f1b46:13475</guid><dc:creator>Joshua Flanagan</dc:creator><slash:comments>0</slash:comments><wfw:commentRss>http://www.lostechies.com/blogs/joshuaflanagan/rsscomments.aspx?PostID=13475</wfw:commentRss><comments>http://www.lostechies.com/blogs/joshuaflanagan/archive/2009/01/15/solution-for-monitoring-multiple-teamcity-servers.aspx#comments</comments><description>&lt;p&gt;Proving yet again that the squeaky wheel gets the grease (this issue rose up to have the second highest number of votes in the TeamCity issue tracker), and what a truly responsive vendor JetBrains is, we now have a solution for the problem of monitoring more than one TeamCity server.&lt;/p&gt;  &lt;p&gt;Its a quick workaround, and requires you to run multiples instances of the notifier, but it satisfies the immediate need for me. Read the full details in &lt;a href="http://jetbrains.net/tracker/issue/TW-4230" target="_blank"&gt;Yegor Yarko’s comments in the issue tracker&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;A pleasant surprise is that the updated notifier still works against a TeamCity 3.x server, for those of us that haven’t had the chance to upgrade to 4.x.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://www.lostechies.com/aggbug.aspx?PostID=13475" width="1" height="1"&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JoshuaFlanagan?a=iDaXPxHRmiA:6VXHl_kKLbI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JoshuaFlanagan?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JoshuaFlanagan/~4/iDaXPxHRmiA" height="1" width="1"/&gt;</description><category domain="http://www.lostechies.com/blogs/joshuaflanagan/archive/tags/teamcity/default.aspx">teamcity</category><feedburner:origLink>http://www.lostechies.com/blogs/joshuaflanagan/archive/2009/01/15/solution-for-monitoring-multiple-teamcity-servers.aspx</feedburner:origLink></item><item><title>Raising awareness for TeamCity tray notifier issue</title><link>http://feedproxy.google.com/~r/JoshuaFlanagan/~3/2i3WDbqc5Jk/raising-awareness-for-teamcity-tray-notifier-issue.aspx</link><pubDate>Wed, 07 Jan 2009 01:54:30 GMT</pubDate><guid isPermaLink="false">ded273ab-9e87-4979-8222-e4e2e46f1b46:12362</guid><dc:creator>Joshua Flanagan</dc:creator><slash:comments>7</slash:comments><wfw:commentRss>http://www.lostechies.com/blogs/joshuaflanagan/rsscomments.aspx?PostID=12362</wfw:commentRss><comments>http://www.lostechies.com/blogs/joshuaflanagan/archive/2009/01/06/raising-awareness-for-teamcity-tray-notifier-issue.aspx#comments</comments><description>&lt;p&gt;&lt;strong&gt;&lt;font color="#ff0000"&gt;UPDATE:&lt;/font&gt;&lt;/strong&gt; JetBrains has released a solution to this issue, &lt;a href="http://www.lostechies.com/blogs/joshuaflanagan/archive/2009/01/15/solution-for-monitoring-multiple-teamcity-servers.aspx"&gt;I posted a follow up&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;Rumor has it that a number of your favorite .NET open source applications (including &lt;a href="http://structuremap.sourceforge.net/" target="_blank"&gt;StructureMap&lt;/a&gt;) will soon have a publicly available continuous integration server. The server will be running TeamCity by JetBrains. I’m a TeamCity fan, and we’ve been using it at work for a while now. Which is the rub. You see, since TeamCity allows you to easily host many projects on a single server (while delegating the building out to separate build agents), JetBrains did not foresee why anyone would ever want to monitor more than one server. This will soon be a very real scenario for followers of many OSS projects. The current tray notifier tool can only connect to a single build server. There aren’t even any command-line parameters that would allow you to run multiple instances of the notifier simultaneously.&lt;/p&gt;  &lt;p&gt;An issue has already been logged with JetBrains, but it doesn’t seem to have much attention &lt;strike&gt;(or priority)&lt;/strike&gt; (I misread their priority icons, it was marked as Major) yet. This post is an attempt to try and remedy that. If you currently use TeamCity, and want to follow the progress of projects like StructureMap, please go vote for &lt;a href="http://www.jetbrains.net/tracker/workspace?currentIssueId=TW-4230" target="_blank"&gt;Allow to watch several TeamCity servers via Tray Notifier&lt;/a&gt;.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://www.lostechies.com/aggbug.aspx?PostID=12362" width="1" height="1"&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JoshuaFlanagan?a=2i3WDbqc5Jk:FnH1FibmoB8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JoshuaFlanagan?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JoshuaFlanagan/~4/2i3WDbqc5Jk" height="1" width="1"/&gt;</description><category domain="http://www.lostechies.com/blogs/joshuaflanagan/archive/tags/teamcity/default.aspx">teamcity</category><feedburner:origLink>http://www.lostechies.com/blogs/joshuaflanagan/archive/2009/01/06/raising-awareness-for-teamcity-tray-notifier-issue.aspx</feedburner:origLink></item><item><title>Arrange Act Assert with StructureMap RhinoAutoMocker</title><link>http://feedproxy.google.com/~r/JoshuaFlanagan/~3/E8QU9Q3Ogq8/arrange-act-assert-with-structuremap-rhinoautomocker.aspx</link><pubDate>Fri, 26 Sep 2008 02:03:18 GMT</pubDate><guid isPermaLink="false">ded273ab-9e87-4979-8222-e4e2e46f1b46:5165</guid><dc:creator>Joshua Flanagan</dc:creator><slash:comments>1</slash:comments><wfw:commentRss>http://www.lostechies.com/blogs/joshuaflanagan/rsscomments.aspx?PostID=5165</wfw:commentRss><comments>http://www.lostechies.com/blogs/joshuaflanagan/archive/2008/09/25/arrange-act-assert-with-structuremap-rhinoautomocker.aspx#comments</comments><description>&lt;p&gt;Fresh on the heels of finally figuring out &lt;a href="http://www.lostechies.com/blogs/joshuaflanagan/archive/2008/09/25/the-rhino-mocks-assertwascalled-method-does-work.aspx" target="_blank"&gt;how to make the AAA syntax in Rhino.Mocks work&lt;/a&gt;, I&amp;#39;ve added support to StructureMap&amp;#39;s RhinoAutoMocker. If you pass MockMode.AAA to the constructor of your RhinoAutoMocker, all of the mocks that it creates will be in replay mode. This is required to successfully use the AssertWasCalled extension method. By default, if you do not pass a MockMode to the constructor, mocks will be created in record mode so that you can continue to set expectations the old fashioned way.&lt;/p&gt;  &lt;div&gt;   &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;[Test]
&lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;void&lt;/span&gt; TheAutoMockerOptionallyPushesInMocksInReplayModeToAllowForAAAsyntax()
{
    var autoMocker = &lt;span style="color:#0000ff;"&gt;new&lt;/span&gt; RhinoAutoMocker&amp;lt;ConcreteClass&amp;gt;(MockMode.AAA);

    autoMocker.ClassUnderTest.CallService();

    autoMocker.Get&amp;lt;IMockedService&amp;gt;().AssertWasCalled(s =&amp;gt; s.Go());
}&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;The code is in the &lt;a href="http://sourceforge.net/svn/?group_id=104740" target="_blank"&gt;trunk&lt;/a&gt;, and will be included in the eventual StructureMap 2.5 release.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Updated Oct 8, 2008:&lt;/strong&gt; The MockMode enumeration to enable AAA syntax has been changed to MockMode.AAA to be more self-explanatory.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://www.lostechies.com/aggbug.aspx?PostID=5165" width="1" height="1"&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JoshuaFlanagan?a=E8QU9Q3Ogq8:mu-LO6IA85k:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JoshuaFlanagan?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JoshuaFlanagan/~4/E8QU9Q3Ogq8" height="1" width="1"/&gt;</description><feedburner:origLink>http://www.lostechies.com/blogs/joshuaflanagan/archive/2008/09/25/arrange-act-assert-with-structuremap-rhinoautomocker.aspx</feedburner:origLink></item><item><title>The Rhino.Mocks' AssertWasCalled method does work</title><link>http://feedproxy.google.com/~r/JoshuaFlanagan/~3/hXCDdE6yTw0/the-rhino-mocks-assertwascalled-method-does-work.aspx</link><pubDate>Thu, 25 Sep 2008 14:08:28 GMT</pubDate><guid isPermaLink="false">ded273ab-9e87-4979-8222-e4e2e46f1b46:5158</guid><dc:creator>Joshua Flanagan</dc:creator><slash:comments>2</slash:comments><wfw:commentRss>http://www.lostechies.com/blogs/joshuaflanagan/rsscomments.aspx?PostID=5158</wfw:commentRss><comments>http://www.lostechies.com/blogs/joshuaflanagan/archive/2008/09/25/the-rhino-mocks-assertwascalled-method-does-work.aspx#comments</comments><description>&lt;p&gt;&lt;/p&gt;  &lt;p&gt;This behavior is probably clearly specified somewhere, but somehow it has been non-obvious to the four people on our team to the point that we were leaning toward banning its use. I didn’t want to go that far, so figured I better figure out the rules.&lt;/p&gt;  &lt;p&gt;I’m referring to the AssertWasCalled extension method in Rhino.Mocks that allows the Arrange/Act/Assert style of testing. We found that sometimes it would behave how we expected, and sometimes it would not, with no apparent explanation. I finally realized that it probably had something to do with how the mock objects were created, so I wrote this simple test:&lt;/p&gt;  &lt;div style="border-right:gray 1px solid;padding-right:4px;border-top:gray 1px solid;padding-left:4px;font-size:8pt;padding-bottom:4px;margin:20px 0px 10px;overflow:auto;border-left:gray 1px solid;width:97.5%;cursor:text;max-height:200px;line-height:12pt;padding-top:4px;border-bottom:gray 1px solid;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;background-color:#f4f4f4;"&gt;   &lt;div style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;     &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   1:&lt;/span&gt; [TestFixture]&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   2:&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;class&lt;/span&gt; AssertCalledTester&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   3:&lt;/span&gt; {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   4:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;private&lt;/span&gt; Dictionary&amp;lt;&lt;span style="color:#0000ff;"&gt;string&lt;/span&gt;, IDependency&amp;gt; dependencies;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   5:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;private&lt;/span&gt; MockRepository mocks;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   6:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   7:&lt;/span&gt;     [SetUp]&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   8:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;void&lt;/span&gt; Setup()&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   9:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  10:&lt;/span&gt;         mocks = &lt;span style="color:#0000ff;"&gt;new&lt;/span&gt; MockRepository();&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  11:&lt;/span&gt;         dependencies = &lt;span style="color:#0000ff;"&gt;new&lt;/span&gt; Dictionary&amp;lt;&lt;span style="color:#0000ff;"&gt;string&lt;/span&gt;, IDependency&amp;gt;()&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  12:&lt;/span&gt;        {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  13:&lt;/span&gt;            {&lt;span style="color:#006080;"&gt;&amp;quot;GenerateMock&amp;quot;&lt;/span&gt;, MockRepository.GenerateMock&amp;lt;IDependency&amp;gt;()},&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  14:&lt;/span&gt;            {&lt;span style="color:#006080;"&gt;&amp;quot;GenerateStub&amp;quot;&lt;/span&gt;, MockRepository.GenerateStub&amp;lt;IDependency&amp;gt;()},&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  15:&lt;/span&gt;            {&lt;span style="color:#006080;"&gt;&amp;quot;StrictMock&amp;quot;&lt;/span&gt;, &lt;span style="color:#0000ff;"&gt;this&lt;/span&gt;.mocks.StrictMock&amp;lt;IDependency&amp;gt;()},&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  16:&lt;/span&gt;            {&lt;span style="color:#006080;"&gt;&amp;quot;DynamicMock&amp;quot;&lt;/span&gt;, &lt;span style="color:#0000ff;"&gt;this&lt;/span&gt;.mocks.DynamicMock&amp;lt;IDependency&amp;gt;()},&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  17:&lt;/span&gt;            {&lt;span style="color:#006080;"&gt;&amp;quot;Stub&amp;quot;&lt;/span&gt;, &lt;span style="color:#0000ff;"&gt;this&lt;/span&gt;.mocks.Stub&amp;lt;IDependency&amp;gt;()},&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  18:&lt;/span&gt;        };&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  19:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  20:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  21:&lt;/span&gt;     [RowTest]&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  22:&lt;/span&gt;     [Row(&lt;span style="color:#006080;"&gt;&amp;quot;GenerateMock&amp;quot;&lt;/span&gt;)]&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  23:&lt;/span&gt;     [Row(&lt;span style="color:#006080;"&gt;&amp;quot;GenerateStub&amp;quot;&lt;/span&gt;)]&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  24:&lt;/span&gt;     [Row(&lt;span style="color:#006080;"&gt;&amp;quot;StrictMock&amp;quot;&lt;/span&gt;)]&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  25:&lt;/span&gt;     [Row(&lt;span style="color:#006080;"&gt;&amp;quot;DynamicMock&amp;quot;&lt;/span&gt;)]&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  26:&lt;/span&gt;     [Row(&lt;span style="color:#006080;"&gt;&amp;quot;Stub&amp;quot;&lt;/span&gt;)]&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  27:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;void&lt;/span&gt; When_does_it_work(&lt;span style="color:#0000ff;"&gt;string&lt;/span&gt; mockStyle)&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  28:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  29:&lt;/span&gt;         var dependency = dependencies[mockStyle];&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  30:&lt;/span&gt;         var caller = &lt;span style="color:#0000ff;"&gt;new&lt;/span&gt; Caller(dependency);&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  31:&lt;/span&gt;         caller.Go();&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  32:&lt;/span&gt;         dependency.AssertWasCalled(d =&amp;gt; d.DoSomething());&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  33:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  34:&lt;/span&gt; }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  35:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  36:&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;class&lt;/span&gt; Caller&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  37:&lt;/span&gt; {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  38:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;private&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;readonly&lt;/span&gt; IDependency dependency;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  39:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  40:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; Caller(IDependency dependency)&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  41:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  42:&lt;/span&gt;         &lt;span style="color:#0000ff;"&gt;this&lt;/span&gt;.dependency = dependency;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  43:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  44:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  45:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;void&lt;/span&gt; Go()&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  46:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  47:&lt;/span&gt;         dependency.DoSomething();&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  48:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  49:&lt;/span&gt; }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  50:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  51:&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;interface&lt;/span&gt; IDependency&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  52:&lt;/span&gt; {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  53:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;void&lt;/span&gt; DoSomething();&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  54:&lt;/span&gt; }&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;The results (3 out of 5 tests fail) clearly illustrated the source of our problems: AssertWasCalled only works automatically if you create your mocks using the new static MockRepository methods. &lt;a href="http://ayende.com/Blog/archive/2008/05/16/Rhino-Mocks--Arrange-Act-Assert-Syntax.aspx" target="_blank"&gt;Ayende’s initial announcement of the feature&lt;/a&gt; used these static methods, but it wasn’t explicit that they were required. They just appeared to be convenience methods. However, reading into the comments, I see that Ayende posted on 6/14/08 that the static methods are indeed special:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The static methods are returning an object that can be used without record/replay. It is already in replay mode, which is needed to use the AAA syntax.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If aren’t using the static methods (or are using something like &lt;a href="http://codebetter.com/blogs/jeremy.miller/archive/2008/02/09/automocker-in-structuremap-2-5.aspx" target="_blank"&gt;StructureMap’s RhinoAutoMocker&lt;/a&gt; where the mocks are created for you), you can still utilize AssertWasCalled as long as you call ReplayAll() before the “act” phase of your test. For example, in the code above I can add “mocks.ReplayAll()” before line 31 to get 4 out of the 5 tests to pass. The only one that fails is the one that uses a StrictMock, which isn’t too worrisome.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://www.lostechies.com/aggbug.aspx?PostID=5158" width="1" height="1"&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JoshuaFlanagan?a=hXCDdE6yTw0:Z08ptgXJuCM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JoshuaFlanagan?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JoshuaFlanagan/~4/hXCDdE6yTw0" height="1" width="1"/&gt;</description><category domain="http://www.lostechies.com/blogs/joshuaflanagan/archive/tags/rhinomocks/default.aspx">rhinomocks</category><category domain="http://www.lostechies.com/blogs/joshuaflanagan/archive/tags/structuremap/default.aspx">structuremap</category><feedburner:origLink>http://www.lostechies.com/blogs/joshuaflanagan/archive/2008/09/25/the-rhino-mocks-assertwascalled-method-does-work.aspx</feedburner:origLink></item><item><title>Running jQuery QUnit tests under Continuous Integration</title><link>http://feedproxy.google.com/~r/JoshuaFlanagan/~3/pAEjQzrz0VU/running-jquery-qunit-tests-under-continuous-integration.aspx</link><pubDate>Thu, 18 Sep 2008 22:23:54 GMT</pubDate><guid isPermaLink="false">ded273ab-9e87-4979-8222-e4e2e46f1b46:5038</guid><dc:creator>Joshua Flanagan</dc:creator><slash:comments>7</slash:comments><wfw:commentRss>http://www.lostechies.com/blogs/joshuaflanagan/rsscomments.aspx?PostID=5038</wfw:commentRss><comments>http://www.lostechies.com/blogs/joshuaflanagan/archive/2008/09/18/running-jquery-qunit-tests-under-continuous-integration.aspx#comments</comments><description>&lt;p&gt;&lt;/p&gt;  &lt;h2&gt;Setup&lt;/h2&gt;  &lt;p&gt;This post assumes you are already writing unit tests for your JavaScript code. If not, check out Chad&amp;#39;s post on &lt;a href="http://www.lostechies.com/blogs/chad_myers/archive/2008/08/28/getting-started-with-jquery-qunit-for-client-side-javascript-testing.aspx" target="_blank"&gt;Getting Started with jQuery QUnit&lt;/a&gt;. We use &lt;a href="http://jquery.com/" target="_blank"&gt;jQuery&lt;/a&gt; and QUnit at work, so my code examples are geared toward those frameworks. However, the approach should be very easy to adapt to your JavaScript framework of choice. &lt;/p&gt;  &lt;h2&gt;Overview&lt;/h2&gt;  &lt;p&gt;A good continuous integration server will let you run your automated tests, fail the build if a test fails, and publish a report of the test results. For all of that to work, your CI tool needs to be able to run the tests from a command-line and harvest the output in a form that it understands. One problem is that JavaScript testing frameworks like QUnit require you to open an HTML page in a browser to run and view the test results. You can certainly launch an HTML page in a browser from a command line (&amp;quot;start mypage.htm&amp;quot;), but you won&amp;#39;t be able to feed the results back to the CI server. We can get around this by using a tool like WatiN to control the browser from NUnit (or some other test framework which is supported by your CI server). WatiN allows you to spawn an instance of Internet Explorer, navigate to a URL, inspect the contents of the rendered DOM, and shutdown the browser, all from within an NUnit test.&lt;/p&gt;  &lt;h2&gt;A simple solution&lt;/h2&gt;  &lt;p&gt;Our first approach was to modify the QUnit test runner script so that it would create an element named TESTRESULTS that held the number of failed tests. We could then run the HTML page containing our tests and use WatiN to verify that TESTRESULTS contained a 0. An entire page of QUnit tests would be reported by NUnit as a single test. Either all of the QUnit tests passed making the single NUnit test pass, or any QUnit test failed and the NUnit test failed. You can see an example of this approach in my comment on Chad’s post referenced earlier. There are two problems with this approach: your total test count is inaccurate (you could have hundreds of QUnit tests which only show up as a single test in the CI test report), and more importantly, it is not immediately obvious which test failed, since it could have been any one of the many QUnit tests within a page.&lt;/p&gt;  &lt;h2&gt;Our current approach&lt;/h2&gt;  &lt;p&gt;A better approach would be to have a single NUnit test for each QUnit test. However, manually writing an NUnit test for each QUnit test sounds like a nightmare. What we need is a way to generate test cases dynamically. MbUnit supports this natively with its Factory attribute. We can get the same behavior in NUnit using the &lt;a href="http://www.nunit.org/index.php?p=iterativeTest" target="_blank"&gt;IterativeTest add-in&lt;/a&gt; (get the source and compile it against your version of NUnit). It allows you to specify a factory method that supplies a series of values to a single test method. When NUnit loads the class with this test method, it will create a separate test case for each value passed to the test method (if your factory method returns 3 values, you will have 3 test cases).&lt;/p&gt;  &lt;p&gt;The full code to our test fixture is at the bottom of this post. The method GetQUnitTestResults takes an HTML page name as input and returns a series of QUnitTest instances. Each QUnitTest instance contains the details of an individual QUnit test run: the filename, the test name, the result, and any related failure message. The method RunQUnitTests is the actual factory method used by the IterativeTest add-in, and allows me to easily add new QUnit HTML pages without having to create new test methods. The gory details of parsing the DOM for the QUnit results are in grabTestResultsFromWebPage. Its not my proudest piece of code, but it gets the job done (for now). This is likely the only method you would need to change if you were using a JavaScript test framework other than QUnit.&lt;/p&gt;  &lt;p&gt;Using this approach, we now get an accurate reflection of our test count, as each QUnit test is counted with every other NUnit test. Even better, when a test fails, we get detailed information about exactly which test failed and why. This is the output of a run that has a failing test:&lt;/p&gt;  &lt;div style="border-right:gray 1px solid;padding-right:4px;border-top:gray 1px solid;padding-left:4px;font-size:8pt;padding-bottom:4px;margin:20px 0px 10px;overflow:auto;border-left:gray 1px solid;width:97.5%;cursor:text;max-height:200px;line-height:12pt;padding-top:4px;border-bottom:gray 1px solid;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;background-color:#f4f4f4;"&gt;   &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;------ Test started: Assembly: DovetailCRM.IntegrationTesting.dll ------

IterativeTest addin loaded.
TestCase &lt;span style="color:#006080;"&gt;&amp;#39;DovetailCRM.IntegrationTesting.Web.JavaScript.JavaScriptTester.QUnit([tageditortester.htm] Post tag change to server module: should post the tag Name and item Id)&amp;#39;&lt;/span&gt;
failed: 
  the item that &lt;span style="color:#0000ff;"&gt;is&lt;/span&gt; tagged expected: 42 actual: 0
  Expected: collection containing &lt;span style="color:#006080;"&gt;&amp;quot;pass&amp;quot;&lt;/span&gt;
  But was:  &amp;lt; &lt;span style="color:#006080;"&gt;&amp;quot;fail&amp;quot;&lt;/span&gt; &amp;gt;
    D:\code\blue\source\DovetailCRM.IntegrationTesting\Web\JavaScript\JavaScriptTesters.cs(128,0): at DovetailCRM.IntegrationTesting.Web.JavaScript.QUnitTestHelpers.ShouldPass(QUnitTest theTest)
    D:\code\blue\source\DovetailCRM.IntegrationTesting\Web\JavaScript\JavaScriptTesters.cs(29,0): at DovetailCRM.IntegrationTesting.Web.JavaScript.JavaScriptTester.QUnit(Object current)


100 passed, 1 failed, 0 skipped, took 16.48 seconds.&lt;/pre&gt;
&lt;/div&gt;

&lt;h2&gt;Notes&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;NUnit add-ins must be compiled against the version of NUnit you are using. Make sure you have the same version of NUnit on your developer desktops and the CI server. We standardized on NUnit 2.4.7 simply because that was the latest version supported by &lt;a href="http://www.testdriven.net/" target="_blank"&gt;TestDriven.NET&lt;/a&gt;.&lt;/li&gt;

  &lt;li&gt;The IterativeTest add-in must be in the &lt;em&gt;addins&lt;/em&gt; sub-folder of the folder that contains your nunit-console.exe. If you want to run the tests via TestDriven.NET, you’ll also want to put a copy of the add-in in &amp;lt;ProgramFiles&amp;gt;\TestDriven.NET 2.0\NUnit\2.4\addins&lt;/li&gt;

  &lt;li&gt;While there is a way to get the TeamCity custom NUnit runner to load add-ins (using /addin:), I ran into trouble getting it all to work with WatiN as well. I discovered a &lt;a href="http://www.jetbrains.net/confluence/display/TCD4/TeamCity+Addin+for+NUnit" target="_blank"&gt;JetBrains add-in for NUnit&lt;/a&gt; that allows nunit-console.exe to report progress to TeamCity in the same way as their custom runner. While it isn’t supposed to be available until TeamCity 4.0, I found the necessary files in &lt;a href="http://jetbrains.net/tracker/issue/TW-5104" target="_blank"&gt;this patch&lt;/a&gt; (see the Attachments section) which enables NUnit 2.4.7 support to TeamCity 3.1.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;The Code&lt;/h2&gt;

&lt;div style="border-right:gray 1px solid;padding-right:4px;border-top:gray 1px solid;padding-left:4px;font-size:8pt;padding-bottom:4px;margin:20px 0px 10px;overflow:auto;border-left:gray 1px solid;width:97.5%;cursor:text;max-height:200px;line-height:12pt;padding-top:4px;border-bottom:gray 1px solid;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;background-color:#f4f4f4;"&gt;
  &lt;div style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;
    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   1:&lt;/span&gt; [TestFixture]&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   2:&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;class&lt;/span&gt; JavaScriptTester&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   3:&lt;/span&gt; {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   4:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;private&lt;/span&gt; IE _ie;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   5:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   6:&lt;/span&gt;     [TestFixtureSetUp]&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   7:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;void&lt;/span&gt; TestFixtureSetUp()&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   8:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   9:&lt;/span&gt;         _ie = TestBrowser.GetInternetExplorer();&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  10:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  11:&lt;/span&gt;    &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  12:&lt;/span&gt;     [IterativeTest(&lt;span style="color:#006080;"&gt;&amp;quot;RunQUnitTests&amp;quot;&lt;/span&gt;)]&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  13:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;void&lt;/span&gt; QUnit(&lt;span style="color:#0000ff;"&gt;object&lt;/span&gt; current)&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  14:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  15:&lt;/span&gt;         ((QUnitTest)current).ShouldPass();&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  16:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  17:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  18:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; IEnumerable RunQUnitTests()&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  19:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  20:&lt;/span&gt;         &lt;span style="color:#0000ff;"&gt;return&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;new&lt;/span&gt;[]&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  21:&lt;/span&gt;                    {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  22:&lt;/span&gt;                        &lt;span style="color:#006080;"&gt;&amp;quot;ContextMenuTester.htm&amp;quot;&lt;/span&gt;,&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  23:&lt;/span&gt;                        &lt;span style="color:#006080;"&gt;&amp;quot;FilterControlTester.htm&amp;quot;&lt;/span&gt;,&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  24:&lt;/span&gt;                        &lt;span style="color:#006080;"&gt;&amp;quot;RepeaterControlTester.htm&amp;quot;&lt;/span&gt;,&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  25:&lt;/span&gt;                        &lt;span style="color:#006080;"&gt;&amp;quot;FinderTester.htm&amp;quot;&lt;/span&gt;,&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  26:&lt;/span&gt;                        &lt;span style="color:#006080;"&gt;&amp;quot;ConsoleScriptTester.htm&amp;quot;&lt;/span&gt;,&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  27:&lt;/span&gt;                        &lt;span style="color:#006080;"&gt;&amp;quot;tageditortester.htm&amp;quot;&lt;/span&gt;,&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  28:&lt;/span&gt;                        &lt;span style="color:#006080;"&gt;&amp;quot;CrudFormTester.htm&amp;quot;&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  29:&lt;/span&gt;                    }.SelectMany(page =&amp;gt; GetQUnitTestResults(page));&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  30:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  31:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  32:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  33:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; IEnumerable&amp;lt;QUnitTest&amp;gt; GetQUnitTestResults(&lt;span style="color:#0000ff;"&gt;string&lt;/span&gt; testPage)&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  34:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  35:&lt;/span&gt;         TestFixtureSetUp();&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  36:&lt;/span&gt;         _ie.GoTo(&lt;span style="color:#0000ff;"&gt;string&lt;/span&gt;.Format(&lt;span style="color:#006080;"&gt;&amp;quot;http://localhost/Content/scripts/tests/{0}&amp;quot;&lt;/span&gt;, testPage));&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  37:&lt;/span&gt;         _ie.WaitForComplete(5);&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  38:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  39:&lt;/span&gt;         &lt;span style="color:#0000ff;"&gt;return&lt;/span&gt; grabTestResultsFromWebPage(testPage);&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  40:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  41:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  42:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; IEnumerable&amp;lt;QUnitTest&amp;gt; grabTestResultsFromWebPage(&lt;span style="color:#0000ff;"&gt;string&lt;/span&gt; testPage)&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  43:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  44:&lt;/span&gt;         &lt;span style="color:#008000;"&gt;// BEWARE: This logic is tightly coupled to the structure of the HTML generated by the QUnit testrunner&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  45:&lt;/span&gt;         &lt;span style="color:#008000;"&gt;// Also, this could probably be greatly simplified with a couple well-crafted xpath expressions&lt;/span&gt;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  46:&lt;/span&gt;         var testOL = _ie.Elements.Filter(Find.ById(&lt;span style="color:#006080;"&gt;&amp;quot;tests&amp;quot;&lt;/span&gt;))[0] &lt;span style="color:#0000ff;"&gt;as&lt;/span&gt; ElementsContainer;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  47:&lt;/span&gt;         &lt;span style="color:#0000ff;"&gt;if&lt;/span&gt; (testOL == &lt;span style="color:#0000ff;"&gt;null&lt;/span&gt;) &lt;span style="color:#0000ff;"&gt;yield&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;break&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  48:&lt;/span&gt;         var documentRoot = XDocument.Load(&lt;span style="color:#0000ff;"&gt;new&lt;/span&gt; StringReader(makeXHtml(testOL.OuterHtml))).Root;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  49:&lt;/span&gt;         &lt;span style="color:#0000ff;"&gt;if&lt;/span&gt; (documentRoot == &lt;span style="color:#0000ff;"&gt;null&lt;/span&gt;) &lt;span style="color:#0000ff;"&gt;yield&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;break&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  50:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  51:&lt;/span&gt;         &lt;span style="color:#0000ff;"&gt;foreach&lt;/span&gt; (var listItem &lt;span style="color:#0000ff;"&gt;in&lt;/span&gt; documentRoot.Elements())&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  52:&lt;/span&gt;         {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  53:&lt;/span&gt;             var testName = listItem.Elements().First( x =&amp;gt; x.Name.Is(&lt;span style="color:#006080;"&gt;&amp;quot;strong&amp;quot;&lt;/span&gt;)).Value;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  54:&lt;/span&gt;             var resultClass = listItem.Attributes().First(x =&amp;gt; x.Name.Is(&lt;span style="color:#006080;"&gt;&amp;quot;class&amp;quot;&lt;/span&gt;)).Value;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  55:&lt;/span&gt;             var failedAssert = String.Empty;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  56:&lt;/span&gt;             &lt;span style="color:#0000ff;"&gt;if&lt;/span&gt; (resultClass == &lt;span style="color:#006080;"&gt;&amp;quot;fail&amp;quot;&lt;/span&gt;)&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  57:&lt;/span&gt;             {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  58:&lt;/span&gt;                 var specificAssertFailureListItem = listItem.Elements()&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  59:&lt;/span&gt;                     .First(x =&amp;gt; x.Name.Is(&lt;span style="color:#006080;"&gt;&amp;quot;ol&amp;quot;&lt;/span&gt;)).Elements()&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  60:&lt;/span&gt;                     .First(x =&amp;gt; x.Name.Is(&lt;span style="color:#006080;"&gt;&amp;quot;li&amp;quot;&lt;/span&gt;) &amp;amp;&amp;amp; x.Attributes().First(a=&amp;gt; a.Name.Is(&lt;span style="color:#006080;"&gt;&amp;quot;class&amp;quot;&lt;/span&gt;)).Value == &lt;span style="color:#006080;"&gt;&amp;quot;fail&amp;quot;&lt;/span&gt;);&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  61:&lt;/span&gt;                 &lt;span style="color:#0000ff;"&gt;if&lt;/span&gt; (specificAssertFailureListItem != &lt;span style="color:#0000ff;"&gt;null&lt;/span&gt;)&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  62:&lt;/span&gt;                 {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  63:&lt;/span&gt;                     failedAssert = specificAssertFailureListItem.Value;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  64:&lt;/span&gt;                 }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  65:&lt;/span&gt;             }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  66:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  67:&lt;/span&gt;             &lt;span style="color:#0000ff;"&gt;yield&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;return&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;new&lt;/span&gt; QUnitTest&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  68:&lt;/span&gt;                              {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  69:&lt;/span&gt;                                  FileName = testPage,&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  70:&lt;/span&gt;                                  TestName = removeAssertCounts(testName),&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  71:&lt;/span&gt;                                  Result = resultClass, Message = failedAssert&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  72:&lt;/span&gt;                              };&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  73:&lt;/span&gt;         }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  74:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  75:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  76:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  77:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;private&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;static&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;string&lt;/span&gt; makeXHtml(&lt;span style="color:#0000ff;"&gt;string&lt;/span&gt; html)&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  78:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  79:&lt;/span&gt;         &lt;span style="color:#0000ff;"&gt;return&lt;/span&gt; html.Replace(&lt;span style="color:#006080;"&gt;&amp;quot;class=pass&amp;quot;&lt;/span&gt;, &lt;span style="color:#006080;"&gt;&amp;quot;class=\&amp;quot;pass\&amp;quot;&amp;quot;&lt;/span&gt;)&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  80:&lt;/span&gt;             .Replace(&lt;span style="color:#006080;"&gt;&amp;quot;class=fail&amp;quot;&lt;/span&gt;, &lt;span style="color:#006080;"&gt;&amp;quot;class=\&amp;quot;fail\&amp;quot;&amp;quot;&lt;/span&gt;)&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  81:&lt;/span&gt;             .Replace(&lt;span style="color:#006080;"&gt;&amp;quot;id=tests&amp;quot;&lt;/span&gt;, &lt;span style="color:#006080;"&gt;&amp;quot;id=\&amp;quot;tests\&amp;quot;&amp;quot;&lt;/span&gt;);&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  82:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  83:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  84:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  85:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;private&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;static&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;string&lt;/span&gt; removeAssertCounts(&lt;span style="color:#0000ff;"&gt;string&lt;/span&gt; fullTagText)&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  86:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  87:&lt;/span&gt;         &lt;span style="color:#0000ff;"&gt;if&lt;/span&gt; (fullTagText == &lt;span style="color:#0000ff;"&gt;null&lt;/span&gt;) &lt;span style="color:#0000ff;"&gt;return&lt;/span&gt; String.Empty;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  88:&lt;/span&gt;         &lt;span style="color:#0000ff;"&gt;int&lt;/span&gt; parenPosition = fullTagText.IndexOf(&lt;span style="color:#006080;"&gt;&amp;#39;(&amp;#39;&lt;/span&gt;);&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  89:&lt;/span&gt;         &lt;span style="color:#0000ff;"&gt;if&lt;/span&gt; (parenPosition &amp;gt; 0)&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  90:&lt;/span&gt;         {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  91:&lt;/span&gt;             &lt;span style="color:#0000ff;"&gt;return&lt;/span&gt; fullTagText.Substring(0, parenPosition - 1);&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  92:&lt;/span&gt;         }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  93:&lt;/span&gt;         &lt;span style="color:#0000ff;"&gt;return&lt;/span&gt; fullTagText;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  94:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  95:&lt;/span&gt; }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  96:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  97:&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;class&lt;/span&gt; QUnitTest&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  98:&lt;/span&gt; {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  99:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;string&lt;/span&gt; FileName { get; set; }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt; 100:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;string&lt;/span&gt; TestName { get; set; }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt; 101:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;string&lt;/span&gt; Result { get; set; }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt; 102:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;string&lt;/span&gt; Message { get; set; }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt; 103:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt; 104:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;override&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;string&lt;/span&gt; ToString()&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt; 105:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt; 106:&lt;/span&gt;         &lt;span style="color:#0000ff;"&gt;return&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;string&lt;/span&gt;.Format(&lt;span style="color:#006080;"&gt;&amp;quot;[{0}] {1}&amp;quot;&lt;/span&gt;, FileName, TestName);&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt; 107:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt; 108:&lt;/span&gt; }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt; 109:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt; 110:&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;static&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;class&lt;/span&gt; QUnitTestHelpers&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt; 111:&lt;/span&gt; {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt; 112:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;static&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;void&lt;/span&gt; ShouldPass(&lt;span style="color:#0000ff;"&gt;this&lt;/span&gt; QUnitTest theTest)&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt; 113:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt; 114:&lt;/span&gt;         Assert.That(theTest.Result.Split(&lt;span style="color:#006080;"&gt;&amp;#39; &amp;#39;&lt;/span&gt;), Has.Member(&lt;span style="color:#006080;"&gt;&amp;quot;pass&amp;quot;&lt;/span&gt;), theTest.Message);&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt; 115:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt; 116:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt; 117:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;static&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;bool&lt;/span&gt; Is(&lt;span style="color:#0000ff;"&gt;this&lt;/span&gt; XName xname, &lt;span style="color:#0000ff;"&gt;string&lt;/span&gt; name)&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt; 118:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt; 119:&lt;/span&gt;         &lt;span style="color:#0000ff;"&gt;return&lt;/span&gt; xname.ToString().Equals(name, StringComparison.OrdinalIgnoreCase);&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt; 120:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt; 121:&lt;/span&gt; }&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;It makes use of our TestBrowser class, which is just a simple wrapper around WatiN’s IE object.&lt;/p&gt;

&lt;div style="border-right:gray 1px solid;padding-right:4px;border-top:gray 1px solid;padding-left:4px;font-size:8pt;padding-bottom:4px;margin:20px 0px 10px;overflow:auto;border-left:gray 1px solid;width:97.5%;cursor:text;max-height:200px;line-height:12pt;padding-top:4px;border-bottom:gray 1px solid;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;background-color:#f4f4f4;"&gt;
  &lt;div style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;
    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   1:&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;static&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;class&lt;/span&gt; TestBrowser&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   2:&lt;/span&gt; {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   3:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;static&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;readonly&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;object&lt;/span&gt; LOCK_ROOT = &lt;span style="color:#0000ff;"&gt;new&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;object&lt;/span&gt;();&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   4:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;static&lt;/span&gt; IE _browser;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   5:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   6:&lt;/span&gt;     &lt;span style="color:#0000ff;"&gt;public&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;static&lt;/span&gt; IE GetInternetExplorer()&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   7:&lt;/span&gt;     {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   8:&lt;/span&gt;         &lt;span style="color:#0000ff;"&gt;if&lt;/span&gt;( _browser == &lt;span style="color:#0000ff;"&gt;null&lt;/span&gt; )&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;   9:&lt;/span&gt;         {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  10:&lt;/span&gt;             &lt;span style="color:#0000ff;"&gt;lock&lt;/span&gt;(LOCK_ROOT)&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  11:&lt;/span&gt;             {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  12:&lt;/span&gt;                 &lt;span style="color:#0000ff;"&gt;if&lt;/span&gt;( _browser == &lt;span style="color:#0000ff;"&gt;null&lt;/span&gt; )&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  13:&lt;/span&gt;                 {&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  14:&lt;/span&gt;                     IE.Settings.AutoMoveMousePointerToTopLeft = &lt;span style="color:#0000ff;"&gt;false&lt;/span&gt;;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  15:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  16:&lt;/span&gt;                     _browser = (IE)BrowserFactory.Create(BrowserType.InternetExplorer);&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  17:&lt;/span&gt;                     _browser.ShowWindow(NativeMethods.WindowShowStyle.Hide);&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  18:&lt;/span&gt;                 }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  19:&lt;/span&gt;             }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  20:&lt;/span&gt;         }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  21:&lt;/span&gt;&amp;#160; &lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  22:&lt;/span&gt;         &lt;span style="color:#0000ff;"&gt;return&lt;/span&gt; _browser;&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:white;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  23:&lt;/span&gt;     }&lt;/pre&gt;

    &lt;pre style="padding-right:0px;padding-left:0px;font-size:8pt;padding-bottom:0px;margin:0em;overflow:visible;width:100%;color:black;border-top-style:none;line-height:12pt;padding-top:0px;font-family:consolas, &amp;#39;Courier New&amp;#39;, courier, monospace;border-right-style:none;border-left-style:none;background-color:#f4f4f4;border-bottom-style:none;"&gt;&lt;span style="color:#606060;"&gt;  24:&lt;/span&gt; }&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://www.lostechies.com/aggbug.aspx?PostID=5038" width="1" height="1"&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JoshuaFlanagan?a=pAEjQzrz0VU:o99efVJGq-8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JoshuaFlanagan?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JoshuaFlanagan/~4/pAEjQzrz0VU" height="1" width="1"/&gt;</description><category domain="http://www.lostechies.com/blogs/joshuaflanagan/archive/tags/teamcity/default.aspx">teamcity</category><category domain="http://www.lostechies.com/blogs/joshuaflanagan/archive/tags/qunit/default.aspx">qunit</category><category domain="http://www.lostechies.com/blogs/joshuaflanagan/archive/tags/jquery/default.aspx">jquery</category><feedburner:origLink>http://www.lostechies.com/blogs/joshuaflanagan/archive/2008/09/18/running-jquery-qunit-tests-under-continuous-integration.aspx</feedburner:origLink></item><item><title>Monkey patching rake for use with TeamCity</title><link>http://feedproxy.google.com/~r/JoshuaFlanagan/~3/wytbKgOxkvs/monkey-patching-rake-for-use-with-teamcity.aspx</link><pubDate>Wed, 10 Sep 2008 22:08:54 GMT</pubDate><guid isPermaLink="false">ded273ab-9e87-4979-8222-e4e2e46f1b46:4874</guid><dc:creator>Joshua Flanagan</dc:creator><slash:comments>6</slash:comments><wfw:commentRss>http://www.lostechies.com/blogs/joshuaflanagan/rsscomments.aspx?PostID=4874</wfw:commentRss><comments>http://www.lostechies.com/blogs/joshuaflanagan/archive/2008/09/10/monkey-patching-rake-for-use-with-teamcity.aspx#comments</comments><description>&lt;p&gt;We use Ruby’s &lt;a href="http://rake.rubyforge.org/" target="_blank"&gt;rake&lt;/a&gt; as our build automation tool.&amp;#160; It provides a nice, XML-free way to define logical groupings of steps to perform, with dependency resolution.&lt;/p&gt;  &lt;p&gt;We use JetBrain’s &lt;a href="http://www.jetbrains.com/teamcity/" target="_blank"&gt;TeamCity&lt;/a&gt; as our continuous integration server. It has a neat feature that provides real-time reporting of build progress. TeamCity does not have any built-in support for rake*, so we use the simpleRunner build runner which allows you to kick off any process. However, when using the simpleRunner, you do not get any progress updates, since TeamCity doesn’t really know what is going on within your custom process.&lt;/p&gt;  &lt;p&gt;Turns out, this is really easy to fix. TeamCity has a &lt;a href="http://www.jetbrains.net/confluence/display/TCD3/Build+Script+Interaction+with+TeamCity" target="_blank"&gt;very simple API&lt;/a&gt; which allows a build script to interact with it. You just have to send specially formatted text messages to standard out (the console). For example, writing the following text to the console from your build script will cause the message “Starting compilation” to appear as the build status (on the project website, or the notification tray):&lt;/p&gt;  &lt;pre&gt;##teamcity[progressStart &amp;#39;Starting compilation&amp;#39;]&lt;/pre&gt;

&lt;p&gt;Now, it would be pretty annoying if we had to litter these messages throughout our build script. Fortunately, in ruby, classes are open by default. This means we can add or change behavior of existing classes, even if we do not have control over the source. In this case, I want to change the Execute method on Rake’s Task class so that it sends a message to TeamCity when a task starts and finishes. I use &lt;a href="http://blog.jayfields.com/2006/12/ruby-alias-method-alternative.html" target="_blank"&gt;Jay Fields’ example of redefining a method&lt;/a&gt; that still needs to call its original implemention (thanks to &lt;a href="http://stevenharman.net/" target="_blank"&gt;Steven Harman&lt;/a&gt; for the help via twitter). I created a file [TeamCity.rb] with the following contents in the same directory as our RAKEFILE:&lt;/p&gt;

&lt;p&gt;module TeamCity 
  &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; de&lt;font face="Courier New"&gt;f teamcity_progress(task) 
    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; teamcity_service_message &amp;#39;progressStart&amp;#39;, task 

    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; yield 

    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; teamcity_service_message &amp;#39;progressFinish&amp;#39;, task 

    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; end &lt;/font&gt;&lt;/p&gt;

&lt;p&gt;&lt;font face="Courier New"&gt;&amp;#160;&amp;#160;&amp;#160; def teamcity_service_message(message, message_value) 
    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; puts &amp;quot;##teamcity[#{message} &amp;#39;#{message_value}&amp;#39;]&amp;quot; 

    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; end 

    &lt;br /&gt;end &lt;/font&gt;&lt;/p&gt;

&lt;p&gt;&lt;font face="Courier New"&gt;class Rake::Task 
    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; include TeamCity 

    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; old_execute = self.instance_method(:execute) &lt;/font&gt;&lt;/p&gt;

&lt;p&gt;&lt;font face="Courier New"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; define_method(:execute) do |args| 
    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; teamcity_progress(&amp;quot;Executing #{name} rake task&amp;quot;) { old_execute.bind(self).call(args) } 

    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; end 

    &lt;br /&gt;end&lt;/font&gt;&lt;/p&gt;

&lt;p&gt;I then conditionally include this patch (so that we do not see all of the annoying TeamCity messages when running the build locally) with the following line in our RAKEFILE:&lt;/p&gt;

&lt;p&gt;&lt;font face="Courier New"&gt;require &amp;#39;TeamCity.rb&amp;#39; if ENV[&amp;quot;teamcity.dotnet.nunitlauncher&amp;quot;]&lt;/font&gt;&lt;/p&gt;

&lt;p&gt;I just arbitrarily picked an environment variable that I know is defined when run within TeamCity, that is not defined on our local machines. There is probably a more appropriate/universally applicable environment variable which indicates “running in TeamCity”.&lt;/p&gt;

&lt;p&gt;We now can see near real-time which rake task is currently running on the CI server. This can be nice when you are trying to fix a broken build, and you want to know if the current build has successfully passed the step that caused the failure. And yeah, it was also a fun way to explore monkey patching with ruby.&lt;/p&gt;

&lt;p&gt;* There is actually an &lt;a href="http://blogs.jetbrains.com/teamcity/2008/03/27/teamcity-rake-build-runner-eap-is-open/" target="_blank"&gt;EAP for a Rake Build Runner&lt;/a&gt;, which I did not find out about until doing some research for this post. From the description, it sounds more geared toward people actually building ruby applications, which does not include us. All of our code is C#/.NET – only our build script is in ruby. However, its probably worth investigating further.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://www.lostechies.com/aggbug.aspx?PostID=4874" width="1" height="1"&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JoshuaFlanagan?a=wytbKgOxkvs:nxIS8XxAtjU:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JoshuaFlanagan?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JoshuaFlanagan/~4/wytbKgOxkvs" height="1" width="1"/&gt;</description><category domain="http://www.lostechies.com/blogs/joshuaflanagan/archive/tags/teamcity/default.aspx">teamcity</category><category domain="http://www.lostechies.com/blogs/joshuaflanagan/archive/tags/ruby/default.aspx">ruby</category><feedburner:origLink>http://www.lostechies.com/blogs/joshuaflanagan/archive/2008/09/10/monkey-patching-rake-for-use-with-teamcity.aspx</feedburner:origLink></item><item><title>Hola Los Techies</title><link>http://feedproxy.google.com/~r/JoshuaFlanagan/~3/LmV15dpC0tk/hola-los-techies.aspx</link><pubDate>Wed, 10 Sep 2008 03:05:29 GMT</pubDate><guid isPermaLink="false">ded273ab-9e87-4979-8222-e4e2e46f1b46:4868</guid><dc:creator>Joshua Flanagan</dc:creator><slash:comments>1</slash:comments><wfw:commentRss>http://www.lostechies.com/blogs/joshuaflanagan/rsscomments.aspx?PostID=4868</wfw:commentRss><comments>http://www.lostechies.com/blogs/joshuaflanagan/archive/2008/09/09/hola-los-techies.aspx#comments</comments><description>&lt;p&gt;I&amp;#39;m very happy to accept an invitation to join the Los Techies community. I&amp;#39;ve been posting at &lt;a href="http://flimflan.com/blog"&gt;http://flimflan.com/blog&lt;/a&gt; for the past four years, and am looking forward to blogging more among some great company.&lt;/p&gt; &lt;p&gt;Subscribers to my old site should continue to get the new content, assuming they are subscribed via &lt;a href="http://feeds.feedburner.com/JoshuaFlanagan"&gt;http://feeds.feedburner.com/JoshuaFlanagan&lt;/a&gt;. However, if you aren&amp;#39;t already subscribed to the &lt;a href="http://feeds.feedburner.com/LosTechies" target="_blank"&gt;Los Techies main feed&lt;/a&gt;, I&amp;#39;d highly recommend it.&lt;/p&gt; &lt;p&gt;Since there is no code in this post, I&amp;#39;m going to keep it real short. Thanks again to the Los Techies crew for having me!&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://www.lostechies.com/aggbug.aspx?PostID=4868" width="1" height="1"&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JoshuaFlanagan?a=LmV15dpC0tk:l32LzOidbGk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JoshuaFlanagan?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JoshuaFlanagan/~4/LmV15dpC0tk" height="1" width="1"/&gt;</description><feedburner:origLink>http://www.lostechies.com/blogs/joshuaflanagan/archive/2008/09/09/hola-los-techies.aspx</feedburner:origLink></item></channel></rss>
