嘲笑'System.Console'的行为

时间:2011-05-02 01:54:18

标签: c# unit-testing interface console inversion-of-control

是否有一种标准的方法可以通过对接口而不是System.Console进行编程来使C#控制台应用程序单元可测试?

例如,使用IConsole接口?

你做过这个,你用过什么样的方法?

您是否公开了应用程序何时需要写入标准输出的事件?

5 个答案:

答案 0 :(得分:14)

我认为你使用界面的方法会起作用,我认为我不会利用事件。假设应用程序不接受命令行参数以外的用户输入,我可能会使用这样的东西来包装Console.Write/Console.WriteLine

public interface IConsoleWriter
{
    void Write(string format, params object[] args);
    void WriteLine(string format, params object[] args);
}

为了测试,我要么创建一个TestConsoleWriter,它将所有写入存储到我可以断言的缓冲区中,或者我会创建一个模拟并验证Write或{{1}用我期望的参数调用。如果你的应用程序要对控制台进行大量的写操作(比如输出+100 MB左右),那么出于性能原因,使用模拟可能会更好,但除此之外我会说你选择哪种方法更容易使用。

然而,这种方法确实有一些限制。如果您正在使用任何无法修改的程序集并且它们写入控制台,那么您将无法看到该输出,因为您无法强制这些类使用您的WriteLine。另一个问题是IConsoleWriterWrite方法有18个左右的重载,所以你可能会包含很多方法。要解决这些限制,您可能只想在测试时使用WriteLine方法将控制台输出重定向到您自己的Console.SetOut

就个人而言,我想我会采用TextWriter方法。它只需要在单元测试开始时添加一行(或者可能在SetOut方法中),并且可以断言写入SetUp的内容。

答案 1 :(得分:9)

您想要更改控制台在单元测试中写入的流。然后你可以放入模拟流或其他任何东西。请查看Mark Seemann在测试控制台上的这篇文章:

http://blogs.msdn.com/b/ploeh/archive/2006/10/21/consoleunittesting.aspx

答案 2 :(得分:4)

昨晚我偶然绊倒了这个帖子:Using reflection to override virtual method tables in C#

@paulo提出答案:LinFu作者:Philip Laureano。

Philip Laureano's Development Blog: Intercepting Console.WriteLine上的用法示例将指导您完成如何拦截对Console.WriteLine方法的调用,以及(在本例中)执行一些其他操作的示例。 .AOP for .NET ... 非常吵,恕我直言!

LinFu可能只是故障单,因为它没有被拦截的东西存在,所以你可以“拦截和修改”呼叫对第三方供应商的程序集的行为“ em>你的上下文“,但没有任何可能影响那个上下文之外的截获类的ACTUAL行为 ......所以LinFu听起来像是一个开始实现”的好地方“控制台应用程序的通用测试框架“。

您甚至可以利用现有的单元测试框架之一。我正在寻找一个开源框架。 NUnit浮现在脑海中。双关语。

无论如何,祝你好运,这是一个有趣的项目。请告诉我们,K?

干杯。基思。

答案 3 :(得分:2)

我建议使用moles

主要是因为我更喜欢让你的设计来确定你的接口和类,而不是你的测试

答案 4 :(得分:0)

如果仅使用一个线程,则此代码将起作用:

[TestClass]
public class MyTests
{
    private StringBuilder output;
    private StringWriter tempOutputWriter;
    private TextWriter originalOutputWriter;

    [TestInitialize]
    public void InitializeTest()
    {
        this.originalOutputWriter = Console.Out;
        this.tempOutputWriter = new StringWriter();
        Console.SetOut(tempOutputWriter);
        this.output = tempOutputWriter.GetStringBuilder();
    }

    [TestCleanup]
    public void CleanupTest()
    {
        Console.SetOut(originalOutputWriter);
        this.tempOutputWriter.Dispose();
    }

    [TestMethod]
    public void Test1()
    {
        Program.Main(new string[] { "1", "2", "3" });
        string output = this.output.ToString();
        ...
        this.output.Clear();
    }

    [TestMethod]
    public void Test2()
    {
        Program.Main(new string[] { "4", "5", "6" });
        string output = this.output.ToString();
        ...
        this.output.Clear();
    }
}