如何使用JUnit来测试某些代码是否抛出异常?
我当然可以这样做:
@Test
public void testFooThrowsIndexOutOfBoundsException() {
boolean thrown = false;
try {
foo.doStuff();
} catch (IndexOutOfBoundsException e) {
thrown = true;
}
assertTrue(thrown);
}
我记得,对于这类情况,有一个注释或Assert.xyz之类的东西远没有JUnit那么笨拙,更符合JUnit的精神。
例如,您想为下面提到的代码片段编写Junit
public int divideByZeroDemo(int a,int b){
return a/b;
}
public void exceptionWithMessage(String [] arr){
throw new ArrayIndexOutOfBoundsException("Array is out of bound");
}
上面的代码用于测试可能发生的某些未知异常,下面的代码用于使用自定义消息断言某些异常。
@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"});
}
我在这里尝试了许多方法,但它们要么很复杂,要么不太符合我的要求。事实上,可以非常简单地编写一个助手方法:
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;
}
}
这样使用:
assertException(new BlastContainer() {
@Override
public void test() throws Exception {
doSomethingThatShouldExceptHere();
}
});
零依赖:无需mockito,无需powermock;在期末考试中表现很好。
如前所述,JUnit中有许多处理异常的方法。但在Java8中还有另一个:使用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接受一个函数接口,它的实例可以用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
披露:我是博客和项目的作者。
我想评论一下这个问题的解决方案,它避免了任何与异常相关的JUnit代码。
我使用assertTrue(布尔值)和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);
}
我使用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
}
}
}
您必须定义FunctionalInterface,因为Runnable没有声明所需的抛出。
@FunctionalInterface
public interface ThrowingRunnable {
void run() throws Throwable;
}
方法如下:
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);