Some Alternative Ways of Reading Context/Specification

Category:General PostTag: :

I have been playing with BDD conventions for several days and while I am no expert, I genuinely enjoy the way this style forces me to consider design before creating code.

I have tried to catch up on all the BDD purse fights going on out there and I understand that there are passionate differences in the way people prefer to interact with their tests. That said, regardless of what verbiage is being used or I believe what we are really doing is

preparing to test / invoking the SUT / asserting what the SUT should have done

In that spirit, can someone tell me the difference between these 3 base classes used for unit tests?

using NUnit.Framework;
 
namespace RoboDojo.War.Specs
{
    public class ContextSpecification
    {
        [SetUp]
        public void MainSetup()
        {
            SetContext();
            Because();
        }
 
        [TearDown]
        protected void MainTeardown()
        {
            CleanUp();
        }
 
        protected virtual void CleanUp() { }
        protected virtual void Because() { }
        protected virtual void SetContext() { }
    }
 
    public class AAA
    {
        [SetUp]
        public void MainSetup()
        {
            Arrange();
            Act();
        }
 
        [TearDown]
        protected void MainTeardown()
        {
            CleanUp();
        }
 
        protected virtual void Act() { }
        protected virtual void Arrange() { }
        protected virtual void CleanUp() { }
    }
 
    public class GWT
    {
        [SetUp]
        public void MainSetup()
        {
            Given();
            When();
        }
 
        [TearDown]
        protected void MainTeardown()
        {
            CleanUp();
        }
 
        protected virtual void When() { }
        protected virtual void Given() { }
        protected virtual void CleanUp() { }
    }
 
}

If there is a big difference there I am not seeing it.

What do the tests themselves look like?

using System;
using NUnit.Framework;
using RoboDojo.Core.BattleField;
using RoboDojo.War.Combat;
 
namespace RoboDojo.War.Specs.Combat_BattleMap
{
    [TestFixture]
    public class When_an_item_is_added : ContextSpecification
    {
        private BattleMap _battleMap;
        private MapFeature _feature;
 
        protected override void SetContext()
        {
            _battleMap = Mother.CreateAnEmptyBattleMap();
            _feature = new MapFeature(0, 0, Guid.NewGuid());
        }
 
        protected override void Because()
        {
            _battleMap.Add(_feature);
        }
 
        [Test]
        public void it_should_appear_on_the_map()
        {
            _battleMap.Exists(_feature.ID).ShouldBeTrue();
        }
 
        [Test]
        public void it_should_be_retrievable_from_the_map()
        {
            _battleMap.Find(_feature.ID).ID.ShouldEqual(_feature.ID);
        }
    }
 
    [TestFixture]
    public class When_an_item_is_added_2 : AAA
    {
        private BattleMap _battleMap;
        private MapFeature _feature;
 
        protected override void Arrange()
        {
            _battleMap = Mother.CreateAnEmptyBattleMap();
            _feature = new MapFeature(0, 0, Guid.NewGuid());
        }
 
        protected override void Act()
        {
            _battleMap.Add(_feature);
        }
 
        [Test]
        public void it_should_appear_on_the_map()
        {
            _battleMap.Exists(_feature.ID).ShouldBeTrue();
        }
 
        [Test]
        public void it_should_be_retrievable_from_the_map()
        {
            _battleMap.Find(_feature.ID).ID.ShouldEqual(_feature.ID);
        }
    }
 
    [TestFixture]
    public class When_an_item_is_added_3 : GWT
    {
        private BattleMap _battleMap;
        private MapFeature _feature;
 
        protected override void Given()
        {
            _battleMap = Mother.CreateAnEmptyBattleMap();
            _feature = new MapFeature(0, 0, Guid.NewGuid());
        }
 
        protected override void When()
        {
            _battleMap.Add(_feature);
        }
 
        [Test]
        public void it_should_appear_on_the_map()
        {
            _battleMap.Exists(_feature.ID).ShouldBeTrue();
        }
 
        [Test]
        public void it_should_be_retrievable_from_the_map()
        {
            _battleMap.Find(_feature.ID).ID.ShouldEqual(_feature.ID);
        }
    }
}

And how about the all important test runner view so that we can see the results of our tests? Can you tell which mental model produced which test?

image

So that last point is my point. The way you choose to think about your contextual configuration doesn?t need to be a slap fight, it is just how you think about it. I personally like to think in the GWT mental model and even apply it to my system components. So what?

There are some nice things people have come up with to reinforce their favorite mental model. SpecUnit for .NET is full of tasty morsels to be begged, borrowed, or stolen into your own tests.

The important thing, IMO, is that I can use these tests in a report to explain to my mom what my code is doing. And mom doesn?t code.

4 thoughts on “Some Alternative Ways of Reading Context/Specification

  1. I particularly like your point that the most important thing is that you can use these tests to communicate with your mom, who doesn’t code. I prefer Given, When, Then because I use them in conversation with business too (I sometimes say “If” instead of “Given”). I’m not sure that “Arrange, Act, CleanUp” are as easy to use. YMMV; if it works for you, go for it.

Comments are closed.