Showing posts with label BDD. Show all posts
Showing posts with label BDD. Show all posts

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.

2008-07-08

Doing BDD, when expecting an exception

I really like the look of my tests specifications, they read almost like plain English.

After fumbling around for a while, I finally get how to specify an expected exception.

namespace Specs_for_Dictionary
{
    [TestFixture]
    public class When_adding_two_items_with_the_same_key
    {
        [Test]
        [ExpectedException(typeof(ArgumentException))]
        public void Should_throw_exception()
        {
            const string key = "UniqueKey";
            Dictionary dictionary = new Dictionary();
            dictionary.Add(key, new object());
            dictionary.Add(key, new object());
        }
    }
}

There is no Setup/Init method. Usually have one, that satisfies the "When_operation_on_state" class name. So that I can reuse the same specification context, for multiple expectations.

But then it hit me, that there can only be one "Should_expectation" method name, because the given "When_operation_on_state" always throws the exception.

2008-07-07

Doing BDD

What is BDD, go read JP's post Getting started with BDD style Context/Specification base naming

Basically it a shift towards specifying behavior instead of testing behavior.

The specification consists of 3 parts:

  1. A namespace with called "Specs_for_subject"
  2. One or more classes called "When_operation_on_state", with a SetUp/Init method that performs the "operation_on_state"
  3. One or more methods called "Should_expectation"

An example:

namespace Specs_for_Dictionary
{
    [TestFixture]
    public class When_adding_an_item_to_an_empty_dictionary
    {
        private Dictionary _dictionary;
        private const string key = "UniqueKey";
        private readonly object value = new object();

        [SetUp]
        public void Init()
        {
            _dictionary = new Dictionary();
            _dictionary.Add(key, value);
        }

        [Test]
        public void Should_contain_item_with_key()
        {
            Assert.That(_dictionary.ContainsKey(key));
        }

        [Test]
        public void Should_contain_item_with_value_for_key()
        {
            Assert.That(_dictionary[key], Is.EqualTo(value));
        }
    }
}

Since the "When_operation_on_state" part is the context, this where the meat is. In TDD I would usually have the test methods do the operation, and sometimes even setting the state. That often made me write brittle tests, that would stop working when making changes to non related code. BDD changed that for the better, not that it is impossible with TDD, but it is much easier with BDD.

An added side effect is the specifications read like plain English, so business rule specifications can actually be read be read by the customer.