]> Capturing console outputs with Microsoft Test Framework 🌐:aligrant.com

Capturing console outputs with Microsoft Test Framework

Alastair Grant | Monday 20 July 2020

Recently I've been really appreciating the MSTest Framework included with Visual Studio, and have been using it more and more over other unit test frameworks; it works out of the box, simple to use and very easy to write effective tests.  Visual Studio will also stub out your unit tests for you for a given class.  And to top if off, you can associate tests with TFS/Azure DevOps test cases.

I am currently working on a console application which takes a number of arguments, this is something that has always been frustrating to test as traditionally one would either need to change the debug arguments for each run, or run scripts against a built exe, which meant no debugging.  I had the bright idea of calling the Console Program.Main() function directly from a unit test, and thus allow me to script all variations on arguments needed, but there were a couple of considerations that needed to be made.

The first thing to do is to make the Program class and the Main method public, which is not required for a Console App to run, but required for another assembly to gain visibility (there are other ways around this if it's a real issue, but setting the visibility to public is the quickest).  And that's about it, you can now run unit tests against your console application entry point.

Capturing Console Output

You might though, want to test the outcome of your inputs.  A console application is fairly enclosed and you're likely to be spitting data out to Standard Output, and not returning data from methods.  Which brings up the next question, how do we see what is being output by the console application?

.NET makes it easy and we can redirect the Standard Output to other locations through a TextWriter.  But for those experienced with MSTest will have realised that the output is already redirected to the test logs.  And in fact, if we try to override this in your ClassInitialize method not a lot happens as MSTest will hijack the console feed again.  This can be worked-around by moving the setup closer into your TestInitialise method, I've used a StringBuilder:

[TestClass]
public class MyTests
{
    private StringBuilder ConsoleOutput { get; set; }
    
    [TestInitialize]
    public void Init()
    {
        Console.SetOut(new StringWriter(this.ConsoleOutput));    // Associate StringBuilder with StdOut
        this.ConsoleOutput.Clear();    // Clear text from any previous text runs
    }
}

Each time a  test is executed, the TestInitialize method is executed first, reassigning our TextWriter and clearing out the previous value (we could also put the clear out in a TestCleanup decorated method).

Now whenever Console.Write is called (or anything else that writes to the standard output stream), the text will be added into the StringBuilder.  This is a fairly blunt approach and essentially dumps the screen output into a string, which you can then search for the data you're interested in, e.g.

Assert.IsTrue(this.ConsoleOutput.ToString().Contains("Expected output"));

A more surgical approach would be to implement your own TextWriter that you can analyse line-by-line as-it-happens.

Recording test outputs

As noted above, by default the test framework normally captures the Standard Output, which is presented in the results of the test.  Fortunately for us the Test Framework also captures the default Tracer, so you can get output by calling Debug.WriteLine().

If you really need to capture the Console Standard Output as a test report, then you will need to resort to proxying two TextWriter instances, capturing Console.Out in setup and adding it with your own TextWriter.  I don't think there is a class like this already in the framework, but it would be trivial to put together if needed.

Breaking from the voyeuristic norms of the Internet, any comments can be made in private by contacting me.