Java 如何使用 JUnit 测试批注断言我的异常消息?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2469911/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 08:07:29  来源:igfitidea点击:

How do I assert my exception message with JUnit Test annotation?

javatestingannotationsjunit4assertion

提问by Cshah

I have written a few JUnit tests with @Testannotation. If my test method throws a checked exception and if I want to assert the message along with the exception, is there a way to do so with JUnit @Testannotation? AFAIK, JUnit 4.7 doesn't provide this feature but does any future versions provide it? I know in .NET you can assert the message and the exception class. Looking for similar feature in the Java world.

我已经用@Test注释编写了一些 JUnit 测试。如果我的测试方法抛出一个已检查的异常,并且我想断言消息和异常,有没有办法用 JUnit@Test注释来做到这一点?AFAIK,JUnit 4.7 不提供此功能,但是否有任何未来版本提供?我知道在 .NET 中你可以断言消息和异常类。在 Java 世界中寻找类似的功能。

This is what I want:

这就是我要的:

@Test (expected = RuntimeException.class, message = "Employee ID is null")
public void shouldThrowRuntimeExceptionWhenEmployeeIDisNull() {}

采纳答案by Jesse Merriman

You could use the @Ruleannotation with ExpectedException, like this:

您可以将@Rule注释与 一起使用ExpectedException,如下所示:

@Rule
public ExpectedException expectedEx = ExpectedException.none();

@Test
public void shouldThrowRuntimeExceptionWhenEmployeeIDisNull() throws Exception {
    expectedEx.expect(RuntimeException.class);
    expectedEx.expectMessage("Employee ID is null");

    // do something that should throw the exception...
    System.out.println("=======Starting Exception process=======");
    throw new NullPointerException("Employee ID is null");
}

Note that the example in the ExpectedExceptiondocs is (currently) wrong - there's no public constructor, so you have to use ExpectedException.none().

请注意,ExpectedException文档中的示例(当前)是错误的 - 没有公共构造函数,因此您必须使用ExpectedException.none().

回答by c_maker

Do you have to use @Test(expected=SomeException.class)? When we have to assert the actual message of the exception, this is what we do.

你必须使用@Test(expected=SomeException.class)吗?当我们必须断言异常的实际消息时,这就是我们所做的。

@Test
public void myTestMethod()
{
  try
  {
    final Integer employeeId = null;
    new Employee(employeeId);
    fail("Should have thrown SomeException but did not!");
  }
  catch( final SomeException e )
  {
    final String msg = "Employee ID is null";
    assertEquals(msg, e.getMessage());
  }
}

回答by Jyothi

If using @Rule, the exception set is applied to all the test methods in the Test class.

如果使用@Rule,则异常集将应用于Test 类中的所有测试方法。

回答by Raystorm

I like the @Ruleanswer. However, if for some reason you don't want to use rules. There is a third option.

我喜欢这个@Rule答案。但是,如果由于某种原因您不想使用规则。还有第三种选择。

@Test (expected = RuntimeException.class)
public void myTestMethod()
{
   try
   {
      //Run exception throwing operation here
   }
   catch(RuntimeException re)
   {
      String message = "Employee ID is null";
      assertEquals(message, re.getMessage());
      throw re;
    }
    fail("Employee Id Null exception did not throw!");
  }

回答by user64141

Raystorm had a good answer. I'm not a big fan of Rules either. I do something similar, except that I create the following utility class to help readability and usability, which is one of the big plus'es of annotations in the first place.

Raystorm有一个很好的答案。我也不是规则的忠实粉丝。我做了类似的事情,除了我创建了以下实用程序类来提高可读性和可用性,这是注释的一大优点。

Add this utility class:

添加此实用程序类:

import org.junit.Assert;

public abstract class ExpectedRuntimeExceptionAsserter {

    private String expectedExceptionMessage;

    public ExpectedRuntimeExceptionAsserter(String expectedExceptionMessage) {
        this.expectedExceptionMessage = expectedExceptionMessage;
    }

    public final void run(){
        try{
            expectException();
            Assert.fail(String.format("Expected a RuntimeException '%s'", expectedExceptionMessage));
        } catch (RuntimeException e){
            Assert.assertEquals("RuntimeException caught, but unexpected message", expectedExceptionMessage, e.getMessage());
        }
    }

