SCd*_*CdF 1915 java junit assert exception junit4
如何以惯用方式使用JUnit4来测试某些代码是否会抛出异常?
虽然我当然可以这样做:
@Test
public void testFooThrowsIndexOutOfBoundsException() {
boolean thrown = false;
try {
foo.doStuff();
} catch (IndexOutOfBoundsException e) {
thrown = true;
}
assertTrue(thrown);
}
Run Code Online (Sandbox Code Playgroud)
我记得有一个注释或一个Assert.xyz或者其他东西,对于这些类型的情况来说,远不如KUndgy 和JUnit的精神.
ska*_*man 2291
JUnit 4支持这个:
@Test(expected = IndexOutOfBoundsException.class)
public void testIndexOutOfBoundsException() {
ArrayList emptyList = new ArrayList();
Object o = emptyList.get(0);
}
Run Code Online (Sandbox Code Playgroud)
参考:https://junit.org/junit4/faq.html#atests_7
Nam*_*ter 1280
编辑现在JUnit5已经发布,最好的选择是使用Assertions.assertThrows()
(参见我的其他答案).
如果您尚未迁移到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();
}
}
Run Code Online (Sandbox Code Playgroud)
这比@Test(expected=IndexOutOfBoundsException.class)
因为如果IndexOutOfBoundsException
之前抛出测试将会失败要好得多foo.doStuff()
看到 详细信息,此文
dav*_*veb 464
小心使用预期的异常,因为它只断言该方法抛出该异常,而不是测试中的特定代码行.
我倾向于使用它来测试参数验证,因为这些方法通常非常简单,但更复杂的测试可能更适合:
try {
methodThatShouldThrow();
fail( "My method didn't throw when I expected it to" );
} catch (MyException expectedException) {
}
Run Code Online (Sandbox Code Playgroud)
运用判断.
Raf*_*iec 210
如前所述,有许多方法可以处理JUnit中的异常.但是对于Java 8,还有另外一个:使用Lambda Expressions.使用Lambda Expressions,我们可以实现如下语法:
@Test
public void verifiesTypeAndMessage() {
assertThrown(new DummyService()::someMethod)
.isInstanceOf(RuntimeException.class)
.hasMessage("Runtime exception occurred")
.hasMessageStartingWith("Runtime")
.hasMessageEndingWith("occurred")
.hasMessageContaining("exception")
.hasNoCause();
}
Run Code Online (Sandbox Code Playgroud)
assertThrown接受一个功能接口,其实例可以使用lambda表达式,方法引用或构造函数引用创建.assertThrown接受该接口将期望并准备好处理异常.
这是相对简单但功能强大的技术.
看看这篇描述这种技术的博客文章:http://blog.codeleak.pl/2014/07/junit-testing-exception-with-java-8-and-lambda-expressions.html
源代码可以在这里找到:https://github.com/kolorobot/unit-testing-demo/tree/master/src/test/java/com/github/kolorobot/exceptions/java8
披露:我是博客和项目的作者.
wal*_*lsh 139
在junit中,有四种方法可以测试异常.
对于junit4.x,使用Test annonation的可选'expected'属性
@Test
public void testFooThrowsIndexOutOfBoundsException() {
Throwable exception = assertThrows(IndexOutOfBoundsException.class, () -> foo.doStuff());
assertEquals("expected messages", exception.getMessage());
}
Run Code Online (Sandbox Code Playgroud)对于junit4.x,请使用ExpectedException规则
@Test(expected = IndexOutOfBoundsException.class)
public void testFooThrowsIndexOutOfBoundsException() {
foo.doStuff();
}
Run Code Online (Sandbox Code Playgroud)你也可以使用在junit 3框架下广泛使用的经典try/catch方式
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();
}
}
Run Code Online (Sandbox Code Playgroud)最后,对于junit5.x,您还可以使用assertThrows,如下所示
@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.
}
}
Run Code Online (Sandbox Code Playgroud)所以
有关详细信息,请参阅此文档和junit5用户指南以获取详细信息.
Bri*_*ice 106
TL;博士
预JDK8:我会建议老好try
- catch
块.(不要忘记fail()
在catch
块之前添加断言)
post-JDK8:使用AssertJ或自定义lambda来断言异常行为.
无论是Junit 4还是JUnit 5.
长话故事
这是可能自己写一个自己做的 try
- catch
块或使用JUnit工具(@Test(expected = ...)
或@Rule ExpectedException
JUnit的规则功能).
但是这些方式并不那么优雅,并且不能将可读性与其他工具完美地结合在一起.
在try
- catch
块,你必须写周围的测试行为块,并写在catch块的断言,这可能是罚款,但很多人发现taht这种风格中断测试的阅读流程.你还需要Assert.fail
在try
块的末尾写一个,否则测试可能会错过断言的一面; PMD,findbugs或Sonar将发现此类问题.
该@Test(expected = ...)
功能很有趣,因为您可以编写更少的代码,然后编写此测试可能不太容易出现编码错误.但这种做法缺乏一些领域.
此外,由于期望放在方法中,取决于测试代码的编写方式,然后测试代码的错误部分可能抛出异常,导致误报测试,我不确定PMD,findbugs或Sonar会给出这些代码的提示.
@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
}
Run Code Online (Sandbox Code Playgroud)该ExpectedException
规则也是尝试修复之前的警告,但使用期望风格使用感觉有点尴尬,EasyMock用户非常清楚这种风格.对某些人来说可能很方便,但如果您遵循行为驱动开发(BDD)或安排行为断言(AAA)原则,则该ExpectedException
规则将不适合这些写作风格.除此之外,它可能会遇到与问题相同的问题@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
}
Run Code Online (Sandbox Code Playgroud)
即使预期的异常放在测试语句之前,如果测试遵循BDD或AAA,它也会破坏您的读取流程.
另请参阅作者JUnit上的此评论问题ExpectedException
.JUnit 4.13-beta-2甚至不赞成这种机制:
拉请求#1519:不推荐使用ExpectedException
方法Assert.assertThrows为验证异常提供了一种更好的方法.此外,与TestWatcher等其他规则一起使用时,ExpectedException的使用容易出错,因为在这种情况下规则的顺序很重要.
所以这些上面的选项都有他们的注意事项,显然不能免于编码器错误.
在创建了这个看起来很有前途的答案后,我发现了一个项目,这是一个例外.
正如项目描述所说,它让编码器用流畅的代码行编写代码来捕获异常并为以后的断言提供此异常.您可以使用任何断言库,如Hamcrest或AssertJ.
从主页获取的快速示例:
// 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();
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,代码非常简单,您可以在特定行上捕获异常,then
API是将使用AssertJ API的别名(类似于使用assertThat(ex).hasNoCause()...
).在某些时候,该项目依赖于FEST-Assert的AssertJ的祖先.编辑:似乎该项目正在酝酿Java 8 Lambdas支持.
目前这个库有两个缺点:
在撰写本文时,值得注意的是,该库基于Mockito 1.x,因为它创建了场景背后测试对象的模拟.由于Mockito仍未更新,因此该库无法使用最终类或最终方法.即使它是基于当前版本中的mockito 2,这也需要声明一个全局模拟制造商(inline-mock-maker
),这可能不是你想要的东西,因为这个模拟器具有与常规模拟器不同的缺点.
它需要另一个测试依赖.
一旦库支持lambdas,这些问题将不适用,但AssertJ工具集将复制该功能.
如果您不想使用catch-exception工具,请考虑所有因素,我将推荐try
- catch
block 的旧方法,至少是JDK7.对于JDK 8用户,您可能更喜欢使用AssertJ,因为它提供的可能不仅仅是声明异常.
使用JDK8,lambdas进入测试场景,并且它们被证明是一种有趣的方式来断言异常行为.AssertJ已经更新,提供了一个很好的流畅API来断言异常行为.
并使用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");
}
Run Code Online (Sandbox Code Playgroud)通过几乎完全重写JUnit 5,断言得到了一点改进,它们可能被证明是一种开箱即用的方式来断言正确的异常.但实际上断言API仍然有点差,外面什么都没有assertThrows
.
@Test
@DisplayName("throws EmptyStackException when peeked")
void throwsExceptionWhenPeeked() {
Throwable t = assertThrows(EmptyStackException.class, () -> stack.peek());
Assertions.assertEquals("...", t.getMessage());
}
Run Code Online (Sandbox Code Playgroud)
正如您所注意到assertEquals
的那样仍在返回void
,因此不允许像AssertJ那样链接断言.
此外,如果你记得名字冲突Matcher
或Assert
,准备遇到同样的冲突Assertions
.
我想得出结论,今天(2017-03-03)AssertJ的易用性,可发现的API,快速的开发速度以及作为事实上的测试依赖是JDK8的最佳解决方案,无论测试框架如何(JUnit或不是),以前的JDK应该依赖try
-catch
即使他们感觉笨拙的块.
这个答案是从另一个没有相同可见性的问题复制而来的,我是同一个作者.
Nam*_*ter 56
现在JUnit 5已经发布,最好的选择是使用Assertions.assertThrows()
(参见Junit 5用户指南).
下面是一个验证抛出异常的示例,并使用Truth对异常消息进行断言:
public class FooTest {
@Test
public void doStuffThrowsIndexOutOfBoundsException() {
Foo foo = new Foo();
IndexOutOfBoundsException e = assertThrows(
IndexOutOfBoundsException.class, foo::doStuff);
assertThat(e).hasMessageThat().contains("woops!");
}
}
Run Code Online (Sandbox Code Playgroud)
其他答案中的方法优势是:
throws
子句中列出预期的异常org.junit Assert
JUnit 4.13 中将添加类似的方法.
Joh*_*han 40
怎么样:捕获一个非常一般的异常,确保它使它脱离catch块,然后断言异常的类是你期望的那样.如果a)异常是错误的类型(例如,如果你有一个Null指针)而b)没有抛出异常,那么这个断言将失败.
public void testFooThrowsIndexOutOfBoundsException() {
Throwable e = null;
try {
foo.doStuff();
} catch (Throwable ex) {
e = ex;
}
assertTrue(e instanceof IndexOutOfBoundsException);
}
Run Code Online (Sandbox Code Playgroud)
Mar*_*szS 36
import static com.googlecode.catchexception.apis.BDDCatchException.*;
@Test
public void testFooThrowsIndexOutOfBoundsException() {
when(() -> foo.doStuff());
then(caughtException()).isInstanceOf(IndexOutOfBoundsException.class);
}
Run Code Online (Sandbox Code Playgroud)
eu.codearte.catch-exception:catch-exception:2.0
Run Code Online (Sandbox Code Playgroud)
wes*_*ton 36
使用AssertJ断言,可以与JUnit一起使用:
import static org.assertj.core.api.Assertions.*;
@Test
public void testFooThrowsIndexOutOfBoundsException() {
Foo foo = new Foo();
assertThatThrownBy(() -> foo.doStuff())
.isInstanceOf(IndexOutOfBoundsException.class);
}
Run Code Online (Sandbox Code Playgroud)
它更好,@Test(expected=IndexOutOfBoundsException.class)
因为它保证测试中的预期行抛出异常并让您检查有关异常的更多详细信息,例如消息,更容易:
assertThatThrownBy(() ->
{
throw new Exception("boom!");
})
.isInstanceOf(Exception.class)
.hasMessageContaining("boom");
Run Code Online (Sandbox Code Playgroud)
rwi*_*zel 32
为了解决同样的问题,我设置了一个小项目:http: //code.google.com/p/catch-exception/
使用这个小助手你会写
verifyException(foo, IndexOutOfBoundsException.class).doStuff();
Run Code Online (Sandbox Code Playgroud)
这比JUnit 4.7的ExpectedException规则简洁得多.与skaffman提供的解决方案相比,您可以指定您期望异常的代码行.我希望这有帮助.
Dil*_*sha 30
更新: JUnit5对异常测试有一个改进:assertThrows
.
以下示例来自:Junit 5用户指南
@Test
void exceptionTesting() {
Throwable exception = assertThrows(IllegalArgumentException.class, () ->
{
throw new IllegalArgumentException("a message");
});
assertEquals("a message", exception.getMessage());
}
Run Code Online (Sandbox Code Playgroud)
使用JUnit 4的原始答案.
有几种方法可以测试抛出异常.我还在我的帖子中讨论了以下选项如何使用JUnit编写出色的单元测试
设置expected
参数@Test(expected = FileNotFoundException.class)
.
@Test(expected = FileNotFoundException.class)
public void testReadFile() {
myClass.readFile("test.txt");
}
Run Code Online (Sandbox Code Playgroud)
运用 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!"));
}
}
Run Code Online (Sandbox Code Playgroud)
用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");
}
Run Code Online (Sandbox Code Playgroud)
您可以在JUnit4 wiki中阅读有关异常测试和bad.robot的异常测试的更多信息 - 期待异常JUnit规则.
Joh*_*kic 21
你也可以这样做:
@Test
public void testFooThrowsIndexOutOfBoundsException() {
try {
foo.doStuff();
assert false;
} catch (IndexOutOfBoundsException e) {
assert true;
}
}
Run Code Online (Sandbox Code Playgroud)
Ale*_*ins 13
恕我直言,在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());
}
Run Code Online (Sandbox Code Playgroud)
在assertTrue
可能会有点强对某些人来说,这样assertThat(e.getMessage(), containsString("the message");
可能是可取的.
Dan*_*fer 12
@Test
void testFooThrowsIndexOutOfBoundsException() {
Throwable exception = expectThrows( IndexOutOfBoundsException.class, foo::doStuff );
assertEquals( "some message", exception.getMessage() );
}
Run Code Online (Sandbox Code Playgroud)
有关JUnit 5的更多信息,请访问http://junit.org/junit5/docs/current/user-guide/#writing-tests-assertions
Hug*_*ins 11
我在这里尝试了很多方法,但它们要么很复杂,要么完全不符合我的要求.实际上,人们可以非常简单地编写辅助方法:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
像这样使用它:
assertException(new BlastContainer() {
@Override
public void test() throws Exception {
doSomethingThatShouldExceptHere();
}
});
Run Code Online (Sandbox Code Playgroud)
零依赖:不需要mockito,不需要powermock; 并且在最后的课程中工作得很好.
Dhe*_*rik 11
我在Mkyong博客中找到的Junit 4最灵活,最优雅的答案.它具有try/catch
使用@Rule
注释的灵活性.我喜欢这种方法,因为您可以读取自定义异常的特定属性.
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("");
}
}
Run Code Online (Sandbox Code Playgroud)
如果您想要一个解决方案:
这是我写的一个实用函数:
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.
}
Run Code Online (Sandbox Code Playgroud)
(摘自我的博客)
使用方法如下:
@Test
public void testThrows()
{
RuntimeException e = expectException( RuntimeException.class, () ->
{
throw new RuntimeException( "fail!" );
} );
assert e.getMessage().equals( "fail!" );
}
Run Code Online (Sandbox Code Playgroud)
在我的情况下,我总是从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));
}
Run Code Online (Sandbox Code Playgroud)
只需制作一个可以关闭和打开的匹配器,如下所示:
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");
}
}
Run Code Online (Sandbox Code Playgroud)
要使用它:
添加public ExpectedException exception = ExpectedException.none();
,然后:
ExceptionMatcher exMatch = new ExceptionMatcher(MyException.class);
exception.expect(exMatch);
someObject.somethingThatThrowsMyException();
exMatch.off();
Run Code Online (Sandbox Code Playgroud)
在必须返回异常的方法之后,我们可以使用断言失败:
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());
}
Run Code Online (Sandbox Code Playgroud)
除了NamShubWriter所说的之外,请确保:
难道不是这样做:
@Rule
public ExpectedException expectedException;
@Before
public void setup()
{
expectedException = ExpectedException.none();
}
Run Code Online (Sandbox Code Playgroud)
最后,这篇博文清楚地说明了如何断言某个异常被抛出。
在JUnit 4或更高版本中,您可以按如下方式测试异常
@Rule
public ExpectedException exceptions = ExpectedException.none();
Run Code Online (Sandbox Code Playgroud)
这提供了许多可用于改进JUnit测试的功能.
如果您看到以下示例,我将在异常上测试3件事.
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");
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1203931 次 |
最近记录: |