I’ve been thinking a lot lately about the current short-comings of SpecsFor. While I’m pretty happy with the end-user experience, the internals have become complex and confused. I’m also encountering new testing scenarios as I’m writing more and more full integration specs with SpecsFor, and I’m finding that SpecsFor isn’t able to help with those scenarios in the ways I would like for it to. I have some ideas for SpecsFor 3.0 that I think will simplify the core while also making SpecsFor more flexible and more powerful.
[more]
One of the fundamental problems with SpecsFor is its dependency on inheritance. It doesn’t matter what kind of code you’re writing, inheritance gets in the way 99.95% of the time, and unfortunately SpecsFor does not fall into the 0.05%. Just look at the base class I created for database integration testing. Specs that want to use the database must derive from this class, but what if they want to test other classes that just depend on the database indirectly? There’s not an easy way to achieve this with the inheritance-based approached. I’d rather have something like this:
public class when_retrieving_products : SpecsFor<ProductProvider>, IUseTheDatabase { private Product[] _products; //Defined on IUseTheDatabase public ProductDataContext Database { get; set; } //Automatically executed within a transaction now, no need for a custom base class. public override void Given() { Database.BuildProduct().Finish(); Database.BuildProduct().Finish(); Database.BuildProduct().Finish(); Database.SubmitChanges(); } public override void When() { _products = SUT.GetAll().ToArray(); } [Test] public void then_it_returns_all_the_products() { _products.Length.ShouldEqual(3); } }
Instead of relying on inheritance, I’m simply implementing an interface in my spec that says “Hey, I want this to be an integration test with database support”. SpecsFor would take care of wiring in other magic. Where would that magic be defined? I’m thinking in a SetUpFixture:
[SetUpFixture] public class AssemblyStartup { [SetUp] public void SetupTestRun() { SpecsFor.Configure(cfg => { cfg.WhenTesting<IUseTheDatabase>().EnrichWith<TransactionalDatabaseBehavior>(); cfg.WhenTesting<SpecsFor<SomeType>>().EnrichWith<SomeTypeBehavior>(); cfg.WhenTestingAnything().EnrichWith<LongTestLogger>(); }); } }
This approach would allow you to easily extend the behavior of SpecsFor in a compositional way. You could attach a new behavior to all of your specs, to only specs that implement a certain interface or derive from a certain type, or even to specs that are decorated with a certain attribute. The possibilities are pretty limitless. Another nice benefit of this approach is that a lot of the cruft that has crept in to the base SpecsFor class could be implemented using this approach.
I still have to write said magic, but I’ve got a good idea of what it is going to look like.
So, what does everyone think? There’s still some inheritance, but since I don’t want to write my own testing framework from scratch, I’m limited to what NUnit will let me do, so the base class isn’t going away anytime soon.
Another thing I’m considering is whether or not to require SpecsFor when using SpecsFor.Mvc. There are a few SpecsFor.Mvc things that will become simpler and easier to implement with the enhancements coming in SpecsFor 3.0. Is anyone using SpecsFor.Mvc without SpecsFor?