Search code examples
xunit.net

xUnit.net does not capture console output


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.


Solution

  • 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.