设置如下:
public interface IFoo
{
void Fizz();
}
[Test]
public void A()
{
var foo = new Mock<IFoo>(MockBehavior.Loose);
foo.Object.Fizz();
foo.Verify(x => x.Fizz());
// stuff here
foo.Verify(x => x.Fizz(), Times.Never()); // currently this fails
}
基本上我想在// stuff here
输入一些代码来使foo.Verify(x => x.Fizz(), Times.Never())
通过。
因为这可能构成moq / unit测试滥用,我的理由是我可以这样做:
[Test]
public void Justification()
{
var foo = new Mock<IFoo>(MockBehavior.Loose);
foo.Setup(x => x.Fizz());
var objectUnderTest = new ObjectUnderTest(foo.Object);
objectUnderTest.DoStuffToPushIntoState1(); // this is various lines of code and setup
foo.Verify(x => x.Fizz());
// reset the verification here
objectUnderTest.DoStuffToPushIntoState2(); // more lines of code
foo.Verify(x => x.Fizz(), Times.Never());
}
基本上,我有一个状态对象,其中需要进行相当多的工作(在制作各种模拟对象和其他模拟对象方面)将其推入State1。然后我想测试从State1到State2的转换。我没有重复或抽象代码,而是只重新使用State1测试,将其推入State2并执行我的断言 - 除了验证调用之外,我可以做所有这些操作。
我不认为你可以重置像这样的模拟。相反,如果您知道在转换到状态1时应该调用Fizz
一次,您可以像这样进行验证:
objectUnderTest.DoStuffToPushIntoState1();
foo.Verify(x => x.Fizz(), Times.Once()); // or however many times you expect it to be called
objectUnderTest.DoStuffToPushIntoState2();
foo.Verify(x => x.Fizz(), Times.Once());
话虽如此,我仍然会为此创建两个单独的测试。作为两个测试,更容易看出转换到状态1是否失败,或者转换到状态2是否失败。此外,当像这样一起测试时,如果您转换到状态1失败,则测试方法退出,并且您转换到状态2不会被测试。
编辑
作为一个例子,我用xUnit测试了以下代码:
[Fact]
public void Test()
{
var foo = new Mock<IFoo>(MockBehavior.Loose);
foo.Object.Fizz();
foo.Verify(x => x.Fizz(), Times.Once(), "Failed After State 1");
// stuff here
foo.Object.Fizz();
foo.Verify(x => x.Fizz(), Times.Once(), "Failed after State 2");
}
此测试失败,并显示消息“状态2后失败”。这模拟了如果将foo推入状态2的方法调用Fizz
会发生什么。如果是这样,第二个Verify
将失败。
再看一下你的代码,因为你正在调用一个方法来验证它在模拟上没有调用另一个方法,我认为你需要将CallBase
设置为true
,以便调用基本的DoStuffToPushIntoState2
而不是mock的覆盖。
我想在创建这篇文章很久之后,他们添加了OP要求的功能,有一个名为Moq.MockExtensions.ResetCalls()的Moq扩展方法。
使用此方法,您可以完全按照您的意愿执行,如下所示:
[Test]
public void Justification()
{
var foo = new Mock<IFoo>(MockBehavior.Loose);
foo.Setup(x => x.Fizz());
var objectUnderTest = new ObjectUnderTest(foo.Object);
objectUnderTest.DoStuffToPushIntoState1(); // this is various lines of code and setup
foo.Verify(x => x.Fizz());
foo.ResetCalls(); // *** Reset the verification here with this glorious method ***
objectUnderTest.DoStuffToPushIntoState2(); // more lines of code
foo.Verify(x => x.Fizz(), Times.Never());
}
更新
现在我们应该在库的最新版本上使用.Invocations.Clear()代替.ResetCalls():
foo.Invocations.Clear()
我还目睹了Times.Exactly(1)使用MoQ进行单元测试时的验证失败,并出现“被称为2次”错误消息。我认为这是MoQ中的一个错误,因为我希望在每次测试运行时都能看到干净的模拟状态。
我的工作是在测试设置中分配一个新的模拟实例和测试目标。
private Mock<IEntityMapper> entityMapperMock;
private OverdraftReportMapper target;
[SetUp]
public void TestSetUp()
{
entityMapperMock = new Mock<IEntityMapper>();
target = new OverdraftReportMapper(entityMapperMock.Object);
}
您可以使用Callback方法而不是Verify,并计算调用次数。
这在Moq Quick Start page上得到证明,因此:
// returning different values on each invocation
var mock = new Mock<IFoo>();
var calls = 0;
mock.Setup(foo => foo.GetCountThing())
.Returns(() => calls)
.Callback(() => calls++);
// returns 0 on first invocation, 1 on the next, and so on
Console.WriteLine(mock.Object.GetCountThing());
取决于你使用哪个版本的Mock,我知道我们可以做到这一点
someMockObject.ResetCalls();
这确实是单元测试滥用,因为您在一次测试中验证两件事。如果你将ObjectUnderTest
初始化从测试中转移到一个常见的设置方法中,你的生活会容易得多。然后,您的测试变得更具可读性并且彼此独立。
除了生产代码之外,还应优化测试代码的可读性和隔离性。对系统行为的一个方面的测试不应该影响其他方面。将公共代码重构为设置方法比尝试重置模拟对象要容易得多。
ObjectUnderTest _objectUnderTest;
[Setup] //Gets run before each test
public void Setup() {
var foo = new Mock<IFoo>(); //moq by default creates loose mocks
_objectUnderTest = new ObjectUnderTest(foo.Object);
}
[Test]
public void DoStuffToPushIntoState1ShouldCallFizz() {
_objectUnderTest.DoStuffToPushIntoState1(); // this is various lines of code and setup
foo.Verify(x => x.Fizz());
}
[Test]
public void DoStuffToPushIntoState2ShouldntCallFizz() {
{
objectUnderTest.DoStuffToPushIntoState2(); // more lines of code
foo.Verify(x => x.Fizz(), Times.Never());
}
除了回答@stackunderflow(哈哈,漂亮的尼克:))
在Moq的后期版本中,Moq.MockExtensions.ResetCalls()
被标记为已过时。应该使用mock.Invocations.Clear()
代替:
foo.Invocations.Clear();
下一个方法适合我(使用Moq.Sequence)
public void Justification()
{
var foo = new Mock<IFoo>(MockBehavior.Loose);
foo.Setup(x => x.Fizz());
var objectUnderTest = new ObjectUnderTest(foo.Object);
objectUnderTest.DoStuffToPushIntoState1(); // this is various lines of code and setup
foo.Verify(x => x.Fizz());
// Some cool stuff
using (Sequence.Create())
{
foo.Setup(x => x.Fizz()).InSequence(Times.Never())
objectUnderTest.DoStuffToPushIntoState2(); // more lines of code
}
}
如果它对您有用,请告诉我