0

I am trying to generate jacoco code coverage report for my restassured testcases pointing to the rest end points. I am using springboot application. My controller class looks like :

@RestController
public class ChallengeController {

    private static final Logger LOGGER = LoggerFactory.getLogger(ChallengeController.class);
    @Autowired
    private ChallengeService challengeService;


    public ChallengeController(ChallengeService challengeService) {
        this.challengeService = challengeService;
    }

    


    @GetMapping(path = "/hello",
            produces = MediaType.TEXT_PLAIN_VALUE)
    @ResponseBody
    public String hello() {
        return "hello";
    }
    @GetMapping(path = "/hello/greeting/{name}",
            produces = MediaType.TEXT_PLAIN_VALUE)
    @ResponseBody
    public String greeting(@PathVariable(value = "name") String name) {
        return challengeService.greeting(name);
    }

}

My service class looks like

@Service
@Component
public class ChallengeService {

    
    private static final Logger LOGGER = LoggerFactory.getLogger(ChallengeService.class);



    public ChallengeService() {

    }


    public String greeting(String name) {
        return "hello " + name;
    }
}

My POM looks like

  <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${maven-surefire-plugin.version}</version>
                <configuration>
                    <argLine>${surefireArgLine}</argLine>
                    <excludes>
                        <exclude>**/ResilienceTests.java</exclude>
                        <exclude>**/CucumberTest.java</exclude>
                        <exclude>**/DummyChallengeTest.java</exclude>
                    </excludes>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.jacoco</groupId>
                <artifactId>jacoco-maven-plugin</artifactId>
                <version>${jacoco-maven-plugin.version}</version>
                <executions>
                    <execution>
                        <id>pre-unit-test</id>
                        <goals>
                            <goal>prepare-agent</goal>
                        </goals>
                        <configuration>
                            <destFile>${project.build.directory}/coverage-reports/jacoco-ut.exec</destFile>
                            <propertyName>surefireArgLine</propertyName>
                        </configuration>
                    </execution>
                    <execution>
                        <id>post-unit-test</id>
                        <phase>test</phase>
                        <goals>
                            <goal>report</goal>
                        </goals>
                        <configuration>
                            <dataFile>${project.build.directory}/coverage-reports/jacoco-ut.exec</dataFile>
                            <outputDirectory>${project.reporting.outputDirectory}/jacoco-ut</outputDirectory>
                        </configuration>
                    </execution>
                    <execution>
                        <id>pre-integration-test</id>
                        <phase>validate</phase>
                        <goals>
                            <goal>prepare-agent-integration</goal>
                        </goals>
                        <configuration>
                            <destFile>${project.build.directory}/coverage-reports/jacoco-it.exec</destFile>
                            <propertyName>failsafeArgLine</propertyName>
                        </configuration>
                    </execution>
                    <execution>
                        <id>post-integration-test</id>
                        <phase>post-integration-test</phase>
                        <goals>
                            <goal>report-integration</goal>
                        </goals>
                        <configuration>
                        <!-- excluding everything except controller class -->
                            <excludes>
                                <exclude>**/common/*.class</exclude>
                                <exclude>**/config/*.class</exclude>
                                <exclude>**/domain/*.class</exclude>
                                <exclude>**/exception/*.class</exclude>
                                <exclude>**/mapper/*.class</exclude>
                                <exclude>**/service/*.class</exclude>
                                <exclude>**/simulator/Main.class</exclude>
                            </excludes>
                            <dataFile>${project.build.directory}/coverage-reports/jacoco-it.exec</dataFile>
                            <outputDirectory>${project.reporting.outputDirectory}/jacoco-it</outputDirectory>
                            <sourceEncoding>${project.build.sourceEncoding}</sourceEncoding>
                            <classFilesDIrectory>${project.build.directory}/classes</classFilesDIrectory>
                        </configuration>
                    </execution>
                    <!-- merging jacoco reports-->
                    <execution>
                        <id>report-aggregate</id>
                        <phase>verify</phase>
                        <goals>
                            <goal>report-aggregate</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>merge-results</id>
                        <phase>verify</phase>
                        <goals>
                            <goal>merge</goal>
                        </goals>
                        <configuration>
                            <fileSets>
                                <fileSet>
                                    <directory>${project.build.directory}/coverage-reports/</directory>
                                    <includes>
                                        <include>*.exec</include>
                                    </includes>
                                </fileSet>
                            </fileSets>
                            <destFile>${project.build.directory}/coverage-reports/aggregate.exec</destFile>
                        </configuration>
                    </execution>
                    <execution>
                        <id>post-merge-report</id>
                        <phase>verify</phase>
                        <goals>
                            <goal>report</goal>
                        </goals>
                        <configuration>
                            <dataFile>${project.build.directory}/coverage-reports/aggregate.exec</dataFile>
                            <outputDirectory>${project.reporting.outputDirectory}/jacoco-aggregate</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

     <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-failsafe-plugin</artifactId>
                <version>${maven-failsafe-plugin.version}</version>
                <executions>
                    <execution>
                        <id>integration-tests</id>
                        <goals>
                            <goal>integration-test</goal>
                            <goal>verify</goal>
                        </goals>
                        <configuration>
                            <argLine>${failsafeArgLine}</argLine>
                            <includes>
                                <include>**/*CucumberTest.java</include>
                                <include>**/*DummyChallengeTest.java</include>
                            </includes>
                            <forkCount>1</forkCount>
                            <reuseForks>true</reuseForks>
                            <!-- When running as a Maven plugin, the JaCoCo agent configuration is prepared by invoking the prepare-agent
                            or prepare-agent-integration goals, before the actual tests are run. This sets a property named argLine which
                            points to the JaCoCo agent, later passed as a JVM argument to the test runner -->

                        </configuration>
                    </execution>
                </executions>
            </plugin>

