我正在编写一些JUnit测试,以验证是否抛出了类型MyCustomException的异常.但是,此异常多次包含在其他异常中,例如在InvocationTargetException中,而InvocationTargetException又包含在RuntimeException中.
什么是确定MyCustomException是否以某种方式导致我实际捕获的异常的最佳方法?我想做这样的事情(见下划线):
Run Code Online (Sandbox Code Playgroud)try { doSomethingPotentiallyExceptional(); fail("Expected an exception."); } catch (RuntimeException e) { if (!e.wasCausedBy(MyCustomException.class) fail("Expected a different kind of exception."); }
我想避免getCause()深入调用一些"层",以及类似的丑陋工作.有更好的方法吗?
(显然,Spring有NestedRuntimeException.contains(Class),它可以做我想要的 - 但我没有使用Spring.)
CLOSED: 好的,我猜有一个实用方法真的没有绕过:-)感谢所有回复的人!
I want to catch an exception, that is nested into another exception. I'm doing it currently this way:
} catch (RemoteAccessException e) {
if (e != null && e.getCause() != null && e.getCause().getCause() != null) {
MyException etrp = (MyException) e.getCause().getCause();
...
} else {
throw new IllegalStateException("Error at calling service 'service'");
}
}
Run Code Online (Sandbox Code Playgroud)
Is there a way to do this more efficient and elegant?
从析构函数抛出异常的主要问题是,在析构函数被调用的那一刻,另一个异常可能是"在飞行中"(std::uncaught_exception() == true),因此在这种情况下做什么并不明显.用新的"覆盖"旧的异常将是处理这种情况的可能方法之一.但是决定在这种情况下必须调用std::terminate(或其他std::terminate_handler).
C++ 11通过std::nested_exception类引入了嵌套异常功能.该特征可用于解决上述问题.旧(未捕获)异常可能只是嵌套到新异常中(反之亦然?)然后可能抛出嵌套异常.但是没有使用这个想法.std::terminate在C++ 11和C++ 14中仍然会出现这种情况.
所以问题.是否考虑过嵌套异常的想法?它有什么问题吗?是不是在C++ 17中会改变这种情况?
所以在C++中嵌套异常的方法std::nested_exception是:
void foo() {
try {
// code that might throw
std::ifstream file("nonexistent.file");
file.exceptions(std::ios_base::failbit);
}
catch(...) {
std::throw_with_nested(std::runtime_error("foo failed"));
}
}
Run Code Online (Sandbox Code Playgroud)
但是这种技术在每个级别使用显式的try/catch块,希望嵌套异常,这至少可以说是丑陋的.
Jon Kalb扩展为"责任获取是初始化"的RAII,是处理异常而不是使用显式try/catch块的更清晰的方法.使用RAII,显式try/catch块主要仅用于最终处理异常,例如,为了向用户显示错误消息.
查看上面的代码,在我看来,输入foo()可以被视为有责任报告任何异常,std::runtime_error("foo failed")并将细节嵌套在嵌套异常中.如果我们可以使用RAII来获得这个责任,那么代码看起来会更清晰:
void foo() {
Throw_with_nested on_error("foo failed");
// code that might throw
std::ifstream file("nonexistent.file");
file.exceptions(std::ios_base::failbit);
}
Run Code Online (Sandbox Code Playgroud)
有没有办法在这里使用RAII语法来替换显式的try/catch块?
为此,我们需要一种类型,当它的析构函数被调用时,检查析构函数调用是否是由异常引起的,如果是,则嵌套该异常,并抛出新的嵌套异常,以便正常地展开.这可能看起来像:
struct Throw_with_nested {
const char *msg;
Throw_with_nested(const char *error_message) : msg(error_message) {}
~Throw_with_nested() {
if (std::uncaught_exception()) {
std::throw_with_nested(std::runtime_error(msg));
}
}
};
Run Code Online (Sandbox Code Playgroud)
但是,std::throw_with_nested()要求"当前处理的异常"处于活动状态,这意味着除了catch块的上下文之外它不起作用.所以我们需要这样的东西:
~Throw_with_nested() {
if (std::uncaught_exception()) {
try …Run Code Online (Sandbox Code Playgroud) 我刚刚完成了一个C++程序,我已经实现了自己的异常(虽然派生自std :: exception).当一个异常导致连锁反应,向上传播错误并引起其他异常时,我应用的做法是在模块的每个适当步骤(读取类)中连接错误消息.即旧的异常本身被删除并创建一个新的异常,但是有一个更长的错误消息.
这可能适用于我的小程序,但我最终对我的方法不太满意.例如,除最后一个异常外,不保留行号(虽然目前不应用)和文件名; 实际上,在第一个例外中,这些信息最受关注.
我认为通过将异常链接在一起可以更好地处理这个问题; 即在新异常的构造函数中提供旧异常.但是如何实施呢?当它们超出方法的范围时,异常是否会死亡,从而阻止使用异常指针?如果异常可以是任何派生类,如何复制和存储异常?
这最终促使我考虑在C++中链接异常是否是一个好主意.也许应该只创建一个异常然后添加额外的数据(就像我一直在做的那样,但可能会以更好的方式)?
你对此有何回应?是否应将由另一个引起的异常链接在一起以保留某种"异常追踪" - 以及如何实施? - 或者是否应该使用单个例外并附加其他数据 - 应该如何做?
例如,我刚刚看到了一个包含 dynamic_cast from std::exceptionto的代码std::nested_exception,
try {
std::throw_with_nested(std::runtime_error("error"));
} catch (std::exception &e) {
auto &nested = dynamic_cast<std::nested_exception&>(e);
std::cout << "ok" << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
第一次,我认为这段代码不会被编译,因为std::nested_exception它不是派生自std::exception,我希望dynamic_cast会对继承进行静态检查,但我错了。
虽然我找不到明确提到dynamic_cast允许这样做的相关标准规范,但我确认所有三个主要编译器(clang/gcc/msvc)都允许dynamic_cast完全不相关的类型。
但是,仍然std::nested_exception不是从 派生的std::exception,所以我认为dynamic_cast将抛出bad_alloc异常并且"ok"永远不会打印。我又错了。
现在,我想知道这是如何工作的。这对std::exceptionand 来说有什么特别的和特殊的std::nested_exception吗?或者,我可以dynamic_cast<A&>(b)在A对象的类型和类型b没有公共基类的情况下取得另一个成功吗?
JUnit 5 如何允许检查嵌套异常?我正在寻找类似于在 JUnit 4 中借助 a 可以完成的操作@org.junit.Rule,如以下代码片段所示:
class MyTest {
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Test
public void checkForNestedException() {
// a JPA exception will be thrown, with a nested LazyInitializationException
expectedException.expectCause(isA(LazyInitializationException.class));
Sample s = sampleRepository.findOne(id);
// not touching results triggers exception
sampleRepository.delete(s);
}
}
Run Code Online (Sandbox Code Playgroud)
根据评论编辑:
Assertions.assertThrows(LazyInitializationException.class)在 JUnit 5 中不起作用,因为LazyInitializationException是 的嵌套异常(原因)JpaSystemException。
只能检查外部异常,这不能按预期工作:
// assertThrows does not allow checking for nested LazyInitializationException
Assertions.assertThrows(JpaSystemException.class, () -> {
Sample s = sampleRepository.getOne(id);
// not …Run Code Online (Sandbox Code Playgroud)