What Is a “Unit” in Unit Testing?

In this post I want to talk about how a unit in unit testing refers to “a unit of behaviour”. I was prompted to write this as I saw a discussion on Twitter about the topic but it felt particularly topical for me as I had fallen foul to what was being discussed on the Twitter thread in the days prior to reading it. Unfortunately I didn’t bookmark the thread and now I can’t find it but it roughly boiled down to a retweet of someone advocating a particular way of naming your tests, but it necessitated having a test for every method of every class. This was the bone of contention in the thread as people were discussing that writing unit tests for every method of every class is the incorrect definition of a unit, rather a unit should be a unit of behaviour. The idea being that you should focus on testing at a higher level than individual methods, focusing on testing use cases and the behaviour of the system. I’ve tried searching for the thread but couldn’t track it down, however I did find this thread from Uncle Bob that is a rough proxy for it and has some good insights too, https://twitter.com/unclebobmartin/status/943552605950750721. I’ve included a screenshot of the full thread at the bottom of this post just in case the tweet goes missing. In the thread Uncle Bob writes:

Further to what’s discussed in the thread, this is where I tripped myself up recently when I was writing a Tic-tac-toe (Noughts and Crosses) game to get a feel for Java. For an explanation of Tic-tac-toe you can read here and you can find the repo for the game at https://github.com/Squaretechre/tic-tac-toe/. When I came to write the tests for a player winning I began by creating a file named after the behaviour I wanted to exercise, so I created something called Player1WinsTests. Then I started writing the individual tests against a class that represents the game’s grid. I wrote tests for all the possible combinations of moves player 1 could win with, 3 over all horizontal and vertical lines and the 2 diagonal winning lines. What I was doing at this point was asserting against a method on the grid called hasWinningPlayer, which was effectively saying given I set the grid up with a winning line for player 1, do you recognise that there is a winning player. So really I wasn’t testing that the game had a winning player at all, I was simply testing if the grid alone had a winning player in it. The assertion looked something like:

private void assertWinningGrid(Player[][] gridArray) {
    Grid grid = new Grid(gridArray);
    assertTrue(grid.hasWinningPlayer());
}

 

However I then ran into trouble when I started to write tests further down the line because I’d been testing an implementation detail and not the overall mechanics of what happens when player 1 makes a winning move in order to win the game. Effectively I was ignoring all the behaviour and mechanics around how the game gets into the state of there being a winning player. I then refactored the tests to test at higher level, through the TicTacToe game class:

private void assertWinWhenPlayerNextMovesTo(Coordinate coordinate, Player[][] grid) {
    // instantiate the game with the test grid
    TicTacToe game = new TicTacToe(player1, player2, new Grid(grid));
    
    // poke the game who'll use the grid in some mysterious way
    game.nextPlayerMoveAt(coordinate);

    Result result = game.result();
    
    // assert against the behaviour of the game, not the grid!
    assertTrue(game.isFinished());
    assertEquals("Dan wins!", result.message());
}

 

Stepping back and refactoring to test through the TicTacToe class solved the integration issues and put Grid back in its rightful place as an implementation detail. When testing through the TicTacToe class the Grid class plays a part in the family of classes that come together to make the game work but it’s not directly tested with unit tests. The correctness of Grid’s behaviour is confirmed as a side effect of the overall system behaviours that are tested and make use of it internally as a collaborator.

In conclusion a unit is a unit of behaviour. I should have been testing the output of the game’s behaviour when player 1 makes a winning move and not the grid which is an implementation detail. When I refactored the tests to test through the TicTacToe class the Grid class is covered due to it being exercised through the TicTacToe class, the TicTacToe is really only a facade. Here’s the Uncle Bob thread mentioned earlier:

 

Buy me a coffee Buy me a coffee

😍 ☕ Thank you! 👍

Share

Leave a Reply

Your email address will not be published. Required fields are marked *

Post comment