I am authoring a java library. Some of the classes that are meant to be used by library users, hold native system resources (over JNI). I\'d like to ensure that the user \"dispo
In general, if you could reliably test whether a resource has been closed, you could just close it yourself.
The first thing to do is to make handling resource easy for the client. Use the Execute Around idiom.
As far as I know, the only use of execute around for resource handling in the Java library is java.security.AccessController.doPrivileged
and that's special (the resource is a magic stack frame, that you really don't want to leave open). I believe Spring has long had a much-needed JDBC library for this. I was certainly using execute-around (didn't know it was called that at the time) for JDBC shortly after Java 1.1 made it vaguely practical.
The library code should look something like:
@FunctionalInterface
public interface WithMyResource {
R use(MyResource resource) throws MyException;
}
public class MyContext {
// ...
public R doAction(Arg arg, WithMyResource with) throws MyException {
try (MyResource resource = acquire(arg)) {
return with.use(resource);
}
}
(Do get the type parameter declarations in the right place.)
Client side usage looks something like:
MyType myResult = yourContext.doContext(resource -> {
...blah...;
return ...thing...;
});
Back to testing. How do we make it easy to test even if the testee exfiltrates the resource from the execute around or some other mechanism is available?
The obvious answer is that you provide the execute around solution to the test. You will need to provide some execute around-using API to verify all of your resources that have been acquired in the scope have also been closed. This should be paired with the context the resource is acquired from rather than using global state.
Depending upon which testing framework your clients are using, you may be able to offer something better. JUnit5, for instance, has an annotation-based extension facility which allows you to supply the context as an argument and also apply checks after each test has executed. (But I haven't used it much, so I'm not going to say anything more.)