Some of us have taken to writing comments in our BDD classes to give us Given, When, Then at a unit level.
So, if I’m writing examples for a cowhand, I might write something like this:
public class CowhandTest {
public void shouldMilkTheCow() {
// Given a repository which knows about one cow
CowRepository shed = mock(CowRepository.class);
Cow daisy = mock(Cow.class);
stub(shed.getCowByName("Daisy")).toReturn(daisy);
// Given a cowhand who knows where the cows are kept
Cowhand cowhand = new Cowhand(shed);
// When I ask the cowhand to milk the cow
cowhand.milkCow("Daisy");
// Then the cow should have been milked
verify(daisy).milk();
}
}
(updated) Olof rightly suggests that cows may not be the clearest way to explain this, so here’s another:
public class LoginControllerTest {
public void shouldRedirectSuccessfulLoginsToTheHomePage() {
// Given an authenticator which allows the user to log in
Authenticator authenticator = mock(Authenticator.class);
stub(authenticator.login("Fred", "P@55word")).toReturn(true);
// When the controller gets Fred's login attempt
LoginController controller = new LoginController(authenticator);
HttpResponse response = controller.attemptLogin("Fred", "P@55word");
// Then the response should redirect us to the home page
assertTrue(response.isRedirect());
assertEquals("/home", response.getRedirectUrl().toString());
}
}
Occasionally I’ll run across something that needs to be explicitly captured in a step as a method; mostly this is sufficient. The audience for the class-level steps is technical, so this works.
You can think of each class as having a stakeholder, or consumer, which is usually another class. There will be something, somewhere in the codebase that uses my Cowhand
, or my LoginController
(or I probably don’t need to write it yet). The exceptions are GUI classes; their stakeholder is the user.
You’ll notice that I’ve mocked out my collaborators – the CowRepository
, or the Authenticator
(using Mockito, because it doesn’t require expectations to be set so it allows me to keep the G/W/T flow).
I may not have written the real Cow
, CowRepository
or Authenticator
class yet. This may be the first time I’ve decided that I need one. In that case, by the time I come to code the class, I’ll already have some examples which describe how I expect it to behave.
Liz;
Could you please translate this to some less-abstract example than milking cows using software..? I just don’t understand what your SUT is all about.
Maybe something including login-logic or some other more day-to-day software example.
Great!
I guess this post was triggered by my questions on BDD and DDD posted here: http://groups.google.com/group/behaviordrivendevelopment/browse_thread/thread/90e864e69c2e72ef
This post puts it all together for me. Thank you for taking the time to explain this.
Now I see that BDD and DDD works nice together.
– Tore Vestues
Liz, thanks a lot for all of your very insightful postings here and in the BDD mailing list! I tried to write down your sample for C# and Rhino.Mocks
[TestFixutre]
public class CowhandTest {
[Test]
public void shouldMilkTheCow() {
// Given a repository which knows about one cow
ICowRepository shed = MockRepository.CreateMock();
Cow daisy = MockRepository.CreateMock();
shed.Stub(x=>x.getCowByName("Daisy")).Return(daisy);
// Given a cowhand who knows where the cows are kept
Cowhand cowhand = new Cowhand(shed);
// When I ask the cowhand to milk the cow
cowhand.milkCow("Daisy");
// Then the cow should have been milked
daisy.AssertWasCalled(x=>x.milk());
}
}
I try to even re-arrange my test further like this (gives me a clearer separation of concerns)
[TestFixture]
public class CowhandTest : SpecificationBase
{
protected override void Context() {
// Given a repository which knows about one cow
ICowRepository shed = MockRepository.CreateMock();
Cow daisy = MockRepository.CreateMock();
shed.Stub(x=>x.getCowByName("Daisy")).Return(daisy);
// Given a cowhand who knows where the cows are kept
Cowhand cowhand = new Cowhand(shed);
}
protected override void Because() {
// When I ask the cowhand to milk the cow
cowhand.milkCow("Daisy");
}
[Test]
public void shouldMilkTheCow() {
// Then the cow should have been milked
daisy.AssertWasCalled(x=>x.milk());
}
}
uuups, my angle brackets in the code have been eaten! Should be
ICowRepository shed = MockRepository.CreateMock<ICowRepository>();
Cow daisy = MockRepository.CreateMock<Cow>();
Fixed – I love that I can do that in WordPress! – Liz
Hi Liz,
I just wanted to write that test with fluentspec (in c#) if you don’t mind.
public class CowhandTest : BehaviorOf {
public void shouldMilkTheCow() {
var daisy = TestObjectFor();
Given.Shed.getCowByName("Daisy").Is(daisy);
When.milkCow("Daisy");
Then.Should(daisy).milk();
}
}
that was with fluent syntax an having Shed as a property in Cowhand
keeping it closer to the example with classic syntax would look like ..
public class CowhandTest {
public void shouldMilkTheCow() {
// Given a repository which knows about one cow
var shed = Create.TestObjectFor();
var daisy = Create.TestObjectFor();
Given.That(shed).getCowByName("Daisy").Is(daisy);
// Given a cowhand who knows where the cows are kept
var cowhand = new Cowhand(shed);
// When I ask the cowhand to milk the cow
cowhand.milkCow("Daisy");
// Then the cow should have been milked
Then.Should(daisy).milk();
}
}
cheers
mike
opps sorry … the types in the generics TestObjectFor BehaviorOf don’t show in the post 😦
Fixed, I hope I got them right – Liz
Since I can edit your comments, I’ve thrown the <code> tags in – thank you, these are great!
In your second example…I think I am missing something. Don’t you need to inject the mock(Authenticator) into the controller? I have not used this framework, so I may be missing something. Thanks.
Hmm, DI without the I. Fixed! Thanks – Liz
Liz! Thanks for shedding some more light to my BDD journey! The login example is much easier to understand, I’m not a farmer haha 🙂
cya