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

Spring测试中ModelMapperService返回Null问题求助

问题:ModelMapperService.forRequest()返回null导致单元测试失败

在后端项目单元测试中,使用ModelMapper实现对象映射,测试ProductManager的getAll()、update()等方法时,出现以下错误:

org.modelmapper.ModelMapper.map(Object, java.lang.Class)执行失败,原因是hakan.productData.core.utilities.mappers.ModelMapperService.forRequest()的返回值为null

其中shouldFindPostById测试方法运行正常(未使用映射逻辑),但涉及对象映射的测试均失败。

相关代码

ProductManager类

@Service
@AllArgsConstructor
@Transactional
public class ProductManager implements ProductService {

    private ProductRepository productRepository;
    private ProductBusinessRules productBusinessRules;
    private ModelMapperService modelMapperService;


    @Override
    @Transactional(readOnly = true)
    public List<GetAllProductsResponse> getAll() {
        List<Product> products = productRepository.findAll();
        List<GetAllProductsResponse> productsResponse= products.stream()
                .map(product -> this.modelMapperService.forResponse().map(product, GetAllProductsResponse.class)).collect(Collectors.toList());
        return productsResponse;
    }

    @Override
    public void add(CreateProductRequest createProductRequest) {
        this.productBusinessRules.checkIfProductNameExists(createProductRequest.getName());
        Product product = this.modelMapperService.forRequest().map(createProductRequest,Product.class);
        this.productRepository.save(product);
    }

    @Override
    public UpdateProductRequest update(UpdateProductRequest updateProductRequest) {
        Product product = this.modelMapperService.forRequest().map(updateProductRequest,Product.class);
        this.productRepository.save(product);
        return updateProductRequest;
    }
}

ModelMapperManager类

@Service
@AllArgsConstructor
public class ModelMapperManager implements ModelMapperService {
    private ModelMapper modelMapper;
    @Override
    public ModelMapper forResponse() {
        this.modelMapper.getConfiguration()
                .setAmbiguityIgnored(true)
                .setMatchingStrategy(MatchingStrategies.LOOSE);
        return this.modelMapper;
    }

    @Override
    public ModelMapper forRequest() {
        this.modelMapper.getConfiguration()
                .setAmbiguityIgnored(true)
                .setMatchingStrategy(MatchingStrategies.STANDARD);
        return this.modelMapper;
    }
}

ProductManagerTest类

@ExtendWith(MockitoExtension.class)
public class ProductManagerTest {


    @Mock
    private ProductRepository productRepository;
    @Mock
    private ProductBusinessRules productBusinessRules;
    @Mock
    private ModelMapperService modelMapperService;
    @Mock
    private ModelMapperManager modelMapper;

    @InjectMocks
    private ProductManager productManager;

    @Test
    @DisplayName("Should Find Post By Id")
    void shouldFindPostById() {
        Product product = new Product(123,"Milk","Drink",10,true,5);
        GetAllProductsResponse expectedProductResponse = new GetAllProductsResponse(123,"Milk","Drink",10,true,5);
        Mockito.when(productRepository.findById(123)).thenReturn(Optional.of(product));

        Optional<Product> actualPostResponse = productManager.getById(123);

        assertThat(actualPostResponse.get().getId()).isEqualTo(expectedProductResponse.getId());
        assertThat(actualPostResponse.get().getName()).isEqualTo(expectedProductResponse.getName());
        System.out.println("Test was successful");
    }

       @Test
       @DisplayName("Should Update By If")
       void shouldUpdateById(){

           Product product = Product.builder()
                   .name("Milk")
                   .category("Drink")
                   .price(10)
                   .quantity(10)
                   .onOffer(true)
                   .build();

           UpdateProductRequest getAllProductsResponse = UpdateProductRequest.builder()
                   .id(1)
                   .name("Coke")
                   .category("Drink")
                   .price(5)
                   .quantity(10)
                   .onOffer(true)
                   .build();
           when(productRepository.save(Mockito.any(Product.class))).thenReturn(product);
           when(productRepository.findById(0)).thenReturn(Optional.ofNullable(product));
           UpdateProductRequest updatedProduct = productManager.update(getAllProductsResponse);
           assertThat(updatedProduct).isNotNull();


       }

}

问题原因

  • 测试类中同时Mock了ModelMapperService接口和它的实现类ModelMapperManager,但ProductManager依赖的是ModelMapperService接口,Mock的ModelMapperService未配置forRequest()和forResponse()方法的返回值,导致调用这两个方法时返回null。
  • 多余的ModelMapperManager Mock属于无效配置,反而容易造成混淆。

解决方案

  1. 移除多余的Mock:删除测试类中@Mock private ModelMapperManager modelMapper;这一行。
  2. 配置ModelMapperService的Mock行为:为modelMapperService的forRequest()和forResponse()方法配置返回Mock的ModelMapper实例,或者直接模拟map()方法的结果。

修改后的测试类示例

@ExtendWith(MockitoExtension.class)
public class ProductManagerTest {

    @Mock
    private ProductRepository productRepository;
    @Mock
    private ProductBusinessRules productBusinessRules;
    @Mock
    private ModelMapperService modelMapperService;
    @Mock // 新增:Mock ModelMapper实例
    private ModelMapper modelMapper;

    @InjectMocks
    private ProductManager productManager;

    @BeforeEach
    void setUp() {
        // 配置modelMapperService的forRequest和forResponse返回mock的modelMapper
        when(modelMapperService.forRequest()).thenReturn(modelMapper);
        when(modelMapperService.forResponse()).thenReturn(modelMapper);
    }

    @Test
    @DisplayName("Should Find Post By Id")
    void shouldFindPostById() {
        // 原有代码不变
        Product product = new Product(123,"Milk","Drink",10,true,5);
        Mockito.when(productRepository.findById(123)).thenReturn(Optional.of(product));

        Optional<Product> actualPostResponse = productManager.getById(123);

        assertThat(actualPostResponse.get().getId()).isEqualTo(123);
        assertThat(actualPostResponse.get().getName()).isEqualTo("Milk");
        System.out.println("Test was successful");
    }

    @Test
    @DisplayName("Should Update By Id")
    void shouldUpdateById(){
        Product product = Product.builder()
                .id(1)
                .name("Coke")
                .category("Drink")
                .price(5)
                .quantity(10)
                .onOffer(true)
                .build();

        UpdateProductRequest updateRequest = UpdateProductRequest.builder()
                .id(1)
                .name("Coke")
                .category("Drink")
                .price(5)
                .quantity(10)
                .onOffer(true)
                .build();

        // 模拟modelMapper的map方法,将updateRequest转换为product
        when(modelMapper.map(updateRequest, Product.class)).thenReturn(product);
        when(productRepository.save(Mockito.any(Product.class))).thenReturn(product);

        UpdateProductRequest updatedProduct = productManager.update(updateRequest);

        assertThat(updatedProduct).isNotNull();
        assertThat(updatedProduct.getName()).isEqualTo("Coke");
    }
}

额外说明

  • 如果不需要验证ModelMapper的具体配置(比如匹配策略),直接模拟map()方法的返回值即可满足测试需求。
  • 若需要测试映射逻辑的正确性,可以考虑使用真实的ModelMapper实例,而非Mock,但单元测试中通常更倾向于Mock外部依赖以隔离测试。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 04:39:55