Search code examples
javamockitojunit4springrunner

Mockito mock object returns cumulative result in second test method


I am using mockito to mock a JpaRepository class that returns a list of Object[]. The mocking is as follow:

@MockBean
private UtilisateurSuiviRepository utilisateurSuiviRepository;

So I'm using this mock object o test diffrent scenarios and return different results.

In the first test method I have this:

    when(utilisateurSuiviRepository
            .findUtilisateursInactifsPourPremiereRelance(10))
            .thenReturn(new ArrayList<Object[]>(){{
                add(new Object[]{8L, Instant.now()});
            }});

In the second test method I have the same thing but with different parameter and return result:

when(utilisateurSuiviRepository
        .findUtilisateursInactifsPourPremiereRelance(16))
        .thenReturn(new ArrayList<Object[]>(){{
            add(new Object[]{5L,null});
        }});

The problem is that depending on the execution order of the methods, my second method remember also the return result of the first mock, so it returns two lists :

new Object[]{8L, Instant.now() // <-- first list of array object
new Object[]{5L,null} //<-- second list of array object

Expecting result is:

new Object[]{5L,null} //<-- what should I get in the second method

I'am aware that mockito keeps the same state for all mocks, so I did this to init the mocks before each test method:

    @Before
    public void setUp(){
        MockitoAnnotations.initMocks(this);
    }

But it does not change anything and I keep getting the same issue.

Note that I'm using SpringRunner to run my test class in a context of spring batch:

@RunWith(SpringRunner.class)
@MyCustomBatchUnitTest
@TestPropertySource(properties = {"spring.batch.job.names=myJob"})

The definition of MyCustomBatchUnitTest is as follow:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@SpringBootTest(classes = { CommonsConfiguration.class })
@AutoConfigureMockMvc
@ActiveProfiles({CommonsConfiguration.PROFILE_BATCH, CommonsConfiguration.PROFILE_TEST, CommonsConfiguration.PROFILE_BATCHTEST})
public @interface MyCustomBatchUnitTest{
    
}

I tried also to init the mock manually inside each method as follow:

utilisateurSuiviRepository = mock(UtilisateurSuiviRepository.class); 
when(utilisateurSuiviRepository
                .findUtilisateursInactifsPourPremiereRelance(16))
                .thenReturn(new ArrayList<Object[]>(){{
                    add(new Object[]{8L, Instant.now()});}});

But in that case the mock method that is just after is not being processed.

Bellow a github link that produce the same problem: https://github.com/MrPenguina/mockito_issue/blob/main/JobTest.java


Solution

  • As explained by the guys in the comments, I had to use:

    Mockito.reset(utilisateurSuiviRepository);
    

    Because MockitoAnnotations.initMocks method is used within @Mock of Mockito framework and not for @MockBean of spring framework.

    Also note that in the context of Spring Batch, I had a final List that I initialize in the declaration, the list is used to hold the result of my JpaRepository by calling the method addAll inside the beforeStep method, which was causing the issue in my case and kept giving me a cumulative result:

    private  final List<Object[]> listeUtilisateurSuiviInactif = new ArrayList<>();
    

    I had to move the initialization of this list inside the beforeStep() of the reader so like that the list will be initialized each time whenever a test method is called.

    In conclusion, I had to correct the mistake above and to use Mockito.reset method inside the @Before method.

    UPDATE

    Other alternative to keep the list intitialization in the declaration instead is to use the annotation @DirtiesContext withing the test class:

    @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)