I have moved my active blog over to tumblr. I've maintained this blog for reference but will be posting to http://www.robustsoftware.co.uk instead. I've pointed my Feedburner feed to tumblr so if you're subscribed already you should already have switched with me.

BDD from scratch – Build your own framework (Part 2)

In part one I covered how to organise your tests into the Given-When-Then style through the use of an abstract base class. In this post I’m going to show how you can manage your mocks more efficiently. Though not really a part of BDD I’ve found it very useful in clarifying the behaviour described in my tests as it reduces the amount of noise caused by the definition of variables.

I’m going to walk you through an example test, starting off with what we finished off with in part 1. We are going to be testing this standard ASP.NET MVC controller method:

namespace RobustSoftware.BddFromScratch.WebApplication.Controllers

{

    public class BooksController : Controller

    {

        private readonly IBookService bookService;

        private readonly IAuthenticationService authenticationService;

 

        public BooksController(IBookService bookService, IAuthenticationService authenticationService)

        {

            this.bookService = bookService;

            this.authenticationService = authenticationService;

        }

 

        public ViewResult YourBooks()

        {

            var currentUser = authenticationService.CurrentUser();

            ViewData.Model = bookService.OwnedBy(currentUser);

            return View("YourBooks");

        }

    }

}

I’m going to be using Moq as my mocking framework for this test; again I’ve applied the same concept to other frameworks such as Rhino Mocks. Here is how our test starts out:

namespace RobustSoftware.BddFromScratch.WebApplication.Test.Controllers.Books

{

    public class DisplayingYourBooks : BehaviourTest

    {

        private MockFactory factory;

        private BooksController controller;

        private Mock<IBookService> bookService;

        private Mock<IAuthenticationService> authenticationService;

        private ViewResult result;

        private List<Book> yourBooks;

        private User currentUser;

 

        protected override void Given()

        {

            factory = new MockFactory(MockBehavior.Loose);

 

            bookService = factory.Create<IBookService>();

            authenticationService = factory.Create<IAuthenticationService>();

 

            yourBooks = new List<Book>();

            currentUser = new User();

 

            authenticationService.Setup(x => x.CurrentUser()).Returns(currentUser);

            bookService.Setup(x => x.OwnedBy(currentUser)).Returns(yourBooks);

 

            controller = new BooksController(bookService.Object, authenticationService.Object);

        }

 

        protected override void When()

        {

            result = controller.YourBooks();

        }

 

        [Then]

        public void ShownYourBooksView()

        {

            Assert.AreEqual("YourBooks", result.ViewName);

        }

 

        [Then]

        public void PassedListOfYourBooks()

        {

            Assert.AreSame(yourBooks, result.ViewData.Model);

        }

    }

}

The obvious step to reduce the amount of test code on display would be to move the setup of the MockFactory up into the base class. We’re going to take it a step further than that though and introduce a mock management class. Here is what it looks like:

namespace RobustSoftware.BddFromScratch.Framework

{

    public class MockManager

    {

        private readonly MockFactory factory;

        private readonly IDictionary<Type, object> mockDictionary;

 

        public MockManager()

        {

            factory = new MockFactory(MockBehavior.Loose);

            mockDictionary = new Dictionary<Type, object>();

        }

 

        public Mock<T> Mock<T>() where T : class

        {

            var type = typeof(T);

 

            if (!mockDictionary.ContainsKey(type))

            {

                mockDictionary.Add(type, factory.Create<T>());

            }

 

            return mockDictionary[type] as Mock<T>;

        }

    }

}

This is a wrapper around a dictionary of mock objects. When we ask for a mock that has not been requested before, one is created. Otherwise, the previously created mock is retrieved from the dictionary and returned. We’ll add the creation of the mock manager to our abstract base class and expose the Mock<T> method for use within our tests:

namespace RobustSoftware.BddFromScratch.Framework

{

    [TestFixture]

    public abstract class BehaviourTest

    {

        private MockManager mockManager;

 

        protected Mock<T> Mock<T>() where T : class

        {

            return mockManager.Mock<T>();

        }

 

        [TestFixtureSetUp]

        public void Setup()

        {

            mockManager = new MockManager();

 

            Given();

            When();

        }

 

        protected abstract void Given();

        protected abstract void When();

    }

}

Now we can utilise the mock manager within our original test, reducing the lines of code considerably:

namespace RobustSoftware.BddFromScratch.WebApplication.Test.Controllers.Books

{

    public class DisplayingYourBooks : BehaviourTest

    {

        private BooksController controller;

        private ViewResult result;

        private List<Book> yourBooks;

        private User currentUser;

 

        protected override void Given()

        {

            yourBooks = new List<Book>();

            currentUser = new User();

 

            Mock<IAuthenticationService>().Setup(x => x.CurrentUser()).Returns(currentUser);

            Mock<IBookService>().Setup(x => x.OwnedBy(currentUser)).Returns(yourBooks);

 

            controller = new BooksController(Mock<IBookService>().Object, Mock<IAuthenticationService>().Object);

        }

 

        protected override void When()

        {

            result = controller.YourBooks();

        }

 

        [Then]

        public void ShownYourBooksView()

        {

            Assert.AreEqual("YourBooks", result.ViewName);

        }

 

        [Then]

        public void PassedListOfYourBooks()

        {

            Assert.AreSame(yourBooks, result.ViewData.Model);

        }

    }

}

As we no longer have to worry about variable declaration and assignment for our mocks, we can leave the test code to signal the intent of our test. As there is no real setup for the fields currentUser and yourBooks, I’d be tempted to in-line those variables but this is a matter of personal taste:

namespace RobustSoftware.BddFromScratch.WebApplication.Test.Controllers.Books

{

    public class DisplayingYourBooks : BehaviourTest

    {

        private BooksController controller;

        private ViewResult result;

        private List<Book> yourBooks = new List<Book>();

        private User currentUser = new User();

 

        protected override void Given()

        {

            Mock<IAuthenticationService>().Setup(x => x.CurrentUser()).Returns(currentUser);

            Mock<IBookService>().Setup(x => x.OwnedBy(currentUser)).Returns(yourBooks);

 

            controller = new BooksController(Mock<IBookService>().Object, Mock<IAuthenticationService>().Object);

        }

 

        protected override void When()

        {

            result = controller.YourBooks();

        }

 

        [Then]

        public void ShownYourBooksView()

        {

            Assert.AreEqual("YourBooks", result.ViewName);

        }

 

        [Then]

        public void PassedListOfYourBooks()

        {

            Assert.AreSame(yourBooks, result.ViewData.Model);

        }

    }

}

As you can see, this reduces the amount of code required to establish the same context to test a given behaviour. The less lines of code you need to set up a test, the easier it is going to be to understand that test in the future. This, along with the more explanatory naming of your test, makes it much easier to maintain that test in the future.

Next up, I’ll show how we can utilise our MockManager to implement auto mocking. This will reduce the amount of code in our test slightly, but more importantly it makes our test suite less brittle.

Related posts