如果在另一个线程中抛出异常,则单元测试失败
Failing a unit test if an exception is thrown in another thread
目前,每当我需要通过测试来响应另一个线程中抛出的异常时,我都会这样写:
package com.example;
import java.util.ArrayList;
import java.util.List;
import org.testng.annotations.Test;
import static java.util.Arrays.asList;
import static java.util.Collections.synchronizedList;
import static org.testng.Assert.fail;
public final class T {
@Test
public void testFailureFromLambda() throws Throwable {
final List<Throwable> errors = synchronizedList(new ArrayList<>());
asList("0", "1", "2").parallelStream().forEach(s -> {
try {
/*
* The actual code under test here.
*/
throw new Exception("Error " + s);
} catch (final Throwable t) {
errors.add(t);
}
});
if (!errors.isEmpty()) {
errors.forEach(Throwable::printStackTrace);
final Throwable firstError = errors.iterator().next();
fail(firstError.getMessage(), firstError);
}
}
}
同步列表可能会被替换为 AtomicReference<Throwable>
,但大体上代码几乎保持不变。
是否有任何标准的(更简洁的)方法可以使用 Java(TestNG, JUnit, Hamcrest, AssertJ 等中可用的任何测试框架来做同样的事情? )?
默认情况下,TestNG 会在抛出异常时使测试方法失败。我相信 JUnit 也会发生同样的事情,如果它抛出意外异常,它会将测试标记为 errored
。
如果您要处理 Streams
,那么您需要将其包装在 RuntimeException
变体中,这样 Java 就不会抱怨。 TestNG 会自动使测试失败。
这是一个示例:
@Test
public void testFailureFromLambdaRefactored() {
asList("0", "1", "2").parallelStream().forEach(s -> {
try {
/*
* The actual code under test here.
*/
if (s.equals("2")) {
throw new Exception("Error " + s);
}
} catch (final Throwable t) {
throw new RuntimeException(t);
}
});
}
这是针对涉及 lambda 和流的场景。一般来说,如果您想知道从 @Test
方法衍生出来的新线程中发生的异常,那么您需要使用 ExecutorService
.
这是一个示例:
@Test
public void testFailureInAnotherThread() throws InterruptedException, ExecutionException {
List<String> list = asList("0", "1", "2");
ExecutorService service = Executors.newFixedThreadPool(2);
List<Future<Void>> futures = service.invokeAll(Arrays.asList(new Worker(list)));
for (Future future : futures) {
future.get();
}
}
public static class Worker implements Callable<Void> {
private List<String> list;
public Worker(List<String> list) {
this.list = list;
}
@Override
public Void call() throws Exception {
for (String s : list) {
if (s.equals("2")) {
throw new Exception("Error " + s);
}
}
return null;
}
}
目前,每当我需要通过测试来响应另一个线程中抛出的异常时,我都会这样写:
package com.example;
import java.util.ArrayList;
import java.util.List;
import org.testng.annotations.Test;
import static java.util.Arrays.asList;
import static java.util.Collections.synchronizedList;
import static org.testng.Assert.fail;
public final class T {
@Test
public void testFailureFromLambda() throws Throwable {
final List<Throwable> errors = synchronizedList(new ArrayList<>());
asList("0", "1", "2").parallelStream().forEach(s -> {
try {
/*
* The actual code under test here.
*/
throw new Exception("Error " + s);
} catch (final Throwable t) {
errors.add(t);
}
});
if (!errors.isEmpty()) {
errors.forEach(Throwable::printStackTrace);
final Throwable firstError = errors.iterator().next();
fail(firstError.getMessage(), firstError);
}
}
}
同步列表可能会被替换为 AtomicReference<Throwable>
,但大体上代码几乎保持不变。
是否有任何标准的(更简洁的)方法可以使用 Java(TestNG, JUnit, Hamcrest, AssertJ 等中可用的任何测试框架来做同样的事情? )?
默认情况下,TestNG 会在抛出异常时使测试方法失败。我相信 JUnit 也会发生同样的事情,如果它抛出意外异常,它会将测试标记为 errored
。
如果您要处理 Streams
,那么您需要将其包装在 RuntimeException
变体中,这样 Java 就不会抱怨。 TestNG 会自动使测试失败。
这是一个示例:
@Test
public void testFailureFromLambdaRefactored() {
asList("0", "1", "2").parallelStream().forEach(s -> {
try {
/*
* The actual code under test here.
*/
if (s.equals("2")) {
throw new Exception("Error " + s);
}
} catch (final Throwable t) {
throw new RuntimeException(t);
}
});
}
这是针对涉及 lambda 和流的场景。一般来说,如果您想知道从 @Test
方法衍生出来的新线程中发生的异常,那么您需要使用 ExecutorService
.
这是一个示例:
@Test
public void testFailureInAnotherThread() throws InterruptedException, ExecutionException {
List<String> list = asList("0", "1", "2");
ExecutorService service = Executors.newFixedThreadPool(2);
List<Future<Void>> futures = service.invokeAll(Arrays.asList(new Worker(list)));
for (Future future : futures) {
future.get();
}
}
public static class Worker implements Callable<Void> {
private List<String> list;
public Worker(List<String> list) {
this.list = list;
}
@Override
public Void call() throws Exception {
for (String s : list) {
if (s.equals("2")) {
throw new Exception("Error " + s);
}
}
return null;
}
}