Golden Master with Example

It can save your days and money too :)

Source Code

The source code the below example is available @ below repositoty

1. Create a unit test and copy the code from GameRunner

  • Create a unit test project that references the Trivia project

  • Add a unit test

  • Copy the GameRunner code over to the unit test

public void GameTest()
{
    var aGame = new Game();
    
    aGame.Add("Chet");
    aGame.Add("Pat");
    aGame.Add("Sue");

    var rand = new Random();

    do
    {
        aGame.Roll(rand.Next(5) + 1);

        if (rand.Next(9) == 7)
        {
            _notAWinner = aGame.WrongAnswer();
        }
        else
        {
            _notAWinner = aGame.WasCorrectlyAnswered();
        }
    } while (_notAWinner);
}

2. Seed the randomizer or hardcode the inputs

The Golden Master test has to be deterministic. Given input X, we expect output Y every time we run it. Due to randomizer, this is not possible. Hence, we will seed the randomizer so that it would produce the same inputs. Another way could be to replace the randomizer with specific inputs

var rand = new Random(345);

3. Capture the output

  • Run the GameTest unit test in the Test Explorer.

  • Click on the GameTest to see the test details.

  • Click on Open additional output for this result (Note: it says something different in previous versions of Visual Studio).

  • Copy the text.

Note: The output I copied didn’t have a newline at the end, so I had to add one. Watch out for missing newlines when you copy output.

private static readonly string GoldenMaster = @"Chet was added
They are player number 1
...
...

This is going to be a long text. You could collapse with the quick Visual Studio shortcut " Ctrl+M+M"

4. Capture Console.WriteLine() output into a variable

StringBuilder capturedOutput = new StringBuilder();
Console.SetOut(new StringWriter(capturedOutput));

5. Finally, time to do some test stuff :)

Finally, compare the actual output with the Golden Master. Cpnverting to lines using split and then comparing is actually optional. In case if the output does not match, it would come handy to find which line is not matching.

var actualLines = capturedOutput.ToString().Split(new[] {Environment.NewLine}, StringSplitOptions.None);
var expectedLines = GoldenMaster.Split(new[] {Environment.NewLine}, StringSplitOptions.None);

Assert.AreEqual(expectedLines,actualLines);

Probably, everything is cool. Sometimes, if everything is cool, it's hard to know if it's really cool or something is wrong

Once in a while, it's a good idea to make an intentional error in an expected output to check if tests are awake.

You made your first Golden Master Test work

6. We could do better

An easy way to do Golden Master testing in C# (also available to Java and many other languages) is to use Approval Tests. It does all the file handling for you, storing and comparing it. Let's see it in action

//Add a class atrribute
  [UseReporter(typeof(DiffReporter))]
  class GameTests
  {
    ...
  }
      
  //Replace your assesstion with below statement
  Approvals.Verify(capturedOutput.ToString());

The first time the "Approvals.Verify" method is executed, as shown in the above line will generate a text file, in the same folder where the test class is, called: GameTests.TestGame.received.txt . Basically, this is our copy of the working code, if we are happy, we just need to change the .received with .approved to approve the output. Once this is done, every time we run the test, ApprovalTests will compare the output with the approved file.

In our example we are just comparing the string output however, the library is capable of much more. You can explore it.

Now you are ready to rip the trivia code apart. Just make sure you run the tests every time you make a change. :)

Here's the full-fledged refactoring that was done in the game class. Refer to commits history to see how the final classes and tests have evolved

The supporting deck is available @ Surviving Legacy Code

Thank You

Last updated