My test class looks like


@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

public class DummyChallengeTest extends AbstractSteps {



    @Test
    public void testHelloEndpoint() {        
        RestAssured.baseURI = "http://localhost:8082";
        RestAssured.basePath ="/hello";
        Response res = given()
                .when().get()
                .then().log().all()
                .extract().response();
        Assert.assertEquals("hello", res.getBody().asString());
    }

    @Test
    public void testGreetingEndpoint() {
        String uuid = UUID.randomUUID().toString();
        //getServiceUrl() returning me the application base url
        String requestUrl = getServiceUrl() + "/hello/greeting/{name}";
                given()
                .pathParam("name", uuid)
                .when().get(requestUrl)
                .then().log().all()
                .statusCode(200)
                .body(is("hello " + uuid)).log().all().extract();
    }
}

Inside target/site/jacoco-it/jacoco-sessions.html I can locate my controller and service java files.

I am getting partial coverage on my controller file only these line


   @Autowired
    private ChallengeService challengeService;


    public ChallengeController(ChallengeService challengeService) {
        this.challengeService = challengeService;
    }

However I am not getting any coverage on the end points, specifically on these lines

@GetMapping(path = "/hello",
            produces = MediaType.TEXT_PLAIN_VALUE)
    @ResponseBody
    public String hello() {
        return "hello";
    }
    @GetMapping(path = "/hello/greeting/{name}",
            produces = MediaType.TEXT_PLAIN_VALUE)
    @ResponseBody
    public String greeting(@PathVariable(value = "name") String name) {
        return challengeService.greeting(name);
    }

Note : 1. My restassured testcases runs fine and all are passing. 2. When I am using Quarkus application, and Quarkus Test, then this same jacoco implementation giving me full coverage for end points. 3. I am getting full coverage for my Unit testcases.

When I am using Quarkus application, and Quarkus Test, then this same jacoco implementation giving me full coverage for end points.

I have seen some people are saying to run your application and test from same jvm. I am not sure how to do this correctly. Also if this solution is possible to implement in jenkins pipeline.

I have not tried Arquillian yet.

2
  • Do you have any unit tests running? Commented Mar 10, 2023 at 8:31
  • Yes. My unit test coverage is perfectly generated. Commented Mar 13, 2023 at 5:17

2 Answers 2

0

I don't know if you've solved it or not. Sorry for not posting earlier.

It seems like you some misconfigurations in the plugin settings.

The steps should be:

  1. Prepare agent for unit tests
  2. Report the unit tests coverage in the test phase
  3. Prepare agent for integration tests in the pre-integration-test phase
  4. Report the integration tests coverage in the post-integration-test phase
  5. Merge the two reports in the post-integration-test phase
  6. Report the merged report from step no. 5 in the post-integration-test phase
  7. Run a coverage check against the reported merged report from step 6 in the verify phase

So, all in all, it should look something like this:

