簡體   English   中英

無法使用mockito對此方法中拋出的異常進行單元測試

[英]Not able to unit test the exception thrown in this method using mockito

我使用mockito編寫了以下單元測試來測試我的EmailService.java類。 我不確定的是,我是否正確地測試了這個(快樂路徑和異常情況)。

此外,我收到此Error: 'void' type not allowed here我的單元測試中的以下代碼段中Error: 'void' type not allowed here

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);
    }

}

EmailService.java

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);
        }
    }
}

不幸的是,問題中給出的代碼有很多問題。 例如:您指示您的測試拋出異常。 期望該異常進入測試。

但:

  try {
        mailSender.send(message);
    } catch (MailException e) {
        LOG.error("Error while sending notification email: ", e);
    }

您的生產代碼正在捕獲異常。 所以你編寫的測試只能在生產代碼不正確時通過

現在,如果測試錯誤:您可以考慮模擬該記錄器對象; 驗證是否發生了對它的調用。 並且您將更改測試用例以避免任何異常。 這就是try / catch的重點; 不是嗎

或者反過來說:如果沒有捕獲就是你想要的; 你的單元測試告訴你try / catch必須消失。

如上所述,這只是一個問題 - 其他答案在列出它們方面做得很好。

從這個角度來看,可以回答的是:不要試圖通過反復試驗來學習單元測試。 相反:獲得一本好書或教程, 學習如何進行單元測試 - 包括如何正確使用模擬框架。

你不應該真的在嘲笑你想要測試的一個類。 我認為你真正想要做的就是模仿MailSender拋出異常。 我注意到你在成功測試中已經使用了模擬MailSender。 再次使用它並設定期望:

 when(mailSender.send(any(SimpleMailMessage.class))).thenThrow(MailException.class);

但正如在@ GhostCat的回答中提到的那樣,你在方法中進行異常處理,因此除了要拋出的異常之外,你還需要指定一個不同的期望。 你可以模擬記錄器,但是模擬靜態記錄器通常需要付出很多努力。 您可能需要考慮重新處理異常處理以使其更容易測試。

我將假設這里的EmailService實現是正確的,並專注於測試。 他們都有缺陷。 雖然testNotify執行時沒有錯誤,但它實際上並沒有測試任何東西。 從技術上講,它至少會確認notify不拋出一個異常時, mailService不拋出異常。 我們可以做得更好。

寫好考試的關鍵是問自己,“這種方法應該做什么?” 在編寫方法之前,您應該能夠回答這個問題。 對於特定測試,請詢問“該輸入應該怎么做?” 或者“當它依賴於此時應該怎么做?”

在第一種情況下,您創建一個MailService ,向其傳遞MailSender以及to和from地址。 調用notify方法時, MailService實例應該做什么? 它應該通過send方法將SimpleMailMessage傳遞給MailSender 這是你如何做到的(注意,我假設MailSender實際上采用MailMessage接口而不是SimpleMailMessage ):

@Mock
private MailSender mailSender;
private EmailService emailService;
private String emailRecipientAddress = "recipient@abc.com";
private String emailSenderAddress = "sender@abc.com";
private String messageBody = "Hello Message Body!!!";

@Before
public void setUp(){
    MockitoAnnotations.initMocks(this);
    emailService = new EmailService(mailSender, emailRecipientAddress, emailSenderAddress);
}

@Test
public void testMessageSent() throws MailException {

    ArgumentCaptor<MailMessage> argument = ArgumentCaptor.forClass(MailMessage.class);

    emailService.notify(messageBody);

    Mockito.verify(mailSender).send(argument.capture());
    Assert.assertEquals(emailRecipientAddress, argument.getValue().getTo());
    Assert.assertEquals(emailSenderAddress, argument.getValue().getFrom());
    Assert.assertEquals(messageBody, argument.getValue().getText());

}

這可以確保EmailService實際上根據傳遞給其構造函數和notify方法的參數發送您期望的消息。 我們不關心MailSender是否在此測試中正確地MailSender它的工作。 我們只是假設它有效 - 大概是因為它要么在別處測試,要么在提供的庫的一部分。

對例外的測試更加微妙。 由於異常被捕獲,記錄,然后被忽略,因此沒有那么多的測試。 我個人不打算檢查是否有任何記錄。 我們真正想要做的是確認如果MailSender拋出MailException那么notify不會拋出異常。 如果MailExceptionRuntimeException那么我們應該測試它。 基本上,你只是模擬mailSender拋出異常。 如果EmailService沒有正確處理它,那么它將拋出異常並且測試將失敗(這使用與前一示例相同的設置):

@Test
public void testMailException() throws MailException {

    Mockito.doThrow(Mockito.mock(MailException.class)).when(mailSender).send(Mockito.any(MailMessage.class));
    emailService.notify(messageBody);

}

或者,我們可以捕獲MailException ,然后顯式地使測試失敗:

@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被允許拋出MailException在其他情況下,則該測試可能無法正常工作。

最后,如果MailException是一個經過檢查的異常 - 也就是說它不是 RuntimeException - 那么你甚至不需要測試它。 如果notify可能拋出MailException那么編譯器會要求它在方法簽名中聲明它。

1)請參閱文檔,了解如何使用異常模擬void方法:

在你的情況下,它應該是這樣的:

doThrow(new MailException()).when(mockEmailService).notify( anyString() );

2)您的testNotify沒有進行適當的測試。 調用實際notify方法后不檢查預期結果。

3)您的testNotifyMailException首先進行testNotifyMailException notify ,然后在非模擬的EmailService上調用實際的notify方法。 模擬notify是測試調用它的代碼而不是您正在模擬的實際方法。

基於上面提到的響應,我通過修改我的實際類和單元測試來實現它。

EmailSendException.java(為了提升testablity而添加的新類)

public class EmailSendException extends RuntimeException {
    private static final long serialVersionUID = 1L;

    public EmailSendException(String message) {
        super(message);
    }

    public EmailSendException(String message, Throwable cause) {
        super(message, cause);
    }
}

EmailService.java(而不是捕獲,拋出RuntimeException)

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;
    private static final String ERROR_MSG = "Error while sending notification email";

    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) {
            throw new EmailSendException(ERROR_MSG, e);
        }
    }
}

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);
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM