I met such a method in one project, and the similar and brief logic is shown as follows
public void BookAClass()
{
_eventPublisher.Publish(new BookReadyEvent());
//Using static class Reader to
//read a configured Storage Place
//to retrieve an ExamResultFile object
var examResultFile = Reader.ReadFile("xxxx");
var examResultLevel = _analyzer.AnalyseExameFile(examResultFile);
if(examResultLevel == ExamResultEnum.Pass)
{
_eventPublisher.Publish(new BookCancelEvent());
}
else//examResultLevel == ExamResultEnum.Failed
{
_eventPublisher.Publish(new BookStartingEvent());
//TODO
}
}
And I tried to implement unit testing for two conditions: Pass and Failed. Regarding the Pass condition
[TestMethod()]
public void BookAClass_Pass_ExecuteAll()
{
var analyzerService = new Mock<AnalyzerService>();
analyzerService.Setup(analyzer =>
analyzer.AnalyseExameFile(new ExamResultFile()))
.Returns(ExamResultEnum.Pass);
var student = new Student(analyzerService.Object);
student.BookAClass();
}
Without a doubt, the unit testing failed, because AnalyseExameFile
will accept an object from the response of Reader.ReadFile("xxxx")
, rather than my fake data, which causes the examResultLevel to be null every time. As a result, the Pass condition will never be triggered.
I knew it would be better if consider the examResultFile
as an input parameter, other than a returned object from the method invocated in the BookAClass
. But the real scenario is much more complex, so I cannot refactor it now.
I wonder if there is any solution to implement unit testing for this condition?
CodePudding user response:
Yes, you can just tell Moq to ignore the argument you provided (and hence consider the setup to be fulfilled for all possible arguments).
In the simplest case, you could write:
analyzerService.Setup(analyzer =>
analyzer.AnalyseExameFile(It.IsAny<ExamResultFile>()))
.Returns(ExamResultEnum.Pass);
This declares that this setup shall be used for any invocations of AnalyseExameFile
, regardless of the argument. In advanced scenarios, you can also do
analyzerService.Setup(analyzer =>
analyzer.AnalyseExameFile(It.Is<ExamResultFile>(x => x.Name == "Hans")))
.Returns(ExamResultEnum.Pass);
This uses the functor to define whether the arguments should match.
If you create your mock as var analyzerService = new Mock<AnalyzerService>(MockBehavior.Strict);
You will be informed about a missing Setup for a call (one that silently returns null, as in your case).
CodePudding user response:
Instead of passing a new ExamResultFile instance you can use the It.IsAny
function to ensure you are matching against any arguments you pass to AnalyseExameFile
analyzerService.Setup(analyzer =>
analyzer.AnalyseExameFile(It.IsAny<ExamResultFile>()))
.Returns(ExamResultEnum.Pass);