    protected abstract void expectException();

}

Then for my unit test, all I need is this code:

然后对于我的单元测试,我只需要以下代码:

@Test
public void verifyAnonymousUserCantAccessPrivilegedResourceTest(){
    new ExpectedRuntimeExceptionAsserter("anonymous user can't access privileged resource"){
        @Override
        protected void expectException() {
            throw new RuntimeException("anonymous user can't access privileged resource");
        }
    }.run(); //passes test; expected exception is caught, and this @Test returns normally as "Passed"
}

回答by Krzysztof Cislo

Actually, the best usage is with try/catch. Why? Because you can control the place where you expect the exception.

实际上,最好的用法是使用 try/catch。为什么?因为你可以控制你期望异常的地方。

Consider this example:

考虑这个例子:

@Test (expected = RuntimeException.class)
public void someTest() {
   // test preparation
   // actual test
}

What if one day the code is modified and test preparation will throw a RuntimeException? In that case actual test is not even tested and even if it doesn't throw any exception the test will pass.

如果有一天代码被修改并且测试准备会抛出一个 RuntimeException 怎么办?在这种情况下,甚至没有测试实际的测试,即使它没有抛出任何异常,测试也会通过。

That is why it is much better to use try/catch than to rely on the annotation.

这就是为什么使用 try/catch 比依赖注解要好得多的原因。

回答by whistling_marmot

Import the catch-exceptionlibrary, and use that. It's much cleaner than the ExpectedExceptionrule or a try-catch.

导入catch-exception库,并使用它。它比ExpectedException规则或try-catch.

Example form their docs:

以他们的文档为例:

import static com.googlecode.catchexception.CatchException.*;
import static com.googlecode.catchexception.apis.CatchExceptionHamcrestMatchers.*;

// given: an empty list
List myList = new ArrayList();

// when: we try to get the first element of the list
catchException(myList).get(1);

// then: we expect an IndexOutOfBoundsException with message "Index: 1, Size: 0"
assertThat(caughtException(),
  allOf(
    instanceOf(IndexOutOfBoundsException.class),
    hasMessage("Index: 1, Size: 0"),
    hasNoCause()
  )
);

回答by Addison Crump

I like user64141's answer but found that it could be more generalized. Here's my take:

我喜欢 user64141 的回答,但发现它可以更概括。这是我的看法:

public abstract class ExpectedThrowableAsserter implements Runnable {

    private final Class<? extends Throwable> throwableClass;
    private final String expectedExceptionMessage;

    protected ExpectedThrowableAsserter(Class<? extends Throwable> throwableClass, String expectedExceptionMessage) {
        this.throwableClass = throwableClass;
        this.expectedExceptionMessage = expectedExceptionMessage;
    }

    public final void run() {
        try {
            expectException();
        } catch (Throwable e) {
            assertTrue(String.format("Caught unexpected %s", e.getClass().getSimpleName()), throwableClass.isInstance(e));
            assertEquals(String.format("%s caught, but unexpected message", throwableClass.getSimpleName()), expectedExceptionMessage, e.getMessage());
            return;
        }
        fail(String.format("Expected %s, but no exception was thrown.", throwableClass.getSimpleName()));
    }

    protected abstract void expectException();

}

Note that leaving the "fail" statement within the try block causes the related assertion exception to be caught; using return within the catch statement prevents this.

请注意,在 try 块中保留“fail”语句会导致相关的断言异常被捕获;在 catch 语句中使用 return 可以防止这种情况。

回答by Johan Boberg

In JUnit 4.13 you can do:

在 JUnit 4.13 中,您可以执行以下操作:

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;

...

@Test
void exceptionTesting() {
  IllegalArgumentException exception = assertThrows(
    IllegalArgumentException.class, 
    () -> { throw new IllegalArgumentException("a message"); }
  );

  assertEquals("a message", exception.getMessage());
}

This also works in JUnit 5but with different imports:

这也适用于JUnit 5,但具有不同的导入:

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

...

回答by aasha

@Test (expectedExceptions = ValidationException.class, expectedExceptionsMessageRegExp = "This is not allowed")
public void testInvalidValidation() throws Exception{
     //test code
}