在使用它来指定默认操作时,我不明白ON_CALL和EXPECT_CALL之间的区别.
到目前为止,我注意到/了解到有两种方法可以调整模拟的默认操作:
ON_CALL(mock, methodX(_)).WillByDefault(Return(0x01));
Run Code Online (Sandbox Code Playgroud)
要么
EXPECT_CALL(mock, methodX(_)).WillRepeatedly(Return(0x01));
Run Code Online (Sandbox Code Playgroud)
有人可以向我解释一下:
Vla*_*sev 48
这两个陈述之间存在微妙但重要的差异.EXPECT_CALL
设置模拟调用的期望.写作
EXPECT_CALL(mock, methodX(_)).WillRepeatedly(do_action);
Run Code Online (Sandbox Code Playgroud)
告诉gMock methodX
可以mock
使用任何参数多次调用,并且当它出现时,mock
将执行do_action
.另一方面,
ON_CALL(mock, methodX(_)).WillByDefault(do_action);
Run Code Online (Sandbox Code Playgroud)
告诉gMock,无论什么时候methodX
被调用mock
,都应该执行do_action
.在您必须在模拟上写下许多期望的情况下,该功能很有用,并且大多数/所有这些都必须指定相同的操作 - 特别是如果它很复杂.您可以在其中指定该操作ON_CALL
,然后EXPECT_CALL
在不明确指定操作的情况下编写s.例如,
ON_CALL(mock, Sign(Eq(0), _))
.WillByDefault(DoAll(SetArgPointee<1>("argument is zero"), Return(0)));
ON_CALL(mock, Sign(Gt(0), _))
.WillByDefault(DoAll(SetArgPointee<1>("argument is positive"), Return(1)));
ON_CALL(mock, Sign(Lt(0), _))
.WillByDefault(DoAll(SetArgPointee<1>("argument is negative"), Return(-1)));
Run Code Online (Sandbox Code Playgroud)
现在,如果你必须写很多EXPECT_CALL
s,你不必mock
每次都指定行为:
EXPECT_CALL(mock, Sign(-4, _));
EXPECT_CALL(mock, Sign(0, _));
EXPECT_CALL(mock, Sign(1, _)).Times(2);
EXPECT_CALL(mock, Sign(2, _));
EXPECT_CALL(mock, Sign(3, _));
EXPECT_CALL(mock, Sign(5, _));
Run Code Online (Sandbox Code Playgroud)
在另一个例子中,假设Sign返回int
,如果你写
ON_CALL(mock, Sign(Gt(0), _)).WillByDefault(Return(1));
EXPECT_CALL(mock, Sign(10, _));
Run Code Online (Sandbox Code Playgroud)
该调用mock.Sign(10)
将返回1作为ON_CALL
指定的调用的默认行为EXPECT_CALL
.但如果你写
EXPECT_CALL(mock, Sign(Gt(0), _).WillRepeatedly(Return(1));
EXPECT_CALL(mock, Sign(10, _));
Run Code Online (Sandbox Code Playgroud)
mock.Sign(10, p)
将返回0 的调用.它将与第二个期望匹配.该期望指定没有明确的操作,gMock将为其生成默认操作.该默认操作是返回返回类型的默认值,该值为0 int
.在这种情况下,第一个期望将被完全忽略.
BЈо*_*вић 11
ON_CALL(mock, methodX(_)).WillByDefault(Return(0x01));
EXPECT_CALL(mock, methodX(_)).WillRepeatedly(Return(0x01));
Run Code Online (Sandbox Code Playgroud)
正如你所说,这两条线完全相同,因此根本没有差异.您可以使用任一方式设置默认操作.
但是,存在逻辑差异:
ON_CALL(mock, methodX(_)).WillByDefault(Return(0x01));
表示可能会调用该方法,如果发生这种情况,则每次调用都将返回0x01EXPECT_CALL(mock, methodX(_)).WillRepeatedly(Return(0x01));
意味着预期将调用该方法,并且每次调用都将返回0x01顺便说一句,在他们的备忘单中有一个设置默认操作,其中说:
要自定义特定方法的默认操作,请使用ON_CALL():
ON_CALL(mock_object, method(matchers))
.With(multi_argument_matcher) ?
.WillByDefault(action);
Run Code Online (Sandbox Code Playgroud)
ON_CALL
这是关于和gMock CookbookEXPECT_CALL
中解释的最重要差异的“官方”解释。
定义模拟对象的行为基本上有两种构造:ON_CALL
和EXPECT_CALL
。
区别?
ON_CALL
定义了当调用模拟方法时会发生什么,但并不暗示对被调用的方法有任何期望。
EXPECT_CALL
不仅定义了行为,还设置了一个期望,即必须使用给定的参数调用该方法给定的次数(并且当您指定顺序时也按给定的顺序)。
既然EXPECT_CALL
做得更多,不是比更好吗ON_CALL
?
并不真地。每个都会EXPECT_CALL
对被测代码的行为添加约束。过多的约束是不好的——甚至比没有足够的约束更糟糕。
这可能是违反直觉的。验证较多的测试怎么会比验证较少的测试更糟糕呢?验证不是测试的全部目的吗?
答案在于测试应该验证什么。 良好的测试可以验证代码的契约性。如果测试过度指定,就不会为实现留下足够的自由度。 因此,在不破坏契约的情况下改变实现(例如重构和优化),这应该是完全可以做到的,但可能会破坏此类测试。然后你必须花时间修复它们,但在下次实现更改时却会看到它们再次损坏。
请记住,无需在一项测试中验证多个属性。事实上,在一次测试中只验证一件事是一种很好的风格。如果这样做,一个错误可能只会破坏一两个测试,而不是几十个(您更愿意调试哪种情况?)。如果您还习惯为测试提供描述性名称来告诉它们验证的内容,那么您通常可以轻松地从测试日志本身猜出问题所在。
ON_CALL
因此默认使用,并且仅EXPECT_CALL
在您实际打算验证调用是否已进行时才使用。