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。 - 多余的
ModelMapperManagerMock属于无效配置,反而容易造成混淆。
解决方案
- 移除多余的Mock:删除测试类中
@Mock private ModelMapperManager modelMapper;这一行。 - 配置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
相关产品推荐
相关产品推荐

