如何编写一个junit来验证是否捕获了该方法引发的异常?

时间:2018-11-23 16:04:11

标签: java spring junit exception-handling

我的Spring Boot应用程序中包含以下代码,该代码可验证电子邮件地址

class EmailValidation {

    public static void validate(List<String> s){
        try {
            for (String address : s) {
                if (s == null || s.indexOf("@") < 0) {  
                    throw new InvalidEmailAddressException("Email address is invalid ");
                }
                new InternetAddress(s);
            }
        } catch(AddressException e){
            LOGGER.Error("Please validate email addresses");
        }
        catch(InvalidEmailAddressesException e){
            LOGGER.error(e.getMessage());
        }
    }

    class InvalidEmailAddressException extends Exception {

        public InvalidEmailAddressException(String message) {
            super(message)
        }
    }
}

我想编写一个Junit测试,该测试将验证是否抛出InvalidEmailAddressesException并执行了CAUGHT。如何在JUnit中做到这一点?

3 个答案:

答案 0 :(得分:0)

总的来说,我同意这种测试可能是不必要的评论。

但是,如果我想测试类似的东西,我将分别测试这两种情况,这需要对您的代码进行少量修改。

首先,我将构建一个仅在存在异常时抛出异常的方法。

public static void checkAddresses(List<String> s) throws AddressException, InvalidEmailAddressException {
    for (String address : s) {
         if (s == null || s.indexOf("@") < 0) {  
             throw new InvalidEmailAddressException("Email address is invalid ");
         }
         new InternetAddress(s);
    }
}

然后我会像这样在您的代码中使用它:

class EmailValidation {

    public static void validate(List<String> s){
         try {
             checkAddresses(s); // a wrapper method that throws the expected exceptions
         } catch(AddressException e){
             LOGGER.Error("Please validate email addresses");
         }
         catch(InvalidEmailAddressesException e){
             LOGGER.error(e.getMessage());
         }
     }

     // add checkAddresses here or somewhere appropriately

     class InvalidEmailAddressException extends Exception {

         public InvalidEmailAddressException(String message) {
             super(message)
         }
     }

}

然后,我将为checkAddresses编写单独的测试,以测试是否为expected,并且为validate编写单独的测试(可能使用与输入相同的输入) checkAddresses),如果未引发异常,则应该通过。

此外,如果您想验证自己的日志,可以尝试使用that之类的东西。

答案 1 :(得分:0)

实际上将Java Exception用于常见原因被认为是一种不好的做法,正如@Michael所说,异常必须是特殊的,因为

  • 他们中断流量控制
  • 它们很慢(更多详细信息,请点击How slow are Java exceptions?
  • 它们不与功能范式混合使用(Java在某种程度上与lamda表达式有关

但是,创建用于包装验证数据的自定义对象是一件好事,InvalidEmailAddressException可以变成CheckedEmail

import java.util.List;
import java.util.stream.Collectors;

public class EmailValidator {

    public List<CheckedEmail> validate(List<String> emailAddresses) {
        return emailAddresses.stream().map(this::validate).collect(Collectors.toList());
    }

    public CheckedEmail validate(String emailAddress) {
        String[] emailParts = emailAddress.toString().split( "@", 3 );
        final boolean valid;
        if ( emailParts.length != 2 ) {
            valid = false;
        } else {
            // More validation can go here using one or more regex
            valid = true;
        }
        return new CheckedEmail(emailAddress, valid);
    }

    public static final class CheckedEmail {
        private final String emailAddress;
        private final boolean valid;

        private CheckedEmail(String emailAddress, boolean valid) {
            this.emailAddress = emailAddress;
            this.valid = valid;
        }

        public String getEmailAddress() {
            return emailAddress;
        }

        public boolean isValid() {
            return valid;
        }
    }
}

这反过来可以很容易地进行测试(并通过参数化测试进行了改进):

import static org.assertj.core.api.Assertions.assertThat;

import java.util.Arrays;
import java.util.List;

import org.junit.Test;

public class EmailValidatorTest {

    private final EmailValidator emailValidator = new EmailValidator();

    @Test
    public void invalid_email() {
        EmailValidator.CheckedEmail checkedEmail = emailValidator.validate("missing.an.at.symbol");

        assertThat(checkedEmail.isValid()).isFalse();
    }

    @Test
    public void valid_email() {
        EmailValidator.CheckedEmail checkedEmail = emailValidator.validate("at.symbol@present");

        assertThat(checkedEmail.isValid()).isTrue();
    }

    @Test
    public void multiple_email_addresses() {
        List<String> emailAddresses = Arrays.asList("missing.an.at.symbol", "at.symbol@present");

        List<EmailValidator.CheckedEmail> checkedEmails = emailValidator.validate(emailAddresses);

        assertThat(checkedEmails)
                .extracting(ce -> ce.getEmailAddress() + " " + ce.isValid())
                .containsExactly(
                        "missing.an.at.symbol false",
                        "at.symbol@present true");
    }
}

如果某个地方只是记录下来,那么:

List<EmailValidator.CheckedEmail> checkedEmails = emailValidator.validate(emailAddresses);

checkedEmails.stream()
        .filter(ce -> !ce.isValid())
        .map(ce -> String.format("Email address [%s] is invalid", ce.getEmailAddress()))
        .forEach(logger::error);

希望这会有所帮助!

答案 2 :(得分:0)

不要以这种方式进行测试。您应该仅测试代码的指定行为,而不是其实现详细信息。

如果您正在测试的方法将委托给抛出 checked 异常的方法,并且您正在测试的方法也没有throws声明检查异常的 compiler 将强制该方法捕获异常。因此,在这种情况下,不需要进行单元测试。

如果您要测试的方法委托抛出 unchecked 异常的方法,请查阅该方法的规范以确定被测方法是否也抛出(传播)该异常。例外。如果它不能传播异常,则应创建一个测试用例,以使委托的方法引发该未经检查的异常。如果该方法传播异常,则测试用例将失败。怎么做?这取决于委托给的方法,但是在大多数情况下,您将需要使用“依赖注入”来提供引发异常的模拟对象。