Improving JaCoCo's branch coverage of a Groovy assert statement
Damir Murat <[email protected]>
| Newsgroups | gmane.comp.lang.groovy.user |
|---|---|
| Message-ID | <a6ac5667-e565-4801-b040-30be1ad7aa88@Spark> |
JaCoCo has long-standing issues with covering calls of methods that throw exceptions.
When such methods are called inside of, if/else branches for example, the result is
partial coverage reported for those branches.
However, there is a JaCoCo idiom (https://github.com/jacoco/jacoco/issues/370#issuecomment-267854179)
that we can use to avoid uncovered code in those cases. The basic idea is to create
and return an exception from a called method and throw that exception from a caller,
like in:
void fail() {
throw create();
}
RuntimeException create() {
return new RuntimeException();
}
How this relates to the Groovy assert statement? For example, for a simple assert
statement like
assert firstName != null
Groovy generates something like
ValueRecorder var1 = new ValueRecorder();
try {
String var10000 = this.firstName;
var1.record(var10000, 8);
var1.record(var10000, 8);
if (var10000 != null) {
var1.clear();
} else {
ScriptBytecodeAdapter.assertFailed(AssertionRenderer.render("assert firstName != null", var1), (Object)null);
}
} catch (Throwable var3) {
var1.clear();
throw var3;
}
The problem with generated code is a
ScriptBytecodeAdapter.assertFailed(AssertionRenderer.render("assert firstName != null", var1), (Object)null);
method call. Inside that method, an exception is created and thrown. Since JaCoCo cannot
cover that line completely, the branch
if (var10000 != null)
will be reported as partially covered.
To avoid those issues, ScriptBytecodeAdapter.assertFailed() can be adapted (or a new
method can be introduced) to return the exception instead of throwing it. And then,
the calling generated code can throw that returned exception.
I have a small project demonstrating the issue and a possible solution here:
https://github.com/dmurat/groovy-assert-jacoco-coverage-problem
What do you think?
Tnx