Spring3+Java17项目邮件服务测试报From address must not be null异常求助
问题解决:From address must not be null 测试异常
问题原因
测试执行时mailSenderUsername字段为null,因为@ExtendWith(MockitoExtension.class)仅提供Mockito的Mock能力,不会处理Spring的@Value注解,导致调用message.setFrom(mailSenderUsername)时触发Spring的非空断言异常。
解决方法
方法1:手动赋值测试字段(单元测试快速修复)
利用Spring的ReflectionTestUtils工具,在测试初始化阶段给mailService的mailSenderUsername字段设置测试值:
@ExtendWith(MockitoExtension.class) class MailServiceImplTest { @Mock private JavaMailSender mailSender; @Mock private MimeMessage mimeMessage; private final MailSimpleRequest mailRequest = Data.givenMailRequest(); @InjectMocks private MailServiceImpl mailService; @BeforeEach void setUp() { // 给私有字段mailSenderUsername赋值测试用发件人邮箱 ReflectionTestUtils.setField(mailService, "mailSenderUsername", "test-sender@example.com"); } @Test void sendMailConfirmationToken() throws MessagingException { when(mailSender.createMimeMessage()).thenReturn(mimeMessage); mailService.sendMailConfirmationToken(mailRequest); verify(mailSender).send(any(MimeMessage.class)); } }
方法2:改用构造注入(推荐的代码优化方案)
重构MailServiceImpl,将依赖通过构造函数注入,避免依赖Spring的字段注入,同时让测试更直观:
重构后的业务类
@Service public class MailServiceImpl implements MailService { private final JavaMailSender mailSender; private final String mailSenderUsername; // 构造注入依赖与配置值 public MailServiceImpl(JavaMailSender mailSender, @Value("${mail.senderUsername}") String mailSenderUsername) { this.mailSender = mailSender; this.mailSenderUsername = mailSenderUsername; } @Override public void sendMailConfirmationToken(MailSimpleRequest mailRequest) throws MessagingException { MimeMessage mimeMessage = mailSender.createMimeMessage(); MimeMessageHelper message = new MimeMessageHelper(mimeMessage, true, "UTF-8"); message.setFrom(mailSenderUsername); message.setTo(mailRequest.toUsers()); message.setSubject(mailRequest.subject()); message.setText("<div><p>Message</p></div>", true); this.mailSender.send(mimeMessage); } }
对应的测试类
@ExtendWith(MockitoExtension.class) class MailServiceImplTest { @Mock private JavaMailSender mailSender; @Mock private MimeMessage mimeMessage; private final MailSimpleRequest mailRequest = Data.givenMailRequest(); private final String testSender = "test-sender@example.com"; private MailServiceImpl mailService; @BeforeEach void setUp() { // 直接通过构造函数传入测试依赖与配置值 mailService = new MailServiceImpl(mailSender, testSender); when(mailSender.createMimeMessage()).thenReturn(mimeMessage); } @Test void sendMailConfirmationToken() throws MessagingException { mailService.sendMailConfirmationToken(mailRequest); verify(mailSender).send(any(MimeMessage.class)); } }
方法3:加载Spring上下文测试(集成测试场景)
如果需要验证完整的Spring配置集成,可以改用@SpringBootTest,通过@TestPropertySource设置测试用配置:
@SpringBootTest @TestPropertySource(properties = {"mail.senderUsername=test-sender@example.com"}) class MailServiceImplTest { @MockBean private JavaMailSender mailSender; @Autowired private MailServiceImpl mailService; private final MailSimpleRequest mailRequest = Data.givenMailRequest(); @Test void sendMailConfirmationToken() throws MessagingException { when(mailSender.createMimeMessage()).thenReturn(mock(MimeMessage.class)); mailService.sendMailConfirmationToken(mailRequest); verify(mailSender).send(any(MimeMessage.class)); } }
方案选择建议
- 方法1:适合快速修复现有单元测试,无需改动业务代码
- 方法2:推荐用于长期维护,构造注入让依赖关系更清晰,测试更易于编写和维护
- 方法3:仅在需要验证Spring配置、Bean生命周期等集成场景时使用,启动速度较慢
内容的提问来源于stack exchange,提问作者diego evangeliste
相关产品推荐
相关产品推荐

