Playing with BDD: ContextSpecification

So I’ve been messing around today with Behavior Driven Design today (Bellware Driven Design? Or is it Context/Specification?). I thought I’d share my super simple example with you today, and see if anyone wants to claw my eyes out for my obvious inadequacies.

Everyone seems to have a ContextSpecification class, but nobody really seems to think it’s terribly important to say what’s in it. That or they show the code (Thanks JP!) but then it’s missing confusing pieces (What’s a “unit_test_container”? Grr @ JP!).

Maybe it’s obvious to everyone else. That’s ok. It really really wasn’t to me.

I ghetto-rolled a ContextSpecification class for myself on top of NUnit. It probably makes the BDD Mafia want to set fire to my children, but it has worked for me. And when I say it has worked for me, I mean I’ve been using it at least 2 hours now. Here it is:

[TestFixture]
public abstract class ContextSpecification
{
    [SetUp]
    public void SetUp()
    {
        EstablishContext();
        Because();     }

    [TearDown]
    public void TearDown()
    {
        AfterEachSpecification();
    }

    protected abstract void Because();
    protected abstract void EstablishContext();

    protected virtual void AfterEachSpecification()
    {
    }

    protected InterfaceType Dependency<InterfaceType>()
    {
        return MockRepository.GenerateMock<InterfaceType>();
    }

    protected InterfaceType Stub<InterfaceType>()
    {
        return MockRepository.GenerateStub<InterfaceType>();
    }
}

So the SetUp method calls EstablishContext() and Because(). Simple.

I was testing this stupid little PasswordService of mine, so I created a PasswordServiceSpecification that setup some mocking and stubbing using our friend Rhino Mocks 3.5 “Now with extra => operators”.

public abstract class PasswordServiceSpecification : ContextSpecification
{
    protected IPasswordService passwordService;
    protected IPasswordStrengthService passwordStrengthService;
    protected IPasswordHashGenerator passwordHashGenerator;

    protected override void EstablishContext()
    {
        passwordHashGenerator = Stub<IPasswordHashGenerator>();
        passwordHashGenerator.Stub(x => x.CalculatePasswordHash(string.Empty))
            .IgnoreArguments()
            .Return(Password.InvalidPassword);

        passwordStrengthService = Stub<IPasswordStrengthService>();
        passwordStrengthService.Stub(x => x.CheckStrength(string.Empty))
            .IgnoreArguments()
            .Return(PasswordStrength.Strong);

        passwordService = new PasswordService(passwordStrengthService,
            passwordHashGenerator);
    }
}

Ain’t she a beauty? That’s really not terribly important, but I wanted to show it, because I hate it when people assume that I don’t care about some bits of the code. Finally, we have actual specifications.

public class When_setting_a_users_password : PasswordServiceSpecification
{
    private Person user;
    private string newPassword;

    protected override void EstablishContext()
    {
        base.EstablishContext();
        user = Dependency<Person>();
        newPassword = "password";
    }

    protected override void Because()
    {
        passwordService.SetUserPassword(user, newPassword);
    }

    [Test]
    public void Should_check_the_password_strength()
    {
        passwordStrengthService.AssertWasCalled(x => x.CheckStrength(newPassword));
    }
}

I’m told that it’s relatively important for the Because to be a single line of code, and the Tests (Should_*) to be a single line of code. It makes sense to me because if it’s a single line of code then you’re explicitly stating “this thing causes this other thing”.  Of course I don’t want to be too dogmatic about that because I can see that becoming pretty clusterfracky when the Should_* gets complicated.

I have a problem though. I wanted to check and see if an exception is being thrown, so I happily wrote this test:

public class When_setting_the_password_on_a_null_user : PasswordServiceSpecification
{
    private string newPassword;

    protected override void EstablishContext()
    {
        base.EstablishContext();
        newPassword = "password";
    }

    protected override void Because()
    {
        passwordService.SetUserPassword(null, newPassword);
    }

    [Test]
    [ExpectedException(typeof(ArgumentNullException))]
    public void Should_throw_an_exception()
    {
        // Do Nothing
    }
}

Too bad it doesn’t work for beans. Since the Because() is called by ContextSpecification.SetUp(), the exception is caused before the test is actually run, so the ExpectedException(…) doesn’t catch the exception thrown in the Because(). I didn’t much like the syntax anyway, but I didn’t really know where to go from there.

I briefly forayed into capturing exceptions from Because’s, but then realized that was a horrible idea.

I emailed Jimmy Bogard about it, because he seems smart. He said he might blog a solution to my woes. Wouldn’t that be hot?

PS- Is it weird that I was a little scared to put Bellware’s name in my blog post, for fear of having my face melted off? I’m pretty sure I’m one of the “noname bloggers” though, so I think I’ll be ok.

PPS- Wow this code is really hard to read. I’m sorry about that. Anyone want to recommend a syntax highlighting code snipped plugin for livewriter that plays nicely with WordPress?

Comments (3) left to “Playing with BDD: ContextSpecification”

  1. Haacked wrote:

    It’s good to experiment. I hope you wouldn’t let that ugly atmosphere discourage you from posting your thoughts. :)

  2. James Thigpen / Capture.Exception - BDD-esque w/ NUnit or: The Ikea Nightstand wrote:

    [...] as not to invoke the rage of the BDD mafia). I wove my own Spec class on top of NUnit back in this blog post. If you recall, I had encountered a problem [...]

  3. James Thigpen wrote:

    Thanks Phil! I appreciate the comment and support! The environment is a little acrid for sure.

    Wow this is so late, it got chomped by my spam system and I didn’t even notice.

Post a Comment

*Required
*Required (Never published)