Tell don't ask

November 29, 2009

BDD with MSpec and Rhino Auto Mocks (part 3)

Filed under: BDD,MSpecs,RhinoMocks — telldontask @ 7:09 pm
Tags: , , ,

In part 1 and part 2 we covered setting up MSpec, writing our specifications and then implementing one of those specifications.

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.

Establishing Context

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.

  1. public class concern_for_product_controller
  2. {
  3.     protected static ProductController _controller;
  4.  
  5.     Establish context =
  6.         () => { _controller = new ProductController(); };
  7. }

Now we can make all of the Product Controller tests inherit this class.

  1. [Subject("Product Search")]
  2. public class when_asked_for_products_matching_search_term : concern_for_product_controller
  3. {
  4.     It should_retrieve_a_list_of_products_with_titles_containing_the_search_term;
  5.     It should_return_the_list_of_products_to_the_user;
  6. }

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.

Rhino AutoMocker

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.

  1. public class concern_for_product_controller
  2. {
  3.     protected static ProductController _controller;
  4.     protected static IProductRepository _productRepository;
  5.     static RhinoAutoMocker<ProductController> mocker;
  6.  
  7.     Establish context =
  8.         () =>
  9.             {
  10.                 mocker = new RhinoAutoMocker<ProductController>();
  11.                 _controller = mocker.ClassUnderTest;
  12.                 _productRepository = mocker.Get<IProductRepository>();
  13.             };
  14. }

So what’s going on here?

  1. We’ve introduced an instance of the AutoMocker for our Product Controller.
  2. To make life easier we’ve introduced a field which references the AutoMocker’s “class under test” (in this case our Product Controller).
  3. 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.

Setting Expectations

  1. static ActionResult _result;
  2.  
  3.  Because of =
  4.      () => { _result = _controller.Search("test"); };
  5.  
  6.  It should_retrieve_a_list_of_products_with_titles_containing_the_search_term =
  7.      () => _productRepository.AssertWasCalled(x => x.FindProducts("test"));

  1. We call a new overloaded version of our controller’s Search method (which accepts a string search term).
  2. 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).

  1. It should_return_the_list_of_products_to_the_user =
  2.     () => _result.is_a_view_and().ViewName.ShouldEqual("SearchResults");

As I’ve gone along here I’ve implemented just enough to have the test compile.

IProductRepository
  1. public interface IProductRepository
  2. {
  3.     IList<Product> FindProducts(string searchTerm);
  4. }

ProductController
  1. public ActionResult Search(string searchTerm)
  2. {
  3.     throw new NotImplementedException();
  4. }

At this point we want to make the test pass so let’s do that.

  1. readonly IProductRepository _productRepository;
  2.  
  3. public ProductController(IProductRepository productRepository)
  4. {
  5.     _productRepository = productRepository;
  6. }
  7.  
  8. public ActionResult Search(string searchTerm)
  9. {
  10.     return View("SearchResults", _productRepository.FindProducts(searchTerm));
  11. }

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).

Stubs

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.

  1. Establish context =
  2.     () =>
  3.         {
  4.             _products = new List<Product>();
  5.             _productRepository.Stub(x => x.FindProducts("test")).Return(_products);
  6.         };

  1. We’ve added an Establish Context to our test (this will be used in conjunction with the one in our base class)
  2. 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.

  1. It should_return_the_list_of_products_to_the_user =
  2.     () => _result.is_a_view_and().ViewData.Model.ShouldEqual(_products);
  3.  
  4. It should_return_the_search_results_page_to_the_user =
  5.     () => _result.is_a_view_and().ViewName.ShouldEqual("SearchResults");

Putting it all together

