A Non-Framework Based “Given When Then” Unit Testing Style

2011 November 8
by admin

I was a tad sceptical about the whole BDD┬áthing I first heard about it (I think it was badly sold to me). It took me a while and the help of Anthony Marcarno (amongst others) to see the value of the Scenario based “Given When Then” way of developing acceptance criteria – and hence tests. Initially (where I was working at the time) we derived real value from simply using was of framing stories to escape the trap of thinking purely in terms of our technical tasks when planning stories. The example/scenario based approach was easy for our (non-technical) product owner and main stakeholder to understand and participate in. This alone is justification enough for the so-called Behaviour Driven Development approach – although I always preferred the term “Acceptance Test Driven Development“.

So when I went to the SpecFlow sessions at the Progressive .Net Tutorials in 2010 I was quite attracted to that framework as a means of driving test code generation from Cucumber specifications (feature files) that could (in theory) be written by our stakeholder or a non-technical analyst. Unfortunately this all came towards the end of a long project, so we never really managed to give the SpecFlow approach a thorough rinse.

I’m now fortunate to be working in a development environment at Esendex where TDD is a fundamental aspect of our approach. So, it’s no surprise there are to very smart and TDD experienced people I get to work with who are constantly looking at improving practises in this area.

So, to cut a long story short, a style evolved earlier this year from the team that does not rely anything but Moq and NUnit as external dependencies in our testing code. We explored StoryQ, FitNesse and SpecFlow (amongst others I think). However, since Esendex has a much more developer centred environment (where project execution is concerned). The (direct) stakeholders are often technical anyway, these frameworks were often found to get in the way of good testing code quality. What resulted was a team testing style that allows for a behaviour driven approach – for unit level, integration and end-t0-end acceptance tests. So behaviour required by customers can drive the development of code more clearly.

So here’s an example story & scenario:

Story:

As a website user

I want to be redirected back to the home page when I successfully log in

So I can carry on using the website with member enabled features

So a simple “happy path” scenario would be:

Given a login page

When valid username/password combination is entered

Then the credentials are used to authenticate

And the user is redirected back to the home page

So, if we were to use C# to build an ASP.Net MVC web application and write test code to cover this scenario in our “old” style (using NUnit and Moq) we could come up with the following simple test fixture for this scenario:

using System.Web.Mvc;
using ExampleTDDWebApp.Web.Controllers;
using ExampleTDDWebApp.Web.Models;
using Moq;
using NUnit.Framework;

namespace ExampleTDDWebApp.Tests
{
    [TestFixture]
    public class AuthenticationControllerTests
    {
        [Test]
        public void Login_WithValidUserCredentialsRedirectsUserToHomePage()
        {
            const string username = "bill@ben.com";
            const string password = "flobberdob";

            var mockAuthenticationService = new Mock();

            mockAuthenticationService
                .Setup(service => service.AuthenticateUserCredentials(username, password))
                .Returns(true);

            var authenticationController = new AuthenticationController(mockAuthenticationService.Object);
            var actionResult = (RedirectToRouteResult) authenticationController.Login(username, password);

            Assert.That(actionResult.RouteValues["controller"], Is.EqualTo("Home"));
            Assert.That(actionResult.RouteValues["action"], Is.EqualTo("Index"));

            mockAuthenticationService.VerifyAll();
        }
    }
}

So that’s not too bad – we’ve driven the code design from tests (simplistic as it is), but it doesn’t really reflect the Scenario – at least in terms of the way it reads. If test code is supposed to act as documentation then it isn’t clear what the scenario was that this fixture came out of. Also we’ve got 2 clear assertions in there and one slightly less clear one (in the form of the mock verification). The other thing here is that the arrange/act part of the test is all in the one test method – again, potentially making it less readable.

How about this then:


using System.Web.Mvc;
using ExampleTDDWebApp.Web.Controllers;
using ExampleTDDWebApp.Web.Models;
using Moq;
using NUnit.Framework;

namespace ExampleTDDWebApp.Tests
{
    [TestFixture]
    public class AuthenticationControllerLoginSuccessfulTests
    {
        private const string VALID_USERNAME = "bill@ben.com";
        private const string VALID_PASSWORD = "flobberdob";

        private Mock _mockAuthenticationService;
        private RedirectToRouteResult _actionResult;

        [SetUp]
        public void GivenALoginController_WhenUSerSuppliesValidCredentials()
        {
            _mockAuthenticationService = new Mock();

            _mockAuthenticationService
                .Setup(service => service.AuthenticateUserCredentials(It.IsAny(), It.IsAny()))
                .Returns(true);

            _actionResult = (RedirectToRouteResult)new AuthenticationController(_mockAuthenticationService.Object).Login(VALID_USERNAME, VALID_PASSWORD);
        }

        [Test]
        public void ThenTheAuthenticationServiceIsCalledWithTheCredentials()
        {
            _mockAuthenticationService.Verify(service => service.AuthenticateUserCredentials(VALID_USERNAME, VALID_PASSWORD));
        }

        [Test]
        public void ThenRedirectionGoesToTheHomeController()
        {
            Assert.That(_actionResult.RouteValues["controller"], Is.EqualTo("Home"));
        }

        [Test]
        public void ThenRedirectionGoesToIndexAction()
        {
            Assert.That(_actionResult.RouteValues["action"], Is.EqualTo("Index"));
        }
    }
}

That’s loads better:

  • Tests clearly separated – this case one assertion/verification per test method.
  • Setup and Test methods reflect the scenario – and hence required behaviour
  • Setup (Given and When) is less dense.
  • Verification on the mock Authentication service is more explicit
This style was adopted earlier this year in the development team at Esendex for pretty much all automated tests we write – Unit, Integration and even End-to-End Acceptance tests. It’s become a part of our development culture – so much so that it feels odd when not writing tests using this style.
What next? Well my esteemed colleague @MrAndrew is currently working on a BDD plugin for NUnit – this hopefully should improve things by adding some useful syntactic sugar into the mix when marking up the test code with more obvious ‘Given’ ‘When’ and ‘Then’ type attributes.
Thx to all the @Esendex dev team (current and former) – @samwessell, @darrenliddell, @neilkilbride, @jbjon and @ruby_gem for bringing me round to this!
No comments yet

Leave a Reply

Note: You can use basic XHTML in your comments. Your email address will never be published.

Subscribe to this comment feed via RSS