Anonymous delegates, lambdas and their akin are here to stay and make our life easier.
Despite being in the framework since day one (well, not in their anonymous incarnation and definitely not in their lambda form) delegates have had some sort of “Cambrian explosion” in their usage since the advent of LINQ. More and more developers have been exposed to their benefits and more a more APIs make use of them.
And all that is fine by me. But the other day one of the developers that works with me had some trouble testing a piece of code he wrote and called me in. Do not why, but they always call me when there is trouble… The code was something like this simplification:
The Subject class needs to be tested, well, its
.FooMethod(), that, in turn, will call a method on a collaborator that returns a collection of
Something objects that will satisfy a given predicate sent to that method as an argument. In code:
And what is what was giving that developer a headache? Testing that the collaborator is called with the correct delegate for each method of the SUT (System Under Test). As always, there are multiple ways to solve a problem. All of them come down to a point: we need to get the arguments of the call to the collaborator.
In our case we are using Rhino Mocks to isolate from the
ICollaborator implementation. The technique we ended up implementing involved several steps:
- getting the arguments of the call to the
.GetArgumentsForCallsMadeOn()extension method. That, effectively, give us access to the actual delegate used in the collaboration
- applying that delegate to a controlled object
- asserting on the execution of the delegate over that controlled object
With that technique tests can be easily written as:
What I do not like
It works but is a bit convoluted. The flow is not “natural” and some value indirection (applying the actual delegate to a controlled argument) is required to “guess into” what’s inside the delegate.
My ease of mind with the solution comes from the rationalization of the fact that delegates themselves provide a level of indirection on top of the action to perform and tests suffer from that indirection.
What else could be done instead
Obtaining the argument form the collaboration is something that needs to be performed regardless; but applying the delegate could be spared if there was a way to peek inside the delegate.
And there is: an expression can be used instead of a delegate. Thus, in the test, the expression needs to be walked through looking for the expected property.
Why is not the proposed solution then? To start with it does not take away the problem of non-obvious flow; it merely changes the assertion mechanics. Another problem is that the collaborator implementation needs to change to accommodate the expression and compile it before applying it to the objects and there is a price to pay in runtime to compile the expression to its executable delegate form. Am I happy to pay that price (and we have not talked how big the price is) for the assertion not to be substantially better? Answer should be obvious by now, as you are reading this paragraph this late in the post
As always, code can be found in my corner of