So finally, our completed test implementation now looks like this…

  1. [Subject("Product Search")]
  2. public class when_asked_for_products_matching_search_term : concern_for_product_controller
  3. {
  4.     static List<Product> _products;
  5.     static ActionResult _result;
  6.  
  7.     Establish context =
  8.         () =>
  9.             {
  10.                 _products = new List<Product>();
  11.                 _productRepository.Stub(x => x.FindProducts("test")).Return(_products);
  12.             };
  13.  
  14.     Because of =
  15.         () => { _result = _controller.Search("test"); };
  16.  
  17.     It should_retrieve_a_list_of_products_with_titles_containing_the_search_term =
  18.         () => _productRepository.AssertWasCalled(x => x.FindProducts("test"));
  19.  
  20.     It should_return_the_list_of_products_to_the_user =
  21.         () => _result.is_a_view_and().ViewData.Model.ShouldEqual(_products);
  22.  
  23.     It should_return_the_search_results_page_to_the_user =
  24.         () => _result.is_a_view_and().ViewName.ShouldEqual("SearchResults");
  25. }

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.

November 19, 2009

BDD with MSpec and Rhino Auto Mocks (part 2)

Filed under: Agile,BDD — telldontask @ 10:07 pm
Tags: , , ,

In part 1, we looked at setting up MSpec and writing our first specifications.

Now we get on to the all important implementation stage.

We ended up with these basic specifications by the end of part 1.

  1. using Machine.Specifications;
  2.  
  3. namespace MSpecExample.Tests.Controllers
  4. {
  5.     [Subject("Product Search")]
  6.     public class when_product_search_page_requested
  7.     {
  8.         It should_return_product_search_page;
  9.     }
  10.  
  11.     [Subject("Product Search")]
  12.     public class when_asked_for_products_matching_search_term
  13.     {
  14.         It should_retrieve_a_list_of_products_with_titles_containing_the_search_term;
  15.         It should_return_the_list_of_products_to_the_user;
  16.     }
  17.  
  18.     [Subject("Product Search")]
  19.     public class when_empty_search_term_entered
  20.     {
  21.         It should_return_an_error_message;
  22.     }
  23. }

Lets start by implementing the first one (when product search page requested).

  1. [Subject("Product Search")]
  2. public class when_product_search_page_requested
  3. {
  4.     static ProductController _controller;
  5.  
  6.     Establish context =
  7.         () => {
  8.             _controller = new ProductController();
  9.         };
  10.  
  11.     It should_return_product_search_page;
  12. }

On first glance, the wacky syntax here might put people off. However, once you start writing these tests you tend to see past it to the specs and the tests themselves.

We start by establishing the context for our test, what we need for this test to actually run. In this case it’s the product controller that we’re testing, and so it makes sense to instantiate the product controller and assign it to a field for use within our tests.

Next we need to actually do something, perform the action which we are testing. In this case we’re talking about the user visiting the “product search page” so I will set up the test to call an action on the product controller called Search.

  1. [Subject("Product Search")]
  2. public class when_product_search_page_requested
  3. {
  4.     static ProductController _controller;
  5.     static ActionResult _result;
  6.  
  7.     Establish context =
  8.         () => { _controller = new ProductController(); };
  9.  
  10.     Because of =
  11.         () => { _result = _controller.Search(); };
  12.  
  13.     It should_return_product_search_page;
  14. }

Note how we have created a Because statement. In BDD terms this is the behaviour we are testing. As with the controller I store the result of calling this action in a field so we can use it elsewhere in the test.

At this point I should point out that in the spirit of TDD, I am only doing enough at this point to make this compile. So our Product Controller currently looks like this.

  1. public class ProductController : Controller
  2. {
  3.     public ActionResult Search()
  4.     {
  5.         throw new NotImplementedException();
  6.     }
  7. }

Jumping back to our test, lets test that the behaviour under test (search) does what we expect.

  1. [Subject("Product Search")]
  2. public class when_product_search_page_requested
  3. {
  4.     static ProductController _controller;
  5.     static ActionResult _result;
  6.  
  7.     Establish context =
  8.         () => { _controller = new ProductController(); };
  9.  
  10.     Because of =
  11.         () => { _result = _controller.Search(); };
  12.  
  13.     It should_return_product_search_page =
  14.         () => { _result.is_a_view_and().ViewName.ShouldEqual("Search"); };
  15. }

