0

I have a test which does not do what is stated in .thenReturn() part of mockito when(). It fails as it goes into the method as when() does not exist an that class is not mocked. I'll past code here, help please:

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@ExtendWith(MockitoExtension.class)
class ClassTest {

    @Mock private ClassA classA;
    @Mock private ClassB classB;
    private ClassC classC;


    @BeforeAll
    void init() {
        classC = new classC(classA, classB);
        MockitoAnnotations.openMocks(this);
    }

    @Test
    @DisplayName("Test description")
    void first_test() {
        ClassD classD = new classD();
        ClassE classE = new classE();

        when(classB.methodInClassB(classE).thenReturn(classD);

        ClassF classF = new ClassF();

        when(classA.apply(classD)).thenReturn(classF);

        ClassE compute = classC.apply(classE);
        verify(classB).methodInClassB(classE);
        verify(classA).apply(classD);
    }
}

For sake of better understanding code as I masked all names for security reasons, here's a Class C as well:

@AllArgsConstructor
public class ClassC {

    private ClassB classB;
    private ClassA classA;

    public ClassE apply (ClassE entity) {
        ClassF computedEntity = classA.apply(classB.methodInClassB(entity));
        entity.setSomething(computedEntity.getSomething());
        return entity;
    }
}

Test fails because execution went into classA.apply() even though it is mocked with when().thenReturn() statement...

MatijaT
  • 1
  • 3
  • 1
    Either do `MockitoAnnotations.openMocks(this);` or use `@ExtendWith(MockitoExtension.class)` but don't do both. Make sure that your classes have a proper `equals` and `hashCode` method as you aren't using the `any()` it will call `equals` on the arguments. If there is some marshalling/unmarshalling in between and you don't have a proper equals it will fallback to the default behavior which is do nothing or return null (or whatever the default value is). That and you are also creating new instances of `ClassB()` and `ClassA()` overriding the mocks. – M. Deinum Sep 29 '22 at 06:59
  • I removed creating new instances of ClassB() and ClassA(), it was some code I added while running the tests as classA there was throwing nullPointerException even though I passked mocked classes in @BeforeAll And nullPointer still occurs event when I only leave @ExtendWith(MockitoExtension.class) and remove MockitoAnnotations.openMocks(this); – MatijaT Sep 29 '22 at 07:08
  • That nullpointer is due to not properly including an equals/hashCode in your classes, implement a proper one and it will work. Your code also wouldn't compile and the obfuscated stuff makes it really hard to read. – M. Deinum Sep 29 '22 at 07:11
  • I added ``` @EqualsAndHashCode @ToString ``` but there's still a nullPointer. As for compilation, I probably removed some stuff which I thought is not related to the question and as for obfuscation, sorry, corporation stuff – MatijaT Sep 29 '22 at 07:16
  • If you have JPA entities adding `@EqualsAndHashCode` is the wrong thing. Make sure you removed the `MockitoAnnotations.openMocks(this);` as that will lead to 2 sets of mocks 1 used in classc and 1 set used to register behavior on. Please provide a sample that works and really relates to your code. – M. Deinum Sep 29 '22 at 07:18
  • Also make sure yuo are actually using the proper `@Test` method, is that from the `org.junit.jupiter.api` package and not from `org.junit` as the latter is for JUnit4 mixing those in 1 class won't work. – M. Deinum Sep 29 '22 at 07:20
  • Also `when(classB.methodInClassB(classE).thenReturn(classD);` is wrong there is a missing `)`. It should be when(classB.methodInClassB(classE)).thenReturn(classD);` (I assumed it was a copy/paste or typo... But looking at your description again... – M. Deinum Sep 29 '22 at 07:22
  • Yes, it was a copy/paste error. Update: The Mockito now works. I removed @ExtendWith(MockitoExtension.class) but now added MockitoAnnotations.openMocks(this); before classC = new classC(classA, classB); in @BeforeAll. Thank you for assistance! – MatijaT Sep 29 '22 at 07:26
  • Which is weird as the `@ExtendWith` should have worked as well. What you can try is instead of using `@BeforeAll` remove that method, add back the `@ExtendWith` and use `@InjectMocks` on the field of type `ClassC`. This will automatically create an instance and inject the created mocks. – M. Deinum Sep 29 '22 at 07:29
  • 1
    Upon some searching Mockito uses beforeEach not beforeAll. See this (same) question https://stackoverflow.com/questions/65543399/mockito-does-not-initialize-mock-in-test-running-with-junit-5-in-beforeall-anno. – M. Deinum Sep 29 '22 at 07:30
  • Yup, that works as well. Plus it's prettier. Thank you again! – MatijaT Sep 29 '22 at 07:32

2 Answers2

0

Since you are creating a new instance of ClassA inside ClassC.apply, you are no longer calling the mocked object from your test class.

theGuardian
  • 439
  • 2
  • 6
  • 17
  • I edited the post, it was excess code I added while trying to resolve the issue. I added it because classA was throwing nullPointerException upon test running.. – MatijaT Sep 29 '22 at 07:05
-1

You must call openMocks before injecting the mocks into your class, otherwise the @Mock-annotated fields will not have been initialized (and are still set to null). The class-level annotation is redundant and the mocks should ideally be created in @BeforeEach, not @BeforeAll.

knittl
  • 246,190
  • 53
  • 318
  • 364
  • edited the post, it was excess code I added while trying to resolve the issue. I added it because classA was throwing nullPointerException upon test running.. However I am passing mocks in @BeforeAll void init() { classC = new classC(classA, classB); MockitoAnnotations.openMocks(this); } aren't I? – MatijaT Sep 29 '22 at 07:06
  • Then you need to call `openMocks` before passing the mocks in the constructor? Simple typo? – knittl Sep 29 '22 at 07:13
  • That plus removing class annotation did the job. Thank you! – MatijaT Sep 29 '22 at 08:08