2008-07-09

Doing BDD, with Rhino Mocks AAA syntax

Ayende proposed a new AAA syntax in Rhino Mocks. I was wondering how it mixed with BDD.

Given this.

using System.Security;

public interface EmailService
{
    void Send(string message);
}

public interface AuthenticationService
{
    bool Authenticate(string username, string password);
}

public class SecureVault
{
    private readonly AuthenticationService _authenticationService;
    private readonly EmailService _emailService;
    private readonly string _secret;

    public SecureVault(AuthenticationService authenticationService, EmailService emailService, string secret)
    {
        _authenticationService = authenticationService;
        _emailService = emailService;
        _secret = secret;
    }

    public string GetSecret(string username, string password)
    {
        if(_authenticationService.Authenticate(username, password))
        {
            _emailService.Send(username + " successfully authenticated, and grabbed secret.");
            return _secret;
        }
        else
        {
            throw new SecurityException("Access denied!");
        }
    }
}

Using a stub test double, the if/else branch can be tested. This is called state based testing.

using System.Security;
using NUnit.Framework;
using NUnit.Framework.SyntaxHelpers;
using Rhino.Mocks;

namespace Specs_for_SecureVault
{
    [TestFixture]
    public class When_getting_secret_with_successful_authentication
    {
        private string _secret;
        private EmailService _emailService;

        [SetUp]
        public void Init()
        {
            var authenticationService = MockRepository.GenerateStub<AuthenticationService>();
            _emailService = MockRepository.GenerateMock<EmailService>();
            authenticationService.Stub(x => x.Authenticate("mortenlyhr", "pass1234")).Return(true);
            var secureVault = new SecureVault(authenticationService, emailService, "mysecretvalue");
            _secret = secureVault.GetSecret("mortenlyhr", "pass1234");
        }

        [Test]
        public void Should_return_secret()
        {
            Assert.That(_secret, Is.EqualTo("mysecretvalue"));
        }
    }

    [TestFixture]
    public class When_getting_secret_with_failed_authentication
    {
        [Test]
        [ExpectedException(typeof(SecurityException))]
        public void Should_throw_exception()
        {
            var authenticationService = MockRepository.GenerateStub<AuthenticationService>();
            var emailService = MockRepository.GenerateMock<EmailService>();
            authenticationService.Stub(x => x.Authenticate("mortenlyhr", "pass1234")).Return(false);
            var secureVault = new SecureVault(authenticationService, emailService, "mysecretvalue");
            secureVault.GetSecret("mortenlyhr", "pass1234");
        }
    }
}

So far so good, but what about interaction based testing?

The EmailService is a prime candidate for this. We can write an additional test for this expectation, in the "When_getting_secret_with_successful_authentication".

[Test]
public void Should_send_an_email_containing_the_username()
{
    _emailService.AssertWasCalled(x=>x.Send(Arg<string>.Matches(s=>s.Contains("mortenlyhr"))));
}

I don't think it can be any clearer that this, thank you BDD and Rhino Mocks ;-) - A match from heaven...

I am starting to see a pattern where mocks are private fields, and stubs have their return values as private fields. This is a lot less noise than when I did TDD, where every dependency was a private field. So again BDD helps you write less brittle specifications.

No comments: