There’s two ways to unit test code that writes to the console (Console.WriteLine() / Console.Write()):
- Capture the output with Console.SetOut().
- Wrap the console method(s) and mock them out in the test. This allows you to capture the output.
In this article, I’ll show how to do both options.
Option 1 – Capture the output with Console.SetOut()
Let’s say you want to unit test the following code that outputs to the console with Console.WriteLine():
public class Greeter
{
public void Greet(string name)
{
Console.WriteLine($"Hello {name}");
}
}
Code language: C# (cs)
You can unit test this by capturing the console output. To do this, call Console.SetOut() and pass in a StringWriter. This redirects values passed into Console.WriteLine() to the StringWriter.
Here’s an example of unit testing this console output by capturing it:
[TestMethod()]
public void TestGreet()
{
//arrange
var greeter = new Greeter();
var name = "Benny";
var stringWriter = new StringWriter();
Console.SetOut(stringWriter);
//act
greeter.Greet(name);
//assert
var output = stringWriter.ToString();
Assert.AreEqual($"Hello {name}\r\n", output);
}
Code language: C# (cs)
Multiple output lines
Notice that StringWriter.ToString() returns one string, including newlines.
- Be sure to include the newline delimiter (\r\n on Windows) in the assertion, otherwise it will fail.
- If there’s multiple output lines, split the output string by the newline delimiter and assert against each output line.
Here’s an example of testing when there’s multiple output lines:
//assert
var outputLines = stringWriter.ToString().Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries);
Assert.AreEqual("What's your name?", outputLines[0]);
Assert.AreEqual($"Hello {name}", outputLines[1]);
Code language: C# (cs)
Option 2 – Wrap the console IO and mock out the wrapper
The console IO methods (i.e. Console.WriteLine()) are static methods, and since your code is dependent on the. You can use the following technique for unit testing code that depends on static methods:
- Wrap the static methods.
- Extract an interface for the wrapper.
- Dependency inject the interface.
- Mock out the interface in the unit tests.
I’ll show step-by-step how to do this in order to unit test the following class that outputs to the console:
public class Greeter
{
public void Greet(string name)
{
Console.WriteLine($"Hello {name}");
}
}
Code language: C# (cs)
Step 1 – Wrap the console IO methods and extract an interface
Create a wrapper class called ConsoleIO. This wraps the call to Console.WriteLine():
public class ConsoleIO : IConsoleIO
{
public void WriteLine(string s)
{
Console.WriteLine(s);
}
}
Code language: C# (cs)
Extract out an interface for the wrapper called IConsoleIO:
public interface IConsoleIO
{
void WriteLine(string s);
}
Code language: C# (cs)
Step 2 – Dependency inject the interface and use it
To use the console wrapper class:
- Constructor inject IConsoleIO.
- Replace calls to the console method(s) with calls to the wrapper methods.
Here’s an example of how to do this:
public class Greeter
{
private readonly IConsoleIO ConsoleIO;
public Greeter(IConsoleIO consoleIO)
{
ConsoleIO = consoleIO;
}
public void Greet()
{
ConsoleIO.WriteLine($"Hello {name}");
}
}
Code language: C# (cs)
Step 3 – Mock out the interface and use it in tests
In the unit test, create the mock IConsoleIO object. Use .Verify() to assert that WriteLine() was called with the expected output.
using Moq;
[TestMethod()]
public void TestGreet()
{
//arrange
var name = "Benny";
var mockConsoleIO = new Mock<IConsoleIO>();
var greeter = new Greeter(mockConsoleIO.Object);
//act
greeter.Greet(name);
//assert
mockConsoleIO.Verify(t => t.WriteLine($"Hello {name}"), Times.Once());
}
Code language: C# (cs)
Note: This is using the Moq mocking library. You can get this by installing the Moq package (Install-Package Moq).