Java 使用反射捕获自定义 ApplicationException 的 NegativeTest 私有方法

Java NegativeTest private method with Reflection to catch custom ApplicationException

因此,我正在使用模拟数据库层测试 AccountService class。 在此 AccountService class 中,有一个私有方法根​​据正则表达式检查从 UI 收到的输入。

我写的正面测试工作正常:

@Test
public void testEmailPatroonCorrect() throws Exception{

    //Correcte emails
    List<String> emails = new ArrayList<>();
    emails.add("user@domain.com");
    emails.add("user@domain.co.in");
    emails.add("user.name@domain.com");
    emails.add("user_name@domain.com");
    emails.add("username@yahoo.corporate.in");

    Class<AccountService> foo = AccountService.class;
    Method method = foo.getDeclaredMethod("checkEmailPatroon", String.class);
    method.setAccessible(true);

    assertThatCode(() -> {
        for(String email : emails){
            method.invoke(AccountService,email);
        }}).doesNotThrowAnyException();

}

然而,为了简单起见,即使列表中只有一个对象,但对于负面测试(包含错误电子邮件模式的列表)

@Test
public void testEmailPatroonFout() throws Exception{

    //Verkeerde emailpatronen
    List<String> emails = new ArrayList<>();
    emails.add(".username@yahoo.com");

    Class<AccountService> foo = AccountService.class;
    Method method = foo.getDeclaredMethod("checkEmailPatroon", String.class);
    method.setAccessible(true);

    assertThatThrownBy(()->{
        for(String email : emails){
            method.invoke(AccountService,email);
        }
    }).isInstanceOf(ApplicationException.class).hasMessage(ApplicationExceptionType.ONGELDIGE_EMAIL.getMsg());

}

测试时抛出的异常为:java.lang.reflect.InvocationTargetException。在应用程序中,ApplicationException 被捕获得很好。 问题是如何为错误的电子邮件模式列表编写适当的测试? (不使用 @VisibleForTesting 功能,因为它是一个学校项目)。

非常感谢!

InvocationTargetException 包装反射调用方法中抛出的异常。所以你可以捕获 InvocationTargetException 并重新抛出它的原因,但我会把它放到一个实用方法中,比如

public interface TestMethod<D,A> {
    void invoke(D d, A a) throws Throwable;
}
static <D,A> TestMethod<D,A> method(
    Class<D> declarer, String name, Class<A> argType) throws ReflectiveOperationException {

    Method method = declarer.getDeclaredMethod(name, argType);
    method.setAccessible(true);
    return (d,a) -> {
        try {
            method.invoke(d, a);
        } catch(InvocationTargetException ex) {
            throw ex.getTargetException();
        }
    };
}

你可以像这样使用

@Test
public void testEmailPatroonFout() throws Exception{
    //Verkeerde emailpatronen
    List<String> emails = new ArrayList<>();
    emails.add(".username@yahoo.com");

    TestMethod<AccountService, String> method
        = method(AccountService.class, "checkEmailPatroon", String.class);

    assertThatThrownBy(() -> {
        for(String email : emails){
            method.invoke(AccountService, email);
        }
    }).isInstanceOf(ApplicationException.class)
      .hasMessage(ApplicationExceptionType.ONGELDIGE_EMAIL.getMsg());
}

TestMethod 接口的形状允许像

这样的替代实现
static <D,A> TestMethod<D,A> method(
    Class<D> declarer, String name, Class<A> argType) throws ReflectiveOperationException {

    Method method = declarer.getDeclaredMethod(name, argType);
    method.setAccessible(true);
    return MethodHandleProxies.asInterfaceInstance(
        TestMethod.class, MethodHandles.lookup().unreflect(method));
}

感谢 Holger,我能够为此目的编写一个工作测试。

@Test
public void testEmailPatroonFoutLoop() throws Throwable {

    //Verkeerde emailpatronen
    List<String> wrongEmails = new ArrayList<>();
    wrongEmails.add(".username@yahoo.com");
    wrongEmails.add("username@yahoo.com.");
    wrongEmails.add("usernameyahoo.com");
    wrongEmails.add("username@yahoo.c");
    wrongEmails.add("use..rname@yahoo.com");

    Class<AccountService> foo = AccountService.class;
    Method method = foo.getDeclaredMethod("checkEmailPatroon", String.class);
    method.setAccessible(true);

    int countedWrongEmails = 0;
    for(String email : wrongEmails){
        try{
            method.invoke(accServ,email);
        }
        catch (InvocationTargetException ie){
            Exception e = (Exception) ie.getTargetException();
            if(e.getMessage().equals(ApplicationExceptionType.ONGELDIGE_EMAIL.getMsg())){
                countedWrongEmails++;
            }
        }
    }
    assertThat(countedWrongEmails).isEqualTo(wrongEmails.size());
}

虽然我看到了编写 TestMethod 接口的好处和优雅,但是我还不具备掌握它的复杂性的知识。所以我会坚持这个我可以在口语考试中解释的测试。