BDD with MSpec and Rhino Auto Mocks (part 2)
November 19, 2009 · 3 minute read · Tags: Agile | BDD | MSpecs | RhinoMocks
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.
- using Machine.Specifications;
- namespace MSpecExample.Tests.Controllers
- {
- [Subject("Product Search")]
- public class when_product_search_page_requested
- {
- It should_return_product_search_page;
- }
- [Subject("Product Search")]
- public class when_asked_for_products_matching_search_term
- {
- It should_retrieve_a_list_of_products_with_titles_containing_the_search_term;
- It should_return_the_list_of_products_to_the_user;
- }
- [Subject("Product Search")]
- public class when_empty_search_term_entered
- {
- It should_return_an_error_message;
- }
- }
Lets start by implementing the first one (when product search page requested).
- [Subject("Product Search")]
- public class when_product_search_page_requested
- {
- static ProductController _controller;
- Establish context =
- () => {
- _controller = new ProductController();
- };
- It should_return_product_search_page;
- }
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.
- [Subject("Product Search")]
- public class when_product_search_page_requested
- {
- static ProductController _controller;
- static ActionResult _result;
- Establish context =
- () => { _controller = new ProductController(); };
- Because of =
- () => { _result = _controller.Search(); };
- It should_return_product_search_page;
- }
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.
- public class ProductController : Controller
- {
- public ActionResult Search()
- {
- throw new NotImplementedException();
- }
- }
Jumping back to our test, lets test that the behaviour under test (search) does what we expect.
- [Subject("Product Search")]
- public class when_product_search_page_requested
- {
- static ProductController _controller;
- static ActionResult _result;
- Establish context =
- () => { _controller = new ProductController(); };
- Because of =
- () => { _result = _controller.Search(); };
- It should_return_product_search_page =
- () => { _result.is_a_view_and().ViewName.ShouldEqual("Search"); };
- }
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).
- public static class TestExtensions
- {
- public static ViewResult is_a_view_and(this ActionResult result)
- {
- return result as ViewResult;
- }
- }
Now we make the test pass.
- public class ProductController : Controller
- {
- public ActionResult Search()
- {
- return View("Search");
- }
- }
Finally we run MSpec and marvel at the results 😉
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.