Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I keep my unit tests independent of each other in this simple case?

I'm writing a small game in C# using Test Driven Development approach. I'm facing a problem:

I have a class CheckersBoard:

public class CheckersBoard
{
    private const string InitialBoardPattern = "B-B-B-B-" +
                                               "-B-B-B-B" +
                                               "B-B-B-B-" +
                                               "--------" +
                                               "--------" +
                                               "-W-W-W-W" +
                                               "W-W-W-W-" +
                                               "-W-W-W-W";

    public SquareStatus[,] Squares { get; private set; }

    public CheckersBoard(IBoardGenerator boardGenerator)
    {
        Squares = boardGenerator.GenerateFromPattern(InitialBoardPattern, 8, 8);
    }

    //....
}

And a class BoardGenerator:

public class BoardGenerator : IBoardGenerator
{

    public SquareStatus[,] GenerateFromPattern(string pattern, int width, int height)
    {
        //....
    }

}

BoardGenerator allows me to initialize CheckersBoard in a very readable way. So, I really would like to use BoardGenerator in my unit tests to avoid ugly array initialization.

But it's against the rule that says that I must keep all my unit tests independent of each other. If the test of GenerateFromPattern fails, that will produce a "cascade" effect and all the tests which use GenerateFromPattern will fail too.

This is the first time I have ever used unit tests so I'm a little bit confused. How can I avoid this problem? Is there something wrong with my design?

like image 917
Simon V. Avatar asked Sep 05 '12 12:09

Simon V.


2 Answers

Apparently this is a common problem without obvious answer, and I happened to deal with two similar questions:

  • Reusing business logic in unit test - advising against, because OP talks about 2 different components (similar to your case)
  • Using tested function to prepare another unit test - advising for, given then usage is scoped to single class (so that new bugs won't possibly crash some totally unrelated component, but instead all will remain within original "crashing scope")

To certain level, reusing business logic (other, tested components) is kind of similar to using 3rd party libraries in unit testing - we assume they work, and don't bother generating extra assertions about them. Will you have any hesitations to use List<T> in unit test? Most likely no. Note however, that List<T> is tested by many other developers daily and has big software house behind it. Can you say the same about BoardGenerator?

Another question to consider is, will the CheckersBoard test code be as obvious to somebody who never worked on BoardGenerator as it is to you? You claim it is readable, but that's common given the authoring. Maybe using array initialization is not as bad (unreadable) as you think it is.

like image 111
k.m Avatar answered Nov 03 '22 02:11

k.m


You could mock your BoardGenerator class or just fake the implementation e.g.

internal class MockBoardGenerator : IBoardGenerator
{
    public SquareStatus[,] GenerateFromPattern(string pattern, int width, int height)
    {
        // return fixed test data
    }
}
like image 22
James Avatar answered Nov 03 '22 02:11

James