Code coverage với Jacoco trong maven multi-module project

Code Coverage là một biện pháp được sử dụng trong quá trình phát triển phần mềm để đánh giá mức độ cover của unit test trên source code. Giúp chúng ta có thể đánh giá được chất lượng code như thế nào, bao nhiêu code của chúng ta đã được và chưa được cover bởi Unit Test.

Trong bài viết này, mình giới thiệu một plugin của Apache Maven tên là JaCoCo Maven Plugin giúp chúng ta có thể đo được số liệu code coverage này các bạn nhé.

Việc tích hợp Jacoco plugin vào project với single module cực kỳ dễ, chỉ cần thêm đoạn config:

<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.1</version>
    <executions>
        <execution>
            <id>agent-for-ut</id>
            <goals>
                <goal>prepare-agent</goal>
            </goals>
        </execution>
    </executions>
</plugin>

vào pom.xml của maven project, bạn đã có thể xuất ra report code coverage rồi.

Phức tạp hơn một tý, bạn sẽ cần phải tích hợp Jacoco vào maven project với multiple module - dạng project luôn luôn là cấu trúc của bao project lớn nhỏ hiện nay.

I. Chuẩn bị multi-module project

Mình đã chuẩn bị sẵn một project nhỏ ở đây, bạn tham khảo nhé: Multi-module project

Project này gồm 4 module: projecta, projectb, projectc, coverage-report (Mình sẽ giới thiệu module này sau)

@ Với nội dung từng file pom.xml như sau:

parent pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.ngockhuong</groupId>
    <artifactId>platform</artifactId>
    <version>1.0</version>
    <packaging>pom</packaging>

    <modules>
        <module>projecta</module>
        <module>projectb</module>
        <module>projectc</module>
    </modules>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

pom.xml (projecta)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.ngockhuong</groupId>
        <artifactId>platform</artifactId>
        <version>1.0</version>
    </parent>
    <artifactId>projecta</artifactId>
    <version>1.0</version>
    <packaging>jar</packaging>
</project>

Với projectb, projectc, tại pom.xml file bạn thay artifactId thành projectb, projectc nhé (dài quá post lên đây mình sợ lăn hư hết chuột)

@ Trong mỗi project (projecta, projectb, projectc) đều có các sample class:

Calculation.java

public class Calculation {
    public int add(int a, int b) {
        return a + b;
    }

    public int sub(int a, int b) {
        if (a >= b) {
            return a - b;
        }

        return b - a;
    }
}

Và unit test class:

CalculationTest.java

import org.junit.Before;
import org.junit.Test;

import static org.junit.Assert.assertEquals;

public class CalculationTest {
    private Calculation calculation;

    @Before
    public void init() {
        calculation = new Calculation();
    }

    @Test
    public void testAdd() {
        assertEquals(4, calculation.add(1, 3));
    }

    @Test
    public void testSub() {
        assertEquals(2, calculation.sub(7, 5));

        assertEquals(9, calculation.sub(3, 12));
    }
}

II. Tích hợp Jacoco

Như mình đã nói từ đầu, việc tích hợp Jacoco vào project với single module giản đơn đến không tưởng. Thêm config vào maven pom, chạy mvn package hoặc mvn install nhận ngay report trong thư mục target.

Tuy nhiên, với multi-module project thì mọi việc không đơn giản đến thế, à mà thật sự cũng đơn giản chứ không phức tạp lắm đâu, chỉ là thêm module và vài config nữa thôi 😆

Bạn tham khảo trước commit Add Jacoco coverage tại project https://github.com/lamngockhuong/jacoco-coverage-multi-module nhé 😉

Chúng ta sẽ phân thành 3 bước cho sang chảnh một tý ✌️

Bước 1: Tạo coverage-report module

Bạn tạo thêm một module coverage-report rỗng với pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>platform</artifactId>
        <groupId>com.ngockhuong</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>coverage-report</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.ngockhuong</groupId>
            <artifactId>projecta</artifactId>
            <version>1.0</version>
        </dependency>
        <dependency>
            <groupId>com.ngockhuong</groupId>
            <artifactId>projectb</artifactId>
            <version>1.0</version>
        </dependency>
        <dependency>
            <groupId>com.ngockhuong</groupId>
            <artifactId>projectc</artifactId>
            <version>1.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.jacoco</groupId>
                <artifactId>jacoco-maven-plugin</artifactId>
                <version>0.8.4</version>
                <configuration>
                    <excludes>
                        <exclude>**/com/ngockhuong/projectc/Calculation.class</exclude>
                    </excludes>
                </configuration>
                <executions>
                    <execution>
                        <id>report-aggregate</id>
                        <phase>verify</phase>
                        <goals>
                            <goal>report-aggregate</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Trong đó:

  • dependencies là nơi khai báo các thư viện phụ thuộc cho module này, việc khai báo ở đây đồng nghĩa với việc báo cho jacoco plugin biết những module mà nó cần cover và report. (Thử thêm bớt và cảm nhận bạn nhé)
  • excludes trong configuration: Cái tên nói lên tất cả, đó là nơi bạn khai báo để bỏ qua các class hoặc package trong kết quả report. (Jacoco thấy những thứ này sẽ tránh xa, dù yêu lắm, thương lắm)
  • id, phase, goals: Các thành phần của maven execution (Cách sử dụng execution ở đây có mô tả chi tiết, bạn có thể xem qua nhé)
  • goal: Định nghĩa goal cho plugin, mỗi plugin sẽ có những goal riêng, với Jacoco plugin bạn tham khảo chi tiết ở đây để sử dụng cho phù hợp trong project nhé - JaCoCo Maven Goals (report-aggregate: Tạo report (HTML, XML, CSV) tổng hợp từ nhiều module trong project)

Bước 2: Config jacoco tại parent pom

Các bạn định nghĩa thêm <module>coverage-report</module> và plugin jacoco như bên dưới:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.ngockhuong</groupId>
    <artifactId>platform</artifactId>
    <version>1.0</version>
    <packaging>pom</packaging>

    <modules>
        <module>projecta</module>
        <module>projectb</module>
        <module>projectc</module>
        <module>coverage-report</module>
    </modules>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.jacoco</groupId>
                <artifactId>jacoco-maven-plugin</artifactId>
                <version>0.8.4</version>
                <executions>
                    <execution>
                        <id>default</id>
                        <goals>
                            <goal>prepare-agent</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Trong đó:

Bước 3: Chạy unit test và xuất report

Cuối cùng, bạn chỉ cần chạy mvn package hoặc mvn clean install tại root project và chờ kết quả success

[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO]
[INFO] platform 1.0 ....................................... SUCCESS [  7.273 s]
[INFO] projecta ........................................... SUCCESS [ 12.125 s]
[INFO] projectb ........................................... SUCCESS [  0.977 s]
[INFO] projectc ........................................... SUCCESS [  0.959 s]
[INFO] coverage-report 1.0 ................................ SUCCESS [  0.752 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 22.284 s
[INFO] Finished at: 2019-07-23T21:00:03+07:00
[INFO] ------------------------------------------------------------------------

Report sẽ tự động generate tại thư mục target của module coverage-report, mở file index.html để xem có những gì trong đó nhé 😜

Như vậy mình đã tích hợp xong Jacoco plugin vào project rồi đó. Hi vọng có thể giúp ích cho bạn trong quá trình học tập và công việc 🤓