C# 在单元测试中检查控制台输出
在抽象类问题的单元测试中,有没有办法检查控制台的输出 我正在使用NUnit&Moq 我的单元测试如下所示:C# 在单元测试中检查控制台输出,c#,unit-testing,testing,nunit,moq,C#,Unit Testing,Testing,Nunit,Moq,在抽象类问题的单元测试中,有没有办法检查控制台的输出 我正在使用NUnit&Moq 我的单元测试如下所示: [Test] public void QuestionAsk() { var mock = new Mock<Question>(new object[]{"question text",true}); mock.CallBase = true; var Question = mock.Object;
[Test]
public void QuestionAsk()
{
var mock = new Mock<Question>(new object[]{"question text",true});
mock.CallBase = true;
var Question = mock.Object;
Question.Ask();
mock.Verify(m => m.Ask(), Times.Exactly(1));
}
[测试]
公众提问
{
var mock=new mock(新对象[]{“问题文本”,true});
mock.CallBase=true;
var-Question=mock.Object;
问;
mock.Verify(m=>m.Ask(),Times.justice(1));
}
在这里我检查这个问题。Ask()被调用,它工作正常。Ask()不返回值,因此无法将其分配给变量。该功能仅输出到控制台
是否有一种方法可以在测试中验证输出==“问题文本”
编辑:忘记提及问题是一个抽象基类
我尝试了Concole。此代码建议的放样方法:
[Test]
public void QuestionAsk()
{
var mock = new Mock<Question>(new object[]{"question text",true});
mock.CallBase = true;
var Question = mock.Object;
using (var consoleText = new StringWriter())
{
Console.SetOut(consoleText);
Question.Ask();
Assert.That(consoleText.ToString(), Is.StringMatching("question text"));
}
mock.Verify(m => m.Ask(), Times.Exactly(1));
}
[测试]
公众提问
{
var mock=new mock(新对象[]{“问题文本”,true});
mock.CallBase=true;
var-Question=mock.Object;
使用(var consoleText=new StringWriter())
{
控制台。放样(控制台文本);
问;
Assert.That(consoleText.ToString(),Is.StringMatching(“问题文本”));
}
mock.Verify(m=>m.Ask(),Times.justice(1));
}
但是它花了236毫秒,这对于测试来说太长了。实现IWriter接口似乎是处理此问题的最佳方式,因此我现在就来试试。您可以使用自定义输出编写器初始化
问题,然后模拟编写器以验证输出:
public interface IOutputWriter
{
void WriteLine(string s);
}
// Use this console writer for your live code
public class ConsoleOutputWriter : IOutputWriter
{
public void WriteLine(string s)
{
Console.WriteLine(s);
}
}
public abstract class Question
{
private readonly IOutputWriter _writer;
private readonly string _text;
private readonly bool _default;
public Question(IOutputWriter writer, params object[] args)
{
_writer = writer;
_text = (string)args[0];
_default = (bool)args[1];
}
public void Ask()
{
_writer.WriteLine(_text);
}
}
[Test]
public void QuestionAsk()
{
var writer = new Mock<IOutputWriter>();
var mock = new Mock<Question>(writer.Object, new object[]{"question text",true});
mock.CallBase = true;
var Question = mock.Object;
Question.Ask();
mock.Verify(m => m.Ask(), Times.Exactly(1));
mock.Verify(w => w.WriteLine(It.Is<string>(s => s == "question text")), Times.Once)
}
公共接口IOutputWriter
{
无效写线(字符串s);
}
//将此控制台编写器用于实时代码
公共类ConsoleOutputWriter:IOOutputWriter
{
公共无效写线(字符串s)
{
控制台。写入线(s);
}
}
公共抽象类问题
{
私有只读IOutputWriter\u writer;
私有只读字符串\u文本;
私有只读bool\u默认值;
公共问题(IOutputWriter编写器,参数对象[]args)
{
_作家=作家;
_text=(字符串)参数[0];
_默认值=(bool)参数[1];
}
公开询问
{
_writer.WriteLine(_text);
}
}
[测试]
公众提问
{
var writer=newmock();
var mock=new mock(writer.Object,new Object[]{“问题文本”,true});
mock.CallBase=true;
var-Question=mock.Object;
问;
mock.Verify(m=>m.Ask(),Times.justice(1));
mock.Verify(w=>w.WriteLine(It.Is(s=>s==“问题文本”)),Times.one)
}
您的测试看起来非常奇怪-您正在使用模拟对象,而不是测试应用程序将使用的真实对象。如果您正在测试Question
对象,那么您应该使用与应用程序使用的Question
类型完全相同的实例。应该模仿什么-问题的依赖关系
。因为类应该单独进行单元测试(否则,依赖性问题将导致对问题的测试失败,这很好)
所以,如果您有Question
,它在控制台上显示一些内容(即它取决于console
),那么单元测试需要模拟这种依赖关系。您不能用Moq模拟控制台,因为它是静态类。因此,您应该为控制台创建抽象,将由问题使用:
public interface IConsole
{
void Write(string message);
}
public class Question
{
private IConsole _console;
private string _message;
public class Question(IConsole console, string message)
{
_console = console;
}
}
现在将此依赖项注入到您的问题中
:
public interface IConsole
{
void Write(string message);
}
public class Question
{
private IConsole _console;
private string _message;
public class Question(IConsole console, string message)
{
_console = console;
}
}
使用此代码,您可以为问题行为编写测试:
[Test]
public void ShouldAskQuestionOnConsole()
{
var message = "Hello World";
var consoleMock = new Mock<IConsole>();
consoleMock.Setup(c => c.Write(message));
var question = new Question(consoleMock.Object, message);
question.Ask();
consoleMock.VerifyAll();
}
现在您有了工作问题
。您应该实现应用程序将使用的IConsole
。它很简单:
public class ConsoleWrapper : IConsole
{
public void Write(string message)
{
Console.WriteLine(message);
}
}
在实际应用程序中,将此实现注入到问题中(这将由依赖项注入框架自动完成):
注意:我会使用一些界面,比如IView
,而不是IConsole
。这将从它使用的UI
类型中完全抽象出Question
类。第二个注意事项是业务逻辑和表示逻辑的分离。通常情况下,您并没有负责两件事的类——保存问题数据(并可能对其进行处理)和与用户交互。通常有类似控制器的东西,它接收用户输入,刷新UI并向业务模型询问数据。请参见:。除了@Keith的链接,实际上您不想从问题“业务对象”调用控制台。执行输出是正在运行的程序的任务,因为您现在不能将该类重新用于(比如)web应用程序。如果您只是让它返回字符串,调用程序可以决定如何输出它。@CodeCaster这是一个很好的观点。我最终将把它移植到web上,所以我应该尽早处理它。我做这个项目是为了学习C。我真的很感激你的建议。谢谢。这似乎是最好的方法。我最初喜欢Console.SetOut方法,因为它只需要一行代码就可以进行现有的测试,但它使我的测试从6ms变为236ms。这将使我瘫痪,因为我计划进行大量的测试。这种性能下降是正常的,还是我做错了什么?粘贴我的代码作为答案可以吗?还是我必须为此提出一个新问题?对不起,我还是新来的。你可以发布你自己问题的答案。我怀疑最后一行mock.Verify(w=>w.WriteLine(It.Is(s=>s==“问题文本”)),Times.Once
应该是writer.Verify(w=>w.WriteLine(It.Is(s=>s==“问题文本”)),Times.Once
。另一个问题,Question
类中的Ask
应该是virtual
。