(I originally posted this on my MSDN blog.)
I’ve previously written about the Behavior-Driven Development style of TDD and about how to use such a style in MSTest. I’ve done this on several personal and business projects and it’s worked pretty well, but I recently got around to trying Aaron Jensen’s MSpec and I have to say there’s no going back. Conceptually my tests are the same but mechanically MSpec just flows so much better.
Here’s test context from a personal game project of mine written in my old MSTest BDD framework:
[TestClass]
public class when_the_planet_is_initialized : PlanetContext
{
protected override void Context()
{
base.Context();
}
protected override void BecauseOf()
{
_planet.Initialize(_location, _radius);
}
[TestMethod]
public void should_initialize_the_renderer()
{
_planetRenderer.AssertWasCalled(x => x.Initialize(_radius));
}
[TestMethod]
public void should_create_terrain()
{
_terrainFactory.AssertWasCalled(x => x.Create(_radius));
}
}
It works, but it’s noisy.
- Each spec requires a minimum of four text lines.
- There’s a lot of type and access control gook that obscures the intent.
- Even though there’s no test-specific context to set up, I still have a Context method that simply calls the base class. Strictly speaking this isn’t required, but I’ve found that if I leave it out and then decide to add it later, I often forget to call base.Context so I always leave this boilerplate code here as a reminder. Call it a defensive habit.
Now here’s the same context re-written in MSpec:
[Subject(typeof(Planet))]
public class when_the_planet_is_initialized : PlanetContext
{
Because of = () =>
_planet.Initialize(_location, _radius);
It should_initialize_the_renderer = () =>
_planetRenderer.AssertWasCalled(x => x.Initialize(_radius));
It should_create_terrain = () =>
_terrainFactory.AssertWasCalled(x => x.Create(_radius));
}
The MSTest version requires 25 text lines, the MSpec version 12 text lines. This context doesn’t require an “Establish context” delegate, but if I wanted to add one later the form is the same as the other clauses and I don’t have to remember to call a base class version. There’s far less compiler gook getting in the way and the code reads very easily. It just oozes elegance. Ok, I suppose Rubyists or Pythonistas might still call this noisy but for C# it’s amazing. It can be daunting at first when your brain exclaims, “That can’t possibly be legal C# code!” but once you figure out that it’s just lambda expressions assigned to delegate fields it all makes perfect sense.
MSpec also includes BDD specification extension methods very similar to the ones I wrote about so you don’t have to worry about writing and maintaining that code, either.
If you’re at all interested in BDD, you owe it to yourself to try MSpec!