In case of Google Cloud Build Log, Console.WriteLine
does work (current year: 2022, using: xunit 2.4.1):
Step #0: Starting test execution, please wait...
Step #0: A total of 1 test files matched the specified pattern.
Step #0: Hello world!
Below an example of a TestLogger
that logs to
- Google Cloud Build Log,
- Test Explorer Standard Output,
- Property
TestLogger.Messages
for assert statements.
I made the ITestOutputHelper
parameter optional in the TestLogger
constructor, because when using the TestLogger.Messages
property you may not want to also log to other outputs (but it might be nice to have only one TestLogger
in you unit test projects).
using Microsoft.Extensions.Logging;
using Xunit.Abstractions;
public class TestLogger<T> : TestLogger, ILogger<T>
{
public TestLogger(ITestOutputHelper? testOutputHelper = null) : base(testOutputHelper)
{ }
}
public class TestLogger : ILogger
{
private readonly ITestOutputHelper? _testOutputHelper;
public TestLogger(ITestOutputHelper? testOutputHelper = null)
{
_testOutputHelper = testOutputHelper;
}
public List<string> Messages { get; set; } = new();
IDisposable ILogger.BeginScope<TState>(TState state) => NoopDisposable.Instance;
bool ILogger.IsEnabled(LogLevel logLevel) => true;
void ILogger.Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
{
var message = formatter(state, exception);
LogLine($"[{logLevel}] {message}");
if (exception != null)
{
LogLine(exception.ToString());
}
}
private void LogLine(string message)
{
Messages.Add(message); // For assert statements in your unit test methods.
if (_testOutputHelper != null)
{
_testOutputHelper?.WriteLine(message); // Test Explorer Standard Output.
Console.WriteLine(message); // Google Cloud Build Log.
}
}
private class NoopDisposable : IDisposable
{
public static readonly NoopDisposable Instance = new();
public void Dispose() { }
}
}
Example usage:
using Xunit.Abstractions;
public class MyTestClass
{
private readonly ITestOutputHelper _testOutputHelper;
public MyTestClass(ITestOutputHelper testOutputHelper)
{
_testOutputHelper = testOutputHelper;
}
[Fact]
public void Test1()
{
var logger = new TestLogger(_testOutputHelper);
logger.LogInformation("Hello world!");
}
[Fact]
public void Test2()
{
var logger = new TestLogger<PleaseInjectMyLogger>(_testOutputHelper);
var x = new PleaseInjectMyLogger(logger);
x.Execute();
var logMessage = Assert.Single(logger.Messages);
Assert.Equal("Hooray!", logMessage);
}
}
Sources: