我正在尝试设计一个单元测试来测试重试循环模式。我能想到的唯一方法是在测试进行到一半时更改嵌入在重试循环核心的方法返回的内容。
例如...我想在测试的前 5 秒内为特定方法抛出异常。然后停止抛出该异常,并在该点之后实际响应一些有效数据。
前 5 秒:
service.MethodToRetry(Arg.Any<string>()).ThrowsForAnyArgs(new Exception());
然后异常条件被移除并且 MethodToRetry() 正常完成。
这是可能的还是我的做法完全错误?我在 c# 中使用 xunit 和 nsubstitute 工作。
最佳答案
注意:这里的测试是为了演示 NSubstitute 的行为。在实际测试中,我们不会测试替代品。 :)
测试重试的一种方法是 stub 多次返回(如果你需要一个条件而不是特定数量的调用失败,这可能不适用于你的情况,但我认为我会从最简单的方法开始):
[Test]
public void StubMultipleCalls() {
Func<string> throwEx = () => { throw new Exception(); };
var sub = Substitute.For<IThingoe>();
// Stub method to fail twice, then return valid data
sub.MethodToRetry(Arg.Any<string>())
.Returns(x => throwEx(), x => throwEx(), x => "works now");
// The substitute will then act like this:
Assert.Throws<Exception>(() => sub.MethodToRetry(""));
Assert.Throws<Exception>(() => sub.MethodToRetry(""));
Assert.AreEqual("works now", sub.MethodToRetry(""));
// Will continue returning last stubbed value...
Assert.AreEqual("works now", sub.MethodToRetry(""));
Assert.AreEqual("works now", sub.MethodToRetry(""));
}
另一种选择是在调用时加入条件:
[Test]
public void StubWithCondition() {
var shouldThrow = true;
var sub = Substitute.For<IThingoe>();
sub.MethodToRetry(Arg.Any<string>()).Returns(x => {
if (shouldThrow) {
throw new Exception();
}
return "works now";
});
Assert.Throws<Exception>(() => sub.MethodToRetry(""));
shouldThrow = false; // <-- can alter behaviour by modifying this variable
Assert.AreEqual("works now", sub.MethodToRetry(""));
}
作为这种方法的修改版本,您还可以替换用于 stub 的回调:
[Test]
public void ReplaceLambda() {
Func<string> methodToRetry = () => { throw new Exception(); };
var sub = Substitute.For<IThingoe>();
sub.MethodToRetry(Arg.Any<string>()).Returns(x => methodToRetry());
Assert.Throws<Exception>(() => sub.MethodToRetry(""));
methodToRetry = () => "works now";
Assert.AreEqual("works now", sub.MethodToRetry(""));
}
理想情况下,我们会尽量避免测试中依赖于时间的逻辑,但如果确实有必要,我们可以在 5 秒后更新第二个示例中的条件,以获得您问题中提到的行为。
关于c# - 如何在单元测试执行中更改方法的返回值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34709632/