You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 15:45:03