How do I write to the Azure Devops task log from xUnit tests?
Asked Answered
C

3

12

I'm running some tests in an Azure Devops pipeline and I'm seeing some failures on the build agent that I don't get locally. I'm trying some low-fi debugging and want to write some chatter out to task log but I can't see how. I've tried Console.WriteLine(), Debug.WriteLine() and Trace.WriteLine() but I don't see any of my messages in the log.

How can I write to the pipeline task log?

Cleres answered 19/7, 2019 at 14:40 Comment(4)
Hi, are you using dotnet test CLI to run your tests in Azure Devops?Inpour
I'm using the VSTest step that Azure added when it created the pipeline. @InpourCleres
I'm not sure this is possible as its been logged as an issue/feature enhancement before github.com/microsoft/azure-pipelines-tasks/issues/3986. There should be a .trx file generated at the end which will likley contain any output logs?Termor
@GregB - were you able to resolve this? if yes, can you please tell us how you went about it?Chickenlivered
R
2

You have to use a logger that's directed at ITestOutputHelper. Details here: .net core 2.0 ConfigureLogging xunit test

If you are Ok with a third party here are two that will simplify the solution: https://blog.martincostello.com/writing-logs-to-xunit-test-output/ https://www.neovolve.com/2018/06/01/ilogger-for-xunit/

Reinertson answered 8/12, 2020 at 18:56 Comment(0)
L
0

At present, I'm not aware of any sane way to do this.

If you really need debugging code whose output is visible from the Azure run, you can do something hackish like the following in your test.

[Fact]
public void LogDebugInfo() {
  var outputString = $"my debug info here";
  Assert.Equal($"", outputString);
}

Obviously this will cause the test to fail, but at least you can see your debug info.

Limitary answered 30/11, 2020 at 14:54 Comment(1)
This seems to truncate the valueImagery
P
0

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:

Polycythemia answered 3/6, 2022 at 9:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.