60

我刚开始测试 xUnit.net,但它似乎没有像我预期的那样捕获任何输出(控制台、调试、跟踪)。

那可能吗?我正在使用带有 xUnit.net 1.8 的示例 .NET 4.0 类库。

4

7 回答 7

70

xUnit.net 2 的情况发生了一些变化。我知道问题是关于早期版本的,但是由于人们会在执行升级后登陆这里,我认为值得指出这一点。

为了在版本 2 的测试输出中看到某种输出,您需要在测试类中(通过构造函数参数)对 的实例进行依赖Xunit.Abstractions.ITestOutputHelper,然后使用WriteLine此接口上的方法。例如:

public class MyTestSpec
{
  private readonly ITestOutputHelper _testOutputHelper;

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

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

你可以选择将你的日志框架连接到这个接口,也许通过注入一个ILog将所有调用转发到ITestOutpuHelper.

我承认默认情况下您不希望这样做,但有时出于诊断目的,它可能非常有用。当您的测试仅在某些基于云的构建和测试服务器上失败时尤其如此!

于 2015-06-25T16:06:31.040 回答
46

如果您Console.Write嵌入到某些您不想重构的类层次结构的深处,这会有所帮助:

    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);
        }

        public override void Write(char value)
        {
            throw new NotSupportedException("This text writer only supports WriteLine(string) and WriteLine(string, params object[]).");
        }
    }
于 2017-11-28T10:34:15.810 回答
2

我使用 Console.SetOut 将 Console.Writes 输出到 .NET 测试日志(在 Visual Studio Code 中)。

using System;
using System.IO;
using Xunit;
using Xunit.Abstractions;

namespace UnitTest
{
    public class TestClass
    {
        private ITestOutputHelper output;
        public TestClass(ITestOutputHelper output)
        {
            this.output = output;
        }

        public class ConsoleWriter : StringWriter
        {
            private ITestOutputHelper output;
            public ConsoleWriter(ITestOutputHelper output)
            {
                this.output = output;
            }

            public override void WriteLine(string m)
            {
                output.WriteLine(m);
            }
        }

        [Fact]
        public void TestName()
        {
            Console.SetOut(new ConsoleWriter(output));
            Assert.True(ToBeTested.Foo());
        }



        public class ToBeTested
        {
            public static bool Foo()
            {
                Console.WriteLine("Foo uses Console.WriteLine!!!");
                return true;
            }
        }

    }

}

但是通过控制台运行测试更容易

dotnet test 

那里将显示输出,而无需对测试类进行任何修改。

这是不同的,因为 .NET 测试日志窗口使用 TRX 格式(Visual Studio 测试结果文件),请参阅

dotnet test -h | grep logger

有关 TRX 的更多信息。

于 2021-07-30T08:04:18.450 回答
1

这里有一个解决方案:https ://xunit.codeplex.com/discussions/211566

只需将其添加到您想要调试输出的构造函数或方法中:

Debug.Listeners.Add(new DefaultTraceListener());
于 2013-04-18T22:38:50.277 回答
1

这是我使用 StringBuilder 捕获输出并仅在测试失败时才输出的简单解决方案:

        [Fact]
        public void UnitTest1()
        {
            var sb = new StringBuilder();
            try
            {
                // ... the test code ...
                sb.AppendLine("Put your debug information here.");
                int expected = 1;
                int actual = 2;
                // What I really want to check:
                Assert.Equal(expected, actual);
            }
            // Catch exceptions from the Assert
            catch (Exception e)
            {
                sb.AppendLine("The original failure:");
                sb.AppendLine(e.Message);
                sb.AppendLine(e.StackTrace);
                Assert.True(false, sb.ToString());
            }
        }

由于只有 Xunit Assert.True() 方法接收消息,因此我在 catch 中使用它并通过其消息提供“日志”信息,如果测试失败,您将看到这些信息。

如果您只在测试中使用 Assert.True() 并提供 sb.ToString() 作为消息,则可以摆脱 try/catch。

于 2021-02-18T09:29:11.977 回答
0

我带着同样的问题来到这里。这就是我最终的结果。我希望它可以帮助别人。

如何编写自定义目标

    /// <summary>
    ///     Use this to output NLog information when running test cases.
    /// </summary>
    [Target("XUnit")]
    public class XUnitTarget : TargetWithLayout
    {
        [RequiredParameter] public ITestOutputHelper OutputHelper { get; set; }

        protected override void Write(LogEventInfo logEvent)
        {
            var logMessage = Layout.Render(logEvent);
            OutputHelper.WriteLine(logMessage);
        }

        /// <summary>
        /// Call this in the test where you wish to enable logging.
        /// </summary>
        /// <param name="testOutputHelper">The xUnit output helper from the test.</param>
        public static void Configure(ITestOutputHelper testOutputHelper)
        {
            var config = new LoggingConfiguration();
            var xUnitTarget = new XUnitTarget
            {
                OutputHelper = testOutputHelper
            };
            config.AddTarget("xUnit", xUnitTarget);
            config.AddRule(LogLevel.Trace, LogLevel.Fatal, xUnitTarget);
            LogManager.Configuration = config;
        }
    }
于 2019-05-14T16:49:25.693 回答
-9

一般来说,依赖日志记录测试是一条糟糕的道路。通过/失败应该是测试的结果。而且他们根本不应该达到有足够的事情发生的阶段,以至于需要查看痕迹。

显示xunit.gui.exe控制台和跟踪输出,xunit.console.exe不显示。如果它很重要,您可以连接一个 TraceListener,它通过制作适当的标准 .NET 配置条目来重定向到一个文件(FileWriterTraceListener如果您用谷歌搜索它,您应该能够连接它)。


更新:正如他的博客文章中所讨论的,Damian Hickey 有一个很好的替代品示例 - 将日志记录连接到 xUnit 2 ITestOutputHelper,如https://github.com/damianh/CapturingLogOutputWithXunit2AndParallelTests/blob/master/src/Lib.Tests中所示/Tests.cs

更新 2:在某些情况下,可以通过使用如下简单的适配器添加日志记录并将其提供给ITestOutputHelper不涉及LogContext的内容(我只有在 F# 中,抱歉):

// 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

这种方法与使用日志上下文的不同之处在于,Logger不会获取到全局 [上下文化] Serilog 的日志记录。

于 2011-08-23T10:13:09.353 回答