1. Overview
1.概述
In this quick tutorial, we’ll learn about the Mockito UnnecessaryStubbingException. This exception is a common exception we’ll likely encounter when using stubs incorrectly.
在这个快速教程中,我们将学习Mockito UnnecessaryStubbingException。这个异常是我们在错误地使用存根时可能会遇到的一个常见异常。
We’ll start by explaining the philosophy behind strict stubbing, and why Mockito encourages its use by default. Then we’ll take a look at exactly what this exception means, and under what circumstances it can occur. Finally, we’ll see an example of how we can suppress this exception in our tests.
我们将首先解释严格存根背后的哲学,以及为什么Mockito鼓励默认使用它。然后我们将看看这个异常到底是什么意思,以及在什么情况下会发生。最后,我们将看到一个例子,说明我们如何在测试中抑制这种异常。
To learn more about testing with Mockito, check out our comprehensive Mockito series.
要了解有关使用Mockito测试的更多信息,请查看我们全面的Mockito系列。
2. Strict Stubbing
2.严格的存根
With version 1.x of Mockito, it was possible to configure and interact with mocks without restrictions. This meant that, over time, tests would often become overcomplicated, and at times harder to debug.
在Mockito的1.x版本中,可以不受限制地配置和交互mock。这意味着,随着时间的推移,测试往往会变得过于复杂,有时甚至难以调试。
Since version 2.+, Mockito has been introducing new features that nudge the framework towards “strictness.” The main goals behind this are:
自2.+版本以来,Mockito一直在引入新的功能,以推动框架走向 “严格”,这背后的主要目标是。
- Detect unused stubs in the test code
- Reduce test code duplication and unnecessary test code
- Promote cleaner tests by removing ‘dead’ code
- Help improve debuggability and productivity
Following these principles helps us create cleaner tests by eliminating unnecessary test code. They also help us avoid copy-paste errors, as well as other developer oversights.
遵循这些原则有助于我们通过消除不必要的测试代码来创建更干净的测试。它们还可以帮助我们避免复制粘贴的错误,以及其他开发人员的疏忽。
To summarise, strict stubbing reports unnecessary stubs, detects stubbing argument mismatch, and makes our tests more DRY (Don’t Repeat Yourself). This facilitates a clean and maintainable codebase.
总而言之,严格存根会报告不必要的存根,检测存根参数的不匹配,并使我们的测试更加DRY(Don’t Repeat Yourself)。这有利于形成干净和可维护的代码库。
2.1. Configuring Strict Stubs
2.1.配置严格的存根
Since Mockito 2.+, strict stubbing is used by default when initializing our mocks using either:
自Mockito 2.+以来,当我们使用以下两种方法初始化Mock时,默认使用严格的存根。
- MockitoJUnitRunner
- MockitoJUnit.rule()
Mockito strongly recommends the use of either of the above. However, there’s also another way to enable strict stubbing in our tests when we’re not leveraging the Mockito rule or runner:
Mockito强烈建议使用上述两种方法。然而,当我们不利用Mockito规则或运行器时,也有另一种方法可以在我们的测试中启用严格的存根。
Mockito.mockitoSession()
.initMocks(this)
.strictness(Strictness.STRICT_STUBS)
.startMocking();
One last important point to make is that in Mockito 3.0, all stubbings will be “strict” and validated by default.
最后要说明的一点是,在Mockito 3.0中,所有的存根都将是 “严格 “的,并且默认为验证的。
3. UnnecessaryStubbingException Example
3.UnnecessaryStubbingException例子
Simply put, an unnecessary stub is a stubbed method call that was never realized during test execution.
简单地说,不必要的存根是指在测试执行期间从未实现的存根方法调用。
Let’s take a look at a simple example:
让我们看一下一个简单的例子。
@Test
public void givenUnusedStub_whenInvokingGetThenThrowUnnecessaryStubbingException() {
when(mockList.add("one")).thenReturn(true); // this won't get called
when(mockList.get(anyInt())).thenReturn("hello");
assertEquals("List should contain hello", "hello", mockList.get(1));
}
When we run this unit test, Mockito will detect the unused stub and throw an UnnecessaryStubbingException:
当我们运行这个单元测试时,Mockito会检测到未使用的存根并抛出一个UnnecessaryStubbingException。
org.mockito.exceptions.misusing.UnnecessaryStubbingException:
Unnecessary stubbings detected.
Clean & maintainable test code requires zero unnecessary code.
Following stubbings are unnecessary (click to navigate to relevant line of code):
1. -> at com.baeldung.mockito.misusing.MockitoUnecessaryStubUnitTest.givenUnusedStub_whenInvokingGetThenThrowUnnecessaryStubbingException(MockitoUnecessaryStubUnitTest.java:37)
Please remove unnecessary stubbings or use 'lenient' strictness. More info: javadoc for UnnecessaryStubbingException class.
Thankfully, it’s quite clear from the error message what the problem is here. We can also see that the exception message even points us to the exact line which causes the error.
值得庆幸的是,从错误信息中可以很清楚地看到问题出在哪里。我们还可以看到,异常信息甚至指出了导致错误的确切行。
Why does this happen? Well, the first when invocation configures our mock to return true when we call the add method with the argument “one.” However, we don’t then invoke this method during the rest of the unit test execution.
为什么会发生这种情况?好吧,第一个when调用配置了我们的mock,当我们用参数“one “调用add方法时返回true。
Mockito is telling us that our first when line is redundant, and perhaps we made an error when configuring our stubs.
Mockito告诉我们,我们的第一个when行是多余的,也许我们在配置存根时出了错。。
Although this example is trivial, it’s easy to imagine when mocking a complex hierarchy of objects how this kind of message can assist debugging and be otherwise very helpful.
虽然这个例子是微不足道的,但很容易想象,当模拟一个复杂的对象层次时,这种消息可以帮助调试,并且在其他方面非常有帮助。
4. Bypassing Strict Stubbing
4.绕过严格的存根
Finally, let’s see how to bypass strict stubs. This is also known as lenient stubbing.
最后,让我们看看如何绕过严格的存根。这也被称为宽松的存根。
Sometimes we need to configure specific stubbing to be lenient, while maintaining all the other stubbings and mocks to use strict stubbing:
有时我们需要将特定的存根配置为宽松的,同时保持所有其他存根和模拟使用严格的存根。
@Test
public void givenLenientdStub_whenInvokingGetThenThrowUnnecessaryStubbingException() {
lenient().when(mockList.add("one")).thenReturn(true);
when(mockList.get(anyInt())).thenReturn("hello");
assertEquals("List should contain hello", "hello", mockList.get(1));
}
In the above example, we use the static method Mockito.lenient() to enable the lenient stubbing on the add method of our mock list.
在上面的例子中,我们使用静态方法Mockito.lenient()来启用我们模拟列表的add方法的宽松存根。
Lenient stubs bypass “strict stubbing” validation rules. For example, when stubbing is declared as lenient, it won’t be checked for potential stubbing problems, such as the unnecessary stubbing described earlier.
宽松的存根绕过了 “严格存根 “的验证规则。例如,当存根被声明为宽松时,它不会被检查出潜在的存根问题,例如前面描述的不必要的存根。
5. Conclusion
5.结论
In this brief article, we introduced the concept of strict stubbing in Mockito, detailing the philosophy behind why it was introduced and why it’s important.
在这篇简短的文章中,我们介绍了Mockito中严格存根的概念,详细介绍了为什么要引入这个概念以及它的重要性。
Then we looked at an example of the UnnecessaryStubbingException, before finishing with an example of how to enable lenient stubbing in our tests.
然后我们看了一个UnnecessaryStubbingException的例子,最后是一个如何在测试中启用宽松存根的例子。
As always, the full source code of the article is available over on GitHub.
一如既往,文章的完整源代码可在GitHub上获得。