0

我有以下代码:

Collection<String> errors = ...;
try (InputStream stream = My.class.getResourceAsStream(resource)) {
   // do stuff
}
catch(IOException ex) {
   errors.add("Fail");
}

当我提供的(有效)输入流应该被关闭时,我正在尝试使用 Byteman Junit Runner 来触发 IOException:

@RunWith(BMUnitRunner.class)
public class MyTest {

    private My my = new My();

    @BMRule(
       name = "force_read_error",
       targetClass = "java.io.InputStream",
       targetMethod = "close()",
       action = "throw new IOException(\"bazinga\")"
    )
    @Test
    public void catches_read_error() throws IOException {
       Collection<String> errors = my.foo("/valid-resource-in-classpath");

       assertThat(errors).containsExactly("Fail");
    }
}

我的测试失败:errors 总是空的,这意味着 Byteman 规则显然没有被执行(它被代理很好地加载,所以我不明白发生了什么)。

如何在通过 try-with-resources 调用的 close 方法上触发 IOException?

4

1 回答 1

1

您的规则没有触发,因为调用时收到的流对象的类

InputStream stream = My.class.getResourceAsStream(resource)

不是“java.io.InputStream”类。它是一个扩展“java.io.InputStream”的类,很可能是“BufferedInputStream”。

要告诉 byteman “为任何扩展 java.io.InputStream 的类触发规则”,您需要在类名前放置一个 '^':

targetClass = "^java.io.InputStream"

此更改可能会产生不希望的副作用,即当扩展“java.io.InputStream”的其他对象关闭时也会触发该规则。为了防止这种情况发生,应该在规则中添加一个条件,仅当调用者匹配“My”类的“foo”方法时才会触发。Byteman 有一个名为“callerMatches”的辅助方法(另请参阅高级教程

您的案例的工作条件是:

condition = "callerMatches(\".+My.foo\",true,true)"

作为 BMRule 注释的完整 Byteman 规则定义应如下所示:

@BMRule(
    name = "force_read_error",
    targetClass = "^java.io.InputStream",
    targetMethod = "close()",
    condition = "callerMatches(\".+My.foo\",true,true)",
    action = "throw new java.io.IOException(\"bazinga\")"
)
于 2015-03-25T17:11:11.180 回答