'Why do Mockito 3.3.0 raise UnnecessaryStubbingException while all when are necessary?
Given the following classes:
class Parent {
Child getChild() {
return null;
}
}
class Child {
List<String> getValues(String param) {
return Collections.emptyList();
}
}
And the following junit test:
@Test
public void name() {
Parent parentMock = mock(Parent.class, RETURNS_DEEP_STUBS);
when(parentMock.getChild().getValues("foo")).thenReturn(List.of("Something"));
when(parentMock.getChild().getValues("bar")).thenReturn(List.of("Something"));
assertThat(parentMock.getChild().getValues("foo")).hasSize(1);
assertThat(parentMock.getChild().getValues("bar")).hasSize(1);
}
Mockito 3.3.0 (with its strict stubbing) raise the following exception:
org.mockito.exceptions.misusing.UnnecessaryStubbingException:
Unnecessary stubbings detected in test class: ClassTest
Clean & maintainable test code requires zero unnecessary code.
There are 1 unnecessary stubbings (click to navigate to relevant line of code):
1. -> at ClassTest.name(ClassTest.java:59)
What I don't understand in this case is that, both when() are called in the test. So I expect Mockito not raise the exception.
Why Mockito is complaining ?
--- edit --- It seems that it is a bug in mockito-core 3.3.0. Upgrading to the latest version 3.12.4 fix the problem. Not sure in which version the bug is fixed, (Adriaan said that the bug doesn't occured in 3.4.3) but it's ok for me to switch to the 3.12.4.
Solution 1:[1]
I just tried with these dependencies:
dependencies {
implementation 'junit:junit:4.13.2'
implementation 'org.mockito:mockito-core:3.4.3'
implementation 'org.assertj:assertj-core:3.22.0'
}
And no error occurred.
I suggest you check your classpath.
Regarding your use of RETURNS_DEEP_STUBS (from the org.mockito.Mockito#RETURNS_DEEP_STUBS JavaDoc):
WARNING: This feature should rarely be required for regular clean code! Leave it for legacy code. Mocking a mock to return a mock, to return a mock, (...), to return something meaningful hints at violation of Law of Demeter or mocking a value object (a well known anti-pattern).
Good quote I've seen one day on the web: every time a mock returns a mock a fairy dies.
Please note that this answer will return existing mocks that matches the stub. This behavior is ok with deep stubs and allows verification to work on the last mock of the chain.
It is intended for fluent API's like Builders, or the infamous Spring WebClient where each call returns this, see: Baeldung.
Maybe you like really compact unit tests. I personally want them to be as explicit (and hopefully readable) as possible, but admittedly that is also a matter of taste and style.
I think I'd write your test something like this:
private final Parent parent = mock(Parent.class);
private final Child child = mock(Child.class);
@Before
public void setup() {
when(parent.getChild()).thenReturn(child);
}
@Test
public void testFoo() {
// SETUP
String param = "foo";
when(child.getValues(param)).thenReturn(List.of("Something"));
// CALL
List<String> result = child.getValues(param);
// VERIFY
assertThat(result).hasSize(1);
}
@Test
public void testBar() {
// SETUP
String param = "bar";
when(child.getValues(param)).thenReturn(List.of("Something"));
// CALL
List<String> result = child.getValues(param);
// VERIFY
assertThat(result).hasSize(1);
}
So I try to get the less relevant part of the stubbing out of the way, either in a @Setup method, or other helper methods (or classes, builders, factories if needed). This helps the test case to focus on the behavior that I want to test.
I also never test multiple times in a single test case, because if the case fails I want it to be immediately clear what the cause is.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
