Blog

  • Home /
  • Blog /
  • Exploring BDD style specifications as a better TDD

Exploring BDD style specifications as a better TDD

February 3, 2008

You might think that I suffer from a severe case of acronymitis judging from the title of this post, but the only thing I suffer from right now is a terrible cold. Anyway, I'm currently looking into how a Behavior-Driven Development approach can solve some issues I have with writing unit tests. I first heard about BDD about a year ago, but I never really paid any attention to it after I've read the following posts about BDD style naming of context/specification:

There's more to BDD than just its style of naming tests, but I don't have a full picture of it yet. Heck, I'm not even sure if I agree with the whole approach that BDD has to offer.  Anyhow, I did a little spike in order to get me started and I was very pleased with the results. I first made up a very simple user story that I would implement for this sample.

        As a music fanatic I need to be able to
        view which records I own for a specific genre,
        so that I can make a choice of what I want to listen.

I first implemented this user story like I would normally do in a test-first way using the AutoMockingContainer I described in a previous post.

Unit tests

[TestFixture]
public class RecordServiceTestFixture 
    : AutoMockingTestFixture<RecordService>
{
    private const Int64 GenreId = 12;

    [Test]
    public void GetAllRecordsForGenre_
                      WithGenreIdentifier_
                      VerifyInteractionWithGenreRepository()
    {
        using(Record)
        {
            Expect.Call(MockGenreRepository.FindBy(GenreId))
                .Return(CreateGenre());
        }

        using(Playback)
        {
            CreateSubject().GetAllRecordsForGenre(GenreId);
        }
    }

    [Test]
    public void GetAllRecordsForGenre_
                      WithGenreIdentifier_
                      VerifyInteractionWithRecordRepository()
    {
        using(Record)
        {
            Genre genre = CreateGenre();
            SetupResult.For(MockGenreRepository.FindBy(0))
                .IgnoreArguments()
                .Return(genre);

            Expect.Call(
                MockRecordRepository.GetAllRecordsFor(genre))
                .Return(CreateListOfRecords());
        }

        using(Playback)
        {
            CreateSubject().GetAllRecordsForGenre(GenreId);
        }
    }

    [Test]
    public void GetAllRecordsForGenre_
                      WithGenreIdentifier_
                      VerifyListOfRecordObjectsIsReturned()
    {
        IEnumerable<Record> expectedRecords = 
            CreateListOfRecords();
        Genre genre = new Genre();

        SetupResult.For(MockGenreRepository.FindBy(0))
            .IgnoreArguments()
            .Return(genre);

        SetupResult.For(
            MockRecordRepository.GetAllRecordsFor(null))
            .IgnoreArguments()
            .Return(expectedRecords);

        using (PlaybackOnly)
        {
            IEnumerable<Record> records = 
            CreateSubject().GetAllRecordsForGenre(GenreId);
            Assert.That(records, Is.SameAs(expectedRecords));
        }
    }

    private static Genre CreateGenre()
    {
        return new Genre();
    }

    private static IEnumerable<Record> CreateListOfRecords()
    {
        return new List<Record>();
    }

    private IGenreRepository MockGenreRepository
    {
        get { return Mock<IGenreRepository>(); }
    }

    private IRecordRepository MockRecordRepository
    {
        get { return Mock<IRecordRepository>(); }
    }
}

(I needed to format the names of the unit tests. My apologies for that.)

Subject-under-test 

public class RecordService
{
    private readonly IGenreRepository _genreRepository;
    private readonly IRecordRepository _recordRepository;

    public RecordService(IGenreRepository genreRepository, 
               IRecordRepository recordRepository)
    {
        _genreRepository = genreRepository;
        _recordRepository = recordRepository;
    }

    public IEnumerable<Record> GetAllRecordsForGenre(
        Int64 genreId)
    {
        Genre genre = _genreRepository.FindBy(genreId);
        IEnumerable<Record> records = 
            _recordRepository.GetAllRecordsFor(genre);
        return records;
    }
}

Take a look at the second and third unit test. Did you notice that these tests contain calls to SetupResult.For. These calls are needed in order to get our tests up-and-running. These stubs ensure that I get to the particular point in my code that I want to test. But still, they feel like noise to me. They blur the test though it is a necessary evil.

With this approach, I also have one test fixture for every subject under test. When my subject has more than one method, all the tests for these methods are assembled in this one test fixture.

With the BDD context/specification naming style, every context is now mapped to one test fixture. So, if I have multiple methods on my subject under test, then I'll have multiple test fixtures. The test cases are the specifications itself.

A fluent language approach is used for naming these contexts/ specifications. See for yourself.

