C# 是否可以测试只向控制台写入一行的函数?

C# 是否可以测试只向控制台写入一行的函数?,c#,asp.net-core,tdd,fluent-assertions,C#,Asp.net Core,Tdd,Fluent Assertions,是否可以使用返回void测试函数,并使用FluentAssertion只向控制台写入一行,如下所示 static void WriteLine() { Console.WriteLine("It works!"); } 您可以通过监视标准输出(处理所有软件输出的是操作系统级流)来实现这一点 如果您实现了类似于示例的内容并监视了最新的输出,那么您可以在这些最重要的消息上使用断言来确定应用程序是否正常运行。您应该而不是具有一个将行写入控制台的函数 您应该有一个函数,将一行写入作为参数传递给

是否可以使用返回void测试函数,并使用
FluentAssertion
只向控制台写入一行,如下所示

static void WriteLine()
{
    Console.WriteLine("It works!");
}

您可以通过监视标准输出(处理所有软件输出的是操作系统级流)来实现这一点

如果您实现了类似于示例的内容并监视了最新的输出,那么您可以在这些最重要的消息上使用断言来确定应用程序是否正常运行。

您应该而不是具有一个将行写入控制台的函数

您应该有一个函数,将一行写入作为参数传递给它的
System.IO.TextWriter

从生产代码中,您将此函数的引用传递给
System.Console.Out
,因此它的行为类似于“向控制台写入一行的函数”

通过测试代码,您可以向它传递一个
System.IO.StringWriter
的实例,以便检查它写入的内容

这是非常著名的依赖注入概念的一个应用,对于编写可测试代码是绝对必要的。在你做的每件事中都要使用它


Wikipedia:

如果您想进行这样的单元测试,您需要将要验证调用的方法提取到接口中

然后将该接口传递给需要使用它的类,而不是让这些类直接调用实现(在本例中为
Console.WriteLine()
)。这被称为“依赖注入”

因此,对于您的示例,您发明了以下接口:

public interface IConsole
{
    void WriteLine(string text);
}
public class UnderTest
{
    public UnderTest(IConsole console)
    {
        _console = console;
    }

    public void WriteLine()
    {
        _console.WriteLine("It works!");
    }

    readonly IConsole _console;
}
var mock = new Mock<IConsole>();
您的真实代码将被传递给以下
IConsole
的具体实现的实例:

public sealed class MyConsole : IConsole
{
    public void WriteLine(string text)
    {
        Console.WriteLine(text);
    }
}
var underTest = new UnderTest(mock.Object);
但是,在测试类时,您将通过
IConsole
的特殊测试实现

假设您有以下使用
IConsole
接口的类:

public interface IConsole
{
    void WriteLine(string text);
}
public class UnderTest
{
    public UnderTest(IConsole console)
    {
        _console = console;
    }

    public void WriteLine()
    {
        _console.WriteLine("It works!");
    }

    readonly IConsole _console;
}
var mock = new Mock<IConsole>();
注意
IConsole
是如何传递给构造函数的-这称为“构造函数注入”

现在,当您调用
UnderTest.WriteLine()
它调用
IConsole.WriteLine(“它工作了!”)时,您想测试一下这一点

为此,首先使用模拟
IConsole
界面:

public interface IConsole
{
    void WriteLine(string text);
}
public class UnderTest
{
    public UnderTest(IConsole console)
    {
        _console = console;
    }

    public void WriteLine()
    {
        _console.WriteLine("It works!");
    }

    readonly IConsole _console;
}
var mock = new Mock<IConsole>();
现在,您可以调用希望调用的方法
IConsole.WriteLine(“它工作!”)

最后,您可以验证是否调用了
IConsole.WriteLine(“它工作!”)

mock.Verify(x => x.WriteLine("It works!"));
将所有这些整合到一个控制台应用程序中:

using System;
using Moq;

namespace Demo
{
    public interface IConsole
    {
        void WriteLine(string text);
    }

    // Not used but included as an example implementation.

    public sealed class MyConsole : IConsole
    {
        public void WriteLine(string text)
        {
            Console.WriteLine(text);
        }
    }

    public class UnderTest
    {
        public UnderTest(IConsole console)
        {
            _console = console;
        }

        public void WriteLine()
        {
            _console.WriteLine("It works!");
        }

        readonly IConsole _console;
    }

    public class Program
    {
        public static void Main()
        {
            var mock = new Mock<IConsole>();
            var underTest = new UnderTest(mock.Object);

            underTest.WriteLine();

            mock.Verify(x => x.WriteLine("It works!"));
        }
    }
}
使用系统;
使用最小起订量;
名称空间演示
{
公共接口IConsole
{
无效写线(字符串文本);
}
//未使用,但作为示例实现包含。
公共密封类MyConsole:IConsole
{
公共无效写线(字符串文本)
{
控制台写入线(文本);
}
}
公共类测试不足
{
公共欠测试(IConsole控制台)
{
_控制台=控制台;
}
公共无效写入行()
{
_console.WriteLine(“它工作了!”);
}
只读IConsole\u控制台;
}
公共课程
{
公共静态void Main()
{
var mock=new mock();
var underTest=新的underTest(模拟对象);
测试不足。WriteLine();
mock.Verify(x=>x.WriteLine(“它能工作!”);
}
}
}
(请注意,要编译此文件,您需要安装Moq。)


在本例中,我没有使用
FluentAssertions
,但您可以将它们与
Moq
一起使用,随您的喜好而定。

您所说的“测试”是什么意思?你是说写一个单元测试吗?如果您试图从单元测试中记录某些内容,我建议您将其写入测试上下文。我的意思是为函数WriteLine(如上所示)编写单元测试,而不是从测试中记录某些内容。是否希望单元测试验证消息是否已打印到控制台?@RogerioSchmitt为什么要这样做?