A year ago, I had little idea how to do TDD (but really wanted to (how frustrating)) and had never heard of BDD... now I do both compulsively. I have been in a .Net development environment, not Java, but I even replaced the "F5 - Run" button with a macro to either run Cucumber (BDD) or MBUnit (TDD) depending if it is a Feature/Scenario or Specification. No debugger if at all possible. $1 in the jar if you use the debugger (JOKING (sort of)).
The process is very awesome. The framework we are additionally using is by The Oracle I've been blessed to come across, and absorbing information from, and that framework he/we use is MavenThought.
Everything starts with BDD. Our BDD is straight up cucumber ontop of iron ruby.
Feature:
Scenario: ....
Given I do blah...
When I do something else...
Then wonderful things happen...
Scenario: ...
And that's not unit testing itself, but it drives the feature, scenario by scenario, and in turn the unit (test) specifications.. So you start on a scenario, and with each step you need to complete in the scenario it drives your TDD.
And the TDD we have been using is kind of BDD in a way, because we look at the behaviours the SUT (System Under Test) requires and one behaviour is specified per specification (class "test" file).
Example:
Here is the Specification for one behaviour: When the System Under Test is created.
There is one more specification (C# When_blah_happens class file) for another behaviour when a property changes, but that is separated out into a separate file.
using MavenThought.Commons.Testing;
using SharpTestsEx;
namespace Price.Displacement.Module.Designer.Tests.Model.Observers
{
/// <summary>
/// Specification when diffuser observer is created
/// </summary>
[ConstructorSpecification]
public class When_diffuser_observer_is_created
: DiffuserObserverSpecification
{
/// <summary>
/// Checks the diffuser injection
/// </summary>
[It]
public void Should_return_the_injected_diffuser()
{
Sut.Diffuser.Should().Be.SameInstanceAs(this.ConcreteDiffuser);
}
}
}
This is probably the simplest behaviour for a SUT, because in this case when it is created, the Diffuser property should be the same as the injected diffuser. I had to use a Concrete Diffuser instead of a Mock because in this case the Diffuser is a Core/Domain object and has no property notification for the interface. 95% of the time we refer to all our dependencies like Dep(), instead of injecting the real thing.
Often we have more than one [It] Should_do_xyz(), and sometimes a fair bit of setup like perhaps upto 10 lines of stubbing. This is just a very simple example with no GivenThat() or AndGivenThatAfterCreated() in that specification.
For setup of each specification we generally only ever need to override a couple methods of the specification:
GivenThat() ==> this happens before the SUT is created.
CreatSut() ==> We auto mock creation of the sut with StructureMap and 90% of time never need to override this, but if you are constructor injecting a Concrete, you have to override this.
AndGivenThatAfterCreated() => this happens after the SUT is created.
WhenIRun() => unless it is a [ConstructorSpecification] we use this to run ONE line of code that is the behaviour we are specifiying for the SUT
Also, if there is common behaviour of two or more specifications of the same SUT, we move that into the base specifcation.
All I gotta do to run the Specification is highlight it's name, example "When_diffuser_observer_is_created" and press F5, because remember, for me F5 runs a Rake task either test:feature[tag] if Cucumber, or test:class[SUT]. Makes sense to me because everytime you run the debugger it's a throw away, no code is created (oh and it costs a $1 (joking)).
This is a very, very clean way of specifying behaviour with TDD and having really, really simple SUTs and simple specifications. If you try and be cowboy coder and write the SUT crappy with hard dependencies, etc, you will feel the pain of trying to do TDD and get fed up / give up OR bite the bullet and do it right.
And here's the actual SUT. We got a little fancy and use PostSharp to add property notify changed on the Diffuser, so hence the Post.Cast<>. And again, that's why I injected a Concrete rather than Mock. Anyway, as you can see the missing behaviour defined in another specification is when anything changes on the Diffuser.
using System.ComponentModel;
using MavenThought.Commons.Events;
using PostSharp;
using Price.Displacement.Core.Products;
using Price.Displacement.Domain;
namespace Price.Displacement.Desktop.Module.Designer.Model.Observers
{
/// <summary>
/// Implementation of current observer for the selected product
/// </summary>
public class DiffuserObserver : AbstractNotifyPropertyChanged, IDiffuserObserver
{
/// <summary>
/// gets the diffuser
/// </summary>
public IDiffuser Diffuser { get; private set; }
/// <summary>
/// Initialize with a diffuser
/// </summary>
/// <param name="diffuser">The diffuser to observe</param>
public void Initialize(IDiffuser diffuser)
{
this.Diffuser = diffuser;
this.NotifyInterface().PropertyChanged += (x, e) => this.OnPropertyChanged(e.PropertyName);
}
/// <summary>
/// Gets the notify interface to use
/// </summary>
/// <returns>The instance of notify property changed interface</returns>
protected INotifyPropertyChanged NotifyInterface()
{
return Post.Cast<Diffuser, INotifyPropertyChanged>((Diffuser)Diffuser);
}
}
}
In conclusion, this BDD / TDD style of development rocks. It took one year but I am a total convert as a way of life. I would not have learned this on my own. I picked up everything from The Oracle http://orthocoders.com/.
Red or Blue pill, the choice is yours.