首页 > 解决方案 > 为什么我的单元测试使用 Moq 的 Verify() 是不确定的?

问题描述

我们使用 Moq 4 进行单元测试,特别是控制器单元测试。我们正在使用 Moq 的 Verify() 方法来确保记录了错误。问题是测试可以通过,但在下一次运行时失败。

我们使用 Serilog 作为我们的记录器。

Action 方法看起来像

public IActionResult Index(){
    try{
         var data = _repository.GetData();
         return View(data);
    }catch(Exception e){
        Log.Error(e.Message);
    }
}

所以单元测试正在使用

_mockLogger.Verify(x=> x.Write(LogEventLevel.Error,It.IsAny<string>()));

mockLogger在测试的构造函数中设置,如

var _mockLogger = new Mock<Serilog.ILogger>();
Log.Logger = _mockLogger.Object;
//...

并且存储库被模拟以在调用时抛出异常。

当它失败时,我们会收到错误消息

“Moq.MoqException 预期在 Mock 上至少调用一次,但从未执行过x=>x.Write(LogEventLevel.Error,It.IsAny<string>())

有任何想法吗?

标签: c#asp.net-coremoqserilognon-deterministic

解决方案


从发布的代码中无法完全看出问题所在。我很欣赏为此制作 MCVE 的难度。所以我要猜两个。

猜测 1:我怀疑您的问题的原因是static您的代码中使用了 s,特别是与记录器有关。

我怀疑正在发生的事情是其他测试(未在帖子中显示)也在修改/定义记录器的行为方式,并且由于记录器是静态的,因此测试相互干扰。

尝试重新设计代码,以便使用 serilog 的 ILogger 接口将日志记录功能的实例依赖注入到被测类中,将其存储在只读字段中,并在您想要记录时使用它。

猜想 2:根据帖子中“...在测试的构造函数中设置”的部分,您没有说(或标记)您正在使用哪个测试框架;但是我用过的少数人更喜欢你在属性方法中而不是在测试的构造函数中做这种事情。例如,NUnit 有 OneTimeSetUp(在该类中的任何测试运行之前)、SetUp(在该类中的每个测试运行之前)、TearDown(在该类中的每个测试运行之后)、OneTimeTearDown(在所有测试之后在那个类中运行)。您的测试的构造函数可能以您不期望的顺序被调用,并且您的测试框架不支持该顺序;而属性化方法序列由框架保证。


推荐阅读