如何在Spring中模拟ModelMapper?

Ale*_*xey 9 java testing junit spring mockito

我正在尝试为我的服务层编写单元测试:

@SpringBootTest
class ClinicServiceTest {

@Mock
private ProcedureRepository procedureRepository;
@InjectMocks
private ClinicService clinicService;

@Test
void setProcedureStatus() {
    when(procedureRepository.findById(1L)).thenReturn(Optional.of(initialEntity));
    when(procedureRepository.saveAndFlush(expectedEntity)).thenReturn(expectedEntity);
    Procedure assertProcedure = clinicService.setProcedureStatus(1L, "CANCELED");
  }
}
Run Code Online (Sandbox Code Playgroud)

当我调用setProcedureStatus方法时,它会抛出 NullPointerException,因为我的服务类使用由 Spring 自动装配的 ModelMapper:

@Service
@RequiredArgsConstructor
public class ClinicService {

private final ProcedureRepository procedureRepository;
private final ModelMapper modelMapper;

public Procedure setProcedureStatus(Long procedureId, String status) {
    ...
    return modelMapper.map(procedureEntity, Procedure.class);
}
Run Code Online (Sandbox Code Playgroud)

}

单元测试不会引发 Spring 上下文,这就是当我从测试中调用 ModelMapper 时,Service 中的 ModelMapper 为 null 的原因。有办法解决这个问题吗?

mts*_*ter 7

您想要编写一个单元测试,因此您不应该引发 spring 上下文。单元测试比集成测试更快(您通常希望提高 spring 上下文)。当谈到单元测试时,您想要测试应用程序的单元部分。

在这种情况下,您将收到 NPE,因为您没有初始化 ModelMapper。

我没有看到您在哪里创建 ClinicService,但有一个示例如何避免 NPE:

@Test
void setProcedureStatus() {
    when(procedureRepository.findById(1L)).thenReturn(Optional.of(initialEntity));
    when(procedureRepository.saveAndFlush(expectedEntity)).thenReturn(expectedEntity);
    // Initialize ClinicService with mocked procedeRepository but real ModelMapper
    ClinicService clinicService = new ClinicService(procedureRepository, new ModelMapper(), ... other dependencies );
    Procedure assertProcedure = clinicService.setProcedureStatus(1L, "CANCELED");
}
Run Code Online (Sandbox Code Playgroud)

或者你也可以模拟 ModelMapper。

另一种方法是使用 MockitoJunitRunner 并在测试下初始化您的类,如下所示:

@RunWith(MockitoJUnitRunner.class)
 public class ExampleTest {
 
     @Mock
     private ModelMapper modelMapper;

     @Mock
     private Repository repository;

     @InjectMocks
     private ClinicService classUnderTest;
  
Run Code Online (Sandbox Code Playgroud)

由于依赖项注入,您的代码测试起来更加简单,因为您只需使用通过构造函数传递的依赖项来创建服务。


wit*_*One 5

如果您正在测试 modelmapper 映射返回值,则可以使用 @Spy。使用示例:

这种方式正在准备一个带有默认构造函数的模型映射器。

@Spy
ModelMapper modelMapper;
Run Code Online (Sandbox Code Playgroud)

或者您可以使用构建器或构造函数自定义模型映射器或任何其他具有实例创建方式的类型。

@Spy
ModelMapper modelMapper=new ModelMapper();
Run Code Online (Sandbox Code Playgroud)

因此,当您使用 @InjectMocks 时,它们将注入服务或相关字段内。