Is there a way I can check what is the output to the console in my unit test of my abstract class Question?
I am using NUnit & Moq.
My unit test looks like this:
[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));
}
Here I am checking that Question.Ask() is called and it works fine. Ask() does not return a value so I cannot assign it to a variable. The function just outputs to the console.
Is there a way I can verify in the test that the output == "question text" ?
EDIT: Forgot to mention Question is an abstract base class.
I tried the Concole.Setout method suggested with this code:
[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));
}
But it took 236 ms which is far too long for a test. Implementing the IWriter interface seems the best way to deal with it so I will give that a go now.
You can initialize Question
with a custom output writer, and then mock the writer to verify the output:
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)
}
Your test looks very strange - you are exercising mocked object instead of testing some real object which will be used by your application. If you are testing Question
object, then you should use exactly same instance of Question
type, as your application would use. What should be mocked - dependencies of Question
. Because classes should be unit-tested in isolation (otherwise problems with dependencies would result in failing tests of Question
which works fine).
So, if you have Question
which displays something on console (i.e. it depends on Console
), then unit-testing requires mocking of this dependency. You can't mock Console
with Moq, because it's static class. Thus you should create abstraction for console, which will be used by Question
:
public interface IConsole
{
void Write(string message);
}
Now inject this dependency to your Question
:
public class Question
{
private IConsole _console;
private string _message;
public class Question(IConsole console, string message)
{
_console = console;
}
}
With this code you can write tests for Question
behavior:
[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();
}
This test specifies, that question should send its message to console, when Ask
is executed. Implementation is simple:
public void Ask()
{
_console.Write(_message);
}
Now you have working Question
. And you should implement IConsole
which will be used by your application. It's simple wrapper:
public class ConsoleWrapper : IConsole
{
public void Write(string message)
{
Console.WriteLine(message);
}
}
In your real application inject this implementation to question (this will can be done automatically by dependency injection framework):
IConsole console = new ConsoleWrapper();
Question question = new Question(console, message);
Notes: I'd go with some interface like IView
instead of IConsole
. That will completely abstract Question
class from type of UI
it works with. And second note is separating of business logic from presentation logic. Usually you don't have class which is responsible for two things - holding data of question (and possibly processing it), and interacting with user. Usually there is something like controller, which receives user input, refreshes UI and asks business model for data.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With