After the big refactor last iteration we're working on the unit tests. One of the things I wanted to get done was to replace all our lousy stubs (none of which would compile now after the refactor) with a true mock framework. When considering framework candidates, I had three requirements: First, it had to be easy to learn and use. Second, it had to be fully compatible with .NET 3.5 (i.e., using expression trees instead of strings to set expectations). Third, it had to be free.
The two candidates I chose are Rhino Mocks 3.5 and MoQ 2.5. Because MoQ is fully documented (Rhino's 3.5 is a massive change, so all of the documentation out there for the previous versions is useless) I went with MoQ. I still have Rhino as a reference just in case MoQ can't handle something. I haven't found one yet.
[more] I'll probably throw up some of the stuff I learned using it, but I wanted to do a little sneak preview just for S&G's. The following is one of my test methods that covers how an exception is logged by a CSV file parser. This test checks three things; it makes sure the filename that Capture gets is passed to the parser, that the Errored event is fired, and that the same exception object thrown by the parser is available for examination in the event arguments of the Errored event:
<span>[</span><span style="color: #2b91af">TestMethod</span><span>()]</span>
<span style="font-weight: bold; color: #2f2fff">public</span><span> </span><span style="font-weight: bold; color: #2f2fff">void</span><span> </span><span style="color: navy">Capture_fires_the_Error_event_if_parsing_fails</span><span>()</span>
<span>{</span>
<span> </span><span style="font-weight: bold; color: #2f2fff">var</span><span> </span><span style="color: navy">cm</span><span> </span><span style="color: fuchsia">=</span><span> </span><span style="color: navy">GetModule</span><span>();</span>
<span> </span><span style="font-weight: bold; color: #2f2fff">var</span><span> </span><span style="color: navy">ex</span><span> </span><span style="color: fuchsia">=</span><span> </span><span style="font-weight: bold; color: #2f2fff">new</span><span> </span><span style="color: #2b91af">FileParseFailedException</span><span>(</span><span style="background: #f3f8f3; color: green">"Lol MoQ"</span><span>);</span>
<span> </span><span style="font-weight: bold; color: #2f2fff">var</span><span> </span><span style="color: navy">threw</span><span> </span><span style="color: fuchsia">=</span><span> </span><span style="font-weight: bold; color: #2f2fff">false</span><span>;</span>
<span> </span><span style="font-weight: bold; color: #2f2fff">var</span><span> </span><span style="color: navy">filename</span><span> </span><span style="color: fuchsia">=</span><span> </span><span style="background: #f3f8f3; color: green">"lolfakefilename.txt"</span><span>;</span>
<span> </span><span style="color: green">// expectations</span>
<span> </span><span style="color: navy">cm</span><span style="color: fuchsia">.</span><span style="color: navy">Errored</span><span> </span><span style="color: fuchsia">+=</span><span> (</span><span style="color: navy">o</span><span>, </span><span style="color: navy">e</span><span>) </span><span style="color: fuchsia">=></span><span> { </span><span style="color: #2b91af">Assert</span><span style="color: fuchsia">.</span><span style="color: navy">AreEqual</span><span>(</span><span style="color: navy">ex</span><span>, </span><span style="color: navy">e</span><span style="color: fuchsia">.</span><span style="color: navy">Error</span><span>); </span><span style="color: navy">threw</span><span> </span><span style="color: fuchsia">=</span><span> </span><span style="font-weight: bold; color: #2f2fff">true</span><span>; };</span>
<span> </span><span style="color: navy">cm</span><span style="color: fuchsia">.</span><span style="color: navy">Parser</span>
<span> </span><span style="color: fuchsia">.</span><span style="color: navy">Mock</span><span>()</span>
<span> </span><span style="color: fuchsia">.</span><span style="color: navy">Expect</span><span>(</span><span style="color: navy">x</span><span> </span><span style="color: fuchsia">=></span><span> </span><span style="color: navy">x</span><span style="color: fuchsia">.</span><span style="color: navy">ParseFile</span><span>(</span>
<span> </span><span style="color: #2b91af">It</span><span style="color: fuchsia">.</span><span style="color: navy">Is</span><span style="color: fuchsia"><</span><span style="font-weight: bold; color: #2f2fff">string</span><span style="color: fuchsia">></span><span>(</span><span style="color: navy">s</span><span> </span><span style="color: fuchsia">=></span><span> </span><span style="color: navy">s</span><span style="color: fuchsia">.</span><span style="color: navy">Equals</span><span>(</span><span style="color: navy">filename</span><span>)), </span>
<span> </span><span style="color: #2b91af">It</span><span style="color: fuchsia">.</span><span style="color: navy">IsAny</span><span style="color: fuchsia"><</span><span style="color: #2b91af">DataTable</span><span style="color: fuchsia">></span><span>()))</span>
<span> </span><span style="color: fuchsia">.</span><span style="color: navy">Throws</span><span>(</span><span style="color: navy">ex</span><span>)</span>
<span> </span><span style="color: fuchsia">.</span><span style="color: navy">Verifiable</span><span>();</span>
<span> </span><span style="color: green">// test</span>
<span> </span><span style="color: navy">cm</span><span style="color: fuchsia">.</span><span style="color: navy">Accessor</span><span>()</span><span style="color: fuchsia">.</span><span style="color: navy">Capture</span><span>(</span><span style="color: navy">filename</span><span>);</span>
<span> </span><span style="color: green">// verify</span>
<span> </span><span style="color: #2b91af">Assert</span><span style="color: fuchsia">.</span><span style="color: navy">IsTrue</span><span>(</span><span style="color: navy">threw</span><span>);</span>
<span> </span><span style="color: navy">cm</span><span style="color: fuchsia">.</span><span style="color: navy">Parser</span><span style="color: fuchsia">.</span><span style="color: navy">Mock</span><span>()</span><span style="color: fuchsia">.</span><span style="color: navy">Verify</span><span>();</span>
<span>}</span>
Its pretty clear what's going on here (if you start reading at the expectations section). When the Errored event fires, we want to make sure we record this fact (threw) and make sure the event argument's Error property contains the same exception object we created earlier in the method.
Our module's Parser object is mocked; I have an extension method for every mocked object so I can quickly access the mock via the Mock() method on the mocked object itself. We grab the mock, and tell it to expect a call to ParseFile, which should contain the same filename we created earlier in the method (we don't care what DataTable we get as the second argument). We then tell the mock to throw our previously created exception object and indicate that we will want to verify this all happened later on by calling Verifiable().
To test, all I have to do is call the Capture method, passing in my fake filename. The method is private to my class, so I have to use an accessor to call it; again, I use an extension method to ease creating and using one. Once I call, all I need to do is verify the event fired and instruct my mock to verify my stated expectations were met. The event handler I used for the Errored event already checked the exception object for correctness prior to Capture returning.
I think this is pretty damn sweet. I'm very impressed with the framework, how easy it is to create unit tests with it, and how clear they are. I'm also completely happy that we were able to ditch our twenty-or-so stub classes and lame mocks we were using before.