When I sit down to write a unit test, my first step is to describe for myself, in English instead of code, what I intend to test. The words I choose give clues as to the structure of the test and the style: interaction-based versus state-based. I’ll describe an example below.
RhinoMocks, and other mocking frameworks like Moq and TypeMock, are a support structure that let you write briefer, more expressive unit tests. RhinoMocks simplifies the quick creation of fake classes that your class under test relies on and interacts with. You can use those fakes for both styles of tests; RhinoMocks can make assertions about interactions in interaction-based tests, and it can provide non-exception-throwing behavior when you just need a stand-in for your state-based tests. I listen to the language of my description to know how I want to use that mock.
For example, your application might have an EmailDispatcher that decides which emails to send, then calls an EmailService to send the emails. You’d like to unit test the logic in the EmailDispatcher without sending thousands of real emails. This is a perfect scenario for RhinoMocks. RhinoMocks can build you a fake EmailService when your tests are running, and let you make assertions about when the EmailDispatcher calls that EmailService. RhinoMocks allows you to assert “When the EmailDispatcher is given input like this, it should call the EmailService with that info.”
Another great use for RhinoMocks is to support a class that has dependencies that aren’t interesting to the test you are writing—the class under test calls some other classes, but they aren’t relevant to the scenario you are testing. You merely need them to not be null and not throw Null Reference Exceptions when your test is running. Continuing with the above example, the EmailDispatcher could rely on a TemplateFormatter class to prepare the text of the email, but because you are writing a test fixture focusing on when emails get sent, and you are not testing how those emails are formatted, the TemplateFormatter should be as low profile in your test as possible. If TemplateFormatter is a simple class, you could just instantiate one and pass it to your EmailDispatcher. On the other hand, if TemplateFormatter requires its own swarm of dependencies, or if some of its operations rely on time-consuming, out-of-process dependencies like a database, replacing it with a fake class generated by RhinoMocks will keep your unit test quick and brief.
What is an example of misusing RhinoMocks? Where RhinoMocks becomes a burden is when it over-specifies the interactions between classes. RhinoMocks can make assertions about how one class calls another, which is its power and its danger. This excessive over-specification inhibits refactoring because it entrenches the coupling between those classes—you’d want to change the internals of one class, but that would break so many tests and require tedious fixing of those tests, dissuading you from making the refactoring. Anything that makes you queasy about doing the right thing is a bad deal. From the EmailDispatcher example, I do want to test that emails are well formatted, but I don’t really care whether this is done by a TemplateFormatter, or some other class, or an EmailDispatcher method, or a private class within the EmailDispatcher, or… whatever. I don’t need to enforce the method by which the EmailDispatcher accomplishes its work, therefore I don’t want to write tests that enforce the way emails get formatted.
My rule of thumb is to tell myself, in English, what I want to verify and listen to the words I choose. Here are two examples.
“I want to test when the EmailDispatcher tells the EmailSender to send mail.” I used words that refer to testing the conditions under which one class calls another. Therefore, I’ll write an interaction-based test, using RhinoMocks’ AssertWasCalled and AssertWasNotCalled methods.
“I want to test that the result from the EmailDispatcher’s work is an email in a certain format.” Looking at the properties on the result of an operation is a good fit for state-based tests. I’ll probably still use RhinoMocks to create a fake EmailSender, since I know the EmailDispatcher is going to call the EmailSender at the end, but I am not going to make any assertions about the methods called by the EmailDispatcher.
There’s a lot of great power in RhinoMocks, where it can give you just the right tool… or enough rope to hang yourself. We talk about responsible Rhino Mocking in Headspring’s Agile Bootcamp, and I’ll be presenting some of the finer points of RhinoMocks at the Central Texas .NET User Group on October 13.