英文:
How to test for no interaction using Mockito
问题
我目前正在学习Tomasz Kaczanowski写的书:“JUnit和Mockito实践单元测试”。现在,我正在阅读关于模拟、存根、测试替身和Mockito框架的部分。
作为练习的一部分,有一个待进行单元测试的程序,它向已订阅的客户端发送消息。以下是我目前遇到困难的部分。
- 如果一个未订阅的客户端尝试取消订阅,应该发生什么情况?对此做出决定,编写一个验证此行为的测试,并相应地使RaceResultsService行为符合要求。
在这种情况下,我希望什么都不会发生。没有异常,没有消息...而且我想进行测试。但我该如何做呢?如何测试什么都不发生(而不深入实现,例如检查包含已订阅客户端的集合的大小)?
以下是我编写的测试,它并不如此:
@Test
void unsubscribingWhenAlreadyUnsubscribedShouldReturnVoid() {
raceResults.removeSubscriber(clientA);
verifyNoInteractions(clientA);
}
以下是RaceResultsService.java
中相关的方法:
public class RaceResultsService {
private Collection<Client> clients = new HashSet<Client>();
// 其他字段和方法
public void removeSubscriber(Client client) {
clients.remove(client);
}
}
显然,removeSubscriber
方法实际上会执行某些操作,而不是什么都不做。它将执行HashSet.remove
,但由于客户端不在clients
集合(字段)中,它将不会修改任何内容。但如何在不实际触及相关的Collection<Client>
的情况下进行测试呢?
我将不胜感激地接受任何建议,谢谢!
英文:
I'm currently studying the book written by Tomasz Kaczanowski: "Practical Unit Testing with JUnit and Mockito". Right now, I'm on the section concerning mocks, stubs, test doubles and the Mockito framework.
As part of the practice, there's a to-be-unit-tested program which sends messages to the subscribed clients. Here's the part of the exercise I'm currently struggling with.
- What should happen if a client that is not subscribed tries to unsubscribe? Make up your mind about it, write a test which verifies this behaviour, and make RaceResultsService behave accordingly.
I would like nothing to happen on such occasion. No exception, no messages... And I want to test it. But how do I do that? How do I test that nothing happens (without getting too deep into implementation and, for example, checking the size of collection containing subscribed clients)?
Here's the test I've written which does not do so:
@Test
void unsubscribingWhenAlreadyUnsubscribedShouldReturnVoid() {
raceResults.removeSubscriber(clientA);
verifyNoInteractions(clientA);
}
And here's the concerned method of the RaceResultsService.java
:
public class RaceResultsService {
private Collection<Client> clients = new HashSet<Client>();
// other fields and methods
public void removeSubscriber(Client client) {
clients.remove(client);
}
}
Obviously, the removeSubscriber
method will actually do something, not nothing. It will execute the HashSet.remove
but, as the client is not in the clients
collection (field), it will not modify anything. But how do I test for this without actually touching the associated Collection<Client>
?
I'd appreciate any suggestions, thanks!
答案1
得分: 1
以下是翻译好的部分:
-
"It would be hard to test that nothing happens. But you shouldn't: a unit test should only test observable behaviour. Whatever happens inside the implementation is not the test's concern."
"很难测试什么都不发生。但你不应该这样做:单元测试应该只测试可观察到的行为。发生在实现内部的事情不是测试的关注点。"
-
"For example, if the implementation is changed to write a warning to a log file to inform the developers that a client might be buggy, this should probably not break the test. (You could argue that logging is also observable behaviour, but it's not usually part of the contract.)"
"例如,如果实现被更改为向日志文件写入警告,以通知开发人员客户端可能存在问题,这可能不会破坏测试。(你可以说日志记录也是可观察的行为,但通常不是契约的一部分。)"
-
"So, what should you test for in this case?"
"那么,在这种情况下,你应该测试什么呢?"
-
"Your test is already testing something, namely, that
raceResults.removeSubscriber(clientA);
does not throw an exception. In other words, it verifies that it's not an error to remove a client that's not subscribed. This might already be enough!""你的测试已经在测试某些东西,也就是
raceResults.removeSubscriber(clientA);
不会抛出异常。换句话说,它验证了移除未订阅的客户端不会导致错误。这可能已经足够了!" -
"If you also have a method like
boolean isSubscribed(Client client)
, you could additionally verify that the client is not subscribed after unsubscribing.""如果你还有一个像
boolean isSubscribed(Client client)
这样的方法,你还可以验证在取消订阅后客户端是否未订阅。" -
"You could also send a message and verify that the unsubscribed client does not receive it."
"你还可以发送一条消息并验证取消订阅的客户端是否收不到它。"
英文:
It would be hard to test that nothing happens. But you shouldn't: a unit test should only test observable behaviour. Whatever happens inside the implementation is not the test's concern.
For example, if the implementation is changed to write a warning to a log file to inform the developers that a client might be buggy, this should probably not break the test. (You could argue that logging is also observable behaviour, but it's not usually part of the contract.)
So, what should you test for in this case?
Your test is already testing something, namely, that raceResults.removeSubscriber(clientA);
does not throw an exception. In other words, it verifies that it's not an error to remove a client that's not subscribed. This might already be enough!
If you also have a method like boolean isSubscribed(Client client)
, you could additionally verify that the client is not subscribed after unsubscribing.
You could also send a message and verify that the unsubscribed client does not receive it.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论