I’ve recently been building a couple of very small fluent interfaces. These have been focused on component testing, where some of their disadvantages aren’t as important. Let me give an example:
public void PartialRecoveryFromTwoSessions() { When .SessionStartsAt("1 Jan 1990 21:25:00", s => s .MessagesProcessed(3, 5, 1)) .And.SessionStartsAt("1 Jan 1990 21:35:00", s => s .MessagesProcessed(2, 6)) .Then .RecoveryStartsFrom(4) .RecoveryWillSkip(5, 6); }
Now, the great advantage of expressing your test in this fashion is that it’s quite easy for someone to look at the test and see what you mean. However, there are a couple of disadvantages worth mentioning:
- Although it makes your intent obvious, it doesn’t make it obvious that your intent has, in fact been satisfied.
- Since you’ve just written a short testing framework, you really ought to write some tests for the framework itself. That would probably involve mocking the object under test.
- Writing this style in C# typically involves creating builder objects. Personally, I try to avoid writing mutable objects wherever possible, and builder objects are about as mutable as they come.
- The overhead of creating an actual framework for testing rather discourages the practice of test-first development.
Despite this, the improved ability of the test to communicate intent makes it well worth doing.
How it works
Martin Fowler’s article gives you the abstract details of how this sort of thing works in C# and Java, but I thought it would be worth explaining the example I’ve built here. You’ll note that there are a large number of violations of standard practice in this explanation. That’s one of the major reasons I’m using this principally in testing code: I really don’t like the stylistic continuity from standard coding practices.
- When is a property that creates a new CaseBuilder object. This object also acts as the stub repository (this implementation doesn’t use a mocking framework, so all test doubles are actual physical objects). Obviously, you’d never violate separation of concerns like that in production code.
- SessionStartsAt is a method that creates a session and adds it to the repository. It then returns the CaseBuilder to allow the method chaining.
- To configure the session, you implement an action on the session. This is a pattern I learnt from the Fluent NHibernate code base. The advanatage of this is that you don’t need to worry about how to get back to the CaseBuilder object after you’ve finished configuring the session.
- “And” does nothing, it just returns the builder object. It’s only there for (natural language) readability.
- “Then” actually performs the test and returns a CheckBuilder. By returning a new type, you restrict what appears in auto-complete and prevent people from expressing logical nonsense in the tests.
If you need to customize how the test runs, you tend to need to insert another delegate into the system. In general, though, I’m quite pleased with the technique. There’s less than 300 lines for the framework outlined above, most of which is relatively trivial code of the form “set property, then return “this” “.