Simple Design: State vs Behaviour testing
Reading time: 2 minsWhen we are facing a problem and taking the classic/Detroit style TDD we tend to start with the outermost object than work our way in. If we look at the Tic Tac Toe game, we may start with testing an empty board.
[code lang=java]
public void TheBoardStartEmpty() {
var board = Board.new();
Assert.That(board, Is.Empty());
}
[/code]
From this we may then process testing to see when the board is not empty, i.e. when a player makes a move.
[code lang=java]
public void BoardNotEmptyAfterMove() {
var board = Board.new();
board.makeMove(1)
Assert.That(board.Empty(), Is.EqualTo(false));
[/code]
Now this is something that I used to do and was happy with it. This approach flows naturally, feels like the simplest action that one can take. However there's a flaw in this approach. It leads down a path of a very state focused testing.
We have to ask ourselves, what is it that we are really interesting in asserting? What is it that we want our system to do? These questions can lead us to a path of more behaviour focused learning. As long as we keep going up the chain of questioning, why am i doing this, why am i doing that.. we eventually get to what weactually want to test.
We should aim to test only what is needed and what will bring us value. This can sometimes be tricky.
So going back to board. Why do we want to know if the board is empty? Because we want to be able to make a move on a board. Why do we want to make a move on a board? Because we want to be able to win a Tic tac Toe game.
That finally gives us the behaviour that we really want to test. We want to be able to verify that a winning move. Knowing this we can now begin testing that more valuable test, but we may find that we still need to assert that the board is first empty. That's perfectly okay as we can set the other test to a pending test so we can continue moving forward, but at least now we have a better understanding why we want to test an empty board when initialising a new board.