How can I use JUnit4 idiomatically to test that some code throws an exception?
我如何使用JUnit4的惯用法来测试一些代码抛出异常?
While I can certainly do something like this:
我当然可以这样做:
@Test
public void testFooThrowsIndexOutOfBoundsException() {
boolean thrown = false;
try {
foo.doStuff();
} catch (IndexOutOfBoundsException e) {
thrown = true;
}
assertTrue(thrown);
}
I recall that there is an annotation or an Assert.xyz or something that is far less kludgy and far more in-the-spirit of JUnit for these sorts of situations.
我记得有一个注释或断言。xyz之类的东西,在这种情况下,它更不笨,也更符合JUnit的精神。
30 个解决方案
#1
1808
JUnit 4 has support for this:
JUnit 4支持这一点:
@Test(expected = IndexOutOfBoundsException.class)
public void testIndexOutOfBoundsException() {
ArrayList emptyList = new ArrayList();
Object o = emptyList.get(0);
}
#2
1116
Edit Now that JUnit5 has released, the best option would be to use Assertions.assertThrows()
(see my other answer).
现在编辑JUnit5已经发布了,最好的选择是使用assertions.assert抛出()(查看我的另一个答案)。
If you haven't migrated to JUnit 5, but can use JUnit 4.7, you can use the ExpectedException
Rule:
如果您还没有迁移到JUnit 5,但是可以使用JUnit 4.7,那么您可以使用ExpectedException规则:
public class FooTest {
@Rule
public final ExpectedException exception = ExpectedException.none();
@Test
public void doStuffThrowsIndexOutOfBoundsException() {
Foo foo = new Foo();
exception.expect(IndexOutOfBoundsException.class);
foo.doStuff();
}
}
This is much better than @Test(expected=IndexOutOfBoundsException.class)
because the test will fail if IndexOutOfBoundsException
is thrown before foo.doStuff()
这比@Test(预期=IndexOutOfBoundsException.class)好得多,因为如果在foo.doStuff()之前抛出IndexOutOfBoundsException,测试将失败。
See this article for details
有关详细信息,请参阅本文。
#3
388
Be careful using expected exception, because it only asserts that the method threw that exception, not a particular line of code in the test.
要小心使用预期的异常,因为它只断言该方法抛出了那个异常,而不是测试中的特定代码行。
I tend to use this for testing parameter validation, because such methods are usually very simple, but more complex tests might better be served with:
我倾向于使用它来测试参数验证,因为这样的方法通常非常简单,但是更复杂的测试可能更好地使用:
try {
methodThatShouldThrow();
fail( "My method didn't throw when I expected it to" );
} catch (MyException expectedException) {
}
Apply judgement.
适用的判断。
#4
182
As answered before, there are many ways of dealing with exceptions in JUnit. But with Java 8 there is another one: using Lambda Expressions. With Lambda Expressions we can achieve a syntax like this:
如前所述,在JUnit中处理异常的方法有很多。但是对于Java 8,还有另外一个:使用Lambda表达式。使用Lambda表达式,我们可以实现这样的语法:
@Test
public void verifiesTypeAndMessage() {
assertThrown(new DummyService()::someMethod)
.isInstanceOf(RuntimeException.class)
.hasMessage("Runtime exception occurred")
.hasMessageStartingWith("Runtime")
.hasMessageEndingWith("occurred")
.hasMessageContaining("exception")
.hasNoCause();
}
assertThrown accepts a functional interface, whose instances can be created with lambda expressions, method references, or constructor references. assertThrown accepting that interface will expect and be ready to handle an exception.
assertcast接受一个功能接口,它的实例可以用lambda表达式、方法引用或构造函数引用创建。断言接受该接口将期望并准备好处理一个异常。
This is relatively simple yet powerful technique.
这是一种相对简单而又强大的技术。
Have a look at this blog post describing this technique: http://blog.codeleak.pl/2014/07/junit-testing-exception-with-java-8-and-lambda-expressions.html
看看这篇描述这种技术的博客文章:http://blog.codeleak.pl/2014/07/junit-test -exception-with-java-8 and lambda-expressions.html ?
The source code can be found here: https://github.com/kolorobot/unit-testing-demo/tree/master/src/test/java/com/github/kolorobot/exceptions/java8
可以在这里找到源代码:https://github.com/kolorobot/unit- testing-demo/tree/master/src/test/java/kolorobot/exceptions/java8。
Disclosure: I am the author of the blog and the project.
披露:我是博客和项目的作者。
#5
65
in junit, there are three ways to test exception.
在junit中,有三种方法来测试异常。
-
use the optional 'expected' attribute of Test annonation
使用测试annonation的可选“期望”属性。
@Test(expected = IndexOutOfBoundsException.class) public void testFooThrowsIndexOutOfBoundsException() { foo.doStuff(); }
-
use the ExpectedException rule
用ExpectedException法则
public class XxxTest { @Rule public ExpectedException thrown = ExpectedException.none(); @Test public void testFooThrowsIndexOutOfBoundsException() { thrown.expect(IndexOutOfBoundsException.class) //you can test the exception message like thrown.expectMessage("expected messages"); foo.doStuff(); } }
-
finally, you also can use the classic try/catch way widely used under junit 3 framework
最后,您还可以使用junit 3框架中广泛使用的经典try/catch方法。
@Test public void testFooThrowsIndexOutOfBoundsException() { try { foo.doStuff(); fail("expected exception was not occured."); } catch(IndexOutOfBoundsException e) { //if execution reaches here, //it indicates this exception was occured. //so we need not handle it. } }
-
so
所以
- the 1st way used when you only want test the type of exception
- 当您只需要测试异常类型时使用的第一种方法。
- the 2nd and 3rd way used when you want test exception message further
- 当您希望进一步测试异常消息时使用的第二和第三种方法。
- if you use junit 3, then the 3rd one is preferred.
- 如果使用junit 3,则首选第三个。
-
for more info, you can read this document for details.
有关更多信息,您可以阅读此文档了解详细信息。
#6
48
tl;dr
博士tl;
-
pre-JDK8 : I will recommend the old good
try
-catch
block.pre-JDK8:我推荐旧好的try-catch块。
-
post-JDK8 : Use AssertJ or custom lambdas to assert exceptional behaviour.
后jdk8:使用AssertJ或自定义lambdas来断言异常行为。
the long story
长故事
It is possible to write yourself a do it yourself try
-catch
block or use the JUnit tools (@Test(expected = ...)
or the @Rule ExpectedException
JUnit rule feature).
您可以自己编写自己的try-catch块或使用JUnit工具(@Test(期望=…)或@Rule ExpectedException JUnit规则特性)。
But these way are not so elegant and don't mix well readability wise with other tools.
但是这些方法并不那么优雅,也不能很好地与其他工具结合。
-
The
try
-catch
block you have to write the block around the tested behavior, and write the assertion in the catch block, that may be fine but many find taht this style interrupts the reading flow of a test. Also you need to write anAssert.fail
at the end of thetry
block otherwise the test may miss one side of the assertions ; PMD, findbugs or Sonar will spot such issues.try-catch块必须围绕测试的行为编写块,并在catch块中编写断言,这可能很好,但是很多人发现这种样式中断了测试的阅读流。你还需要写一个断言。在try块结束时失败,否则测试可能会错过断言的一方;PMD、findbugs或Sonar将会发现这些问题。
-
The
@Test(expected = ...)
feature is interesting as you can write less code and then writing this test is supposedly less prone to coding errors. But ths approach is lacking a some areas.@Test(expect =…)特性很有趣,因为您可以编写更少的代码,然后编写这个测试就不太容易出现编码错误。但这种方法缺乏一些领域。
- If the test needs to check additional things on the exception like the cause or the message (good exception messages are really important, having a precise exception type may not be enough).
- 如果测试需要检查异常,比如原因或消息(好的异常消息是非常重要的,但是有一个精确的异常类型可能还不够)。
-
Also as the expectation is placed around in the method, depending on how the tested code is written then the wrong part of the test code can throw the exception, leading to false positive test and I m not sure that PMD, findbugs or Sonar will give hints on such code.
也期望被放置的方法,这取决于测试代码编写错误的测试代码的一部分可以抛出的异常,导致假阳性测试和我不确定PMD,findbugs或声纳会提示这样的代码。
@Test(expected = WantedException.class) public void call2_should_throw_a_WantedException__not_call1() { // init tested tested.call1(); // may throw a WantedException // call to be actually tested tested.call2(); // the call that is supposed to raise an exception }
-
The
ExpectedException
rule is also an attempt to fix the previous caveats, but it feels a bit awkward to use as it uses an expectation style, EasyMock users knows very well this style. It might be convenient for some, but if you follow Behaviour Driven Development (BDD) or Arrange Act Assert (AAA) principles theExpectedException
rule won't fit in those writing style. Aside of that it may suffer from the same issue as the as the@Test
way, depending where you place the expectation.期望值规则也是一种修正之前的警告的尝试,但是当它使用一种期望样式时,它感觉有点笨拙,EasyMock用户非常清楚这种风格。这可能对某些人来说是很方便的,但是如果您遵循行为驱动开发(BDD)或安排Act Assert (AAA)原则,那么预期的使用规则将不适用于那些写作风格。除此之外,它可能会遭受与@Test方式相同的问题,这取决于您放置期望的位置。
@Rule ExpectedException thrown = ExpectedException.none() @Test public void call2_should_throw_a_WantedException__not_call1() { // expectations thrown.expect(WantedException.class); thrown.expectMessage("boom"); // init tested tested.call1(); // may throw a WantedException // call to be actually tested tested.call2(); // the call that is supposed to raise an exception }
Even the expected exception is placed before the test statement, it breaks your reading flow if the tests follow BDD or AAA.
即使在测试语句之前放置了预期的异常,如果测试遵循BDD或AAA,它也会破坏您的阅读流。
Also see this comment issue on JUnit of the author of
ExpectedException
.还可以看到这个注释问题,它是关于预期的作者的JUnit。
So these above options have all their load of caveats, and clearly not immune to coder errors.
以上这些选项都有大量的警告,而且显然不能对编码错误免疫。
-
There's a project I became aware after creating this answer that looks promising, it's catch-exception.
在创建了这个看起来很有前途的答案之后,我发现了一个项目,它是一个例外。
As the description of the project says, it let a coder write in a fluent line of code catching the exception and offer this exception for later assertion. And you can use any assertion library like Hamcrest or AssertJ.
正如该项目的描述所述,它让编码人员以流畅的代码编写捕获异常的代码,并为后面的断言提供这个异常。您可以使用像Hamcrest或AssertJ这样的断言库。
A rapid example taken from the home page :
来自主页的一个快速示例:
// given: an empty list List myList = new ArrayList(); // when: we try to get the first element of the list when(myList).get(1); // then: we expect an IndexOutOfBoundsException then(caughtException()) .isInstanceOf(IndexOutOfBoundsException.class) .hasMessage("Index: 1, Size: 0") .hasNoCause();
As you can see the code is really straightforward, you catch the exception on a specific line, the
then
API is an alias that will use AssertJ APIs (similar to usingassertThat(ex).hasNoCause()...
). At some point the project relied on FEST-Assert the ancestor of AssertJ. EDIT: It seems the project is brewing a Java 8 Lambdas support.您可以看到代码非常简单,在特定的行上捕获异常,然后API是使用AssertJ API的别名(类似于使用AssertJ . hasnocause()…)。在某个时候,这个项目依赖于FEST-Assert断言的祖先。编辑:似乎该项目正在酝酿Java 8 Lambdas支持。
Currently this library has two shortcomings :
目前该图书馆有两个不足之处:
-
At the time of this writing it is noteworthy to say this library is based on Mockito 1.x as it creates a mock of the tested object behind the scene. As Mockito is still not updated this library cannot work with final classes or final methods. And even if it was based on mockito 2 in the current version, this would require to declare a global mock maker (
inline-mock-maker
), something that may not what you want, as this mockmaker has different drawbacks that the regular mockmaker.在撰写本文时,值得注意的是,该库基于Mockito 1。当它在场景后面创建一个被测试对象的模拟时。由于Mockito还没有更新,这个库不能使用final类或final方法。即使它是基于当前版本的mockito 2,这也需要声明一个全球模拟制造商(inline-mock-maker),这可能不是你想要的,因为这个mockmaker有不同的缺点,就是普通的mockmaker。
-
It requires yet another test dependency.
它还需要另一个测试依赖项。
These issues won't apply once the library will support lambdas, however the functionality will be duplicated by AssertJ toolset.
一旦库支持lambdas,这些问题就不适用了,然而,AssertJ工具集将复制该功能。
Taking all into account if you don't want to use the catch-exception tool, I will recommend the old good way of the
try
-catch
block, at least up to the JDK7. And for JDK 8 users you might prefer to use AssertJ as it offers may more than just asserting exceptions.如果您不想使用catch-exception工具,那么我将建议您使用try-catch块的旧好的方法,至少是到JDK7。对于JDK 8用户,您可能更喜欢使用AssertJ,因为它提供的可能不仅仅是断言异常。
-
-
With the JDK8, lambdas enter the test scene, and they have proved to be an interesting way to assert exceptional behaviour. AssertJ has been updated to provide a nice fluent API to assert exceptional behaviour.
随着JDK8的出现,lambdas进入了测试场景,它们被证明是一种非常有趣的方式来断言异常行为。AssertJ已经被更新,以提供一个很好的API来断言异常行为。
And a sample test with AssertJ :
以及一个带有AssertJ的样本测试:
@Test public void test_exception_approach_1() { ... assertThatExceptionOfType(IOException.class) .isThrownBy(() -> someBadIOOperation()) .withMessage("boom!"); } @Test public void test_exception_approach_2() { ... assertThatThrownBy(() -> someBadIOOperation()) .isInstanceOf(Exception.class) .hasMessageContaining("boom"); } @Test public void test_exception_approach_3() { ... // when Throwable thrown = catchThrowable(() -> someBadIOOperation()); // then assertThat(thrown).isInstanceOf(Exception.class) .hasMessageContaining("boom"); }
-
With a near complete rewrite of JUnit 5, assertions have been improved a bit, they may prove interesting as an out of the box way to assert properly exception. But really the assertion API is still a bit poor, there's nothing outside
assertThrows
.在几乎完全重写了JUnit 5之后,断言已经得到了一些改进,它们可能会被证明是一种有趣的,因为它是一种可以断言正确异常的方法。但是断言API仍然有点糟糕,没有任何外部断言。
@Test @DisplayName("throws EmptyStackException when peeked") void throwsExceptionWhenPeeked() { Throwable t = assertThrows(EmptyStackException.class, () -> stack.peek()); Assertions.assertEquals("...", t.getMessage()); }
As you noticed
assertEquals
is still returningvoid
, and as such doesn't allow chaining assertions like AssertJ.当您注意到assertEquals仍然返回void时,因此不允许像AssertJ这样的断言。
Also if you remember name * with
Matcher
orAssert
, be prepared to meet the same * withAssertions
.如果您还记得与Matcher或Assert的名称冲突,请准备好面对与断言相同的冲突。
I'd like to conclude that today (2017-03-03) AssertJ's ease of use, discoverable API, rapid pace of development and as a de facto test dependency is the best solution with JDK8 regardless of the test framework (JUnit or not), prior JDKs should instead rely on try
-catch
blocks even if they feel clunky.
我想得出这样的结论:今天(2017-03-03)AssertJ的易用性,可发现API,快速的发展速度,作为一个事实上的测试依赖是最好的解决方案与JDK8不管测试框架(JUnit与否),之前jdk应该依靠即使他们觉得笨拙的try - catch块。
This answer has been copied from another question that don't have the same visibility, I am the same author.
这个答案是从另一个没有同样可见性的问题中复制过来的,我是同一个作者。
#7
31
BDD Style Solution: JUnit 4 + Catch Exception + AssertJ
@Test
public void testFooThrowsIndexOutOfBoundsException() {
when(foo).doStuff();
then(caughtException()).isInstanceOf(IndexOutOfBoundsException.class);
}
Source code
- https://gist.github.com/mariuszs/23f4e1e1857c28449b61
- https://gist.github.com/mariuszs/23f4e1e1857c28449b61
Dependencies
eu.codearte.catch-exception:catch-exception:1.3.3
#8
30
How about this: Catch a very general exception, make sure it makes it out of the catch block, then assert that the class of the exception is what you expect it to be. This assert will fail if a) the exception is of the wrong type (eg. if you got a Null Pointer instead) and b) the exception wasn't ever thrown.
这样:捕获一个非常普遍的异常,确保它从Catch块中取出,然后断言异常的类是您所期望的。这个断言将会失败,如果a)异常是错误的类型(如。如果你得到一个空指针,而b)这个异常不会被抛出。
public void testFooThrowsIndexOutOfBoundsException() {
Throwable e = null;
try {
foo.doStuff();
} catch (Throwable ex) {
e = ex;
}
assertTrue(e instanceof IndexOutOfBoundsException);
}
#9
30
Using an AssertJ assertion, which can be used alongside JUnit:
使用AssertJ断言,它可以与JUnit一起使用:
import static org.assertj.core.api.Assertions.*;
@Test
public void testFooThrowsIndexOutOfBoundsException() {
Foo foo = new Foo();
assertThatThrownBy(() -> foo.doStuff())
.isInstanceOf(IndexOutOfBoundsException.class);
}
It's better than @Test(expected=IndexOutOfBoundsException.class)
because it guarantees the expected line in the test threw the exception and lets you check more details about the exception, such as message, easier:
它比@Test(expect =IndexOutOfBoundsException.class)更好,因为它保证测试中的预期行抛出异常,并允许您检查关于异常的更多细节,例如消息,更容易:
assertThatThrownBy(() ->
{
throw new Exception("boom!");
})
.isInstanceOf(Exception.class)
.hasMessageContaining("boom");
Maven/Gradle instructions here.
Maven / Gradle指令。
#10
29
To solve the same problem I did set up a small project: http://code.google.com/p/catch-exception/
为了解决同样的问题,我确实设置了一个小项目:http://code.google.com/p/catch-exception/。
Using this little helper you would write
你可以用这个小帮手来写。
verifyException(foo, IndexOutOfBoundsException.class).doStuff();
This is less verbose than the ExpectedException rule of JUnit 4.7. In comparison to the solution provided by skaffman, you can specify in which line of code you expect the exception. I hope this helps.
这比JUnit 4.7的期望值规则更简洁。与skaffman提供的解决方案相比,您可以指定期望异常的代码行。我希望这可以帮助。
#11
21
Update: JUnit5 has an improvement for exceptions testing: assertThrows
.
更新:JUnit5对异常测试有改进:assert抛出。
following example is from: Junit 5 User Guide
以下示例来自:Junit 5用户指南。
@Test
void exceptionTesting() {
Throwable exception = assertThrows(IllegalArgumentException.class, () ->
{
throw new IllegalArgumentException("a message");
});
assertEquals("a message", exception.getMessage());
}
Original answer using JUnit 4.
使用JUnit 4的原始答案。
There are several ways to test that an exception is thrown. I have also discussed the below options in my post How to write great unit tests with JUnit
有几种方法可以测试抛出异常。我还在文章中讨论了如何用JUnit编写优秀的单元测试。
Set the expected
parameter @Test(expected = FileNotFoundException.class)
.
设置预期的参数@Test(期望= FileNotFoundException.class)。
@Test(expected = FileNotFoundException.class)
public void testReadFile() {
myClass.readFile("test.txt");
}
Using try
catch
使用试着抓住
public void testReadFile() {
try {
myClass.readFile("test.txt");
fail("Expected a FileNotFoundException to be thrown");
} catch (FileNotFoundException e) {
assertThat(e.getMessage(), is("The file test.txt does not exist!"));
}
}
Testing with ExpectedException
Rule.
测试与ExpectedException规则。
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void testReadFile() throws FileNotFoundException {
thrown.expect(FileNotFoundException.class);
thrown.expectMessage(startsWith("The file test.txt"));
myClass.readFile("test.txt");
}
You could read more about exceptions testing in JUnit4 wiki for Exception testing and bad.robot - Expecting Exceptions JUnit Rule.
在JUnit4 wiki中,您可以阅读更多关于异常测试的内容。机器人-期望异常JUnit规则。
#12
17
You can also do this:
你也可以这样做:
@Test
public void testFooThrowsIndexOutOfBoundsException() {
try {
foo.doStuff();
assert false;
} catch (IndexOutOfBoundsException e) {
assert true;
}
}
#13
17
Now that JUnit 5 has released, the best option is to use Assertions.assertThrows()
(see the Junit 5 User Guide).
现在,JUnit 5已经发布了,最好的选择是使用assertions.assert抛出()(参见JUnit 5用户指南)。
Here is an example that verifies an exception is thrown, and uses Truth to make assertions on the exception message:
下面是一个验证异常的示例,并使用Truth在异常消息上做出断言:
public class FooTest {
@Test
public void doStuffThrowsIndexOutOfBoundsException() {
Foo foo = new Foo();
IndexOutOfBoundsException e = assertThrows(
IndexOutOfBoundsException.class, foo::doStuff);
assertThat(e).hasMessageThat().contains("woops!");
}
}
The advantages over the approaches in the other answers are:
其他答案的优点是:
- Built into JUnit
- 内置JUnit
- You get a useful exception message if the code in the lambda doesn't throw an exception, and a stacktrace if it throws a different exception
- 如果lambda中的代码没有抛出异常,并且抛出一个不同的异常,则会得到一个有用的异常消息。
- Concise
- 简洁的
- Allows your tests to follow Arrange-Act-Assert
- 允许您的测试遵循排列- act - assert。
- You can precisely indicate what code you are expecting to throw the exception
- 您可以精确地指出您希望抛出异常的代码。
- You don't need to list the expected exception in the
throws
clause - 您不需要在抛出子句中列出预期的异常。
- You can use the assertion framework of your choice to make assertions about the caught exception
- 您可以使用您选择的断言框架来断言捕获的异常。
A similar method will be added to org.junit Assert
in JUnit 4.13.
一个类似的方法将被添加到org。junit 4.13中的junit断言。
#14
12
IMHO, the best way to check for exceptions in JUnit is the try/catch/fail/assert pattern:
在JUnit中检查异常的最好方法是try/catch/fail/assert模式:
// this try block should be as small as possible,
// as you want to make sure you only catch exceptions from your code
try {
sut.doThing();
fail(); // fail if this does not throw any exception
} catch(MyException e) { // only catch the exception you expect,
// otherwise you may catch an exception for a dependency unexpectedly
// a strong assertion on the message,
// in case the exception comes from anywhere an unexpected line of code,
// especially important if your checking IllegalArgumentExceptions
assertEquals("the message I get", e.getMessage());
}
The assertTrue
might be a bit strong for some people, so assertThat(e.getMessage(), containsString("the message");
might be preferable.
对于某些人来说,assertTrue可能有些强大,所以assertThat(. getmessage (), containsString(“消息”);可能是更可取的。
#15
10
JUnit 5 Solution
@Test
void testFooThrowsIndexOutOfBoundsException() {
Throwable exception = expectThrows( IndexOutOfBoundsException.class, foo::doStuff );
assertEquals( "some message", exception.getMessage() );
}
More Infos about JUnit 5 on http://junit.org/junit5/docs/current/user-guide/#writing-tests-assertions
更多关于JUnit 5的信息:http://junitorg/junit5/docs/current/userguide/# writing- tests-断言。
#16
9
I tried many of the methods here, but they were either complicated or didn't quite meet my requirements. In fact, one can write a helper method quite simply:
我在这里尝试了很多方法,但是它们要么复杂要么不完全符合我的要求。事实上,一个人可以很简单地写一个助手方法:
public class ExceptionAssertions {
public static void assertException(BlastContainer blastContainer ) {
boolean caughtException = false;
try {
blastContainer.test();
} catch( Exception e ) {
caughtException = true;
}
if( !caughtException ) {
throw new AssertionFailedError("exception expected to be thrown, but was not");
}
}
public static interface BlastContainer {
public void test() throws Exception;
}
}
Use it like this:
使用它是这样的:
assertException(new BlastContainer() {
@Override
public void test() throws Exception {
doSomethingThatShouldExceptHere();
}
});
Zero dependencies: no need for mockito, no need powermock; and works just fine with final classes.
零依赖:不需要mockito,不需要powermock;在最后的课上也可以。
#18
7
Java 8 solution
If you would like a solution which:
如果你想要一个解决方案:
- Utilizes Java 8 lambdas
- 利用Java 8λ
- Does not depend on any JUnit magic
- 不依赖于任何JUnit魔法吗?
- Allows you to check for multiple exceptions within a single test method
- 允许您在单个测试方法中检查多个异常。
- Checks for an exception being thrown by a specific set of lines within your test method instead of any unknown line in the entire test method
- 检查您的测试方法中特定的一行代码所抛出的异常,而不是在整个测试方法中使用任何未知的行。
- Yields the actual exception object that was thrown so that you can further examine it
- 生成抛出的实际异常对象,以便您可以进一步检查它。
Here is a utility function that I wrote:
这是我写的一个效用函数
public final <T extends Throwable> T expectException( Class<T> exceptionClass, Runnable runnable )
{
try
{
runnable.run();
}
catch( Throwable throwable )
{
if( throwable instanceof AssertionError && throwable.getCause() != null )
throwable = throwable.getCause(); //allows "assert x != null : new IllegalArgumentException();"
assert exceptionClass.isInstance( throwable ) : throwable; //exception of the wrong kind was thrown.
assert throwable.getClass() == exceptionClass : throwable; //exception thrown was a subclass, but not the exact class, expected.
@SuppressWarnings( "unchecked" )
T result = (T)throwable;
return result;
}
assert false; //expected exception was not thrown.
return null; //to keep the compiler happy.
}
(摘自我的博客)
Use it as follows:
使用如下:
@Test
public void testThrows()
{
RuntimeException e = expectException( RuntimeException.class, () ->
{
throw new RuntimeException( "fail!" );
} );
assert e.getMessage().equals( "fail!" );
}
#19
6
In my case I always get RuntimeException from db, but messages differ. And exception need to be handled respectively. Here is how I tested it:
在我的例子中,我总是从db获得RuntimeException,但是消息不同。异常需要分别处理。下面是我的测试方法:
@Test
public void testThrowsExceptionWhenWrongSku() {
// Given
String articleSimpleSku = "999-999";
int amountOfTransactions = 1;
Exception exception = null;
// When
try {
createNInboundTransactionsForSku(amountOfTransactions, articleSimpleSku);
} catch (RuntimeException e) {
exception = e;
}
// Then
shouldValidateThrowsExceptionWithMessage(exception, MESSAGE_NON_EXISTENT_SKU);
}
private void shouldValidateThrowsExceptionWithMessage(final Exception e, final String message) {
assertNotNull(e);
assertTrue(e.getMessage().contains(message));
}
#20
5
In JUnit 4 or later you can test the exceptions as follows
在JUnit 4中或稍后,您可以测试以下的异常。
@Rule
public ExpectedException exceptions = ExpectedException.none();
this provides a lot of features which can be used to improve our JUnit tests.
If you see the below example I am testing 3 things on the exception.
这提供了许多可以用来改进JUnit测试的特性。如果您看到下面的示例,我正在测试异常中的3个东西。
- The Type of exception thrown
- 抛出的异常类型。
- The exception Message
- 异常消息
- The cause of the exception
- 例外的原因。
public class MyTest {
@Rule
public ExpectedException exceptions = ExpectedException.none();
ClassUnderTest classUnderTest;
@Before
public void setUp() throws Exception {
classUnderTest = new ClassUnderTest();
}
@Test
public void testAppleisSweetAndRed() throws Exception {
exceptions.expect(Exception.class);
exceptions.expectMessage("this is the exception message");
exceptions.expectCause(Matchers.<Throwable>equalTo(exceptionCause));
classUnderTest.methodUnderTest("param1", "param2");
}
}
#21
5
We can use an assertion fail after the method that must return an exception:
在必须返回异常的方法之后,我们可以使用断言失败:
try{
methodThatThrowMyException();
Assert.fail("MyException is not thrown !");
} catch (final Exception exception) {
// Verify if the thrown exception is instance of MyException, otherwise throws an assert failure
assertTrue(exception instanceof MyException, "An exception other than MyException is thrown !");
// In case of verifying the error message
MyException myException = (MyException) exception;
assertEquals("EXPECTED ERROR MESSAGE", myException.getMessage());
}
#22
4
Additionally to what NamShubWriter has said, make sure that:
另外,NamShubWriter曾说过,要确保:
- The ExpectedException instance is public (Related Question)
- 期望的实例是公开的(相关的问题)
- The ExpectedException isn't instantiated in say, the @Before method. This post clearly explains all the intricacies of JUnit's order of execution.
- 在@Before方法中没有实例化期望。这篇文章清楚地解释了JUnit执行顺序的所有复杂性。
Do not do this:
不要这样做:
@Rule
public ExpectedException expectedException;
@Before
public void setup()
{
expectedException = ExpectedException.none();
}
Finally, this blog post clearly illustrates how to assert that a certain exception is thrown.
最后,这篇博文清楚地说明了如何断言某个异常被抛出。
#23
4
Just make a Matcher that can be turned off and on, like this:
只要做一个可以关闭的Matcher,就像这样:
public class ExceptionMatcher extends BaseMatcher<Throwable> {
private boolean active = true;
private Class<? extends Throwable> throwable;
public ExceptionMatcher(Class<? extends Throwable> throwable) {
this.throwable = throwable;
}
public void on() {
this.active = true;
}
public void off() {
this.active = false;
}
@Override
public boolean matches(Object object) {
return active && throwable.isAssignableFrom(object.getClass());
}
@Override
public void describeTo(Description description) {
description.appendText("not the covered exception type");
}
}
To use it:
使用它:
add public ExpectedException exception = ExpectedException.none();
, then:
添加公众期望的异常=期望的,没有();
ExceptionMatcher exMatch = new ExceptionMatcher(MyException.class);
exception.expect(exMatch);
someObject.somethingThatThrowsMyException();
exMatch.off();
#24
2
The most flexible and elegant answer for Junit 4 I found in the Mkyoung blog. It have the flexibility of the try/catch using the @Rule. I like this approach because I need to read specific attributes of a customized exception.
我在Mkyoung博客中找到了对Junit 4最灵活和最优雅的回答。它具有使用@Rule的try/catch的灵活性。我喜欢这种方法,因为我需要读取定制异常的特定属性。
package com.mkyong;
import com.mkyong.examples.CustomerService;
import com.mkyong.examples.exception.NameNotFoundException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.hasProperty;
public class Exception3Test {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void testNameNotFoundException() throws NameNotFoundException {
//test specific type of exception
thrown.expect(NameNotFoundException.class);
//test message
thrown.expectMessage(is("Name is empty!"));
//test detail
thrown.expect(hasProperty("errCode")); //make sure getters n setters are defined.
thrown.expect(hasProperty("errCode", is(666)));
CustomerService cust = new CustomerService();
cust.findByName("");
}
}
#25
1
Take for example, you want to write Junit for below mentioned code fragment
例如,您需要为下面提到的代码片段编写Junit。
public int divideByZeroDemo(int a,int b){
return a/b;
}
public void exceptionWithMessage(String [] arr){
throw new ArrayIndexOutOfBoundsException("Array is out of bound");
}
The above code is to test for some unknown exception that may occur and the below one is to assert some exception with custom message.
上面的代码是为了测试可能发生的未知异常,下面的代码是通过自定义消息断言某个异常。
@Rule
public ExpectedException exception=ExpectedException.none();
private Demo demo;
@Before
public void setup(){
demo=new Demo();
}
@Test(expected=ArithmeticException.class)
public void testIfItThrowsAnyException() {
demo.divideByZeroDemo(5, 0);
}
@Test
public void testExceptionWithMessage(){
exception.expectMessage("Array is out of bound");
exception.expect(ArrayIndexOutOfBoundsException.class);
demo.exceptionWithMessage(new String[]{"This","is","a","demo"});
}
#26
0
With Java 8 you can create a method taking a code to check and expected exception as parameters:
使用Java 8,您可以创建一个方法,使用代码检查和预期的异常作为参数:
private void expectException(Runnable r, Class<?> clazz) {
try {
r.run();
fail("Expected: " + clazz.getSimpleName() + " but not thrown");
} catch (Exception e) {
if (!clazz.isInstance(e)) fail("Expected: " + clazz.getSimpleName() + " but " + e.getClass().getSimpleName() + " found", e);
}
}
and then inside your test:
然后在你的测试中:
expectException(() -> list.sublist(0, 2).get(2), IndexOutOfBoundsException.class);
Benefits:
好处:
- not relying on any library
- 不依赖任何图书馆。
- localised check - more precise and allows to have multiple assertions like this within one test if needed
- 本地化检查——更精确,如果需要,允许在一个测试中有多个这样的断言。
- easy to use
- 易于使用的
#27
0
My solution using Java 8 lambdas:
我的解决方案使用Java 8 lambdas:
public static <T extends Throwable> T assertThrows(Class<T> expected, ThrowingRunnable action) throws Throwable {
try {
action.run();
Assert.fail("Did not throw expected " + expected.getSimpleName());
return null; // never actually
} catch (Throwable actual) {
if (!expected.isAssignableFrom(actual.getClass())) { // runtime '!(actual instanceof expected)'
System.err.println("Threw " + actual.getClass().getSimpleName()
+ ", which is not a subtype of expected "
+ expected.getSimpleName());
throw actual; // throw the unexpected Throwable for maximum transparency
} else {
return (T) actual; // return the expected Throwable for further examination
}
}
}
You have to define a FunctionalInterface, because Runnable
doesn't declare the required throws
.
您必须定义一个FunctionalInterface,因为Runnable没有声明所需的抛出。
@FunctionalInterface
public interface ThrowingRunnable {
void run() throws Throwable;
}
The method can be used as follows:
该方法可采用如下方法:
class CustomException extends Exception {
public final String message;
public CustomException(final String message) { this.message = message;}
}
CustomException e = assertThrows(CustomException.class, () -> {
throw new CustomException("Lorem Ipsum");
});
assertEquals("Lorem Ipsum", e.message);
#28
0
There are two ways of writing test case
编写测试用例有两种方法。
- Annotate the test with the exception which is thrown by the method. Something like this
@Test(expected = IndexOutOfBoundsException.class)
- 用方法抛出的异常对测试进行注释。类似于这个@Test(预期= IndexOutOfBoundsException.class)
-
You can simply catch the exception in the test class using the try catch block and assert on the message that is thrown from the method in test class.
您可以使用try catch块在测试类中捕获异常,并在测试类中从方法抛出的消息上断言。
try{ } catch(exception to be thrown from method e) { assertEquals("message", e.getmessage()); }
I hope this answers your query Happy learning...
我希望这能回答您的查询愉快的学习……
#29
0
The one-way exchange-pattern does not seem to be supported any more by the HTTP Endpoint.latest Anypoint Studio version.
https://1.gravatar.com/avatar/42d13a27f6019502ab66b6aa820b10ea?s=50&d=identicon&r=G' class='avatar avatar-50' height='50' width='50' /> http://synq.cloud/' rel='external nofollow' class='url'>Mathews says:
单向交换模式似乎不再受HTTP端点的支持。最新Anypoint Studio版本。https://1.gravatar.com/avatar/42d13a27f6019502ab66b6aa820b10ea?s=50&d= 'avatar avatar-50' height='50' width='50' /> http://synq。cloud/' rel='外部nofollow' class='url'>Mathews说:
Changed Expression #[message.inboundProperties.’http.request.uri’ != ‘/favicon.ico’] in 5.1.0 to make it work expression=”#[message.inboundProperties.’http.request’ !=’/favicon.ico’]” with Anypoint Studio 5.2.0 version expression=”#[message.inboundProperties[‘http.request.path’] != ‘/favicon.ico’]”
改变表达式# http.request[message.inboundProperties。”。uri /图标“! =”。在5.1.0中,使其工作表达式= " #[message.inboundProperties. " http。请求“! =”/图标。" Anypoint Studio 5.2.0版本的表达式= " #[message.inboundProperties[' http.request]。路径']! = ' / ico)”位于
Hope it helps!
希望它可以帮助!
Cheers,
欢呼,
#30
-1
I wanted to comment with my solution to this problem, which avoided needing any of the exception related JUnit code.
我想对这个问题的解决方案进行注释,它避免了需要任何异常相关的JUnit代码。
I used assertTrue(boolean) combined with try/catch to look for my expected exception to be thrown. Here's an example:
我使用了assertTrue(boolean)结合try/catch来查找要抛出的预期异常。这里有一个例子:
public void testConstructor() {
boolean expectedExceptionThrown;
try {
// Call constructor with bad arguments
double a = 1;
double b = 2;
double c = a + b; // In my example, this is an invalid option for c
new Triangle(a, b, c);
expectedExceptionThrown = false; // because it successfully constructed the object
}
catch(IllegalArgumentException e) {
expectedExceptionThrown = true; // because I'm in this catch block
}
catch(Exception e) {
expectedExceptionThrown = false; // because it threw an exception but not the one expected
}
assertTrue(expectedExceptionThrown);
}
#1
1808
JUnit 4 has support for this:
JUnit 4支持这一点:
@Test(expected = IndexOutOfBoundsException.class)
public void testIndexOutOfBoundsException() {
ArrayList emptyList = new ArrayList();
Object o = emptyList.get(0);
}
#2
1116
Edit Now that JUnit5 has released, the best option would be to use Assertions.assertThrows()
(see my other answer).
现在编辑JUnit5已经发布了,最好的选择是使用assertions.assert抛出()(查看我的另一个答案)。
If you haven't migrated to JUnit 5, but can use JUnit 4.7, you can use the ExpectedException
Rule:
如果您还没有迁移到JUnit 5,但是可以使用JUnit 4.7,那么您可以使用ExpectedException规则:
public class FooTest {
@Rule
public final ExpectedException exception = ExpectedException.none();
@Test
public void doStuffThrowsIndexOutOfBoundsException() {
Foo foo = new Foo();
exception.expect(IndexOutOfBoundsException.class);
foo.doStuff();
}
}
This is much better than @Test(expected=IndexOutOfBoundsException.class)
because the test will fail if IndexOutOfBoundsException
is thrown before foo.doStuff()
这比@Test(预期=IndexOutOfBoundsException.class)好得多,因为如果在foo.doStuff()之前抛出IndexOutOfBoundsException,测试将失败。
See this article for details
有关详细信息,请参阅本文。
#3
388
Be careful using expected exception, because it only asserts that the method threw that exception, not a particular line of code in the test.
要小心使用预期的异常,因为它只断言该方法抛出了那个异常,而不是测试中的特定代码行。
I tend to use this for testing parameter validation, because such methods are usually very simple, but more complex tests might better be served with:
我倾向于使用它来测试参数验证,因为这样的方法通常非常简单,但是更复杂的测试可能更好地使用:
try {
methodThatShouldThrow();
fail( "My method didn't throw when I expected it to" );
} catch (MyException expectedException) {
}
Apply judgement.
适用的判断。
#4
182
As answered before, there are many ways of dealing with exceptions in JUnit. But with Java 8 there is another one: using Lambda Expressions. With Lambda Expressions we can achieve a syntax like this:
如前所述,在JUnit中处理异常的方法有很多。但是对于Java 8,还有另外一个:使用Lambda表达式。使用Lambda表达式,我们可以实现这样的语法:
@Test
public void verifiesTypeAndMessage() {
assertThrown(new DummyService()::someMethod)
.isInstanceOf(RuntimeException.class)
.hasMessage("Runtime exception occurred")
.hasMessageStartingWith("Runtime")
.hasMessageEndingWith("occurred")
.hasMessageContaining("exception")
.hasNoCause();
}
assertThrown accepts a functional interface, whose instances can be created with lambda expressions, method references, or constructor references. assertThrown accepting that interface will expect and be ready to handle an exception.
assertcast接受一个功能接口,它的实例可以用lambda表达式、方法引用或构造函数引用创建。断言接受该接口将期望并准备好处理一个异常。
This is relatively simple yet powerful technique.
这是一种相对简单而又强大的技术。
Have a look at this blog post describing this technique: http://blog.codeleak.pl/2014/07/junit-testing-exception-with-java-8-and-lambda-expressions.html
看看这篇描述这种技术的博客文章:http://blog.codeleak.pl/2014/07/junit-test -exception-with-java-8 and lambda-expressions.html ?
The source code can be found here: https://github.com/kolorobot/unit-testing-demo/tree/master/src/test/java/com/github/kolorobot/exceptions/java8
可以在这里找到源代码:https://github.com/kolorobot/unit- testing-demo/tree/master/src/test/java/kolorobot/exceptions/java8。
Disclosure: I am the author of the blog and the project.
披露:我是博客和项目的作者。
#5
65
in junit, there are three ways to test exception.
在junit中,有三种方法来测试异常。
-
use the optional 'expected' attribute of Test annonation
使用测试annonation的可选“期望”属性。
@Test(expected = IndexOutOfBoundsException.class) public void testFooThrowsIndexOutOfBoundsException() { foo.doStuff(); }
-
use the ExpectedException rule
用ExpectedException法则
public class XxxTest { @Rule public ExpectedException thrown = ExpectedException.none(); @Test public void testFooThrowsIndexOutOfBoundsException() { thrown.expect(IndexOutOfBoundsException.class) //you can test the exception message like thrown.expectMessage("expected messages"); foo.doStuff(); } }
-
finally, you also can use the classic try/catch way widely used under junit 3 framework
最后,您还可以使用junit 3框架中广泛使用的经典try/catch方法。
@Test public void testFooThrowsIndexOutOfBoundsException() { try { foo.doStuff(); fail("expected exception was not occured."); } catch(IndexOutOfBoundsException e) { //if execution reaches here, //it indicates this exception was occured. //so we need not handle it. } }
-
so
所以
- the 1st way used when you only want test the type of exception
- 当您只需要测试异常类型时使用的第一种方法。
- the 2nd and 3rd way used when you want test exception message further
- 当您希望进一步测试异常消息时使用的第二和第三种方法。
- if you use junit 3, then the 3rd one is preferred.
- 如果使用junit 3,则首选第三个。
-
for more info, you can read this document for details.
有关更多信息,您可以阅读此文档了解详细信息。
#6
48
tl;dr
博士tl;
-
pre-JDK8 : I will recommend the old good
try
-catch
block.pre-JDK8:我推荐旧好的try-catch块。
-
post-JDK8 : Use AssertJ or custom lambdas to assert exceptional behaviour.
后jdk8:使用AssertJ或自定义lambdas来断言异常行为。
the long story
长故事
It is possible to write yourself a do it yourself try
-catch
block or use the JUnit tools (@Test(expected = ...)
or the @Rule ExpectedException
JUnit rule feature).
您可以自己编写自己的try-catch块或使用JUnit工具(@Test(期望=…)或@Rule ExpectedException JUnit规则特性)。
But these way are not so elegant and don't mix well readability wise with other tools.
但是这些方法并不那么优雅,也不能很好地与其他工具结合。
-
The
try
-catch
block you have to write the block around the tested behavior, and write the assertion in the catch block, that may be fine but many find taht this style interrupts the reading flow of a test. Also you need to write anAssert.fail
at the end of thetry
block otherwise the test may miss one side of the assertions ; PMD, findbugs or Sonar will spot such issues.try-catch块必须围绕测试的行为编写块,并在catch块中编写断言,这可能很好,但是很多人发现这种样式中断了测试的阅读流。你还需要写一个断言。在try块结束时失败,否则测试可能会错过断言的一方;PMD、findbugs或Sonar将会发现这些问题。
-
The
@Test(expected = ...)
feature is interesting as you can write less code and then writing this test is supposedly less prone to coding errors. But ths approach is lacking a some areas.@Test(expect =…)特性很有趣,因为您可以编写更少的代码,然后编写这个测试就不太容易出现编码错误。但这种方法缺乏一些领域。
- If the test needs to check additional things on the exception like the cause or the message (good exception messages are really important, having a precise exception type may not be enough).
- 如果测试需要检查异常,比如原因或消息(好的异常消息是非常重要的,但是有一个精确的异常类型可能还不够)。
-
Also as the expectation is placed around in the method, depending on how the tested code is written then the wrong part of the test code can throw the exception, leading to false positive test and I m not sure that PMD, findbugs or Sonar will give hints on such code.
也期望被放置的方法,这取决于测试代码编写错误的测试代码的一部分可以抛出的异常,导致假阳性测试和我不确定PMD,findbugs或声纳会提示这样的代码。
@Test(expected = WantedException.class) public void call2_should_throw_a_WantedException__not_call1() { // init tested tested.call1(); // may throw a WantedException // call to be actually tested tested.call2(); // the call that is supposed to raise an exception }
-
The
ExpectedException
rule is also an attempt to fix the previous caveats, but it feels a bit awkward to use as it uses an expectation style, EasyMock users knows very well this style. It might be convenient for some, but if you follow Behaviour Driven Development (BDD) or Arrange Act Assert (AAA) principles theExpectedException
rule won't fit in those writing style. Aside of that it may suffer from the same issue as the as the@Test
way, depending where you place the expectation.期望值规则也是一种修正之前的警告的尝试,但是当它使用一种期望样式时,它感觉有点笨拙,EasyMock用户非常清楚这种风格。这可能对某些人来说是很方便的,但是如果您遵循行为驱动开发(BDD)或安排Act Assert (AAA)原则,那么预期的使用规则将不适用于那些写作风格。除此之外,它可能会遭受与@Test方式相同的问题,这取决于您放置期望的位置。
@Rule ExpectedException thrown = ExpectedException.none() @Test public void call2_should_throw_a_WantedException__not_call1() { // expectations thrown.expect(WantedException.class); thrown.expectMessage("boom"); // init tested tested.call1(); // may throw a WantedException // call to be actually tested tested.call2(); // the call that is supposed to raise an exception }
Even the expected exception is placed before the test statement, it breaks your reading flow if the tests follow BDD or AAA.
即使在测试语句之前放置了预期的异常,如果测试遵循BDD或AAA,它也会破坏您的阅读流。
Also see this comment issue on JUnit of the author of
ExpectedException
.还可以看到这个注释问题,它是关于预期的作者的JUnit。
So these above options have all their load of caveats, and clearly not immune to coder errors.
以上这些选项都有大量的警告,而且显然不能对编码错误免疫。
-
There's a project I became aware after creating this answer that looks promising, it's catch-exception.
在创建了这个看起来很有前途的答案之后,我发现了一个项目,它是一个例外。
As the description of the project says, it let a coder write in a fluent line of code catching the exception and offer this exception for later assertion. And you can use any assertion library like Hamcrest or AssertJ.
正如该项目的描述所述,它让编码人员以流畅的代码编写捕获异常的代码,并为后面的断言提供这个异常。您可以使用像Hamcrest或AssertJ这样的断言库。
A rapid example taken from the home page :
来自主页的一个快速示例:
// given: an empty list List myList = new ArrayList(); // when: we try to get the first element of the list when(myList).get(1); // then: we expect an IndexOutOfBoundsException then(caughtException()) .isInstanceOf(IndexOutOfBoundsException.class) .hasMessage("Index: 1, Size: 0") .hasNoCause();
As you can see the code is really straightforward, you catch the exception on a specific line, the
then
API is an alias that will use AssertJ APIs (similar to usingassertThat(ex).hasNoCause()...
). At some point the project relied on FEST-Assert the ancestor of AssertJ. EDIT: It seems the project is brewing a Java 8 Lambdas support.您可以看到代码非常简单,在特定的行上捕获异常,然后API是使用AssertJ API的别名(类似于使用AssertJ . hasnocause()…)。在某个时候,这个项目依赖于FEST-Assert断言的祖先。编辑:似乎该项目正在酝酿Java 8 Lambdas支持。
Currently this library has two shortcomings :
目前该图书馆有两个不足之处:
-
At the time of this writing it is noteworthy to say this library is based on Mockito 1.x as it creates a mock of the tested object behind the scene. As Mockito is still not updated this library cannot work with final classes or final methods. And even if it was based on mockito 2 in the current version, this would require to declare a global mock maker (
inline-mock-maker
), something that may not what you want, as this mockmaker has different drawbacks that the regular mockmaker.在撰写本文时,值得注意的是,该库基于Mockito 1。当它在场景后面创建一个被测试对象的模拟时。由于Mockito还没有更新,这个库不能使用final类或final方法。即使它是基于当前版本的mockito 2,这也需要声明一个全球模拟制造商(inline-mock-maker),这可能不是你想要的,因为这个mockmaker有不同的缺点,就是普通的mockmaker。
-
It requires yet another test dependency.
它还需要另一个测试依赖项。
These issues won't apply once the library will support lambdas, however the functionality will be duplicated by AssertJ toolset.
一旦库支持lambdas,这些问题就不适用了,然而,AssertJ工具集将复制该功能。
Taking all into account if you don't want to use the catch-exception tool, I will recommend the old good way of the
try
-catch
block, at least up to the JDK7. And for JDK 8 users you might prefer to use AssertJ as it offers may more than just asserting exceptions.如果您不想使用catch-exception工具,那么我将建议您使用try-catch块的旧好的方法,至少是到JDK7。对于JDK 8用户,您可能更喜欢使用AssertJ,因为它提供的可能不仅仅是断言异常。
-
-
With the JDK8, lambdas enter the test scene, and they have proved to be an interesting way to assert exceptional behaviour. AssertJ has been updated to provide a nice fluent API to assert exceptional behaviour.
随着JDK8的出现,lambdas进入了测试场景,它们被证明是一种非常有趣的方式来断言异常行为。AssertJ已经被更新,以提供一个很好的API来断言异常行为。
And a sample test with AssertJ :
以及一个带有AssertJ的样本测试:
@Test public void test_exception_approach_1() { ... assertThatExceptionOfType(IOException.class) .isThrownBy(() -> someBadIOOperation()) .withMessage("boom!"); } @Test public void test_exception_approach_2() { ... assertThatThrownBy(() -> someBadIOOperation()) .isInstanceOf(Exception.class) .hasMessageContaining("boom"); } @Test public void test_exception_approach_3() { ... // when Throwable thrown = catchThrowable(() -> someBadIOOperation()); // then assertThat(thrown).isInstanceOf(Exception.class) .hasMessageContaining("boom"); }
-
With a near complete rewrite of JUnit 5, assertions have been improved a bit, they may prove interesting as an out of the box way to assert properly exception. But really the assertion API is still a bit poor, there's nothing outside
assertThrows
.在几乎完全重写了JUnit 5之后,断言已经得到了一些改进,它们可能会被证明是一种有趣的,因为它是一种可以断言正确异常的方法。但是断言API仍然有点糟糕,没有任何外部断言。
@Test @DisplayName("throws EmptyStackException when peeked") void throwsExceptionWhenPeeked() { Throwable t = assertThrows(EmptyStackException.class, () -> stack.peek()); Assertions.assertEquals("...", t.getMessage()); }
As you noticed
assertEquals
is still returningvoid
, and as such doesn't allow chaining assertions like AssertJ.当您注意到assertEquals仍然返回void时,因此不允许像AssertJ这样的断言。
Also if you remember name * with
Matcher
orAssert
, be prepared to meet the same * withAssertions
.如果您还记得与Matcher或Assert的名称冲突,请准备好面对与断言相同的冲突。
I'd like to conclude that today (2017-03-03) AssertJ's ease of use, discoverable API, rapid pace of development and as a de facto test dependency is the best solution with JDK8 regardless of the test framework (JUnit or not), prior JDKs should instead rely on try
-catch
blocks even if they feel clunky.
我想得出这样的结论:今天(2017-03-03)AssertJ的易用性,可发现API,快速的发展速度,作为一个事实上的测试依赖是最好的解决方案与JDK8不管测试框架(JUnit与否),之前jdk应该依靠即使他们觉得笨拙的try - catch块。
This answer has been copied from another question that don't have the same visibility, I am the same author.
这个答案是从另一个没有同样可见性的问题中复制过来的,我是同一个作者。
#7
31
BDD Style Solution: JUnit 4 + Catch Exception + AssertJ
@Test
public void testFooThrowsIndexOutOfBoundsException() {
when(foo).doStuff();
then(caughtException()).isInstanceOf(IndexOutOfBoundsException.class);
}
Source code
- https://gist.github.com/mariuszs/23f4e1e1857c28449b61
- https://gist.github.com/mariuszs/23f4e1e1857c28449b61
Dependencies
eu.codearte.catch-exception:catch-exception:1.3.3
#8
30
How about this: Catch a very general exception, make sure it makes it out of the catch block, then assert that the class of the exception is what you expect it to be. This assert will fail if a) the exception is of the wrong type (eg. if you got a Null Pointer instead) and b) the exception wasn't ever thrown.
这样:捕获一个非常普遍的异常,确保它从Catch块中取出,然后断言异常的类是您所期望的。这个断言将会失败,如果a)异常是错误的类型(如。如果你得到一个空指针,而b)这个异常不会被抛出。
public void testFooThrowsIndexOutOfBoundsException() {
Throwable e = null;
try {
foo.doStuff();
} catch (Throwable ex) {
e = ex;
}
assertTrue(e instanceof IndexOutOfBoundsException);
}
#9
30
Using an AssertJ assertion, which can be used alongside JUnit:
使用AssertJ断言,它可以与JUnit一起使用:
import static org.assertj.core.api.Assertions.*;
@Test
public void testFooThrowsIndexOutOfBoundsException() {
Foo foo = new Foo();
assertThatThrownBy(() -> foo.doStuff())
.isInstanceOf(IndexOutOfBoundsException.class);
}
It's better than @Test(expected=IndexOutOfBoundsException.class)
because it guarantees the expected line in the test threw the exception and lets you check more details about the exception, such as message, easier:
它比@Test(expect =IndexOutOfBoundsException.class)更好,因为它保证测试中的预期行抛出异常,并允许您检查关于异常的更多细节,例如消息,更容易:
assertThatThrownBy(() ->
{
throw new Exception("boom!");
})
.isInstanceOf(Exception.class)
.hasMessageContaining("boom");
Maven/Gradle instructions here.
Maven / Gradle指令。
#10
29
To solve the same problem I did set up a small project: http://code.google.com/p/catch-exception/
为了解决同样的问题,我确实设置了一个小项目:http://code.google.com/p/catch-exception/。
Using this little helper you would write
你可以用这个小帮手来写。
verifyException(foo, IndexOutOfBoundsException.class).doStuff();
This is less verbose than the ExpectedException rule of JUnit 4.7. In comparison to the solution provided by skaffman, you can specify in which line of code you expect the exception. I hope this helps.
这比JUnit 4.7的期望值规则更简洁。与skaffman提供的解决方案相比,您可以指定期望异常的代码行。我希望这可以帮助。
#11
21
Update: JUnit5 has an improvement for exceptions testing: assertThrows
.
更新:JUnit5对异常测试有改进:assert抛出。
following example is from: Junit 5 User Guide
以下示例来自:Junit 5用户指南。
@Test
void exceptionTesting() {
Throwable exception = assertThrows(IllegalArgumentException.class, () ->
{
throw new IllegalArgumentException("a message");
});
assertEquals("a message", exception.getMessage());
}
Original answer using JUnit 4.
使用JUnit 4的原始答案。
There are several ways to test that an exception is thrown. I have also discussed the below options in my post How to write great unit tests with JUnit
有几种方法可以测试抛出异常。我还在文章中讨论了如何用JUnit编写优秀的单元测试。
Set the expected
parameter @Test(expected = FileNotFoundException.class)
.
设置预期的参数@Test(期望= FileNotFoundException.class)。
@Test(expected = FileNotFoundException.class)
public void testReadFile() {
myClass.readFile("test.txt");
}
Using try
catch
使用试着抓住
public void testReadFile() {
try {
myClass.readFile("test.txt");
fail("Expected a FileNotFoundException to be thrown");
} catch (FileNotFoundException e) {
assertThat(e.getMessage(), is("The file test.txt does not exist!"));
}
}
Testing with ExpectedException
Rule.
测试与ExpectedException规则。
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void testReadFile() throws FileNotFoundException {
thrown.expect(FileNotFoundException.class);
thrown.expectMessage(startsWith("The file test.txt"));
myClass.readFile("test.txt");
}
You could read more about exceptions testing in JUnit4 wiki for Exception testing and bad.robot - Expecting Exceptions JUnit Rule.
在JUnit4 wiki中,您可以阅读更多关于异常测试的内容。机器人-期望异常JUnit规则。
#12
17
You can also do this:
你也可以这样做:
@Test
public void testFooThrowsIndexOutOfBoundsException() {
try {
foo.doStuff();
assert false;
} catch (IndexOutOfBoundsException e) {
assert true;
}
}
#13
17
Now that JUnit 5 has released, the best option is to use Assertions.assertThrows()
(see the Junit 5 User Guide).
现在,JUnit 5已经发布了,最好的选择是使用assertions.assert抛出()(参见JUnit 5用户指南)。
Here is an example that verifies an exception is thrown, and uses Truth to make assertions on the exception message:
下面是一个验证异常的示例,并使用Truth在异常消息上做出断言:
public class FooTest {
@Test
public void doStuffThrowsIndexOutOfBoundsException() {
Foo foo = new Foo();
IndexOutOfBoundsException e = assertThrows(
IndexOutOfBoundsException.class, foo::doStuff);
assertThat(e).hasMessageThat().contains("woops!");
}
}
The advantages over the approaches in the other answers are:
其他答案的优点是:
- Built into JUnit
- 内置JUnit
- You get a useful exception message if the code in the lambda doesn't throw an exception, and a stacktrace if it throws a different exception
- 如果lambda中的代码没有抛出异常,并且抛出一个不同的异常,则会得到一个有用的异常消息。
- Concise
- 简洁的
- Allows your tests to follow Arrange-Act-Assert
- 允许您的测试遵循排列- act - assert。
- You can precisely indicate what code you are expecting to throw the exception
- 您可以精确地指出您希望抛出异常的代码。
- You don't need to list the expected exception in the
throws
clause - 您不需要在抛出子句中列出预期的异常。
- You can use the assertion framework of your choice to make assertions about the caught exception
- 您可以使用您选择的断言框架来断言捕获的异常。
A similar method will be added to org.junit Assert
in JUnit 4.13.
一个类似的方法将被添加到org。junit 4.13中的junit断言。
#14
12
IMHO, the best way to check for exceptions in JUnit is the try/catch/fail/assert pattern:
在JUnit中检查异常的最好方法是try/catch/fail/assert模式:
// this try block should be as small as possible,
// as you want to make sure you only catch exceptions from your code
try {
sut.doThing();
fail(); // fail if this does not throw any exception
} catch(MyException e) { // only catch the exception you expect,
// otherwise you may catch an exception for a dependency unexpectedly
// a strong assertion on the message,
// in case the exception comes from anywhere an unexpected line of code,
// especially important if your checking IllegalArgumentExceptions
assertEquals("the message I get", e.getMessage());
}
The assertTrue
might be a bit strong for some people, so assertThat(e.getMessage(), containsString("the message");
might be preferable.
对于某些人来说,assertTrue可能有些强大,所以assertThat(. getmessage (), containsString(“消息”);可能是更可取的。
#15
10
JUnit 5 Solution
@Test
void testFooThrowsIndexOutOfBoundsException() {
Throwable exception = expectThrows( IndexOutOfBoundsException.class, foo::doStuff );
assertEquals( "some message", exception.getMessage() );
}
More Infos about JUnit 5 on http://junit.org/junit5/docs/current/user-guide/#writing-tests-assertions
更多关于JUnit 5的信息:http://junitorg/junit5/docs/current/userguide/# writing- tests-断言。
#16
9
I tried many of the methods here, but they were either complicated or didn't quite meet my requirements. In fact, one can write a helper method quite simply:
我在这里尝试了很多方法,但是它们要么复杂要么不完全符合我的要求。事实上,一个人可以很简单地写一个助手方法:
public class ExceptionAssertions {
public static void assertException(BlastContainer blastContainer ) {
boolean caughtException = false;
try {
blastContainer.test();
} catch( Exception e ) {
caughtException = true;
}
if( !caughtException ) {
throw new AssertionFailedError("exception expected to be thrown, but was not");
}
}
public static interface BlastContainer {
public void test() throws Exception;
}
}
Use it like this:
使用它是这样的:
assertException(new BlastContainer() {
@Override
public void test() throws Exception {
doSomethingThatShouldExceptHere();
}
});
Zero dependencies: no need for mockito, no need powermock; and works just fine with final classes.
零依赖:不需要mockito,不需要powermock;在最后的课上也可以。
#17
#18
7
Java 8 solution
If you would like a solution which:
如果你想要一个解决方案:
- Utilizes Java 8 lambdas
- 利用Java 8λ
- Does not depend on any JUnit magic
- 不依赖于任何JUnit魔法吗?
- Allows you to check for multiple exceptions within a single test method
- 允许您在单个测试方法中检查多个异常。
- Checks for an exception being thrown by a specific set of lines within your test method instead of any unknown line in the entire test method
- 检查您的测试方法中特定的一行代码所抛出的异常,而不是在整个测试方法中使用任何未知的行。
- Yields the actual exception object that was thrown so that you can further examine it
- 生成抛出的实际异常对象,以便您可以进一步检查它。
Here is a utility function that I wrote:
这是我写的一个效用函数
public final <T extends Throwable> T expectException( Class<T> exceptionClass, Runnable runnable )
{
try
{
runnable.run();
}
catch( Throwable throwable )
{
if( throwable instanceof AssertionError && throwable.getCause() != null )
throwable = throwable.getCause(); //allows "assert x != null : new IllegalArgumentException();"
assert exceptionClass.isInstance( throwable ) : throwable; //exception of the wrong kind was thrown.
assert throwable.getClass() == exceptionClass : throwable; //exception thrown was a subclass, but not the exact class, expected.
@SuppressWarnings( "unchecked" )
T result = (T)throwable;
return result;
}
assert false; //expected exception was not thrown.
return null; //to keep the compiler happy.
}
(摘自我的博客)
Use it as follows:
使用如下:
@Test
public void testThrows()
{
RuntimeException e = expectException( RuntimeException.class, () ->
{
throw new RuntimeException( "fail!" );
} );
assert e.getMessage().equals( "fail!" );
}
#19
6
In my case I always get RuntimeException from db, but messages differ. And exception need to be handled respectively. Here is how I tested it:
在我的例子中,我总是从db获得RuntimeException,但是消息不同。异常需要分别处理。下面是我的测试方法:
@Test
public void testThrowsExceptionWhenWrongSku() {
// Given
String articleSimpleSku = "999-999";
int amountOfTransactions = 1;
Exception exception = null;
// When
try {
createNInboundTransactionsForSku(amountOfTransactions, articleSimpleSku);
} catch (RuntimeException e) {
exception = e;
}
// Then
shouldValidateThrowsExceptionWithMessage(exception, MESSAGE_NON_EXISTENT_SKU);
}
private void shouldValidateThrowsExceptionWithMessage(final Exception e, final String message) {
assertNotNull(e);
assertTrue(e.getMessage().contains(message));
}
#20
5
In JUnit 4 or later you can test the exceptions as follows
在JUnit 4中或稍后,您可以测试以下的异常。
@Rule
public ExpectedException exceptions = ExpectedException.none();
this provides a lot of features which can be used to improve our JUnit tests.
If you see the below example I am testing 3 things on the exception.
这提供了许多可以用来改进JUnit测试的特性。如果您看到下面的示例,我正在测试异常中的3个东西。
- The Type of exception thrown
- 抛出的异常类型。
- The exception Message
- 异常消息
- The cause of the exception
- 例外的原因。
public class MyTest {
@Rule
public ExpectedException exceptions = ExpectedException.none();
ClassUnderTest classUnderTest;
@Before
public void setUp() throws Exception {
classUnderTest = new ClassUnderTest();
}
@Test
public void testAppleisSweetAndRed() throws Exception {
exceptions.expect(Exception.class);
exceptions.expectMessage("this is the exception message");
exceptions.expectCause(Matchers.<Throwable>equalTo(exceptionCause));
classUnderTest.methodUnderTest("param1", "param2");
}
}
#21
5
We can use an assertion fail after the method that must return an exception:
在必须返回异常的方法之后,我们可以使用断言失败:
try{
methodThatThrowMyException();
Assert.fail("MyException is not thrown !");
} catch (final Exception exception) {
// Verify if the thrown exception is instance of MyException, otherwise throws an assert failure
assertTrue(exception instanceof MyException, "An exception other than MyException is thrown !");
// In case of verifying the error message
MyException myException = (MyException) exception;
assertEquals("EXPECTED ERROR MESSAGE", myException.getMessage());
}
#22
4
Additionally to what NamShubWriter has said, make sure that:
另外,NamShubWriter曾说过,要确保:
- The ExpectedException instance is public (Related Question)
- 期望的实例是公开的(相关的问题)
- The ExpectedException isn't instantiated in say, the @Before method. This post clearly explains all the intricacies of JUnit's order of execution.
- 在@Before方法中没有实例化期望。这篇文章清楚地解释了JUnit执行顺序的所有复杂性。
Do not do this:
不要这样做:
@Rule
public ExpectedException expectedException;
@Before
public void setup()
{
expectedException = ExpectedException.none();
}
Finally, this blog post clearly illustrates how to assert that a certain exception is thrown.
最后,这篇博文清楚地说明了如何断言某个异常被抛出。
#23
4
Just make a Matcher that can be turned off and on, like this:
只要做一个可以关闭的Matcher,就像这样:
public class ExceptionMatcher extends BaseMatcher<Throwable> {
private boolean active = true;
private Class<? extends Throwable> throwable;
public ExceptionMatcher(Class<? extends Throwable> throwable) {
this.throwable = throwable;
}
public void on() {
this.active = true;
}
public void off() {
this.active = false;
}
@Override
public boolean matches(Object object) {
return active && throwable.isAssignableFrom(object.getClass());
}
@Override
public void describeTo(Description description) {
description.appendText("not the covered exception type");
}
}
To use it:
使用它:
add public ExpectedException exception = ExpectedException.none();
, then:
添加公众期望的异常=期望的,没有();
ExceptionMatcher exMatch = new ExceptionMatcher(MyException.class);
exception.expect(exMatch);
someObject.somethingThatThrowsMyException();
exMatch.off();
#24
2
The most flexible and elegant answer for Junit 4 I found in the Mkyoung blog. It have the flexibility of the try/catch using the @Rule. I like this approach because I need to read specific attributes of a customized exception.
我在Mkyoung博客中找到了对Junit 4最灵活和最优雅的回答。它具有使用@Rule的try/catch的灵活性。我喜欢这种方法,因为我需要读取定制异常的特定属性。
package com.mkyong;
import com.mkyong.examples.CustomerService;
import com.mkyong.examples.exception.NameNotFoundException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.hasProperty;
public class Exception3Test {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void testNameNotFoundException() throws NameNotFoundException {
//test specific type of exception
thrown.expect(NameNotFoundException.class);
//test message
thrown.expectMessage(is("Name is empty!"));
//test detail
thrown.expect(hasProperty("errCode")); //make sure getters n setters are defined.
thrown.expect(hasProperty("errCode", is(666)));
CustomerService cust = new CustomerService();
cust.findByName("");
}
}
#25
1
Take for example, you want to write Junit for below mentioned code fragment
例如,您需要为下面提到的代码片段编写Junit。
public int divideByZeroDemo(int a,int b){
return a/b;
}
public void exceptionWithMessage(String [] arr){
throw new ArrayIndexOutOfBoundsException("Array is out of bound");
}
The above code is to test for some unknown exception that may occur and the below one is to assert some exception with custom message.
上面的代码是为了测试可能发生的未知异常,下面的代码是通过自定义消息断言某个异常。
@Rule
public ExpectedException exception=ExpectedException.none();
private Demo demo;
@Before
public void setup(){
demo=new Demo();
}
@Test(expected=ArithmeticException.class)
public void testIfItThrowsAnyException() {
demo.divideByZeroDemo(5, 0);
}
@Test
public void testExceptionWithMessage(){
exception.expectMessage("Array is out of bound");
exception.expect(ArrayIndexOutOfBoundsException.class);
demo.exceptionWithMessage(new String[]{"This","is","a","demo"});
}
#26
0
With Java 8 you can create a method taking a code to check and expected exception as parameters:
使用Java 8,您可以创建一个方法,使用代码检查和预期的异常作为参数:
private void expectException(Runnable r, Class<?> clazz) {
try {
r.run();
fail("Expected: " + clazz.getSimpleName() + " but not thrown");
} catch (Exception e) {
if (!clazz.isInstance(e)) fail("Expected: " + clazz.getSimpleName() + " but " + e.getClass().getSimpleName() + " found", e);
}
}
and then inside your test:
然后在你的测试中:
expectException(() -> list.sublist(0, 2).get(2), IndexOutOfBoundsException.class);
Benefits:
好处:
- not relying on any library
- 不依赖任何图书馆。
- localised check - more precise and allows to have multiple assertions like this within one test if needed
- 本地化检查——更精确,如果需要,允许在一个测试中有多个这样的断言。
- easy to use
- 易于使用的
#27
0
My solution using Java 8 lambdas:
我的解决方案使用Java 8 lambdas:
public static <T extends Throwable> T assertThrows(Class<T> expected, ThrowingRunnable action) throws Throwable {
try {
action.run();
Assert.fail("Did not throw expected " + expected.getSimpleName());
return null; // never actually
} catch (Throwable actual) {
if (!expected.isAssignableFrom(actual.getClass())) { // runtime '!(actual instanceof expected)'
System.err.println("Threw " + actual.getClass().getSimpleName()
+ ", which is not a subtype of expected "
+ expected.getSimpleName());
throw actual; // throw the unexpected Throwable for maximum transparency
} else {
return (T) actual; // return the expected Throwable for further examination
}
}
}
You have to define a FunctionalInterface, because Runnable
doesn't declare the required throws
.
您必须定义一个FunctionalInterface,因为Runnable没有声明所需的抛出。
@FunctionalInterface
public interface ThrowingRunnable {
void run() throws Throwable;
}
The method can be used as follows:
该方法可采用如下方法:
class CustomException extends Exception {
public final String message;
public CustomException(final String message) { this.message = message;}
}
CustomException e = assertThrows(CustomException.class, () -> {
throw new CustomException("Lorem Ipsum");
});
assertEquals("Lorem Ipsum", e.message);
#28
0
There are two ways of writing test case
编写测试用例有两种方法。
- Annotate the test with the exception which is thrown by the method. Something like this
@Test(expected = IndexOutOfBoundsException.class)
- 用方法抛出的异常对测试进行注释。类似于这个@Test(预期= IndexOutOfBoundsException.class)
-
You can simply catch the exception in the test class using the try catch block and assert on the message that is thrown from the method in test class.
您可以使用try catch块在测试类中捕获异常,并在测试类中从方法抛出的消息上断言。
try{ } catch(exception to be thrown from method e) { assertEquals("message", e.getmessage()); }
I hope this answers your query Happy learning...
我希望这能回答您的查询愉快的学习……
#29
0
The one-way exchange-pattern does not seem to be supported any more by the HTTP Endpoint.latest Anypoint Studio version.
https://1.gravatar.com/avatar/42d13a27f6019502ab66b6aa820b10ea?s=50&d=identicon&r=G' class='avatar avatar-50' height='50' width='50' /> http://synq.cloud/' rel='external nofollow' class='url'>Mathews says:
单向交换模式似乎不再受HTTP端点的支持。最新Anypoint Studio版本。https://1.gravatar.com/avatar/42d13a27f6019502ab66b6aa820b10ea?s=50&d= 'avatar avatar-50' height='50' width='50' /> http://synq。cloud/' rel='外部nofollow' class='url'>Mathews说:
Changed Expression #[message.inboundProperties.’http.request.uri’ != ‘/favicon.ico’] in 5.1.0 to make it work expression=”#[message.inboundProperties.’http.request’ !=’/favicon.ico’]” with Anypoint Studio 5.2.0 version expression=”#[message.inboundProperties[‘http.request.path’] != ‘/favicon.ico’]”
改变表达式# http.request[message.inboundProperties。”。uri /图标“! =”。在5.1.0中,使其工作表达式= " #[message.inboundProperties. " http。请求“! =”/图标。" Anypoint Studio 5.2.0版本的表达式= " #[message.inboundProperties[' http.request]。路径']! = ' / ico)”位于
Hope it helps!
希望它可以帮助!
Cheers,
欢呼,
#30
-1
I wanted to comment with my solution to this problem, which avoided needing any of the exception related JUnit code.
我想对这个问题的解决方案进行注释,它避免了需要任何异常相关的JUnit代码。
I used assertTrue(boolean) combined with try/catch to look for my expected exception to be thrown. Here's an example:
我使用了assertTrue(boolean)结合try/catch来查找要抛出的预期异常。这里有一个例子:
public void testConstructor() {
boolean expectedExceptionThrown;
try {
// Call constructor with bad arguments
double a = 1;
double b = 2;
double c = a + b; // In my example, this is an invalid option for c
new Triangle(a, b, c);
expectedExceptionThrown = false; // because it successfully constructed the object
}
catch(IllegalArgumentException e) {
expectedExceptionThrown = true; // because I'm in this catch block
}
catch(Exception e) {
expectedExceptionThrown = false; // because it threw an exception but not the one expected
}
assertTrue(expectedExceptionThrown);
}