java:S1118 causes problems with code coverage in quality gate

Hi,

our developers reported this problem:
a utility class did not have a private constructor, so the rule
java:S1118 “Utility classes should not have public constructors” brought up an issue.

But after fixing this issue the quality gate failed again because of insufficient code coverage - the condition
has 80% code coverage.

2024-01-26_16h55_57

The Maven build uses Jacoco 0.8.7 and Jacoco 0.8.0 implemented several filters, including

SonarJava was updated using Jacoco 0.8.0 in 2018 already
https://sonarsource.atlassian.net/browse/SONARJAVA-2608

It doesn’t make sense to have tests for private empty constructors and given the facts above,
this should not have happened.

They used a @Generated annotation to get rid of the problem, but it doesn’t feel right and jacoco github

has also

Remark: A Generated annotation should only be used for code actually generated by compilers or tools, never for manual exclusion.

What went wrong, is it a bug in Jacoco or in Sonarqube ?
Has anyone else similar problems ?

Gilbert

As long as a file is present in a coverage report, the coverage report is 100% responsible for reporting what lines of that file can be covered by code. Since I can see a little bit of code being covered in that file, this means JaCoCo has decided the line can be covered by code. SonarQube has fully delegated that to the JaCoCo report. :slight_smile:

So I suggest raising an issue with JaCoCo.

OK, we’ve found the reason.
The Jacoco filter “Private empty constructors that do not have arguments” means

public class VsnrUtils() {
  
}

BUT the compliant example of java:S1118 “Utility classes should not have public constructors” has

Means the developers have used the example in the Sonarqube rule description

public class VsnrUtils() {
  throw new IllegalStateException();
}

and Jacoco complained. Simply deleting the throws fixed the issue with Jacoco.

The rule description should be corrected.

Hello @anon67236913,

Thanks for your message. I think this situation might be confusing. However, when I looked a bit closer, I felt the rule’s Compliant example does make sense.

There’s a difference between having an empty private constructor and a private constructor throwing an Exception. In the first case, we want to restrict the ability to create a class only inside a class itself. It could be when you want to use a factory method or implement some sort of IoC. So you still want to have a constructor but its visibility should be private. So technically there could still be created instances of the class.

In the case of Utility classes, the story is different. You don’t want the instances of that class at all. To implement this restriction, an exception should be thrown. And this is actually what the rule is about.

So now we have a problem, There’s a piece of code that makes sense, but is impossible to cover with tests.

Unfortunately, this happens sometimes, as tools are not perfect. Sometimes we have to accept the low code coverage. And sadly, there’s no such functionality either in JaCoCo or SonarQube as “accept/ignore Code coverage”.

In your case, I would think of a few things:

  • Ask JaCoCo if they can filter out your case too or at least open this discussion
  • In our team, we usually can accept lower coverage and still can merge if the coverage on the new code after the merge still satisfies the requirement. We often put comments explaining why some lines of code are impossible to cover.

Finally, I would love to avoid changing the rule just because some lines are impossible to cover. However, I truly understand your pain point (we have similar situations from time to time), so we’ll think what we can do, so you have a better User experience.

Best,
Margarita

Hi @Margarita_Nedzelska ,

of course you’re right, as throwing an execption is known/best practice - used that myself.
I’ve created an issue at JaCoCo github

Maybe you can vote for it, thanks.

Gilbert

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.