Monday, February 2, 2009

BDD using NBehave + Rhino Mocks AMC

Update: I found an old blog post by Aslak Hellesøy (the main developer behind Cucumber) that touches on this subject.

Some time ago, I investigated what BDD is all about. In essence, it's TDD with a twist. For example, the word "test" implies that we're testing what someone (we?) already made, but TDD says we're going to write the tests before the actual implementation of the unit of code. Somewhat, the word "test" has a direction backwards, while "should" has a direction forward. Hence, "should" is more comprehensive to use when describing and specifying the future. Makes sense?

What I'd like to show you is a piece of code I wrote to see how NBehave's story runner could work with Rhino Mocks' Auto Mocking Container[1, 2].

[Story, Test]
public void GetMoneyWhenPassGO()
{
// Set up and initialize
var mocks = new MockRepository();
var container = new Rhino.Testing.AutoMocking.AutoMockingContainer(mocks);
container.Initialize();

// Resolve and obtain references
IPlayerTurn turn = container.Create<PlayerTurn>();
IBoard board = container.Resolve<IBoard>();
IPlayer player = container.Resolve<IPlayer>();
turn.AddPlayers(new List<Player>() {player});

// Story begins here
var story = new Story("Player recieves money when passes 'GO'");

story
.AsA("Player")
.IWant("to recieve money when I pass 'GO'")
.SoThat("I can buy things that generate money");

story
.WithScenario("Normal play scenario")
.Given("A board with 4 squares", () => Expect.Call(board.NumberOfSquares).Return(4))
.And("a player near 'GO'", () => Expect.Call(board.GetIndexForPlayer(player)).Return(2))
.And("mockery has started", () => mocks.ReplayAll())
.When("player passes 'GO'",() => turn.PlayerSequence(player, 3))
.Then("the player earns $4000", () => player.AssertWasCalled(x => x.Credit(4000)));

}

When this test is run with ReSharper, the test passes and outputs the story with indentation. Nice! (Except the "mockery has started" part of the story..)

Now a question pops up: since we have only specified and tested the first (top-level) interaction, what about the rest of the interactions? I think this is a good question that leads us further down the rabbit hole.

A written user story comes from a dialogue with a person with domain knowledge. Hopefully, after some discussion, we have understood some of the moving parts of the domain problem the customer wants us to solve. Let's assume that we want to implement a single feature at a time, a couple of questions arise: should we start top-down or bottom-up? And how far "up" should we go, i.e. should we start with the UI or the domain model, if we choose a top-down approach?

If we choose to start with the domain model, then I think the above way of specifying the behavior looks nice. The key question is where the classes in the story come from originally. I have no easy answer for that. Of course they should originate from the domain problem, but how? "The model is the code - the code is the model", but it probably takes a while to "get it right". Maybe code like the above could help us to see if we have understood the problem in the first place?

Now back to the question: "what about the rest of the interactions"? We have ensured that the class which is in "the center" of the particular interaction chain (the player turn) lives in a faked world (a small board and a player near go) and we finally assert that when something happens (player passes 'GO') then some state has changed (the player gets money). The nice thing is that we now know more about what functionality the dependent classes should provide. For example, IBoard needs to have a method GetIndexForPlayer and if there is a class implementing that interface, then a NotImplementedException is probably thrown from that method, in order to compile. Next step could be to start thinking on that particular method and choose to either write a mocked unit test or an "ordinary" unit test.

Of course, real acceptance tests are also needed, but the purpose and scope of those tests are quite different. At least that's what I think.

What do you think?

2 comments:

Pawel Tarnik said...

> Of course, real acceptance tests are also needed,
> but the purpose and scope of those tests are quite different.
>
IMO, scenarios (Given/When/Then) are by definition acceptance criteria, so it's natural to make acceptance tests out of them. You can see this approach e.g. in Stormwind.Accuracy (http://using.stormwindproject.org:8081/display/accuracy/Home).

But I haven't used BDD in any real project so far, so perhaps I'm missing something.

Gustaf Nilsson Kotte said...

Yes, I agree with you Pawel! I was a bit unclear in my post. Let me explain..

Acceptance tests' purpose is to test parts of the system. The Given/When/Then-style is definitly useful here!

The purpose of my mocked scenarios is to drive the design of a domain model (written in code) and get code that compiles and passes the test. So there's nothing about UI at this level, only logic and responsibility of the particular class.

Though, I would definitly like to investigate this further, i.e. if it scales or not to do this in a real world scenario.