Following on from our previous post on convention testing we can extend the conventions by considering the standards we want to follow when we write methods on our immutable types.
One of the conventions we’ve been following is the use of methods that create a near clone of the current object, but with a specific change present. We’ve been giving these methods names that start with
With - in the functional programming world they’re commonly known as withers.
For our first test, let’s check that the return type of each wither is the same as the declaring type. Enforcing this ensures that we can chain withers together without losing type information as we go.
We just check that the return type of the method is the same as the declaring type. The because string provides additional information that gets included if the test fails. The goal is to make it as obvious as possible what went wrong so that the fix can be completed as quickly as possible.
To find all the withers that need testing, we iterate over all known immutable types looking for methods with names that start with
Good news - all our existing withers satisfy the test.
Now we want to check the parameters we pass to the withers - the parameter names should match the names of the properties that are going to be modified. Using this convention makes the withers more predictable when we’re using them - the parameter names shown in IntelliSense will accurately reflect the changes being made.
We start by finding all the properties of the declaring type and creating a set of all the names of the public properties. Each of the parameters is then checked to see if the parameter name is present - using a case insensitive check. Again we include a because string to describe what’s expected.
This time around, we have a couple of test failures, both on the class
Firstly, the method
WithSelection() fails because the parameter
word needs to be renamed to
Secondly, the method
WithNoSelection() fails because it’s not a wither - it doesn’t have any parameters at all.
To fix this second test failure, let’s rename the method to
ClearSelection() so that it’s not picked up by these tests.
But, we should have some tests for it, surely? Let’s create tests for clearers, with slightly different conventions:
Our first test is essentially identical to the same test for our withers - it might be worth merging the two later on.
Next, we want to check that the name of the clearer identifies the property we’re going to clear.
These additional conventions will help us construct any new immutable types correctly as we move forward.
What conventions do you have in your projects? How many of those conventions are currently managed by hand? Do you think any of them could be cross-checked by some smart unit tests?