JUnit:可能'期望'包装异常?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JUnit:可能'期望'包装异常?相关的知识,希望对你有一定的参考价值。
我知道可以在JUnit中定义'预期'异常,执行:
@Test(expect=MyException.class)
public void someMethod() { ... }
但是,如果总是抛出相同的异常,但具有不同的“嵌套”原因会怎样。
有什么建议?
答案
您可以将测试代码包装在try / catch块中,捕获抛出的异常,检查内部原因,log / assert / whatever,然后重新抛出异常(如果需要)。
另一答案
从JUnit 4.11开始,您可以使用ExpectedException
规则的expectCause()
方法:
import static org.hamcrest.CoreMatchers.*;
// ...
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Test
public void throwsNestedException() throws Exception {
expectedException.expectCause(isA(SomeNestedException.class));
throw new ParentException("foo", new SomeNestedException("bar"));
}
另一答案
如果您使用的是最新版本的JUnit,则可以扩展默认测试运行器以便为您处理(无需将每个方法包装在try / catch块中)
ExtendedTestRunner.java - 新的测试运行器:
public class ExtendedTestRunner extends BlockJUnit4ClassRunner
{
public ExtendedTestRunner( Class<?> clazz )
throws InitializationError
{
super( clazz );
}
@Override
protected Statement possiblyExpectingExceptions( FrameworkMethod method,
Object test,
Statement next )
{
ExtendedTest annotation = method.getAnnotation( ExtendedTest.class );
return expectsCauseException( annotation ) ?
new ExpectCauseException( next, getExpectedCauseException( annotation ) ) :
super.possiblyExpectingExceptions( method, test, next );
}
@Override
protected List<FrameworkMethod> computeTestMethods()
{
Set<FrameworkMethod> testMethods = new HashSet<FrameworkMethod>( super.computeTestMethods() );
testMethods.addAll( getTestClass().getAnnotatedMethods( ExtendedTest.class ) );
return testMethods;
}
@Override
protected void validateTestMethods( List<Throwable> errors )
{
super.validateTestMethods( errors );
validatePublicVoidNoArgMethods( ExtendedTest.class, false, errors );
}
private Class<? extends Throwable> getExpectedCauseException( ExtendedTest annotation )
{
if (annotation == null || annotation.expectedCause() == ExtendedTest.None.class)
return null;
else
return annotation.expectedCause();
}
private boolean expectsCauseException( ExtendedTest annotation) {
return getExpectedCauseException(annotation) != null;
}
}
ExtendedTest.java - 用于标记测试方法的注释:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface ExtendedTest
{
/**
* Default empty exception
*/
static class None extends Throwable {
private static final long serialVersionUID= 1L;
private None() {
}
}
Class<? extends Throwable> expectedCause() default None.class;
}
ExpectCauseException.java - 新的JUnit语句:
public class ExpectCauseException extends Statement
{
private Statement fNext;
private final Class<? extends Throwable> fExpected;
public ExpectCauseException( Statement next, Class<? extends Throwable> expected )
{
fNext= next;
fExpected= expected;
}
@Override
public void evaluate() throws Exception
{
boolean complete = false;
try {
fNext.evaluate();
complete = true;
} catch (Throwable e) {
if ( e.getCause() == null || !fExpected.isAssignableFrom( e.getCause().getClass() ) )
{
String message = "Unexpected exception cause, expected<"
+ fExpected.getName() + "> but was<"
+ ( e.getCause() == null ? "none" : e.getCause().getClass().getName() ) + ">";
throw new Exception(message, e);
}
}
if (complete)
throw new AssertionError( "Expected exception cause: "
+ fExpected.getName());
}
}
用法:
@RunWith( ExtendedTestRunner.class )
public class MyTests
{
@ExtendedTest( expectedCause = MyException.class )
public void someMethod()
{
throw new RuntimeException( new MyException() );
}
}
另一答案
您可以随时手动执行此操作:
@Test
public void someMethod() {
try{
... all your code
} catch (Exception e){
// check your nested clauses
if(e.getCause() instanceof FooException){
// pass
} else {
Assert.fail("unexpected exception");
}
}
另一答案
你可以为异常创建一个Matcher。这甚至在您使用Arquillian的@RunWith(Arquillian.class)
等其他测试运行器时也能正常工作,因此您无法使用上面建议的@RunWith(ExtendedTestRunner.class)
方法。
这是一个简单的例子:
public class ExceptionMatcher extends BaseMatcher<Object> {
private Class<? extends Throwable>[] classes;
// @SafeVarargs // <-- Suppress warning in Java 7. This usage is safe.
public ExceptionMatcher(Class<? extends Throwable>... classes) {
this.classes = classes;
}
@Override
public boolean matches(Object item) {
for (Class<? extends Throwable> klass : classes) {
if (! klass.isInstance(item)) {
return false;
}
item = ((Throwable) item).getCause();
}
return true;
}
@Override
public void describeTo(Description descr) {
descr.appendText("unexpected exception");
}
}
然后将它与@Rule和ExpectedException一起使用,如下所示:
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void testSomething() {
thrown.expect(new ExceptionMatcher(IllegalArgumentException.class, IllegalStateException.class));
throw new IllegalArgumentException("foo", new IllegalStateException("bar"));
}
由Craig Ringer于2012年添加编辑:增强且更可靠的版本:
- 基本用法与上述不变
- 可以传递可选的第一个参数
boolean rethrow
来抛出无法匹配的异常。这样可以保留嵌套异常的堆栈跟踪,以便于调试。 - 使用Apache Commons Lang ExceptionUtils来处理cause循环并处理一些常见异常类使用的非标准异常嵌套。
- 自我描述包括可接受的例外
- 失败时自我描述包括遇到的异常的原因堆栈
- 处理Java 7警告。删除旧版本上的
@SaveVarargs
。
完整代码:
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
public class ExceptionMatcher extends BaseMatcher<Object> {
private Class<? extends Throwable>[] acceptedClasses;
private Throwable[] nestedExceptions;
private final boolean rethrow;
@SafeVarargs
public ExceptionMatcher(Class<? extends Throwable>... classes) {
this(false, classes);
}
@SafeVarargs
public ExceptionMatcher(boolean rethrow, Class<? extends Throwable>... classes) {
this.rethrow = rethrow;
this.acceptedClasses = classes;
}
@Override
public boolean matches(Object item) {
nestedExceptions = ExceptionUtils.getThrowables((Throwable)item);
for (Class<? extends Throwable> acceptedClass : acceptedClasses) {
for (Throwable nestedException : nestedException以上是关于JUnit:可能'期望'包装异常?的主要内容,如果未能解决你的问题,请参考以下文章