为什么我的单元测试使用 Moq 的 Verify() 是不确定的?
Why are my unit tests using Moq's Verify() non-deterministic?
我们使用 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 expected invocation on the Mock at least once but was never peformed x=>x.Write(LogEventLevel.Error,It.IsAny<string>())
"
有什么想法吗?
从 posted 代码中无法完全看出问题所在。我明白为此制作 MCVE 有多么困难。所以我要猜两个。
猜测 1: 我怀疑你的问题的原因是在你的代码中使用了 static
s,特别是与记录器有关。
我怀疑正在发生的事情是其他测试(未在 post 中显示)也是 modifying/defining 记录器的行为方式,并且由于记录器是静态的,因此测试会干扰每个其他.
尝试重新设计代码,以便将日志记录功能的 实例 依赖注入到被测 class 中,使用 serilog 的 ILogger 接口,将其存储在只读文件中字段并在您要记录时使用它。
猜测 2: 基于 post 的部分“...在测试的构造函数中设置”你没有说(或标记)您正在使用哪个测试框架;但是我用过的少数方法更喜欢你在属性方法中而不是在测试的构造函数中做这种事情。例如,NUnit 有 OneTimeSetUp(在 class 中的任何测试之前是 运行)、SetUp(在 class 中的每个测试之前是 运行)、TearDown(在 运行 之后class 中的每个测试都是 运行),OneTimeTearDown(在 class 中的所有测试都是 运行 之后)。测试的构造函数可能以您不期望的顺序被调用,并且您的测试框架不支持这种顺序;而属性方法序列由框架保证。
我们使用 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 expected invocation on the Mock at least once but was never peformed
x=>x.Write(LogEventLevel.Error,It.IsAny<string>())
"
有什么想法吗?
从 posted 代码中无法完全看出问题所在。我明白为此制作 MCVE 有多么困难。所以我要猜两个。
猜测 1: 我怀疑你的问题的原因是在你的代码中使用了 static
s,特别是与记录器有关。
我怀疑正在发生的事情是其他测试(未在 post 中显示)也是 modifying/defining 记录器的行为方式,并且由于记录器是静态的,因此测试会干扰每个其他.
尝试重新设计代码,以便将日志记录功能的 实例 依赖注入到被测 class 中,使用 serilog 的 ILogger 接口,将其存储在只读文件中字段并在您要记录时使用它。
猜测 2: 基于 post 的部分“...在测试的构造函数中设置”你没有说(或标记)您正在使用哪个测试框架;但是我用过的少数方法更喜欢你在属性方法中而不是在测试的构造函数中做这种事情。例如,NUnit 有 OneTimeSetUp(在 class 中的任何测试之前是 运行)、SetUp(在 class 中的每个测试之前是 运行)、TearDown(在 运行 之后class 中的每个测试都是 运行),OneTimeTearDown(在 class 中的所有测试都是 运行 之后)。测试的构造函数可能以您不期望的顺序被调用,并且您的测试框架不支持这种顺序;而属性方法序列由框架保证。