javaspring-bootcucumber

MockBean in Spring Boot with Cucumber results in NullPointerException


I am testing with Spring Boot (3.3.0) and Cucumber (7.18.0). I have a repository which I want to mock and I have included the necessary (I think) configuration for Spring and Cucumber but I get "java.lang.NullPointerException: Cannot invoke "com.example.sampleproject.repository.ArtistRepository.findById(Object)" because "this.artistRepository" is null".

Here is my step def class:

@SpringBootTest
@ContextConfiguration(classes = {SampleProjectApplication.class, CucumberSpringConfiguration.class})
public class FindArtistByIdStepDefs {

    @MockBean
    private ArtistRepository artistRepository;
    private DataLoaderService dataLoaderService;
    

    private List<ArtistEntity> mockedArtists = new ArrayList<>();
    private List<AlbumEntity> mockedAlbums = new ArrayList<>();

    public FindArtistByIdStepDefs(DataLoaderService dataLoaderService) {
        this.dataLoaderService = dataLoaderService;
    }

    @Given("artists exist in the DB")
    public void theFollowingArtistsExistInTheDB(DataTable dataTable) {
        List<Map<String, String>> map = dataTable.asMaps(String.class, String.class);

        for (Map<String, String> row : map) {
            ArtistEntity artist = new ArtistEntity();
            String name = row.get("name");
            String description = row.get("description");
            long id = Long.parseLong(row.get("id"));
            artist.setArtist(name);
            artist.setDescription(description);
            artist.setId(id);
            mockedArtists.add(artist);

            Mockito.when(artistRepository.findById(artist.getId())).thenReturn(Optional.of(artist));
        }

Then I have CucumberSpring config:

@CucumberContextConfiguration
@SpringBootTest(classes = SampleProjectApplication.class)
public class CucumberSpringConfiguration {

    public CucumberSpringConfiguration() {
        System.out.println("CucumberSpringConfiguration initialized");
    }
}

Test Runner:

@Suite
@SelectClasspathResource("features")
@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "com.example.sampleproject.steps, com.example.sampleproject.config")
public class TestRunner {
}

When I add @CucumberContextConfiguration on the FindArtistByIdStepDefs class and remove the other it works. But I want this configuration to be more centralized since I have another step def class and plan to have more.

What might be causing @MockBean to result in a NullPointerException when using a centralized configuration? How can I ensure that the MockBean is properly injected while keeping a centralized configuration?


Solution

  • Your step definition class should look like this.

    public class FindArtistByIdStepDefs {
    
        private final ArtistRepository artistRepository;
        private final DataLoaderService dataLoaderService;
        
    
        private List<ArtistEntity> mockedArtists = new ArrayList<>();
        private List<AlbumEntity> mockedAlbums = new ArrayList<>();
    
        public FindArtistByIdStepDefs(
            DataLoaderService dataLoaderService,
            ArtistRepository artistRepository
        ) {
            this.dataLoaderService = dataLoaderService;
            this.artistRepository = artistRepository;
        } 
    ...
    

    And your context configuration should like this:

    @CucumberContextConfiguration
    @SpringBootTest(classes = SampleProjectApplication.class)
    public class CucumberSpringConfiguration {
    
        @MockBean
        private ArtistRepository artistRepository;
    }
    

    By declaring the ArtistRepository as MockBean the mocks will be injected in the FindArtistByIdStepDefs. You can then use mockito as you would normally.

    Additionally both CucumberSpringConfiguration and FindArtistByIdStepDefs should be on the glue path. If they're in the same package, they probably are.