使用ReSharper,在长时间运行的单元测试期间如何显示调试输出?

41
我正在使用ReSharper测试运行器和xUnitContrib Resharper插件的xUnit。
当我有一个长时间运行的测试时,我想能够在单元测试输出窗口中输出一些进度指示器。
我尝试过Debug.WriteLines、Trace.WriteLine和Console.WriteLine。所有这些都有相同的行为-直到测试完成,输出窗口才不会显示任何内容。
例如:
[Fact]
public void Test()
{
    Debug.WriteLine("A");
    Trace.WriteLine("B");
    Console.WriteLine("C");

    Thread.Sleep(10000);
}

测试在10秒钟结束并完成之前不会输出任何内容。我该如何获得实时输出?
更新1: 我也尝试了MSTest和NUnit。只有NUnit可以实时输出。 MSTest和XUnit直到测试完成才返回任何输出。奇怪的是,虽然XUnit和NUnit的测试输出看起来像这样:
A
B
C

MSTest的输出如下:

C


Debug Trace:

A
B

鉴于所有这些变化,我认为答案是由测试运行程序实现决定如何和何时输出。有人知道是否可以配置XUnit测试运行程序吗?
更新2:
我认为这一定是xUnitContrib的缺陷。已经发布到他们的CodePlex问题跟踪器。

当我处于调试模式时,Debug.WriteLine() 对我来说完全正常。因此,如果您想以一般方式登录,我建议使用 Brenton 的答案。 - DerApe
@derape - 不,它不适用于xunit。在测试完成之前不会出现任何输出。 - Matt Johnson-Pint
我明白了,我还没有使用过XUnit - 只用过NUnit。而且NUnit的日志记录确实可以沿着调试线程工作... - brenton
1
是的,NUnit可以做到这一点。请查看我的更新。 - Matt Johnson-Pint
8个回答

35
如果您使用的是xUnit.net 1.x,那么您可能曾经将输出写入控制台、调试或跟踪。当xUnit.net v2默认打开并行化时,这种输出捕获机制不再适用;无法知道可能正在并行运行的许多测试中,哪些负责写入这些共享资源。从v1.x升级到v2.x的用户应该改用其中的两种新方法之一。
请参阅此处,以了解如何在xUnit.net v2中执行日志记录的示例:

http://xunit.github.io/docs/capturing-output.html

这是一个例子:

using Xunit;
using Xunit.Abstractions;

public class MyTestClass
{
    private readonly ITestOutputHelper output;

    public MyTestClass(ITestOutputHelper output)
    {
        this.output = output;
    }

    [Fact]
    public void MyTest()
    {
        var temp = "my class!";
        output.WriteLine("This is output from {0}", temp);
    }
}

这正是我在寻找的。谢谢! - nrodic
1
这对于从测试中编写输出非常有用,但是当您需要从测试调用的代码实时输出信息时,您必须修改被测试的代码以传入和利用ITestOutputHelper。我理解并发问题使得处理代码输出变得困难,我只是想为其他人指出这个解决方案有一个限制。使用类似跟踪侦听器或劫持Debug.Write*方法的默认输出流将是不错的选择。 - Dave Parker

13

NUnit中可以这样做:

Console.SetOut(TestContext.Progress);

这个回答晚了,是因为我遇到了同样的问题,不过我已经解决了。或许对其他人有帮助。


这在我的 .net core linux + nunit 3.11 上没有起作用。 - mbalsam
感谢您的更新。很抱歉,我没有环境来尝试修复它。 - Yitzchak

6

ReSharper在单元测试中某种方式移除了默认的监听器。要在输出窗口中显示文本,只需添加这一行代码:

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

这对我来说不起作用,无论是在单元测试会话窗口还是在输出窗口(使用MSTest)。 - Robert Harvey

4

我也曾遇到过同样的问题,只是比你提问的时间晚了8年。 :-(

我非常确定这在某个时候是由Resharper自动实现的,因为我记不得以前要手动操作。但是在之后的某个时刻,它停止工作了。

解决方案(适用于NUnit)是在你的NUnit单元测试的顶部添加以下代码行。

Console.SetOut(TestContext.Progress)

这会使您现有的所有控制台输出(Console.Out.WriteLine(""))直接输出到Resharper单元测试运行器输出部分,当单元测试正在执行时。

2
XunitLogger 使用 AsyncLocal<T> 来跟踪日志记录上下文,因此可以将对 Trace.WritelineConsole.Writeline 的调用路由到正确的 ITestOutputHelper 实例。
用法:
static class ClassBeingTested
{
    public static void Method()
    {
        Trace.WriteLine("From Trace");
        Console.WriteLine("From Console");
        Console.Error.WriteLine("From Console Error");
    }
}

public class TestBaseSample  :
    XunitLoggingBase
{
    [Fact]
    public void Write_lines()
    {
        WriteLine("From Test");
        ClassBeingTested.Method();

        var logs = XunitLogger.Logs;

        Assert.Contains("From Test", logs);
        Assert.Contains("From Trace", logs);
        Assert.Contains("From Console", logs);
        Assert.Contains("From Console Error", logs);
    }

    public TestBaseSample(ITestOutputHelper output) :
        base(output)
    {
    }
}

0

对于Console.WriteLine,这是一种方法(可能有更好的方法,但这对我有效):

public class ConsoleOutputHelper : TextWriter, ITestOutputHelper
{
    private readonly ITestOutputHelper _helper;

    public ConsoleOutputHelper(ITestOutputHelper helper) => _helper = helper;

    public override void WriteLine(string? message)
    {
        try
        {
            _helper.WriteLine(message);
        }
        catch (InvalidOperationException ex) when (ex.Message.Contains("no currently active test"))
        {
        }
    }

    public override void WriteLine(string format, params object?[] args) => 
        _helper.WriteLine(format, args);

    public override Encoding Encoding => new UTF8Encoding();
}

然后,在您的测试构造函数中:

public MyTests(ITestOutputHelper helper) => 
    Console.SetOut(new ConsoleOutputHelper(helper));

...然后,在你正在测试的代码中

Console.Out.WriteLine("Beginning something...");

更新

Trace.WriteLine 在生产代码中可能更好。

设置如下(需要添加和删除跟踪侦听器):

public class MyTests : IDisposable
{
    private readonly TraceOutputHelper _traceOutputHelper;

    public MyTests(ITestOutputHelper helper)
    {
        _traceOutputHelper = new TraceOutputHelper(helper);
        Trace.Listeners.Add(_traceOutputHelper);
    }

    public void Dispose() => Trace.Listeners.Remove(_traceOutputHelper);

...而且TraceOutputHelper与上面的ConsoleOutputHelper非常相似:

public class TraceOutputHelper : TextWriterTraceListener, ITestOutputHelper
{
    private readonly ITestOutputHelper _helper;

    public TraceOutputHelper(ITestOutputHelper helper) => _helper = helper;

    public override void WriteLine(string? message)
        => _helper.WriteLine(message);

    public void WriteLine(string format, params object[] args) => _helper.WriteLine(format, args);
}

0
我发现最简单的方法是利用log4net并创建一个控制台记录器。在运行过程中,您可以调用logger.Info("info here");或log.Debug("info here"); - 真正任何您喜欢的日志记录级别 - 输出将显示在Resharper Unit Test Sessions中。
请阅读有关log4net框架的更多信息在Apache log4net主页上配置示例也将非常有价值。

不是的。我添加了log4net,但这些消息甚至没有出现在单元测试输出中。我使用了一个简单的控制台appender - 来自示例的那个。 - Matt Johnson-Pint

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接