How to replace Lombok @Generated with a custom annotation for JaCoCo code coverage reports

Maybe it is Monday and you are pairing to catch up while drinking your morning coffee. When you randomly open the project dependencies file and start wondering why Lombok is there. In this post Guille tell us how to get rid of Lombok!

How to replace Lombok @Generated with a custom annotation for JaCoCo code coverage reports

Maybe it is Monday and you are pairing to catch up while drinking your morning coffee. When you randomly open the project dependencies file and start wondering why Lombok is there. That was us the other day. So I decided it was time to get rid of it.

Kotlin has in itself mostly all the tools that Lombok provided to Java. But after migrating from Java to Kotlin we still had the @Generated tag to exclude classes from JaCoCo code coverage reports.

The tech-debt ticket was on standby for a few months until we finally decided to get rid of Lombok. And that was possible with a very simple and custom annotation:

@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS)
annotation class ExcludeClassFromJacocoGeneratedReport

As you can see, you don’t longer need to name the annotation like @Generated. Which is good, because it’s not very self-explanatory.

The name has to include the Generated word though. See this repo.

There’s that, and a retention policy we set as runtime: @Retention(AnnotationRetention.RUNTIME)

See here: https://github.com/jacoco/jacoco/releases/tag/v0.8.2

We can make another tag for method-level exclusion, if wanted.

@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class ExcludeMethodFromJacocoGeneratedReport

Now, how can we quickly try it out? Let’s make a PoC. Class A should be covered. Class B should be excluded. And class C should have one method covered and one method excluded.

package com.byeLombok

import com.byeLombok.common.ExcludeClassFromJacocoGeneratedReport
import com.byeLombok.common.ExcludeMethodFromJacocoGeneratedReport

class PocClassA {
    fun go() = "it's fine"
}

@ExcludeClassFromJacocoGeneratedReport
class PocClassB {
    fun go() = "it's fine"

    fun method() = println("i'm a rebel'")
}

class PocClassC {
    @ExcludeMethodFromJacocoGeneratedReport
    fun go() = "it's fine"

    fun method() = println("i'm a rebel'")
}
package com.byeLombok

import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test

class PocTest {
    @Test
    internal fun `only covers class A, excludes B, and one method from C lacks of test coverage`() {
        val dummyValueA = PocClassA().go()
        val dummyValueB = PocClassB().go()
        val dummyValueC = PocClassB().go()

        assertEquals(dummyValueA, dummyValueB)
        assertEquals(dummyValueB, dummyValueC)
    }
}

This is the report from SonarCloud, running JaCoCo with CircleCi:

SonarCloud report marking a class A as covered and a class C as not covered

As you can see, the green color bar marks the covered lines. The red ones, are the lines without test coverage. And the lines with no colors were actually excluded.

Summary

You can create one or two annotations to exclude both classes and methods from JaCoCo test coverage report, and get rid of Lombok. The annotations have one requirement though: they have to include the word Generated.