Home > front end >  Mocked interface doesn't mock
Mocked interface doesn't mock

Time:05-10

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);
  • Related