I have a class in Sonar:
public class Foo {
..... much code ....
}
And Sonar is reporting 1/2 branches covered on that public class Foo
line. What does this mean? How do you test a line which declares a class?
Edit: in case it matters, this is Sonar v3.5.
Edit 2: A screenshot showing what I mean, note the 1/2 beside the "public class" on line 9. When hovering above this I get a tooltip stating "1 branches are covered by tests"
Edit #3: Ok, upon a bit more investigation, I've narrowed it down to smallest snippet I can find that triggers this:
public class Foo {
Foo(final String s) {
assert (s != null);
}
}
If that assert doesn't exist in the constructor, the "N/2 branches covered" flag doesn't get generated in Sonar. If the assert is gone, then the flag goes away as well. So my guess is that it's based upon the branches within the constructor? (this code has 0/4 branches covered for the assert line, and 0/2 for the public class line).
SonarQube itself does not calculate coverage. To include coverage results in your analysis, you must set up a third-party coverage tool and configure SonarQube to import the results produced by that tool. Below, you'll find guidelines and resources, as well as language- and tool-specific analysis parameters.
To add coverage to your Maven project you need to use the jacoco-maven-plugin and its report goal to create a code coverage report. Typically, you would create a specific Maven profile for executing the unit tests with instrumentation and producing the coverage report only on demand.
to increase your code coverage, the suggestion is to write tests. It can be unit tests (easiest way) or other tests (Integration test, System tests) which may contribute to coverage when a tool can report coverage for these.
Branches (C1 Coverage)JaCoCo also calculates branch coverage for all if and switch statements. This metric counts the total number of such branches in a method and determines the number of executed or missed branches. Branch coverage is always available, even in absence of debug information in the class files.
It looks like this is an issue related to the JaCoCo code coverage component of Sonar. JaCoCo works on compiled bytecode rather than Java source, and the Java compiler can produce code which is not directly related to the underlying source.
Looking at the docs for JaCoCo, there's a section which reads (emphasis added):
In some situations it is not obvious, why particular lines do have highlighting or have a particular color. The reason is that the underlying code coverage library JaCoCo works on Java class files only. In some cases the Java compiler creates extra byte code for a particular line of source code. Such situations might be filtered by future versions of JaCoCo/EclEmma.
Following the link in the passage takes you to the FilteringOptions page on the Jacoco's GH site, and it mentions a number of ways in which the JDK can potentially produce code which will will trigger these "spurious" code coverage warnings.
However, that's not what's at play here (or not exactly).
As mentioned JaCoCo works on Java bytecode, so any code which is produced by the compiler that isn't directly attributed to the source will count towards coverage.
In my specific case, I had an assert
which, in the source, represents a branch at the point where the assert happens, but also at a "global" level. If you look at the bytecode for the Foo
class defined above (do a javap -c Foo
), you'll see:
Compiled from "Foo.java"
public class Foo extends java.lang.Object{
static final boolean $assertionsDisabled;
Foo(java.lang.String);
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: getstatic #2; //Field $assertionsDisabled:Z
7: ifne 22
10: aload_1
11: ifnonnull 22
14: new #3; //class java/lang/AssertionError
17: dup
18: invokespecial #4; //Method java/lang/AssertionError."<init>":()V
21: athrow
22: return
static {};
Code:
0: ldc_w #5; //class Foo
3: invokevirtual #6; //Method java/lang/Class.desiredAssertionStatus:()Z
6: ifne 13
9: iconst_1
10: goto 14
13: iconst_0
14: putstatic #2; //Field $assertionsDisabled:Z
17: return
Note line 7, which is a conditional branch dependent upon whether or not assertions are enabled. Thus if you have a class with a plain Java assert
in it, you'll have this branch somewhere in the bytecode, and this is what produces the "N/2 branches executed" coverage warning on the class declaration, where N is either 0 or 1 depending upon if the class was ever exercised by a test (1) or not (0).
Edit: note that this is also mentioned in https://sourceforge.net/apps/trac/eclemma/wiki/FilteringOptions:
Blocks that throw AssertionErrors - Entire block should be ignored if a condition (if !assertion throw new AssertionError)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With