[TestFixture]
[Category("RecordServiceTestFixture2")]
public class 
When_retrieving_all_records_for_a_specific_genre 
    : Specification<RecordService>
{
    private const Int64 GenreId = 12;
    
    private Genre _genre;
    private IEnumerable<Record> _records;

    protected override void Before_each_specification()
    {
        _genre = new Genre();
        _records = new List<Record>();

        SetupResult.For(MockGenreRepository.FindBy(0))
                .IgnoreArguments()
                .Return(_genre);
        SetupResult.For(
            MockRecordRepository.GetAllRecordsFor(null))
            .IgnoreArguments()
            .Return(_records);
    }

    [Test]
    public void Then_find_the_genre_for_the_specified_id()
    {
        BackToRecord(MockGenreRepository);
        using(Record)
        {
            Expect.Call(MockGenreRepository.FindBy(GenreId))
                .Return(_genre);
        }

        using(Playback)
        {
            CreateSubject().GetAllRecordsForGenre(GenreId);
        }
    }

    [Test]
    public void Then_find_all_records_for_a_specific_genre()
    {
        BackToRecord(MockRecordRepository);
        using(Record)
        {
            Expect.Call(
            MockRecordRepository.GetAllRecordsFor(_genre))
                .Return(_records);
        }

        using(Playback)
        {
            CreateSubject().GetAllRecordsForGenre(GenreId);
        }        
    }

    [Test]
    public void Should_return_a_list_of_records()
    {
        using(PlaybackOnly)
        {
            IEnumerable<Record> records = 
              CreateSubject().GetAllRecordsForGenre(GenreId);
            Assert.That(records, Is.SameAs(_records));    
        }
    }

    private IGenreRepository MockGenreRepository
    {
        get { return Mock<IGenreRepository>(); }
    }

    private IRecordRepository MockRecordRepository
    {
        get { return Mock<IRecordRepository>(); }
    }
}

This looks a lot cleaner to me. The name of the test fixture now describes the context for the tests. If I have multiple contexts, then I have multiple test fixtures. I still put all these contexts in one code file, which is named TestFixture.cs. This way I can use the shortcut CTRL-SHIFT-N of Resharper to locate these. I use the Category attribute of NUnit to group the contexts in the test runner.

The test cases itself describe the actual behavior of of my subject-under-test. With this approach I was able to move the setup code to the Before_each_specification method of the context. I made some minor changes to the code of my base test fixture (which is now called Specification) that leverages the AutoMockingContainer.

public abstract class Specification<TSubject> 
    where TSubject : class
{
    private MockRepository _mockRepository;
    private AutoMockingContainer _autoMockingContainer;

    protected AutoMockingContainer AutoMockingContainer
    {
        get { return _autoMockingContainer; }
    }

    protected MockRepository MockRepository
    {
        get { return _mockRepository; }
    }

    protected IDisposable Playback
    {
        get { return MockRepository.Playback(); }
    }

    protected IDisposable PlaybackOnly
    {
        get
        {
            using (Record)
            { }

            return Playback;
        }
    }

    protected IDisposable Record
    {
        get { return MockRepository.Record(); }
    }

    protected TSubject CreateSubject()
    {
        return _autoMockingContainer.Create<TSubject>();
    }

    protected TDependecy Mock<TDependecy>() 
        where TDependecy : class
    {
        return _autoMockingContainer.Get<TDependecy>();
    }

    protected TDependecy Stub<TDependecy>() 
        where TDependecy : class
    {
        _autoMockingContainer.Mark<TDependecy>().Stubbed();
        return _autoMockingContainer.Get<TDependecy>();
    }

    protected virtual void Before_each_specification()
    {}

    protected virtual void After_each_specification()
    {}
           
    public void BackToRecord(Object mock)
    {
        MockRepository.BackToRecord(mock);
    }

    [SetUp]
    public void BaseSetUp()
    {
        _mockRepository = new MockRepository();
        _autoMockingContainer = 
            new AutoMockingContainer(_mockRepository);
        _autoMockingContainer.Initialize();

        Before_each_specification();

        CreateSubject();
    }

    [TearDown]
    public void BaseTearDown()
    {
        After_each_specification();

        _autoMockingContainer = null;
        _mockRepository = null;
    }
}

Running the specifications with a test runner gives this results:

BddTestRun

This approach results in very readable and concise unit tests. Every unit test describes a specification of the software you're trying to build. It also enables you to focus on a single specification at a time. The obligatory setup code is now banished to the Setup method, which reduces the amount of noise and prevents from having duplicate code in your tests.

For naming the contexts/specifications, I'm using Agile Joe's most excellent BDD macro. There's also a screen cast that is very helpful and its definitely worth your time if you're serious about using this approach. He explains how to setup the BDD macro and how to use it in Visual Studio.

I really like this approach and I can't wait to start using it in my day-to-day coding efforts.

Profile picture of Jan Van Ryswyck

Jan Van Ryswyck

Thank you for visiting my blog. I’m a professional software developer since Y2K. A blogger since Y2K+5. Curator of the Awesome Talks list. Past organizer of the European Virtual ALT.NET meetings. Thinking and learning about all kinds of technologies since forever.

Comments

About

Thank you for visiting my website. I’m a professional software developer since Y2K. A blogger since Y2K+5. Curator of the Awesome Talks list. Past organizer of the European Virtual ALT.NET meetings. Thinking and learning about all kinds of technologies since forever.

Contact information

(+32) 496 38 00 82

infonull@nullprincipal-itnull.be