xUnit.net does not capture console output

2019-03-17 06:42发布

问题:

I just started testing xUnit.net, but it doesn't seem to capture any output (Console, Debug, Trace), as I would have expected.

Is that possible? I am using a sample .NET 4.0 class-library with xUnit.net 1.8.

回答1:

In general, it's a bad road to go down to be reliant on logging and tests. The pass/fail should be the outcome of the tests. And they simply shouldn't get to the stage where there's enough stuff going on that looking at a trace will be necessary.

The xunit.gui.exe shows Console and Trace output, xunit.console.exe does not. If it's important, you could hook up a TraceListener which redirects to a file by making appropriate standard .NET config entries (Theres' a FileWriterTraceListener which you should be able to hook in if you google it).


UPDATE: As discussed in his blog post, Damian Hickey has a good example of a possible substitute - wiring logging to the xUnit 2 ITestOutputHelper as demonstrated in https://github.com/damianh/CapturingLogOutputWithXunit2AndParallelTests/blob/master/src/Lib.Tests/Tests.cs

UPDATE 2: In some cases, one can add logging and feed it to the ITestOutputHelper without involving LogContext by using a simple adapter as follows (I only have it in F#, sorry):

// Requirement: Make SUT depend on Serilog NuGet
// Requirement: Make Tests depend on Serilog.Sinks.Observable

type TestOutputAdapter(testOutput : Xunit.Abstractions.ITestOutputHelper) =
    let formatter = Serilog.Formatting.Display.MessageTemplateTextFormatter(
        "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] {Message}{NewLine}{Exception}", null);
    let write logEvent =
        use writer = new System.IO.StringWriter()
        formatter.Format(logEvent, writer);
        writer |> string |> testOutput.WriteLine
    member __.Subscribe(source: IObservable<Serilog.Events.LogEvent>) =
        source.Subscribe write

let createLogger hookObservers =
    LoggerConfiguration()
        .WriteTo.Observers(Action<_> hookObservers)
        .CreateLogger()
let createTestOutputLogger (output: ITestOutputHelper) =
    let adapter = TestOutputAdapter testOutputHelper
    createLogger (adapter.Subscribe >> ignore)

type Tests(testOutputHelper) =
    let log = createTestOutputLogger testOutputHelper

    [<Fact>] let feedToSut () =
        // TODO pass log to System Under Test either as a ctor arg or a method arg

The difference with this approach vs using the log context is that logging to the global [contextualized] Serilog Logger will not get picked up.



回答2:

The situation has changed a little with xUnit.net 2. I know the question is about an earlier version, but as people will land here having performed the upgrade, I thought it was worth pointing this out.

In order to see some kind of output in the test output in version 2 you will need to take a dependency in your test class (via a constructor argument) on an instance of ITestOutputHelper, then use the WriteLine method on this interface. E.g.:

public class MyTestSpec
{
  private readonly ITestOutputHelper _testOutputHelper;

  public MyTestSpec(ITestOutputHelper testOutputHelper)
  {
    _testOutputHelper = testOutputHelper;
  }

  [Fact]
  public void MyFact()
  {
    _testOutputHelper.WriteLine("Hello world");
  }
}

You could choose to hook up your logging framework to this interface, perhaps by injecting an ILog implementation that forwarded all calls to ITestOutpuHelper.

I acknowledge that you won't want to do this by default, but for diagnostic purposes from time to time it can be quite useful. This is especially true where your tests only fail on some cloud based build & test server!



回答3:

This can help if your Console.Write is embedded deep down some class hierarchy that you don't want to refactor:

    public MyTestClass(ITestOutputHelper output)
    {
        var converter = new Converter(output);
        Console.SetOut(converter);
    }

    private class Converter : TextWriter
    {
        ITestOutputHelper _output;
        public Converter(ITestOutputHelper output)
        {
            _output = output;
        }
        public override Encoding Encoding
        {
            get { return Encoding.Whatever; }
        }
        public override void WriteLine(string message)
        {
            _output.WriteLine(message);
        }
        public override void WriteLine(string format, params object[] args)
        {
            _output.WriteLine(format, args);
        }
    }


回答4:

There is a solution as found here: https://xunit.codeplex.com/discussions/211566

Simply add this to your constructor or method where you want debugging output:

Debug.Listeners.Add(new DefaultTraceListener());


标签: xunit.net