Search code examples
mockitojunit4spring-boot-testdata-jpa-test

@MockBean does not get mocked when using @DataJpaTest


Env:
2 datasources - one PostgreSQL and MongoDB
JDK 17
Spring Boot 3.1.1
JUnit 4

eventRepository is not returning mock when called by updateScheduler Service for @DataJpaTest.

Everything bellow works fine when using @SpringBootTest.

What I am missing?

@MockBean EventRepository eventRepository; when(this.eventRepository.findByIdentifierFlapIn(any())).thenReturn(mock);

@Repository
public interface EventRepository extends MongoRepository<Flap, String> {
    List<Flap> findByIdentifierFlapIn(List<String> identifierFlap);
//Everything commented is what I am experiencing the problem. Not commented just works fine

@RunWith(SpringRunner.class)
//@DataJpaTest
//@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
//@ComponentScan(basePackages = {"com.X.resultado", "com.X.updates"})
//@Import({
//        Configuracao.class,
//        EventRepository.class
//})
@SpringBootTest
@TestPropertySource("classpath:application-test.properties")
public class TestUpdater {

    @Autowired
    ResultadoRepository resultadoRepository;
    @MockBean
    EventRepository eventRepository;
    @InjectMocks @Autowired
    UpdateScheduler updateScheduler;
    (...)

    @Before
    public void setUp() {
        closeable = MockitoAnnotations.openMocks(this);
        resultadoBaronezaList = List.of(
                new ResultBar(),
                new ResultBar(),
                new ResultBar()
        );
        ResultBarList.get(0).setId(1L);
        ResultBarList.get(0).setIdentifierFlap("identifier1");
        (...)
}
@Service
public class UpdateScheduler {
    private final Logger logger = getLogger(this.getClass());
    private final ResultadoRepository resultadoRepository;
    private final EventRepository eventRepository;

    @Autowired
    public UpdateScheduler(ResultadoRepository resultadoRepository, EventRepository eventRepository) {
        this.resultadoRepository = resultadoRepository;
        this.eventRepository = eventRepository;
    }

    @Scheduled(fixedRateString = "${bar.timeUpdate}")
    public void verificaAtualizacoes() {
        List<ResultadoBar> ativos = resultadoRepository.findAllByType("1");
        List<String> identifierFlapList = ativos.stream().map(ResultadoBar::getIdentifierFlap).toList();
//**HERE the mock is not working**
        List<Flap> flapsModificados = eventRepository.findByIdentifierFlapIn(identifierFlapList);
        List<ResultadoBar> resultadoBarList = new ArrayList<>();
        (...)
@Test
    public void validaClearForcado(){
        resultadoRepository.saveAll(resultadoBarList);
        List<Flap> mock = new ArrayList<>(flapList);
        mock.get(0).setLastSeenFlap("Feb 27, 2024 3:00:00 PM");
        when(this.eventRepository.findByIdentifierFlapIn(any())).thenReturn(mock);
        updateScheduler.verificaAtualizacoes();
        var r0 = resultadoRepository.findById(1L).get();
        assertEquals(LocalDateTime.of(2024,2,27,15,0,0), r0.getDataUltimoFlap());
        assertTrue(r0.isClearForcado());
    }

Solution

  • In summary was a bit of human error. Let me explain so others facing the same issue can take a step back and see if it is the same.

    My tests needs the ID.

    var r0 = resultadoRepository.findById(1L).get();
    

    With @AutoConfigureTestDatabase each test will increment the ID on the base.

    @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
    

    To fix that I just need to set DirtiesContext

    @DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
    

    When debugging the service I was noticing that was not returning the mock - so I was stopping thinking there were a problem. But, I really don't know, why mockito runs twice. If I resume the debug, I got again in the line with the correct mock and the test is successful.

    enter image description here

    Works the same as the @SpringBootTest but it needs @DirtiesContext

    @RunWith(SpringRunner.class)
    @DataJpaTest
    @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
    @ComponentScan(basePackages = {"com.X.resultado", "com.X.updates"})
    @Import({
            Configuracao.class,
            EventRepository.class
    })
    //@SpringBootTest
    @TestPropertySource("classpath:application-test.properties")
    @DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
    public class TestUpdater {
    
        @Autowired
        ResultadoRepository resultadoRepository;
        @MockBean
        EventRepository eventRepository;
        @InjectMocks @Autowired
        UpdateScheduler updateScheduler;
    

    I can accept a second answer if someone tell me:

    • Why "mockito/whataver" pass twice in the same code
    • Why @SpringBootTest doesn't need DirtiesContext

    Thanks