Stateless
In the previous post I introduced the Precondition and Postcondition concepts. Today we are going to explore some examples and see how the specification (pairs of pre and post conditions) will guide our tests.
Let’s start with methods that don’t depend on the instance state, usually called static methods.
Hip to be Square
Consider a method Square that takes an int and returns the square:
1
| |
What is the Pre? What do we need in order to run Square? Not much, any int should do. So I just want to indicate that x has a value (any value) by using the constant X0:
1 2 3 | |
What is the Post? It we have a input value (the result should be X02 no matter what’s the input value X0.
So now our method could look something like (r indicates the result value):
1 2 3 | |
And that would help us to create a test that does something like:
1 2 3 4 5 6 7 8 9 10 11 | |
Last bit, what happens if we use a value that does not satisfy the pre? Diving by zero, etc? Exactly, you get an exception!
Adding an element to a Collection
Consider the method of the interface ICollection<T> that (to make things easier) returns a new collection.
1
| |
In order to add an element to the collection we don’t need any particular condition on the element to be added so let’s write a similar precondition as before to indicate that the element has a value E0:
1 2 | |
When the element is added we could say we expect it to be part of the collection:
1 2 3 | |
With that information the test should look like:
1 2 3 4 5 6 7 8 9 10 11 12 | |
Now that we have test, we can write the actual implementation.
Pair programming is great and when doing TDD is nice to play “ping pong” by letting one of the developers write the test and the other one write the implementation to satisfy that test (and only that). Is an excellent exercise to see how complete our tests are.
So given the test and specification above the implemented code could look like:
1 2 3 4 5 6 7 8 9 10 11 12 | |
Does it pass the test? Yes it does.
Does it smell? Yes it does!
But why? Where’s the problem? Exactly, our specification is incomplete!
The test should work for any given collection not only empty ones. We are not making any reference to the values in the collection before the method is called and after. Let’s do a new version including the values of the collection c as C0:
1 2 3 | |
What’s next?
The completeness of the test will depend on how good our specifications are.
With a bit of practice we will feel more comfortable writing specifications and use them to drive our unit tests.
Looking at examples that work with immutable classes is the first step, but not always classes are immutable or we can work with a paradigm/language/framework that supports natively that kind of operations.
In the next post I’ll cover working with the instance state and use it in our specifications.