Code coverage là một số liệu dùng để đánh giá bao nhiêu line of code của chúng ta đã được cover bằng Unit Test. Số liệu này rất có ích, giúp chúng ta có thể đánh giá được chất lượng code của chúng ta 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 với các bạn 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é.
Mình sẽ tạo mới một Maven project với một ví dụ đơn giản có Unit Test như sau:
JUnit dependency như sau:
1 2 3 4 5 6 |
<dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.11.2</version> <scope>test</scope> </dependency> |
Class Calculation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
package com.huongdanjava.mavenjacocoplugin; 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; } } |
Class CalculationTest:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
package com.huongdanjava.mavenjacocoplugin; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; public class CalculationTest { private static Calculation calculation; @BeforeAll public static 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)); } } |
JaCoCo Maven Plugin:
1 2 3 4 5 |
<plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.8.12</version> </plugin> |
OK, bắt đầu nào các bạn!
JaCoCo Maven Plugin định nghĩa rất nhiều goal, nhưng có một goal mà chúng ta cần phải khai báo để plugin này có thể tính toán được số liệu code coverage cho chúng ta, đó là goal: prepare-agent. Giả sử bây giờ mình khai báo goal này trong project example ở trên:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.8.12</version> <executions> <execution> <id>agent-for-ut</id> <goals> <goal>prepare-agent</goal> </goals> </execution> </executions> </plugin> |
rồi chạy command “mvn test” thì các bạn sẽ thấy kết quả như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
/Users/khanhnguyenj/.sdkman/candidates/java/21.0.4-tem/bin/java -Dmaven.multiModuleProjectDirectory=/Users/khanhnguyenj/Documents/code/huongdanjava.com/maven-jacoco-plugin -Djansi.passthrough=true -Dmaven.home=/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3 -Dclassworlds.conf=/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/bin/m2.conf -Dmaven.ext.class.path=/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven-event-listener.jar -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=50095:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 -Dsun.stderr.encoding=UTF-8 -classpath /Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/boot/plexus-classworlds.license:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/boot/plexus-classworlds-2.8.0.jar org.codehaus.classworlds.Launcher -Didea.version=2024.2.3 test [INFO] Scanning for projects... [INFO] [INFO] ----------------< com.huongdanjava:maven-jacoco-plugin >---------------- [INFO] Building maven-jacoco-plugin 0.0.1-SNAPSHOT [INFO] from pom.xml [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- jacoco:0.8.12:prepare-agent (agent-for-ut) @ maven-jacoco-plugin --- [INFO] argLine set to -javaagent:/Users/khanhnguyenj/.m2/repository/org/jacoco/org.jacoco.agent/0.8.12/org.jacoco.agent-0.8.12-runtime.jar=destfile=/Users/khanhnguyenj/Documents/code/huongdanjava.com/maven-jacoco-plugin/target/jacoco.exec [INFO] [INFO] --- resources:3.3.1:resources (default-resources) @ maven-jacoco-plugin --- [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory /Users/khanhnguyenj/Documents/code/huongdanjava.com/maven-jacoco-plugin/src/main/resources [INFO] [INFO] --- compiler:3.13.0:compile (default-compile) @ maven-jacoco-plugin --- [INFO] Recompiling the module because of added or removed source files. [WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent! [INFO] Compiling 1 source file with javac [debug target 21] to target/classes [INFO] [INFO] --- resources:3.3.1:testResources (default-testResources) @ maven-jacoco-plugin --- [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory /Users/khanhnguyenj/Documents/code/huongdanjava.com/maven-jacoco-plugin/src/test/resources [INFO] [INFO] --- compiler:3.13.0:testCompile (default-testCompile) @ maven-jacoco-plugin --- [INFO] Recompiling the module because of changed dependency. [WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent! [INFO] Compiling 1 source file with javac [debug target 21] to target/test-classes [INFO] [INFO] --- surefire:3.2.5:test (default-test) @ maven-jacoco-plugin --- [INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider [INFO] [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running com.huongdanjava.mavenjacocoplugin.CalculationTest [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.021 s -- in com.huongdanjava.mavenjacocoplugin.CalculationTest [INFO] [INFO] Results: [INFO] [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 1.041 s [INFO] Finished at: 2024-10-14T07:42:12+08:00 [INFO] ------------------------------------------------------------------------ Process finished with exit code 0 |
Như các bạn thấy ở dòng:
1 2 |
[INFO] --- jacoco:0.8.12:prepare-agent (agent-for-ut) @ maven-jacoco-plugin --- [INFO] argLine set to -javaagent:/Users/khanhnguyenj/.m2/repository/org/jacoco/org.jacoco.agent/0.8.12/org.jacoco.agent-0.8.12-runtime.jar=destfile=/Users/khanhnguyenj/Documents/code/huongdanjava.com/maven-jacoco-plugin/target/jacoco.exec |
một tập tin tên là jacoco.exec đã được tạo ra trong thư mục target của project. Đây chính là tập tin chứa kết quả report code coverage mà JaCoCo Maven Plugin đã tạo ra. Tập tin này ở dạng binary nên chúng ta không thể xem trực tiếp được nhưng chúng ta vẫn có một cách khác để xem kết quả report này bằng cách sử dụng goal report của JaCoCo Maven Plugin này.
Các bạn hãy chạy project Maven với goal jacoco:report:
1 |
mvn clean test jacoco:report |
để xem kết quả như thế nào nhé!
Lúc này, nếu các bạn kiểm tra thư mục target của project, các bạn sẽ thấy một thư mục tên là site chứa thông tin về code coverage mà JaCoCo Maven Plugin đã tạo ra sau khi chúng ta chạy project với goal report trên:
Mở tập tin index.html trong thư mục target/site/jacoco các bạn sẽ thấy kết quả như sau:
Đây chính là tất cả các thông tin về code coverage trong project của chúng ta. Các bạn có thể dễ dàng thấy bao nhiêu % code coverage cho từng package trong project của chúng ta phải không?
Các bạn có thể click vào từng package để xem chi tiết:
Như các bạn thấy, chúng ta có thể xem chi tiết code coverage đến từng method. Các bạn cũng có thể click vào từng method để xem chúng ta đã coverage những đoạn code nào.
Mặc định, những đoạn code nào đã được coverage thì đoạn code đó sẽ được high light bằng màu xanh lá cây, còn ngược lại sẽ là màu đỏ. Ở đây chúng ta còn trường hợp nữa là code Unit Test của chúng ta chưa cover hết tất cả các case có thể xảy ra trong một dòng code nào đó. Lúc này, đoạn code này sẽ được hight light bằng màu vàng.
Ví dụ như, nếu bây giờ mình sửa lại code của CalculationTest, remove:
1 |
assertEquals(9, calculation.sub(3, 12)); |
trong method testSub() thì các bạn sẽ thấy code coverage report cho class Calculation như sau:
Kết quả như trên cho thấy, code Unit Test của chúng ta chưa chạy qua dòng code “return b – a” (nên bị high light bằng màu đỏ) và ở dòng code “if (a >= b)”, còn một case nữa mà chúng ta chưa cover (do đó, đoạn code này high light bằng màu vàng).
Ha Nguyen
jacoco này chạy khi unit test,
Có cách nào cho nó chạy lúc test end to end không bạn nhỉ.
Khanh Nguyen
Không đâu bạn ơi! Code coverage có cần cho test end to end không bạn nhỉ?
Long
code coverage nằm trong whitebox, test end to end thì không tính code coverage được đâu bạn.