OrthoCoders

You can code it, I can help!

Method Specification

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
int Square(int x)

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
/// Pre: x = X0
int Square(int x)
/// ???

What is the Post? It we have a input value (the result should be X0^2 no matter what’s the input value X0.

So now our method could look something like (r indicates the result value):

1
2
3
/// Pre: x = X0
int Square(int x)
/// Post: r = X0 * X0

And that would help us to create a test that does something like:

1
2
3
4
5
6
7
8
9
10
11
[Test]
public void It_should_return_the_square_value([Values(...)]int x)
{
  // arrange (nothing to do)

  // act
  var r = Math.Square(x);

  // assert
  Assert.That(r, Is.EqualTo(x * x));
}

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
ICollection<T> Add(T e);

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
/// Pre: e = E0
ICollection<T> Add(T e);

When the element is added we could say we expect it to be part of the collection:

1
2
3
/// Pre: e = E0
ICollection<T> Add(T e);
/// Post: r should include E0

With that information the test should look like:

1
2
3
4
5
6
7
8
9
10
11
12
[Test]
public void It_should_include_the_value_in_the_collection([Values(...)]T e)
{
  // arrange
  var c = new TCollection();

  // act
  var r = c.Add(e);

  // assert
  Assert.That(r, List.Contains(e));
}

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
public SomeCollection : ICollection<T>
{
  public SomeCollection(T []elements)
  {
    // store the elements
  }

  public ICollection<T> Add(T e)
  {
    return new SomeCollection<T>(new []{e});
  }
}

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
/// Pre: e = E0 and c = C0
ICollection<T> Add(T e);
/// Post: r = C0 + E0 (no matter the order)

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.

Comments