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

SpringBoot中WebFlux与Mockito结合出现NullPointerException问题排查

修复Spring WebFlux + Mockito单元测试NullPointerException问题

问题根源分析

  1. 测试类缺少Spring测试上下文注解:@MockBean需要Spring上下文来初始化mock实例,你的测试类仅标注@AutoConfigureMockMvc,未启动Spring测试上下文,导致productRepository等mock实例未被创建,触发NullPointerException。
  2. MockMvc错误使用@MockBean:@AutoConfigureMockMvc会自动配置MockMvc实例,无需手动用@MockBean标注,手动mock会导致MockMvc无法正确绑定到Spring上下文。
  3. Controller中ExcelService硬编码实例化:ExcelService excelService = new ExcelService()未通过依赖注入,导致测试类中@MockBean ExcelService无法替换真实实例,破坏测试隔离性。
  4. Mockito原生初始化与Spring注解冲突:MockitoAnnotations.openMocks(this)是Mockito原生的mock初始化方式,和Spring Boot的@MockBean注解冲突,导致Spring管理的mock无法正确注入。
  5. GET请求错误设置请求体:GET请求不需要传递请求体,测试中content(String.valueOf(flux))属于无效操作,还会导致请求格式错误。

具体修复步骤

1. 修正测试类的Spring注解与依赖注入

给测试类添加@WebFluxTest(ProductController.class)注解启动WebFlux测试上下文,同时正确注入MockMvc:

@WebFluxTest(ProductController.class)
@AutoConfigureMockMvc
class ProductControllerTest {

    @Autowired
    MockMvc mockMvc;

    @MockBean
    ProductRepository productRepository;

    @MockBean
    ExcelService excelService;

    // 移除MockitoAnnotations.openMocks(this),由Spring自动管理mock

2. 修改Controller中ExcelService的注入方式

将硬编码实例改为依赖注入,确保测试时能被mock替换:

@CrossOrigin
@RestController
public class ProductController {

    @Autowired
    private ProductRepository productRepository;

    @Autowired // 改为依赖注入
    private ExcelService excelService;

    // 其他代码不变

3. 修正测试方法中的错误逻辑

移除无效的content设置,同时正确mock ExcelService的返回值,避免调用真实方法:

@Test
void should_get_products_in_Excel() throws Exception {
    ProductData productData = new ProductData();
    productData.setTimestamp(1L);
    productData.setProductId("id");
    productData.setProductName("name");
    productData.setRegistrationDetails(new ArrayList<>());
    productData.setEpaRegNumber("");
    productData.setLifecycleState("");

    Flux<ProductData> flux = Flux.just(productData);

    // Mock repository的返回
    when(productRepository.findAll()).thenReturn(flux);

    // Mock ExcelService的返回,避免调用真实方法
    CompletableFuture<ByteArrayOutputStream> mockOutputStream = CompletableFuture.completedFuture(new ByteArrayOutputStream());
    when(excelService.productsToExcel(any(Flux.class))).thenReturn(mockOutputStream);

    // 构建GET请求,无需设置content
    RequestBuilder builder = MockMvcRequestBuilders
            .get("/products/excel")
            .accept(MediaType.APPLICATION_OCTET_STREAM);

    // 执行请求并验证响应
    mockMvc.perform(builder)
            .andExpect(status().isOk())
            .andExpect(header().string("Content-Disposition", startsWith("attachment; filename=star-products-")));
}

4. 移除冲突的Mockito初始化代码

删除@BeforeEach中的MockitoAnnotations.openMocks(this),@WebFluxTest和@MockBean已由Spring负责mock的创建和注入,无需手动初始化。


额外优化建议

  • 使用any(Flux.class)等Mockito匹配器时,需导入org.mockito.ArgumentMatchers类。
  • 添加响应状态和响应头的验证,确保接口行为符合预期。
  • 若需要更轻量的单元测试,可使用Mockito原生注解替代Spring测试注解:
class ProductControllerTest {

    @Mock
    ProductRepository productRepository;

    @Mock
    ExcelService excelService;

    @InjectMocks
    ProductController productController;

    private MockMvc mockMvc;

    @BeforeEach
    void setup() {
        MockitoAnnotations.openMocks(this);
        mockMvc = MockMvcBuilders.standaloneSetup(productController).build();
    }

    // 测试方法逻辑不变
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 01:20:32