Using an extension method (thank you James Broome and JP Boodhoo) we can simultaneously check that our result is a view and also get a typed reference to the result (as a ViewResult).

  1. public static class TestExtensions
  2. {
  3.     public static ViewResult is_a_view_and(this ActionResult result)
  4.     {
  5.         return result as ViewResult;
  6.     }
  7. }

Now we make the test pass.

  1. public class ProductController : Controller
  2. {
  3.     public ActionResult Search()
  4.     {
  5.         return View("Search");
  6.     }
  7. }

Finally we run MSpec and marvel at the results ;-)

image

We still have several not implemented specs, but the one we have implemented now appears as a simple line of text. If there were any problems with the test (if it failed) we would see a red error message by the failing specification.

In part 3 we’ll look at more complex controller actions and introduce Rhino Auto Mocker.

November 10, 2009

BDD with MSpec and Rhino Auto Mocks (part 1)

Filed under: C# — telldontask @ 11:40 pm
Tags: , , ,

I’ve long been a fan of test driven development in theory but in practice have experienced many of the issues which turn people off TDD and unit testing in general.

Brittle tests which do too much, tell you very little about the meaning behind the code and are more a hindrance than a help when it comes to making changes at a later date.

Well as you may have guessed, given the title of this post, I have found an answer to these problems in the form of BDD using MSpec and Rhino Automocking. I have been using this approach for a good while now and continue to be pleasantly surprised by just how much fun it is writing my tests, but also how stupidly easy they are to change, and how well they document my project’s requirements.

Update: You can get MSpec from github.

External Tool

It’s a good idea to set up an external tool in Visual Studio to run your MSpec tests and produce html output.

Create a new external tool which launches mspec.exe with the following arguments.

$(TargetName)$(TargetExt) --html "$(ProjectDir)\Report.html"

Make sure the initial directory is $(BinDir) and tick Use output window.

image 

Using your favourite Test Runner

Included in the MSpec download are scripts to configure various test runners to recognise and run MSpec tests.

image

Simply run the bat file which relates to your build runner and way you go!

Writing Specifications

Ensure you add a reference (in your Tests Project) to Machine.Specifications.dll.

Now the fun begins.

I’ve created an ASP.NET MVC site and empty class library Tests project.

Let’s say we want to create a simple page which allows users to search for a product.

We’ll start by creating a new folder in our tests folder called Controllers and add a new class file called ProductControllerTets.cs

Having discussed this feature in detail with the client, I’ve a pretty good idea of what they want, so I start with the following.

  1. using Machine.Specifications;
  2.  
  3. namespace MSpecExample.Tests.Controllers
  4. {
  5.     [Subject("Product Search")]
  6.     public class when_product_search_page_requested
  7.     {
  8.         It should_return_product_search_page;
  9.     }
  10.  
  11.     [Subject("Product Search")]
  12.     public class when_asked_for_products_matching_search_term
  13.     {
  14.         It should_retrieve_a_list_of_products_with_titles_containing_the_search_term;
  15.         It should_return_the_list_of_products_to_the_user;
  16.     }
  17.  
  18.     [Subject("Product Search")]
  19.     public class when_empty_search_term_entered
  20.     {
  21.         It should_return_an_error_message;
  22.     }
  23. }

What I particularly like about this, is that you can really think about exactly what you’re doing and express it in code which will eventually become executable tests without actually implementing any code (yet!).

If we now build our test project and run the tests using the console runner via the external tool we set up earlier), we’ll get a report.html file in the tests project which looks like this…

image

In part 2, we’ll start implementing these tests.

In part 3, we’ll introduce Rhino AutoMocker.

Hello WordPress!

Filed under: Uncategorized — telldontask @ 11:31 pm

I’ve decided to switch my blog to WordPress.

I’ll be using Windows Live Writer to write most of my posts and the excellent Paste as Visual Studio Code plugin to handle c# code excerpts.

Theme: Rubric. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.