<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>${jacoco-maven-plugin.version}</version>
    <executions>
        <!-- Prepare the agent for the Unit Tests -->
        <execution>
            <id>pre-unit-test</id>
            <goals>
                <goal>prepare-agent</goal>
            </goals>
            <configuration>
                <destFile>${project.build.directory}/coverage-reports/jacoco-ut.exec</destFile>
                <propertyName>surefireArgLine</propertyName>
            </configuration>
        </execution>

        <!-- Generate the report for the Unit Tests -->
        <execution>
            <id>post-unit-test</id>
            <phase>test</phase>
            <goals>
                <goal>report</goal>
            </goals>
            <configuration>
                <dataFile>${project.build.directory}/coverage-reports/jacoco-ut.exec</dataFile>
                <outputDirectory>${project.reporting.outputDirectory}/jacoco-ut</outputDirectory>
            </configuration>
        </execution>

        <!-- Prepare the agent for the Integration Tests -->
        <execution>
            <id>pre-integration-test</id>
            <phase>pre-integration-test</phase>
            <goals>
                <goal>prepare-agent-integration</goal>
            </goals>
            <configuration>
                <destFile>${project.build.directory}/coverage-reports/jacoco-it.exec</destFile>
                <propertyName>failsafeArgLine</propertyName>
            </configuration>
        </execution>

        <!-- Generate the report for the Integration Test -->
        <execution>
            <id>post-integration-test</id>
            <phase>post-integration-test</phase>
            <goals>
                <goal>report-integration</goal>
            </goals>
            <configuration>
                <!-- excluding everything except controller class -->
                <excludes>
                    <exclude>**/common/*.class</exclude>
                    <exclude>**/config/*.class</exclude>
                    <exclude>**/domain/*.class</exclude>
                    <exclude>**/exception/*.class</exclude>
                    <exclude>**/mapper/*.class</exclude>
                    <exclude>**/service/*.class</exclude>
                    <exclude>**/simulator/Main.class</exclude>
                </excludes>
                <dataFile>${project.build.directory}/coverage-reports/jacoco-it.exec</dataFile>
                <outputDirectory>${project.reporting.outputDirectory}/jacoco-it</outputDirectory>
                <sourceEncoding>${project.build.sourceEncoding}</sourceEncoding>
                <classFilesDIrectory>${project.build.directory}/classes</classFilesDIrectory>
            </configuration>
        </execution>
        
        <!-- Merge Unit Tests and Integration Tests reports -->
        <execution>
            <id>merge-unit-and-integration</id>
            <phase>post-integration-test</phase>
            <goals>
                <goal>merge</goal>
            </goals>
            <configuration>
                <fileSets>
                    <fileSet>
                        <directory>${project.build.directory}/coverage-reports</directory>
                        <includes>
                            <include>*.exec</include>
                        </includes>
                    </fileSet>
                </fileSets>
                <destFile>${project.build.directory}/coverage-reports/merged.exec</destFile>
            </configuration>
        </execution>

        <!-- Report the merged report -->
        <execution>
            <id>report-merged-report</id>
            <phase>post-integration-test</phase>
            <goals>
                <goal>report</goal>
            </goals>
            <configuration>
                <dataFile>${project.build.directory}/coverage-reports/merged.exec</dataFile>
                <outputDirectory>${project.reporting.outputDirectory}/jacoco-merged</outputDirectory>
            </configuration>
        </execution>

        <!-- Check the coverage -->
        <execution>
            <id>check</id>
            <phase>verify</phase>
            <goals>
                <goal>check</goal>
            </goals>
            <configuration>
                <rules>
                    <rule>
                        <element>BUNDLE</element>
                        <!-- Make sure you exclude your tests from coverage -->
                        <excludes>
                            <exclude>*Test</exclude>
                            <exclude>*IT</exclude>
                        </excludes>
                        <limits>
                            <limit>
                                <counter>INSTRUCTION</counter>
                                <value>COVEREDRATIO</value>
                                <!-- This will probably fail your build :) -->
                                <minimum>100</minimum>
                            </limit>
                        </limits>
                    </rule>
                </rules>
                <dataFile>${project.build.directory}/coverage-reports/merged.exec</dataFile>
            </configuration>
        </execution>
    </executions>
</plugin>


Sign up to request clarification or add additional context in comments.

Comments

0

Sorry for posting after so long. I solved this by attaching the jacoco agent to the jvm. You can replicate the same in your local system by following below steps. I have followed the similar approach to implement this solution in my dockerised env.

Pre req : Download the latest Jacoco distribution from Jacoco's official website. Create a new directory inside the tomcat server directory named 'jaoco'. Copy the jacocoagent.jar, jacocoantjar and jacococli.jar inside the newly created directory. Put your application war file inside the tomcat webaps folder.

Step 1 : From tomcat /bin folder run command set CATALINA_OPTS="-javaagent:C:/apache-tomcat-9.0.56/jacoco/jacocoagent.jar=destfile=C:/apache-tomcat-9.0.56/jacoco/jacoco.exec,outputfile=file,append=false"

Jacoco agent should be successfully attached to the JVM now. When we start the tomcat, there should be a jaoco.exec file generated at the provided destination file path.

Step 2 : Start tomcat locally. Open cmd and then go to tomcat/bin folder and execute command - startup Check that a jacoco.exec file should be generated with 0 kb.

Step 3 : Make sure the application running on local host should not be up with your actuator end point - /actuator/health

Step 4 : Run your RestAssured testcases from your ide pointing to the localhost url application url.

Step 5 : Shutdown the tomact (This is very important. With shutting down reports will not generate)

Step 6 : heck your jacoco exec file is now have increased in size.

Step 7 : Create a folder named 'report' inside the jacoco directory available inside jacoco older. We will utilize this folder to generate the html report. No you need to generate a html reort from jacoco.exec file by excuting the command from C:\apache-tomcat-9.056\jacoco

java -jar C:/apache-tomcat-9.0.56/jacoco/jacococli.jar report jacoco.exec --classfile C:/apache-tomcat-9.0.56/webaps/your-application-local-hasg-SNAPSHOT/WEB-INF/classes --html C:apache-tomcat-9.0.56/jacco/report/

Note - If you want a specific class instead of all the classes then mention a particular folder, for example classfile C:/apache-tomcat-9.0.56/webaps/your-application-local-hasg-SNAPSHOT/WEB-INF/classes/com/project-foldername/controller This will generate code coverage for only controller classes where you have your end points.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.