FYI: If you're familiar with extension methods, and how to use them in testing sceneries...the interesting part of this post is at the bottom starting at: "Ok, on to the point..."
The C# extension methods give some amazing power when it comes to extending functionality of objects (we don't own) and I've spotted a pattern on several blogs and example unit testing snippets, especially in the Context Specification style testing areas that I find interesting.
The concept is to basically use the C# extension methods within a unit testing environment to give the system under test (SUT) more readability/understandability within the test code itself.
Here's an example of how you might normally write a unit test given the following SUT.
public class SystemUnderTest
{
public SystemUnderTest() { PropertyUnderTest = "Hello World!"; }
public string SomeStringProperty { get; set; }
public bool SomeBoolProperty { get; set; }
}
You might write some unit tests that might look like...
var sut = new SystemUnderTest();
Assert.IsTrue(sut.SomeBoolProperty);
Assert.AreEqual(sut.SomeStringProperty, "Hello World!");
Now, the assertions above are small enough it's pretty easy to tell what's going on, however when you think about what your looking at, it actually present the best readability.
Let's take the string's AreEqual assertion for example... You first read the "AreEqual", so now you have to allocate some (undefined as of yet) space in your head to store some data points that need to be evaluated all at once. (maybe I'm getting lazy as I get old, but the less I have to think when reading tests the more time I can spend understanding the domain being tested...)
Again, the example is over simplified, but I think you get the point.
What if you could make the test syntax read and flow in a very readable and understandable manner?
That's what the specification extensions give you. Given the two tests above and an a couple helper extension methods living in the testing library I could write something like.
var sut = new SystemUnderTest();
sut.SomeBoolProperty.ShouldBeTrue();
sut.SomeBoolProperty.ShouldEqual("Hello World!");
It may just be me, but that just feels better, is more understandable, and the great thing is I didn't have to impact my domain objects to support this style of test...
Another great benefit is you don't have to type "Assert.xxxx(YYzzz)" each time you want to create an assertion. You can just type sut.SomeThing.{this where you get help from intellasense} giving you some great context based assertion options.
I googled for a library that had a pre-built set of extension assertions and ended up finding the http://code.google.com/p/specunit-net/source/browse/ by Scott Bellware. If you dig into the source of the project you can find a helper class called SpecificationExtensions.cs which basically gives you all the "Should..{your assertion here}" extension methods.
Ok, on to the point real point (sorry it's taken so long).
After downloading and playing with the extension specifications from Spec Unit, I thought what if we made that more fluent?
So I gave it a quick spike and instead of writing some tests that look like...
sut.SomeStringProperty.ShouldNotBeNull();
sut.SomeStringProperty.ShouldBeOfType(typeof(string));
sut.SomeStringProperty.ShouldEqual("Hello World!");
You could have less wordy code and still retain all the meaning and readability with a set of fluent specification extensions.
sut.SomeStringProperty
.ShouldNotBeNull()
.ShouldBeOfType(typeof(string))
.ShouldEqual("Hello World!");
I haven't figured out what sorts of bad things this style of assertion could bring... but we'll experiment for a while...
Here's an example console app with the extensions included.
DISCLAIMER: I haven't tested all the extensions so if you notice any issues please feel free to let me know...
public static IEnumerable ShouldContain(this IEnumerable collection, Func expectedCriteria)
{
collection.Any(expectedCriteria).ShouldBeTrue();
return collection;
}
http://code.google.com/p/shouldit/
ShouldIt is an open source library of fluent specification extensions that can be used with any unit testing framework.