You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot集成Cucumber时@MockBean空指针异常的排查与解决

问题

使用Spring Boot 3.3.0和Cucumber 7.18.0进行测试时,尝试Mock ArtistRepository,但出现空指针异常:

java.lang.NullPointerException: Cannot invoke "com.example.sampleproject.repository.ArtistRepository.findById(Object)" because "this.artistRepository" is null

步骤定义类代码:

@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));
        }
    }
}

CucumberSpring配置类:

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

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

测试运行类:

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

当把@CucumberContextConfiguration注解直接加到FindArtistByIdStepDefs类并移除集中配置类时,测试能正常运行,但希望保留集中式配置(因存在多个步骤定义类且计划新增更多)。需要解决:使用集中式配置时@MockBean空指针的原因?如何在保持集中式配置的同时确保MockBean正确注入?

原因分析
  • 重复上下文配置冲突:步骤定义类上同时标注了@SpringBootTest和@ContextConfiguration,而集中式配置类CucumberSpringConfiguration已经通过@CucumberContextConfiguration和@SpringBootTest声明了上下文规则。这种重复配置会导致Cucumber初始化时,无法正确识别有效的上下文配置,最终使@MockBean的注入逻辑失效。
  • 步骤类实例化逻辑异常:若步骤定义类存在Spring注解但未正确关联到集中式上下文,Spring容器无法对其完成依赖注入,@MockBean标记的字段自然无法被实例化,触发空指针。
解决方案

1. 移除步骤定义类上的Spring测试注解

删除FindArtistByIdStepDefs类上的@SpringBootTest和@ContextConfiguration注解,仅保留@MockBean和构造注入逻辑,让Cucumber完全依赖集中式配置类初始化Spring上下文:

public class FindArtistByIdStepDefs {

    @MockBean
    private ArtistRepository artistRepository;
    private final 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));
        }
    }
}

2. 确认配置类被正确扫描

检查测试运行类TestRunner的GLUE_PROPERTY_NAME参数,确保已包含配置类所在包com.example.sampleproject.config,保证Cucumber能找到并使用集中式配置类。

3. 验证MockBean注入时机(可选)

若问题仍存在,可在步骤定义类中添加@PostConstruct方法验证注入状态,避免在构造阶段使用未初始化的MockBean:

@PostConstruct
public void init() {
    Assert.notNull(artistRepository, "ArtistRepository mock should be injected");
}

内容的提问来源于stack exchange,提问作者sjs

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 18:40:20