So far we’ve covered very simple tests for code which has no external dependencies and does very little. Now we’ll turn our attention to those tests which (on the face of it) are more complicated and cover code which relies on data access, email services etc.
Lets tackle the the “when asked for products matching search term” specification.
As before, we’ll start by establishing the context for this test, and to cut down on duplication, I’ve moved the basic context setup to its own class.
Now we can make all of the Product Controller tests inherit this class.
To implement the specification “should retrieve a list of products…” we want to simply check that the controller asks for a list of products from wherever we’re hoping to get our data from.
In the spirit of writing tests first we haven’t got any kind of data access layer yet so now we decide we need a Product Repository.
To help test our repository we can introduce Rhino AutoMocker to the equation. Rhino AutoMocker is included as part of an AutoMocking library distributed with StructureMap.
To use it in your project, simply download StructureMap and reference StructureMap.AutoMocking and Rhino.Mocks in your project.
We need to wire up the automocker so we can use it in our tests. To that end we’ll change our base class.
So what’s going on here?
- We’ve introduced an instance of the AutoMocker for our Product Controller.
- To make life easier we’ve introduced a field which references the AutoMocker’s “class under test” (in this case our Product Controller).
- We’ve asked AutoMocker for an instance of IProductRepository and also referenced this as a handy field (which we can use in our tests).
Now we can turn our attention back to the test.
- We call a new overloaded version of our controller’s Search method (which accepts a string search term).
- Then we perform a simple check to assert that the FindProducts method was called on IProductRepository.
Finally, we want to check what gets sent back to the view. For now we’ll simply check the name of the view being returned (and that the result is a view).
As I’ve gone along here I’ve implemented just enough to have the test compile.
At this point we want to make the test pass so let’s do that.
Now our MSpec tests will pass.
When we run the tests, AutoMocker will generate a mocked version of our IProductRepository which our test uses to assert which methods were called. By letting AutoMocker generate our mocks we cut down on a lot of boilerplate code but don’t really loose anything in terms of flexibility and control (we can manually add mocks if we don’t want AutoMocker handling them for us).
Now lets say we want to check that the list of products retrieved from the repository is actually sent back with the view.
For this, we can set up the automocked IProductRepository to return a specific list of products which we can then check are sent back to the user.
Lets start by telling automocker to stub the Search method and return a new list of products.
- We’ve added an Establish Context to our test (this will be used in conjunction with the one in our base class)
- We’ve said that when the FindProducts method is called with the parameter “test” then a new generic list of Product will be returned
Now we’ll change the implementation of the spec to check that this same list of products is returned with the view. Whilst we’re at it we’ll move the test for the view’s name into it’s own specification.
Putting it all together
So finally, our completed test implementation now looks like this…
Hopefully these posts have demonstrated how well MSpec and Rhino AutoMocker can work together. The key is that the tests are easy to maintain and clearly state the criteria by which to judge whether the code under test is valid.
These criteria are stated as human understandable specifications which can be agreed with your users before writing any code. RhinoAutoMocker makes mocking and stubbing very straightforward and helps you to defer making decisions until the last responsible moment.
You will find the source code for this sample project on GitHub.