… or how one could implement the It-Syntax introduced by MSpec. If you don’t know what the hell I’m talking about take a look at this code. (I don’t know if that’s the current MSpec syntax but I think you’ll get where I’d like to take you … )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | |
What you’ll see in this post is part of a spike I did some month ago while I was considering to move the xUnit.BDDExtensions syntax to something more GWT and MSpec like. I finally decided against that, partially because I realized that there is absolutely no need for a MSpec-Clone; partially because I’m still not so happy with the implications / side effects of the syntax (nearly everything in you spec needs to become static). Anyway, recently JP Boodhoo introduced the same feature to his jpboodhoo.bdd codebase. A lot of the comments requested a more detailed description of how he implemented that feature. While I can’t answer that question, I can talk about how I tackled that problem. So here we go!
Some prerequisites first
If you wonder how such a sweet syntax is possible in C#, here are some clues:
Ituses fields with ommited access modifiers (therefore private fields).It,When, etc. areDelegatetypes.- Field-initializers are used to specifiy the delegates inline. (and because field-initializers are run before the constructor you can only access static and no instance members here. Thats the sideeffect I mentioned earlier).
A closer look at my spike
The following class is the heart of the spike I implemented. It takes a blank object and uses reflection to
build an ISpecHandler instance which is ultimately used to run the specification.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
As you might guess a lot of extension methods are used in here, together with the composite pattern. Lets start be inspecting the AllFields()
extension method.
1 2 3 4 5 | |
As its name implies this method reflects all the fields from the supplied object. It returns a collection of IFieldInfo objects.
IFieldInfo? Yes I introduced a little adapter here, which helped me to introduce better test isolation in the spike code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | |
And here is how the code that finds all the It delegates was implemented.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
The basic stuff happens in the AllHandlersOf method which filters the supplied fields by the field type and also removes all compiler generated fields (which
start with CS$ in their names). The content (aka the delegate) of each field that is left is read and wrapped in a SpecHandler instance.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | |
Last but not least there is the Then extension method which uses the composite pattern to wrap all found spec handlers into a single instance.
1 2 3 4 | |
Conclusion
It’s not as hard to implement such a syntax in C# as one might initially think. Although the code shown in this post is only spike code and not considered production ready, you can derive all what’s necessary to implement such a syntax. Fields, omitted access modifiers, field-initializers and delegates are the basic parts together with some reflection logic to execute the code.