Spock 测试:调用过多
Spock testing: Too many invocation
我编写了用于将事件从一个队列手动重新排队到另一个队列的服务。
public class ReQueueService {
private final RabbitTemplate rabbitTemplate;
public void retry() {
InfoLog infoLog;
while (rabbitTemplate != null &&
(infoLog = (InfoLog) rabbitTemplate.receiveAndConvert(EVENT_WAITING_FOR_REQUEUE)) != null
) {
rabbitTemplate.convertAndSend(SOME_QUEUE, infoLog.getSomeEvent());
}
}
}
我面临的问题是:
Too many invocations for:
1 * rabbitTemplate.convertAndSend(SOME_QUEUE, _ as SomeEvent) >> {
arguments ->
assert infoLog.getSomeEvent() == arguments[1]
} (2 invocations)
Matching invocations (ordered by last occurrence):
2 * rabbitTemplate.convertAndSend(SOME_QUEUE, ...
而我的测试代码如下所示:
class ReQueueServiceTest extends Specification {
def "should resend single event to some queue" () {
given:
InfoLog infoLog = Fixtures.createInfoLog()
def rabbitTemplate = Mock(RabbitTemplate){
receiveAndConvert(EVENT_WAITING_FOR_REQUEUE) >> { infoLog }
}
ReQueueService reSyncService = new ReQueueService(rabbitTemplate)
when:
reSyncService.retry()
then:
1 * rabbitTemplate.convertAndSend(SOME_QUEUE, _ as SomeEvent) >> {
arguments ->
assert infoLog.getSomeEvent() == arguments[1]
}
}
}
问题是,如果我只存根一个事件,为什么我有 2 个调用?
编辑:
感谢回购 link。一旦我可以 运行 测试并实时检查行为,就很容易找出问题所在。首先,我将对您实际想要测试的内容进行有根据的猜测:
- mock 的
receiveAndConvert
方法在首次调用时应 return str
再次调用时应 null
。
- 随后您想要验证
while
循环 运行 恰好是 1 次迭代,即 convertAndSend
是使用您期望的参数调用的。
这可以通过
实现
receiveAndConvert("FOO") >>> [str, null]
1 * rabbitTemplate.convertAndSend("BAR", str)
(在存根方法中不需要丑陋的断言,参数已经根据您的参数约束进行了验证)
如果我稍微重构您的规范以获得更漂亮的变量名和更少的冗长,它看起来像这样:
class ReSyncServiceTest extends Specification {
def "should resend single event to resource sync queue"() {
given:
def message = "someValue"
def rabbitTemplate = Mock(RabbitTemplate) {
receiveAndConvert("FOO") >>> [message, null]
}
when:
new ReSyncService(rabbitTemplate).retry()
then:
1 * rabbitTemplate.convertAndSend("BAR", message)
}
}
P.S.: 你的带有断言的版本没有明确地 return 任何东西,但隐含地是最后一个断言的结果。小心点。使用 >> { ... }
你是在存根方法结果!在 Git 中的版本中,它总是 return true
并且测试仅终止,因为您添加了 1 *
限制。如果它不存在,你就会有一个无限循环。您的代码没有按照您的想法进行操作。也许 Spock 手册可以帮助您。 :-)
P.P.S.: 也许您想重构您的应用程序代码,使其更易于理解和维护,并减少一些 "smart"。也没有必要在每次迭代中检查 rabbitTemplate != null
,一次就足够了。这个怎么样?
@Slf4j
@Service
@AllArgsConstructor
public class ReSyncService {
private final RabbitTemplate rabbitTemplate;
public void retry() {
if (rabbitTemplate == null)
return;
String event;
while (null != (event = getEventFromQueue()))
rabbitTemplate.convertAndSend("BAR", event);
}
protected String getEventFromQueue() {
return (String) rabbitTemplate.receiveAndConvert("FOO");
}
}
我编写了用于将事件从一个队列手动重新排队到另一个队列的服务。
public class ReQueueService {
private final RabbitTemplate rabbitTemplate;
public void retry() {
InfoLog infoLog;
while (rabbitTemplate != null &&
(infoLog = (InfoLog) rabbitTemplate.receiveAndConvert(EVENT_WAITING_FOR_REQUEUE)) != null
) {
rabbitTemplate.convertAndSend(SOME_QUEUE, infoLog.getSomeEvent());
}
}
}
我面临的问题是:
Too many invocations for:
1 * rabbitTemplate.convertAndSend(SOME_QUEUE, _ as SomeEvent) >> {
arguments ->
assert infoLog.getSomeEvent() == arguments[1]
} (2 invocations)
Matching invocations (ordered by last occurrence):
2 * rabbitTemplate.convertAndSend(SOME_QUEUE, ...
而我的测试代码如下所示:
class ReQueueServiceTest extends Specification {
def "should resend single event to some queue" () {
given:
InfoLog infoLog = Fixtures.createInfoLog()
def rabbitTemplate = Mock(RabbitTemplate){
receiveAndConvert(EVENT_WAITING_FOR_REQUEUE) >> { infoLog }
}
ReQueueService reSyncService = new ReQueueService(rabbitTemplate)
when:
reSyncService.retry()
then:
1 * rabbitTemplate.convertAndSend(SOME_QUEUE, _ as SomeEvent) >> {
arguments ->
assert infoLog.getSomeEvent() == arguments[1]
}
}
}
问题是,如果我只存根一个事件,为什么我有 2 个调用?
编辑:
感谢回购 link。一旦我可以 运行 测试并实时检查行为,就很容易找出问题所在。首先,我将对您实际想要测试的内容进行有根据的猜测:
- mock 的
receiveAndConvert
方法在首次调用时应 returnstr
再次调用时应null
。 - 随后您想要验证
while
循环 运行 恰好是 1 次迭代,即convertAndSend
是使用您期望的参数调用的。
这可以通过
实现receiveAndConvert("FOO") >>> [str, null]
1 * rabbitTemplate.convertAndSend("BAR", str)
(在存根方法中不需要丑陋的断言,参数已经根据您的参数约束进行了验证)
如果我稍微重构您的规范以获得更漂亮的变量名和更少的冗长,它看起来像这样:
class ReSyncServiceTest extends Specification {
def "should resend single event to resource sync queue"() {
given:
def message = "someValue"
def rabbitTemplate = Mock(RabbitTemplate) {
receiveAndConvert("FOO") >>> [message, null]
}
when:
new ReSyncService(rabbitTemplate).retry()
then:
1 * rabbitTemplate.convertAndSend("BAR", message)
}
}
P.S.: 你的带有断言的版本没有明确地 return 任何东西,但隐含地是最后一个断言的结果。小心点。使用 >> { ... }
你是在存根方法结果!在 Git 中的版本中,它总是 return true
并且测试仅终止,因为您添加了 1 *
限制。如果它不存在,你就会有一个无限循环。您的代码没有按照您的想法进行操作。也许 Spock 手册可以帮助您。 :-)
P.P.S.: 也许您想重构您的应用程序代码,使其更易于理解和维护,并减少一些 "smart"。也没有必要在每次迭代中检查 rabbitTemplate != null
,一次就足够了。这个怎么样?
@Slf4j
@Service
@AllArgsConstructor
public class ReSyncService {
private final RabbitTemplate rabbitTemplate;
public void retry() {
if (rabbitTemplate == null)
return;
String event;
while (null != (event = getEventFromQueue()))
rabbitTemplate.convertAndSend("BAR", event);
}
protected String getEventFromQueue() {
return (String) rabbitTemplate.receiveAndConvert("FOO");
}
}