I previously wrote about some ideas I was toying with for Specificity to add a base class to aid in writing BDD style test fixtures in a portable (works with any unit testing framework) fashion. I’ve been using a solution like I posted then ever since, and have concluded the design is a bit fragile. Relying on the actual test methods to properly use the Result property in order to ensure Arrange and Act are called, just isn’t a production quality design.
That got me to thinking, however. What I was trying to do here was a poor man’s attempt at AOP (Aspect Oriented Programming). The .NET world already has a better (though not perfect) solution for AOP in the form of ContextBoundObject. I should be able to leverage that and get a much better implementation of the concepts I wanted. In fact, there’s a CodePlex project that uses this idea already, called MSTestExtensions. I didn’t care for the way you had to write extensions using MSTestExtensions, and it wasn’t going to work out of the box for my BDD base class in any event, so I had to just borrow the idea without using it. So, I spent a couple of days hacking, and the result is now in the Specificity project.
I’m still not entirely convinced I’ve got the correct extension mechanism baked in, so if you start to use this code, beware that I’m very likely to make breaking changes in this area of the API.
So, what did I come up with? First, there’s an ExtendableTestFixture base class that you inherit from if you want to be able to “extend” your test methods. This provides all of the ContextBoundObject magic, as well as provides the necessary hooks for you to be able to create TestExtensionAttribute attributes that actually modify the process flow of the test methods, allowing you to add code before or after the test method. The way you code a TestExtensionAttribute is where I’m most likely to make breaking changes.
The BDD base class, called Observation in the previous post, is now called SpecificationContext. It inherits from TestExtensionBase and uses an internal SpecificationContextAttribute to cause all test methods to run the Arrange, Act and Teardown virtual methods. The use is the same as in the previous post (minus the Result property magic), but the implementation is now much more robust.
There is a down side. ContextBoundObject adds some overhead to object creation and method calls that will slow your tests down. It’s my experience that the overhead isn’t all that great, and I’m more than willing to except it for the benefits provided here, but you’ll have to judge for yourself.
There’s a couple of other test extension attributes included as well, just to illustrate that this is useful even if you’re not interested in using the SpecificationContext concept. There’s an ExpectedDurationAttribute which allows you to declaratively specify that a test shouldn’t take longer than some amount of time, and a TestTransactionAttribute that declaratively wraps your test method in a TransactionScope. I’ll admit, I’m not convinced attributes like these are appropriate… I’d prefer the non-declarative approaches, for many of the same reasons that I prefer Specify.That(action).ShouldThrow<Exception>() over ExpectedExceptionAttribute. However, I don’t want to force my opinions here on users, and I did need some attributes to illustrate what’s possible here.
I’d love to hear feed back on this.