使用Mockito模拟SpringBoot邮件组件时,为何仍生成真实EML文件?
为什么Mock JavaMailSender后仍会生成真实EML文件?
核心原因很直接:你业务代码里的文件创建逻辑和JavaMailSender的Mock完全无关,不管MimeMessage怎么Mock,文件都会被创建出来。
具体分析
看你的业务代码:
public File createEml(String filename) throws IOException, MessagingException { File file = new File(filename); // 这里已经在磁盘创建空文件了 try(FileOutputStream fos = new FileOutputStream(file)) { // 打开输出流进一步确认文件存在 MimeMessage mimeMessage = javaMailSender.createMimeMessage(); mimeMessage.writeTo(fos); } return file; }
new File(filename)执行时,操作系统就会在指定路径生成一个空文件;后续的FileOutputStream只是打开这个文件准备写入。你的Mock只拦截了mimeMessage.writeTo()的写入动作,但文件创建的动作在调用JavaMailSender之前就已经完成了,所以测试结束后磁盘上还是会留下空的test.eml。
解决方案
方案1:抽象文件操作逻辑(推荐)
把文件创建、输出流获取这些和文件系统相关的逻辑抽成独立组件,这样就能在测试中Mock掉,彻底避免真实文件操作。
修改业务代码:
@Resource private JavaMailSender javaMailSender; @Resource private FileOperator fileOperator; public File createEml(String filename) throws IOException, MessagingException { File file = fileOperator.createFile(filename); try(OutputStream outputStream = fileOperator.getOutputStream(file)) { MimeMessage mimeMessage = javaMailSender.createMimeMessage(); mimeMessage.writeTo(outputStream); } return file; } // 新增文件操作接口和实现 public interface FileOperator { File createFile(String filename) throws IOException; OutputStream getOutputStream(File file) throws IOException; } @Service public class DefaultFileOperator implements FileOperator { @Override public File createFile(String filename) throws IOException { return new File(filename); } @Override public OutputStream getOutputStream(File file) throws IOException { return new FileOutputStream(file); } }
修改测试代码:
@InjectMocks private EmailManager emailManager = new EmailManager(); @Mock private JavaMailSender javaMailSender; @Mock private FileOperator fileOperator; @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); } @Test void testCreateEml() throws MessagingException, IOException { // Mock MimeMessage MimeMessage mimeMessage = Mockito.mock(MimeMessage.class); Mockito.doNothing().when(mimeMessage).writeTo(Mockito.any(OutputStream.class)); Mockito.when(javaMailSender.createMimeMessage()).thenReturn(mimeMessage); // Mock文件操作,用内存流替代真实文件输出流 Mockito.when(fileOperator.createFile(Mockito.anyString())).thenReturn(new MockFile()); Mockito.when(fileOperator.getOutputStream(Mockito.any(File.class))).thenReturn(new ByteArrayOutputStream()); // 执行测试 emailManager.createEml("test.eml"); // 验证核心逻辑是否执行 Mockito.verify(javaMailSender).createMimeMessage(); Mockito.verify(mimeMessage).writeTo(Mockito.any(OutputStream.class)); } // 自定义MockFile,避免触发真实文件系统操作 static class MockFile extends File { public MockFile() { super("mock-file"); } }
方案2:测试后自动清理文件
如果不想重构代码,可以在测试结束后手动删除生成的文件,适合简单场景:
@AfterEach void cleanUp() { File emlFile = new File("test.eml"); if (emlFile.exists()) { emlFile.delete(); } }
但这种方式依赖测试环境的文件系统,不够灵活,不推荐在复杂测试场景中使用。
内容的提问来源于stack exchange,提问作者Tan
相关产品推荐
相关产品推荐

