The Java-Gradle-jacoco task adds synthetic fields while the Spring application is running, causing the test to calculate the number of declared fields in the class to fail

Gradle-jacoco task adds synthetic fields while the Spring application is running, causing the test to calculate the number of declared fields in the class to fail… here is a solution to the problem.

Gradle-jacoco task adds synthetic fields while the Spring application is running, causing the test to calculate the number of declared fields in the class to fail

I have this class :

public class UserPurchaseUtil {
    public static final String JSON_PROP_ID = "id";
    public static final String JSON_PROP_USER_ID = "user_id";
     See Confluence: First Time Member Promotion (FTM)
    public static final String JSON_PROP_LAST_PURCHASE_TIME = "last_purchase_time";

}

Then, I want to make sure

that I pay attention to the changes in all the values in this class, and by “following”, I want to make sure

  • Every time I remove or add some constants, the test fails;
  • Check all values.

So I have this test:

@Slf4j
@RunWith(MockitoJUnitRunner.class)
public class UserPurchaseUtilTest {

@Test
    public void testNumberOfConstantsAndTheirValues() {
        int numberOfConstants = UserPurchaseUtil.class.getDeclaredFields().length;
         just to ensure we test all the constants' values when we add new ones. Now is 3.
        Assert.assertEquals(3, numberOfConstants);

Assert.assertEquals("id", UserPurchaseUtil.JSON_PROP_ID);
        Assert.assertEquals("user_id", UserPurchaseUtil.JSON_PROP_USER_ID);
        Assert.assertEquals("last_purchase_time", UserPurchaseUtil.JSON_PROP_LAST_PURCHASE_TIME);
    }
}

However, this simple test failed :

expected:<3> but was:<4>
Expected :3
Actual   :4
 <Click to see difference>

So, why?

Edit:

Oh my goodness. Now when debugging, I can now see the fourth field.

private static transient boolean[] com.xxx.utils.UserPurchaseUtil.$jacocoData

This is a Spring Boot project.

enter image description here

Solution

Gradle jacoco-related tasks like jacocoTestReport and jacocoTestCoverageVerification are interfering with my reflection checks for all classes.

I found this issue :

https://github.com/jacoco/jacoco/issues/168

My code uses reflection. Why does it fail when I execute it with JaCoCo?

To collect execution data JaCoCo instruments the classes under test which adds two members to the classes: A private static field $jacocoData and a private static method $jacocoInit(). Both members are marked as synthetic.

Please change your code to ignore synthetic members. This is a good practice anyways as also the Java compiler creates synthetic members in certain situation.

I

think in this case I should ignore synthetic members when counting. isSynthetic() means that a member is (somewhat) added by the compiler at runtime.

So it will be like:

int nonSynthetic = 0;
Field[] allFields = UserPurchaseUtil.class.getDeclaredFields();
for (Field f: allFields) {
     ignore synthetic methods, which are added at runtime by jacoco (or other libraries)
    if (!f.isSynthetic()) {
        nonSynthetic ++;
    }
}
Assert.assertEquals(3, nonSynthetic);

Related Problems and Solutions