如何为EmailService的bookingEmail方法编写JUnit测试(含两种Runner及参数捕获)
EmailService的JUnit测试实现方案
针对EmailService类的bookingEmail方法,以下是对应三个需求的具体实现:
1. 使用@RunWith(SpringRunner.class)实现测试
SpringRunner是SpringJUnit4ClassRunner的别名,用于在JUnit 4中集成Spring上下文,适合需要加载部分Spring组件的测试场景。
修正后的测试代码
@RunWith(SpringRunner.class) @SpringBootTest(classes = {EmailService.class, TestConfig.class}) // 引入测试配置类 public class EmailServiceTest { private ScheduledTimeList testScheduledTimeList; private static final String TEST_APP_EMAIL = "test@example.com"; @Autowired @Qualifier("emailConfigBean") private FreeMarkerConfigurationFactoryBean emailConfig; @MockBean // 替换Spring上下文中的HttpEmailService为Mock实例 private HttpEmailService httpEmailService; @Autowired private EmailService emailService; @Before public void setup() { // 构造测试用的ScheduledTimeList(根据实际类结构补充属性) testScheduledTimeList = new ScheduledTimeList(); } @Test public void bookingEmail_Success() throws Exception { // 执行测试方法 String result = emailService.bookingEmail(testScheduledTimeList, TEST_APP_EMAIL); // 验证返回结果 Assert.assertEquals("success", result); // 验证邮件发送方法被调用一次 Mockito.verify(httpEmailService, Mockito.times(1)).sendEmail(Mockito.any(EmailRequest.class)); } }
关键说明
@SpringBootTest指定加载EmailService和测试配置类,确保FreeMarker配置和业务类被正确初始化。@MockBean自动替换Spring上下文里的HttpEmailService为Mock对象,避免触发真实邮件发送。- 修正原有测试代码错误:无需手动实例化
EmailService,Spring会自动完成注入。
2. 使用@RunWith(MockitoJUnitRunner.class)实现测试
完全可以用MockitoJUnitRunner实现,这种方式无需加载Spring上下文,纯依赖Mockito完成Mock和注入,测试速度更快,适合单元测试场景。
测试代码实现
@RunWith(MockitoJUnitRunner.class) public class EmailServiceMockitoTest { private ScheduledTimeList testScheduledTimeList; private static final String TEST_APP_EMAIL = "test@example.com"; @Mock // Mock FreeMarker配置类 private FreeMarkerConfigurationFactoryBean emailConfig; @Mock // Mock邮件发送服务 private HttpEmailService httpEmailService; @InjectMocks // 自动将Mock对象注入到EmailService实例 private EmailService emailService; @Before public void setup() throws Exception { // 构造测试用的ScheduledTimeList testScheduledTimeList = new ScheduledTimeList(); // Mock FreeMarker相关逻辑,避免真实模板加载 Configuration mockConfig = Mockito.mock(Configuration.class); Template mockTemplate = Mockito.mock(Template.class); Mockito.when(emailConfig.getObject()).thenReturn(mockConfig); Mockito.when(mockConfig.getTemplate("emailTemplateNames.ftl")).thenReturn(mockTemplate); // Mock模板渲染结果 Mockito.when(FreeMarkerTemplateUtils.processTemplateIntoString(mockTemplate, Mockito.anyMap())) .thenReturn("<html>Test Email Content</html>"); } @Test public void bookingEmail_Success() throws Exception { String result = emailService.bookingEmail(testScheduledTimeList, TEST_APP_EMAIL); Assert.assertEquals("success", result); Mockito.verify(httpEmailService, Mockito.times(1)).sendEmail(Mockito.any(EmailRequest.class)); } }
关键说明
@RunWith(MockitoJUnitRunner.class)启用Mockito的JUnit集成,自动初始化@Mock和@InjectMocks标注的对象。@InjectMocks会自动将所有@Mock依赖注入到EmailService,无需手动构造实例。- 若需验证真实模板渲染逻辑,可去掉FreeMarker相关Mock,手动初始化
FreeMarkerConfigurationFactoryBean并指定测试模板路径。
3. 捕获方法调用的参数
使用Mockito的ArgumentCaptor可以捕获方法调用时传入的参数,用于验证参数的正确性。
示例代码(以SpringRunner测试为例)
@Test public void bookingEmail_VerifyEmailRequest() throws Exception { // 创建参数捕获器 ArgumentCaptor<EmailRequest> emailRequestCaptor = ArgumentCaptor.forClass(EmailRequest.class); // 执行测试方法 emailService.bookingEmail(testScheduledTimeList, TEST_APP_EMAIL); // 捕获sendEmail方法的参数 Mockito.verify(httpEmailService).sendEmail(emailRequestCaptor.capture()); EmailRequest capturedRequest = emailRequestCaptor.getValue(); // 验证参数属性 Assert.assertEquals(TEST_APP_EMAIL, capturedRequest.getTo()); Assert.assertEquals("Subject", capturedRequest.getSubject()); Assert.assertNotNull(capturedRequest.getHtmlContent()); // 验证模板变量是否正确渲染(使用真实模板时) Assert.assertTrue(capturedRequest.getHtmlContent().contains(TEST_APP_EMAIL)); }
关键说明
ArgumentCaptor.forClass(EmailRequest.class)创建对应类型的参数捕获器。Mockito.verify(httpEmailService).sendEmail(emailRequestCaptor.capture())触发参数捕获,需确保方法已被调用。- 通过
capturedRequest可获取实际传入的参数,验证其属性是否符合业务预期。
内容的提问来源于stack exchange,提问作者Eshan Akash
相关产品推荐
相关产品推荐

