JUnit中Autowiring遇空指针异常:Configuration与JavaMailSender注入失败
问题根源分析
你遇到的空指针异常核心原因是依赖注入的时机和对象管理范围不匹配:
- 类级别的变量初始化(
private EmailService emailService = new EmailService(...))是在测试类实例被创建的瞬间执行的,这时候Spring还没完成@Autowired标注的configuration和javaMailSender的注入,这两个对象还是null,直接用来构造EmailService自然会导致空指针。 - 你手动
new出来的EmailService并不受Spring容器管理,即便后续Spring完成了依赖注入,也不会把值同步到这个手动创建的对象里。
而当你给EmailService加@Autowired时,Spring会全权负责这个对象的创建和依赖注入,所有依赖都会被正确赋值,所以能正常运行。
解决方案
下面提供几种可行的解决方式,你可以根据自己的需求选择:
方案1:让Spring管理EmailService并注入Mock对象(推荐)
如果你的EmailService本身是Spring Bean(比如标注了@Service),可以用@MockBean替换掉它依赖的AuthUser,让Spring自动完成注入,不需要手动实例化:
@SpringBootTest public class EmailServiceTest { @Autowired private EmailService emailService; @MockBean // Spring会把这个Mock对象注入到EmailService中 private AuthUser authUser; @Test public void testSendAttachment() throws IOException { List<Attachment> attachments = new ArrayList<>(); attachments.add(new Attachment(new Archive(TestData.getDocs(), "test").getZipArchive(), "documents.zip")); attachments.add(new Attachment(new Archive(TestData.getDocs(), "test").getZipArchive(), "documents2.zip")); Email mail = new Email("a", "b", "Requested archive attached", "Thank you for using our services, please refer to the attached archive", attachments); emailService.sendMailWithAttachment(mail); } @Test public void shouldSendConfirmationEmail(){ Mockito.when(authUser.getUsername()).thenReturn("email@email.com"); emailService.sendConfirmationToCooperateEmail(); } }
方案2:在依赖注入完成后手动初始化EmailService
如果一定要手动构造EmailService,可以把初始化逻辑放到@BeforeEach方法中,这个方法会在每个测试执行前运行,此时Spring已经完成了@Autowired字段的注入:
@SpringBootTest public class EmailServiceTest { @Autowired private Configuration configuration; @Autowired private JavaMailSender javaMailSender; private AuthUser authUser; private EmailService emailService; @BeforeEach void setUp() { authUser = Mockito.mock(AuthUser.class); // 此时configuration和javaMailSender已经被Spring注入,不会为null emailService = new EmailService(javaMailSender, configuration, authUser); } @Test public void testSendAttachment() throws IOException { // 测试逻辑不变 } @Test public void shouldSendConfirmationEmail(){ // 测试逻辑不变 } }
方案3:用构造函数注入测试类依赖(更安全的方式)
避免字段注入的初始化顺序问题,改用构造函数注入测试类的依赖,这样依赖在测试类实例创建时就已初始化完成:
@SpringBootTest public class EmailServiceTest { private final Configuration configuration; private final JavaMailSender javaMailSender; private AuthUser authUser; private EmailService emailService; // Spring会自动把依赖传入构造函数 public EmailServiceTest(Configuration configuration, JavaMailSender javaMailSender) { this.configuration = configuration; this.javaMailSender = javaMailSender; } @BeforeEach void setUp() { authUser = Mockito.mock(AuthUser.class); emailService = new EmailService(javaMailSender, configuration, authUser); } // 测试方法不变 }
内容的提问来源于stack exchange,提问作者Artjom Prozorov
相关产品推荐
相关产品推荐

