使用模拟 TextWriter 测试控制台输出

Testing console output using a mock TextWriter

我正在尝试为输出到控制台的 class 编写单元测试。为了从此 class 捕获控制台输出,我决定创建一个模拟 TextWriter 对象并在测试 class 中将控制台输出重定向到它。

[TestMethod]
public void TestMethod()
{
    var outLines = new List<string>();
    var mockWriter = new Mock<TextWriter>();
    mockWriter.Setup(writer => writer.WriteLine(It.IsAny<string>()))
        .Callback<string>(s => outLines.Add(s));
    Console.SetOut(mockWriter.Object);

    // ConsoleLogger is the class being tested
    // It will essentially just print its parameter to the console.
    var logger = new ConsoleLogger();
    logger.Error("foo");

    // Uncommenting this will cause the test to pass
    // Console.WriteLine("foo");

    Assert.AreEqual(1, outLines.Count);
    Assert.AreEqual("foo", outLines[0]);
}

此代码无效。但是,如果我在测试中执行 Console.WriteLine("foo") 它将起作用。

更奇怪的是,在调试测试时,来自 Logger class 的控制台似乎仍然被重定向到任何地方,因为单元测试除了 Assert 异常之外没有输出任何内容。

所以基本上我的问题是为什么模拟代码在从记录器 class 写入时没有得到 运行?

我想原因一定是你的ConsoleLoggerclass(你没有显示谁的代码?)实际上调用了another overload或其他方法。

如果其他成员是 virtualabstract,Moq 将覆盖它。由于您没有为其他重载提供 Setup,Moq 将提供带有 loose 模拟的 "empty" 实现,或者将使用 [=28= 抛出异常]strict 模拟。试试改成:

var mockWriter = new Mock<TextWriter>(MockBehavior.Strict);

看看这样的异常是否有助于发生。我的猜测:这是超载 WriteLine(object)。使用严格模拟通常是个好主意。

但是,如果其他成员是非虚拟成员,则 Moq 没有机会修改其实现。然后将使用原始 class TextWriter 的实现。该实现可能会调用另一个 虚拟的成员,您可以 Setup 那个。