【问题标题】:xUnit.net does not capture console outputxUnit.net 不捕获控制台输出
【发布时间】:2011-10-31 14:56:16
【问题描述】:

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

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

【问题讨论】:

    标签: xunit.net


    【解决方案1】:

    如果您的 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[]).");
            }
        }
    

    【讨论】:

    • 如果您使用任何 TextWriter 的其他方法会发生什么。例如,Write?
    • @NechemiaHoffmann,这是很久以前的事了,但我相信在内部他们都调用了 WriteLine 的这些重载
    • 不,恰恰相反。覆盖 Write(char) 将使 WriteLine(string) 工作,但不是相反。见stackoverflow.com/questions/17712607/…
    【解决方案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 的更多信息。

    【讨论】:

      【解决方案3】:

      这是我使用 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。

      【讨论】:

      • Xunit 没有为此提供一个不错的方法,所以你的方法对我有帮助!
      【解决方案4】:

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

      How to write a custom target

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

      【讨论】:

        【解决方案5】:

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

        您可以选择将您的日志框架连接到此接口,或许可以通过注入将所有调用转发到ITestOutpuHelperILog 实现。

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

        【讨论】:

        • 顺便说一下,ITestOutputHelper 是在 Xunit.Abstractions 命名空间中找到的。
        • official example code。它仍然适用于 .NET 核心项目。
        • 如果你已经有一个自定义对象实例(即MyDatabaseFixture)被传入这个MyTestSpec构造函数而不是ITestOutputHelper
        • 运行dotnet test 时没有看到输出。在运行 .NET Core 测试运行程序时使用此接口有什么秘密吗? (编辑:看起来日志仅在断言失败后显示,因此意图似乎是日志只能用于调试故障,我可以忍受,不情愿。)
        • @JonathanB。运行dotnet test --logger "console;verbosity=detailed"
        【解决方案6】:

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

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


        更新:正如his blog post 中所讨论的,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
        

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

        【讨论】:

        • 感谢您的回复。我正在使用 xunit.console.exe。我知道这不是一个好的解决方案,也不是真正的预期用途。我需要它的原因是在使用 TDD 创建新类时调试一些字符串操作。
        • “总的来说,依赖日志记录和测试是一条糟糕的道路。” - 是的,但是在我设置测试时使用 Console.Writeline 输出内容非常有帮助。有时我正在测试一个方法,并且需要查看该方法的输出(例如,在进行自定义序列化时是否正确序列化)以插入回测试以进行比较。
        • 切换到 NUnit,它捕获标准输出。如果测试失败,了解生产代码中发生了什么非常重要,在这种情况下,日志记录变得很重要。将任何特定于测试框架的依赖项注入到生产代码中,这样我就可以在运行测试时看到输出,这简直是荒谬的。而且我发现当人们开始告诉你“xxx 是个坏主意……”时,虽然听起来很酷,但这通常是你遇到问题的工具无能的借口。
        • 所以 xUnit 的作者认为,在没有 WHY 信息的情况下失败的测试是一个很棒的设计?
        • 我强烈反对您关于“依赖日志记录和测试是一条糟糕的道路”的评论。我们运行集成测试(除了单元测试),如果发生故障,在进一步调查之前查看日志信息以了解究竟是什么失败是非常有帮助的。
        【解决方案7】:

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

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

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

        【讨论】:

        • 对于那些稍后调整的人,这不再是推荐的做法。见xunit.github.io/docs/capturing-output.html
        • 哦,伙计们,那些 xunit 家伙,如果它不够复杂,那不是 xunit -.- Ofc .Net Core 上没有 IMessageSink.... MsTestV2 来救援!
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-11-08
        • 1970-01-01
        • 2014-08-15
        • 1970-01-01
        • 2021-05-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多