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

使用@InjectMocks时测试触发NPE,直接实例化则正常问题排查

问题描述

我正在练习RESTful API端点,采用TDD开发获取所有供应商的端点时,用@InjectMocks注入VendorServiceImpl会触发空指针异常(NPE),但直接在setup()里实例化服务(vendorService = new VendorServiceImpl(VendorMapper.INSTANCE, vendorRepository);)就能正常通过测试。怀疑NPE和VendorMapper有关,想通过@InjectMocks让测试通过,相关代码如下:


VendorServiceTest

package guru.springfamework.services;

import guru.springfamework.api.v1.mapper.VendorMapper;
import guru.springfamework.api.v1.model.VendorDTO;
import guru.springfamework.domain.Vendor;
import guru.springfamework.repositories.VendorRepository;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.test.web.servlet.MockMvc;

import java.util.Arrays;
import java.util.List;

import static org.junit.Assert.*;
import static org.mockito.Mockito.when;

public class VendorServiceTest {

    public static final String NAME = "Tasty";
    public static final Long ID = 1L;

    @Mock
    VendorMapper vendorMapper;

    @Mock
    VendorRepository vendorRepository;

    @InjectMocks
    VendorServiceImpl vendorService;

    //VendorService vendorService;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        //vendorService = new VendorServiceImpl(VendorMapper.INSTANCE, vendorRepository);
    }

    @Test
    public void getAllVendors() {
        //given
        List<Vendor> vendors = Arrays.asList(new Vendor(), new Vendor(), new Vendor());

        when(vendorRepository.findAll()).thenReturn(vendors);

        //when
        List<VendorDTO> vendorDTOList = vendorService.getAllVendors();

        //then
        assertEquals(3, vendorDTOList.size());
    }

    @Test
    public void findByName() {
    }
}

VendorServiceImpl

package guru.springfamework.services;

import guru.springfamework.api.v1.mapper.VendorMapper;
import guru.springfamework.api.v1.model.VendorDTO;
import guru.springfamework.repositories.VendorRepository;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.stream.Collectors;

@Service
public class VendorServiceImpl implements VendorService {

    private final VendorMapper vendorMapper;
    private final VendorRepository vendorRepository;

    public VendorServiceImpl(VendorMapper vendorMapper, VendorRepository vendorRepository) {
        this.vendorMapper = vendorMapper;
        this.vendorRepository = vendorRepository;
    }

    @Override
    public List<VendorDTO> getAllVendors() {
        return vendorRepository
                .findAll()
                .stream()
                .map(vendor -> {
                    VendorDTO vendorDTO = vendorMapper.vendorToVendorDTO(vendor);
                    vendorDTO.setVendorUrl("/api/v1/vendors/" + vendor.getId());
                    return vendorDTO;
                })
                .collect(Collectors.toList());
    }

    @Override
    public VendorDTO findByName(String name) {
        return vendorMapper.vendorToVendorDTO(vendorRepository.findByName(name));
    }

    @Override
    public VendorDTO getVendorById(Long id) {
        return vendorMapper.vendorToVendorDTO(vendorRepository.findById(id).orElseThrow(RuntimeException::new));
    }
}

VendorMapper

package guru.springfamework.api.v1.mapper;

import guru.springfamework.api.v1.model.VendorDTO;
import guru.springfamework.domain.Vendor;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;

@Mapper
public interface VendorMapper {

    VendorMapper INSTANCE = Mappers.getMapper(VendorMapper.class);

    VendorDTO vendorToVendorDTO(Vendor vendor);
}

问题原因与解决方案

问题根源

你用@Mock注解了VendorMapper,但没有给这个Mock对象的vendorToVendorDTO方法设置返回值。当测试执行时,vendorMapper.vendorToVendorDTO(vendor)会返回null,后续调用vendorDTO.setVendorUrl()就会触发NPE。

而直接实例化服务时用的是VendorMapper.INSTANCE,这是MapStruct生成的真实实现类,它能正常返回非空的VendorDTO对象,所以不会报错。

解决步骤

方案1:给Mock的Mapper设置返回值

在getAllVendors测试方法里,添加对vendorMapper方法的模拟逻辑:

// 先导入静态方法:import static org.mockito.ArgumentMatchers.any;

@Test
public void getAllVendors() {
    //given
    List<Vendor> vendors = Arrays.asList(new Vendor(), new Vendor(), new Vendor());
    // 模拟Mapper的转换方法,返回非空的VendorDTO
    when(vendorMapper.vendorToVendorDTO(any(Vendor.class))).thenReturn(new VendorDTO());

    when(vendorRepository.findAll()).thenReturn(vendors);

    //when
    List<VendorDTO> vendorDTOList = vendorService.getAllVendors();

    //then
    assertEquals(3, vendorDTOList.size());
}

方案2:使用真实的Mapper实现

如果你的测试需要验证MapStruct的映射逻辑,不应该MockVendorMapper,而是直接使用VendorMapper.INSTANCE,此时只需MockVendorRepository:

// 去掉@Mock VendorMapper
@Mock
VendorRepository vendorRepository;

@InjectMocks
VendorServiceImpl vendorService;

@Before
public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);
    // 手动传入真实的Mapper实例
    vendorService = new VendorServiceImpl(VendorMapper.INSTANCE, vendorRepository);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 15:25:44