Spring Boot单元测试中@Autowired注入属性为空抛空指针异常如何解决
问题原因
- 你在测试类的
setUp方法中是手动new BuilderServiceImpl(builderRepository,adminUserRepository)创建的服务实例,并非由Spring IoC容器托管。@Autowired注解的字段注入逻辑只有Spring容器创建的对象才会触发,手动new的对象不会自动完成字段注入,因此autoMapper属性默认是null。 - 你虽然给测试类加了
@SpringBootTest注解,但并未实际使用容器注入的BuilderServiceImpl实例,注解没有起到对应的作用。
解决方案
方案1:改用Spring官方推荐的构造器注入(推荐)
Spring 4.3+之后,类中只有一个构造器的情况下,依赖会自动注入,不需要加@Autowired注解。你已经用了Lombok的@RequiredArgsConstructor,只需要把autoMapper改成final成员即可,调整后的BuilderServiceImpl代码:
@Service @RequiredArgsConstructor @Slf4j public class BuilderServiceImpl implements BuilderService{ // 改为final成员,不需要@Autowired,@RequiredArgsConstructor会自动生成带该参数的构造器 private final AutoMapper autoMapper; private final BuilderRepository builderRepository; private final AdminUserRepository adminUserRepository; @Override public BuilderDto getByEmail(String email){ } @Override public List<BuilderMinDto> getAll() {} @Override public List<BuilderMinDto> getAll(int page, int size) {} @Override public SaveBuilderResponse create(Builder builder){ var str = autoMapper.getDummyText(); Builder savedBuilder = builderRepository.save(builder); return new SaveBuilderResponse(savedBuilder); } }
调整测试类的setUp方法,传入AutoMapper实例即可,你可以根据测试需要选择传入真实实例或者Mock实例:
@BeforeEach void setUp(){ autoCloseable = MockitoAnnotations.openMocks(this); // 新增AutoMapper实例,也可以用@Mock创建模拟实例满足特殊测试需求 AutoMapper autoMapper = new AutoMapper(); underTest = new BuilderServiceImpl(autoMapper, builderRepository, adminUserRepository); }
方案2:直接使用@SpringBootTest的容器注入能力,用@MockBean替换依赖
不需要手动创建服务实例,直接从容器中注入待测试的服务,用@MockBean替换掉不需要真实调用的Repository依赖,适合需要完整Spring上下文的集成测试场景:
@SpringBootTest @Slf4j class BuilderServiceImplTest { // 用@MockBean替换容器中的Repository实现 @MockBean private BuilderRepository builderRepository; @MockBean private AdminUserRepository adminUserRepository; // 直接注入容器托管的待测试服务,AutoMapper会自动完成注入 @Autowired private BuilderService underTest; @Test void getByEmail(){} @Test @Disabled void getAll() { } @Test @Disabled void testGetAll() {} @Test void create() { //given Builder builder = new Builder(); builder.setName("John Johnson"); builder.setCompanyName("Builders Test"); builder.setEmail("test@builders.com"); //when underTest.create(builder); //then ArgumentCaptor<Builder> builderArgumentCaptor = ArgumentCaptor.forClass(Builder.class); verify(builderRepository).save(builderArgumentCaptor.capture()); assertThat(builderArgumentCaptor.getValue()).isEqualTo(builder); } }
内容的提问来源于stack exchange,提问作者Michael
相关产品推荐
相关产品推荐

