Test-Driven Test Development

Writing a Red Test

Integration Tests

Back in the root of our project folder, let’s create a project using an existing .NET testing framework.

At the time of writing, there are a many choices to choose from: xUnit, NUnit, MSTest, and more.

To make this tutorial easier for most developers out there, let’s use the most popular one: xUnit

Let’s make a new xUnit test project now by running this command from the root project folder:

dotnet new xunit -n MiniSpec.Specs

This will create a new project folder MiniSpec.Specs/. Let’s go there and write an integration test!

We’ll create a test which:

  • Runs minispec.exe with the MyTests.dll DLL assembly provided as an argument
  • Asserts that the output contains text which indicates that TestShouldPass() passed
  • Asserts that the output contains text which indicates that TestShouldFail() failed

What is minispec.exe? It doesn’t exist yet, but that’s the program we’ll make to run tests!

Rename UnitTest1.cs to IntegrationTest.cs and replace its content with the following:

IntegrationTest.cs

using Xunit;

public class IntegrationTest {

    [Fact]
    public void ExpectedSpecsPassAndFail() {
        // Arrange
        var minispecExe = System.IO.File.Exists("minispec.exe") ?
            "minispec.exe" : "minispec"; // No .exe extension on Linux
            
        using var minispec = new System.Diagnostics.Process {
            StartInfo = {
                RedirectStandardOutput = true, // Get the StandardOutput
                RedirectStandardError = true,  // Get the StandardError
                FileName = minispecExe,
                Arguments = "MyTests.dll"
            }
        };

        // Act
        minispec.Start();
        minispec.WaitForExit();
        var StandardOutput = minispec.StandardOutput.ReadToEnd();
        var StandardError = minispec.StandardError.ReadToEnd();
        var output = $"{StandardOutput}{StandardError}";
        minispec.Kill();

        // Assert
        Assert.Contains("PASS TestShouldPass", output);
        Assert.Contains("FAIL TestShouldFail", output);
        Assert.Contains("Kaboom!", output);
    }
}

Review

So, what’s happening here?

  • We assume that there will be a minispec.exe executable (or simply minispec on Linux).
  • We invoke the minispec.exe process passing the DLL with our defined tests as an argument.
  • We read StandardOutput and StandardError from the process result, i.e. all of the program’s console output.
  • StandardOutput and StandardError are combined because we don’t currently care which the results output to.
  • We look for expected messages in the output, e.g. PASS [testname] or FAIL [testname]

We’re totally making up some of these things as we go along, e.g. the PASS/FAIL messages.
This is how TDD works. We just need to make it fail, then pass, then we can change it later!