Java 无法使用mockito对此方法中引发的异常进行单元测试
我已经使用Java 无法使用mockito对此方法中引发的异常进行单元测试,java,unit-testing,junit,mockito,Java,Unit Testing,Junit,Mockito,我已经使用mockito编写了以下单元测试来测试我的EmailService.java类。我不确定的是,我是否正确地测试了它(快乐路径和异常场景) 此外,我得到了这个错误:此处不允许使用“void”类型 在我的单元测试中的以下代码段中 when(mockEmailService.notify(anyString())).thenThrow(MailException.class); 我知道,由于我的notify()方法返回void,我得到了该异常。但不知道如何解决这个问题。在我的单元测试或实际
mockito
编写了以下单元测试来测试我的EmailService.java
类。我不确定的是,我是否正确地测试了它(快乐路径和异常场景)
此外,我得到了这个错误:此处不允许使用“void”类型
在我的单元测试中的以下代码段中
when(mockEmailService.notify(anyString())).thenThrow(MailException.class);
我知道,由于我的notify()
方法返回void,我得到了该异常。但不知道如何解决这个问题。在我的单元测试或实际类或两者中是否需要任何代码更改
有人能给我引路吗
EmailServiceTest.java
public class EmailServiceTest {
@Rule
public MockitoJUnitRule rule = new MockitoJUnitRule(this);
@Mock
private MailSender mailSender;
@Mock
private EmailService mockEmailService;
private String emailRecipientAddress = "recipient@abc.com";
private String emailSenderAddress = "sender@abc.com";
private String messageBody = "Hello Message Body!!!";
@Test
public void testNotify() {
EmailService emailService = new EmailService(mailSender, emailRecipientAddress, emailSenderAddress);
emailService.notify(messageBody);
}
@Test(expected = MailException.class)
public void testNotifyMailException() {
when(mockEmailService.notify(anyString())).thenThrow(MailException.class);
EmailService emailService = new EmailService(mailSender, emailRecipientAddress, emailSenderAddress);
emailService.notify(messageBody);
}
}
public class EmailService {
private static final Log LOG = LogFactory.getLog(EmailService.class);
private static final String EMAIL_SUBJECT = ":: Risk Assessment Job Summary Results::";
private final MailSender mailSender;
private final String emailRecipientAddress;
private final String emailSenderAddress;
public EmailService(MailSender mailSender, String emailRecipientAddress,
String emailSenderAddress) {
this.mailSender = mailSender;
this.emailRecipientAddress = emailRecipientAddress;
this.emailSenderAddress = emailSenderAddress;
}
public void notify(String messageBody) {
SimpleMailMessage message = new SimpleMailMessage();
message.setSubject(EMAIL_SUBJECT);
message.setTo(emailRecipientAddress);
message.setFrom(emailSenderAddress);
message.setText(messageBody);
try {
mailSender.send(message);
} catch (MailException e) {
LOG.error("Error while sending notification email: ", e);
}
}
}
public class EmailServiceTest {
@Rule
public MockitoJUnitRule rule = new MockitoJUnitRule(this);
@Mock
private MailSender mailSender;
private String emailRecipientAddress = "recipient@abc.com";
private String emailSenderAddress = "sender@abc.com";
private String messageBody = "Hello Message Body!!!";
@Mock
private EmailService mockEmailService;
@Test
public void testNotify() {
EmailService EmailService = new EmailService(mailSender, emailRecipientAddress, emailSenderAddress);
EmailService.notify(messageBody);
}
@Test(expected = KlarnaEmailSendException.class)
public void testNotifyMailException() {
doThrow(new KlarnaEmailSendException("Some error message")).when(mockKlarnaEmailService).notify(anyString());
mockKlarnaEmailService.notify(messageBody);
}
}
EmailService.java
public class EmailServiceTest {
@Rule
public MockitoJUnitRule rule = new MockitoJUnitRule(this);
@Mock
private MailSender mailSender;
@Mock
private EmailService mockEmailService;
private String emailRecipientAddress = "recipient@abc.com";
private String emailSenderAddress = "sender@abc.com";
private String messageBody = "Hello Message Body!!!";
@Test
public void testNotify() {
EmailService emailService = new EmailService(mailSender, emailRecipientAddress, emailSenderAddress);
emailService.notify(messageBody);
}
@Test(expected = MailException.class)
public void testNotifyMailException() {
when(mockEmailService.notify(anyString())).thenThrow(MailException.class);
EmailService emailService = new EmailService(mailSender, emailRecipientAddress, emailSenderAddress);
emailService.notify(messageBody);
}
}
public class EmailService {
private static final Log LOG = LogFactory.getLog(EmailService.class);
private static final String EMAIL_SUBJECT = ":: Risk Assessment Job Summary Results::";
private final MailSender mailSender;
private final String emailRecipientAddress;
private final String emailSenderAddress;
public EmailService(MailSender mailSender, String emailRecipientAddress,
String emailSenderAddress) {
this.mailSender = mailSender;
this.emailRecipientAddress = emailRecipientAddress;
this.emailSenderAddress = emailSenderAddress;
}
public void notify(String messageBody) {
SimpleMailMessage message = new SimpleMailMessage();
message.setSubject(EMAIL_SUBJECT);
message.setTo(emailRecipientAddress);
message.setFrom(emailSenderAddress);
message.setText(messageBody);
try {
mailSender.send(message);
} catch (MailException e) {
LOG.error("Error while sending notification email: ", e);
}
}
}
public class EmailServiceTest {
@Rule
public MockitoJUnitRule rule = new MockitoJUnitRule(this);
@Mock
private MailSender mailSender;
private String emailRecipientAddress = "recipient@abc.com";
private String emailSenderAddress = "sender@abc.com";
private String messageBody = "Hello Message Body!!!";
@Mock
private EmailService mockEmailService;
@Test
public void testNotify() {
EmailService EmailService = new EmailService(mailSender, emailRecipientAddress, emailSenderAddress);
EmailService.notify(messageBody);
}
@Test(expected = KlarnaEmailSendException.class)
public void testNotifyMailException() {
doThrow(new KlarnaEmailSendException("Some error message")).when(mockKlarnaEmailService).notify(anyString());
mockKlarnaEmailService.notify(messageBody);
}
}
不幸的是,问题中给出的代码存在许多问题。例如:指示测试抛出一个异常。并期望该异常在测试中得到弥补 但是: 您的生产代码正在捕获异常。因此,您编写了一个测试,只有在生产代码不正确时才能通过 现在,如果测试是错误的:您可以研究模拟记录器对象;验证是否发生了对它的调用。您可以将测试用例更改为而不是期望出现任何异常。这就是try/catch的全部要点;不是吗 或者反过来说:如果你不想抓住它;单元测试告诉您,尝试/捕获必须停止 如前所述,这只是这里的一个问题——其他答案很好地列出了它们 从这个角度来看,答案可能是:不要试图通过反复试验来学习单元测试。取而代之的是:找一本好书或教程,学习如何进行单元测试——包括如何正确使用模拟框架 1)关于如何模拟
void
方法的文档,例外情况如下:
在您的情况下,应该是这样的:
doThrow(new MailException()).when(mockEmailService).notify( anyString() );
2) 您的testNotify
没有进行正确的测试。调用actualnotify
后,方法不检查预期结果
3) 您的
testNotifyMailException
首先模拟notify
,然后在非模拟的EmailService上调用实际的notify
方法模拟notify
的全部目的是测试调用它的代码,而不是实际模拟的方法。您不应该真正模拟您试图测试的类。我认为您真正想要做的是模拟MailSender抛出异常。我注意到您已经在成功的测试中使用了模拟邮件发送者。只需再次使用此选项并设定期望值:
when(mailSender.send(any(SimpleMailMessage.class))).thenThrow(MailException.class);
public class EmailServiceTest {
@Rule
public MockitoJUnitRule rule = new MockitoJUnitRule(this);
@Mock
private MailSender mailSender;
private String emailRecipientAddress = "recipient@abc.com";
private String emailSenderAddress = "sender@abc.com";
private String messageBody = "Hello Message Body!!!";
@Mock
private EmailService mockEmailService;
@Test
public void testNotify() {
EmailService EmailService = new EmailService(mailSender, emailRecipientAddress, emailSenderAddress);
EmailService.notify(messageBody);
}
@Test(expected = KlarnaEmailSendException.class)
public void testNotifyMailException() {
doThrow(new KlarnaEmailSendException("Some error message")).when(mockKlarnaEmailService).notify(anyString());
mockKlarnaEmailService.notify(messageBody);
}
}
但是正如@GhostCat的回答中提到的,您正在方法中进行异常处理,因此您需要指定一个不同的期望值,而不是要抛出的异常。您可以模拟记录器,但模拟静态记录器通常需要付出更多的努力。您可能需要考虑重新处理异常处理以便于测试。 < P>基于上述响应,我通过修改实际的类和单元测试来工作。 EmailSendException.java(添加新类以提高可测试性)
public class EmailServiceTest {
@Rule
public MockitoJUnitRule rule = new MockitoJUnitRule(this);
@Mock
private MailSender mailSender;
private String emailRecipientAddress = "recipient@abc.com";
private String emailSenderAddress = "sender@abc.com";
private String messageBody = "Hello Message Body!!!";
@Mock
private EmailService mockEmailService;
@Test
public void testNotify() {
EmailService EmailService = new EmailService(mailSender, emailRecipientAddress, emailSenderAddress);
EmailService.notify(messageBody);
}
@Test(expected = KlarnaEmailSendException.class)
public void testNotifyMailException() {
doThrow(new KlarnaEmailSendException("Some error message")).when(mockKlarnaEmailService).notify(anyString());
mockKlarnaEmailService.notify(messageBody);
}
}
EmailService.java(不是捕获,而是抛出RuntimeException)
public class EmailServiceTest {
@Rule
public MockitoJUnitRule rule = new MockitoJUnitRule(this);
@Mock
private MailSender mailSender;
private String emailRecipientAddress = "recipient@abc.com";
private String emailSenderAddress = "sender@abc.com";
private String messageBody = "Hello Message Body!!!";
@Mock
private EmailService mockEmailService;
@Test
public void testNotify() {
EmailService EmailService = new EmailService(mailSender, emailRecipientAddress, emailSenderAddress);
EmailService.notify(messageBody);
}
@Test(expected = KlarnaEmailSendException.class)
public void testNotifyMailException() {
doThrow(new KlarnaEmailSendException("Some error message")).when(mockKlarnaEmailService).notify(anyString());
mockKlarnaEmailService.notify(messageBody);
}
}
EmailServiceTest.java(模拟和测试)
public class EmailServiceTest {
@Rule
public MockitoJUnitRule rule = new MockitoJUnitRule(this);
@Mock
private MailSender mailSender;
private String emailRecipientAddress = "recipient@abc.com";
private String emailSenderAddress = "sender@abc.com";
private String messageBody = "Hello Message Body!!!";
@Mock
private EmailService mockEmailService;
@Test
public void testNotify() {
EmailService EmailService = new EmailService(mailSender, emailRecipientAddress, emailSenderAddress);
EmailService.notify(messageBody);
}
@Test(expected = KlarnaEmailSendException.class)
public void testNotifyMailException() {
doThrow(new KlarnaEmailSendException("Some error message")).when(mockKlarnaEmailService).notify(anyString());
mockKlarnaEmailService.notify(messageBody);
}
}
我将假设这里的
EmailService
实现是正确的,并将重点放在测试上。他们都有缺陷。虽然testNotify
执行时没有错误,但它实际上并没有测试任何东西。从技术上讲,当mailService
没有引发异常时,它至少会确认notify
没有引发异常。我们可以做得更好
编写好测试的关键是问自己,“这个方法应该做什么?”你应该能够在编写方法之前回答这个问题。对于特定的测试,请询问“它应该如何处理此输入?”或“当其依赖项执行此操作时,它应该如何处理?”
在第一种情况下,创建一个MailService
,向其传递mailssender
和发送地址。调用MailService
实例的notify
方法时,该实例应该做什么?它应该通过send
方法将simpleEmailMessage
传递给mailssender
。下面是如何做到这一点的(注意,我假设mailssender
实际上使用MailMessage
接口,而不是SimpleMailMessage
):
或者,我们可以捕获邮件异常
,然后显式地使测试失败:
@Test
public void testMailExceptionAlternate() {
try {
Mockito.doThrow(Mockito.mock(MailException.class)).when(mailSender).send(Mockito.any(MailMessage.class));
emailService.notify(messageBody);
} catch (MailException ex){
Assert.fail("MailException was supposed to be caught.");
}
}
这两种方法都确认了所需的行为。第二个问题在测试内容上更为明确。不过,缺点是,如果在其他情况下允许notify
抛出mailseption
,那么该测试可能不起作用
最后,如果
maileexception
是一个已检查的异常,即它不是RuntimeException
,那么您甚至不需要测试它。如果notify
可能抛出MailException
,那么编译器将要求它在方法签名中声明它。抱歉,复制粘贴问题。修正了你的单元测试失败了吗?你收到了什么错误信息?第一条正在通过,第二条没有通过。根据mockito,语法不正确。不确定我的生产代码是否需要返工或单元测试。请试一试。获取异常org.springframework.mail.MailException是抽象的;无法实例化
尝试使用具体的扩展类,如MailSendException
。真的吗?这似乎很奇怪,您能粘贴新的testNotifyMailException()方法吗?@Test(预期=MailException.class)public void testNotifyMailException(){when(mailSender.s