Code Coverage (độ phủ mã) là một chỉ số cho thấy phần trăm mã nguồn của ứng dụng được thực thi trong quá trình kiểm thử. Nó giúp xác định chất lượng kiểm thử, xác định các khu vực chưa được kiểm tra và ưu tiên viết kiểm thử mới. Theo Atlassian, 2025, mức độ phủ tối ưu là 70–80% — trên ngưỡng này, chi phí kiểm thử bắt đầu vượt quá lợi ích.
Những điểm chính
Code Coverage (độ phủ mã) là một chỉ số định lượng xác định phần nào của mã nguồn ứng dụng đã được thực thi trong quá trình kiểm thử. Nó được biểu thị bằng phần trăm và được tính bằng tỷ lệ số dòng/nhánh được thực thi trên tổng số. Độ phủ cao không đảm bảo không có lỗi, nhưng giảm nguy cơ bỏ sót lỗi.
Độ phủ mã giúp nhóm: tìm ra các khu vực chưa được kiểm thử của mã, đưa ra quyết định về ưu tiên viết kiểm thử và theo dõi động thái chất lượng kiểm thử trong CI/CD. Trong phát triển di động, độ phủ đặc biệt quan trọng đối với logic kinh doanh, mô hình dữ liệu và kho lưu trữ — các lớp có xác suất lỗi cao nhất.
Một lầm tưởng phổ biến: “100% độ phủ = chất lượng hoàn hảo”. Trong thực tế, độ phủ 100% cực kỳ hiếm và thường đạt được với cái giá là các kiểm thử hời hợt. Độ phủ hiệu quả không phải là cuộc đua theo phần trăm, mà là độ phủ chiến lược các đường dẫn quan trọng và các trường hợp biên. Độ phủ không nói lên điều gì về chất lượng của bản thân kiểm thử: một kiểm thử có thể vượt qua nhưng không xác minh tính đúng đắn của kết quả.
Có nhiều chỉ số Code Coverage khác nhau, mỗi chỉ số đo lường các khía cạnh khác nhau của kiểm thử. Line coverage (độ phủ dòng) là chỉ số đơn giản nhất, hiển thị phần trăm các dòng mã được thực thi. Branch coverage (độ phủ nhánh) đo lường các nhánh if-else và switch nào đã được kiểm thử.
Line coverage đếm mỗi dòng mã nguồn là được thực thi hay không. Nếu một dòng chứa toán tử điều kiện hoặc vòng lặp, dòng đó được coi là đã thực thi nếu luồng điều khiển đến được nó, ngay cả khi không phải tất cả các nhánh đều được xử lý. Đây là chỉ số ít nghiêm ngặt nhất, nhưng dễ hiểu nhất để đánh giá trực quan.
Branch coverage đánh giá liệu tất cả các nhánh có thể có trong mã đã được kiểm thử chưa. Đối với mỗi if-else, cả hai nhánh đều được xem xét: true và false. Đối với switch, mỗi case được xem xét. Branch coverage được coi là chỉ số nghiêm ngặt hơn line coverage và thường phát hiện ra các kịch bản chưa được kiểm thử.
| Chỉ số | Đo lường gì | Độ khó đạt được |
|---|---|---|
| Line | Phần trăm dòng mã được thực thi | Thấp |
| Branch | Phần trăm nhánh được thực thi (if/else, switch) | Trung bình |
| Function | Phần trăm hàm và phương thức được gọi | Thấp |
| Condition | Phần trăm biểu thức con logic (&&, ||) | Cao |
Path coverage là chỉ số nghiêm ngặt nhất, yêu cầu xác minh tất cả các kết hợp có thể có của các nhánh trong một hàm. Trong thực tế, path coverage hiếm khi được sử dụng do số lượng kết hợp tăng theo cấp số nhân: một hàm có 10 nhánh có 1024 đường dẫn có thể có.
Trong phát triển di động, các công cụ khác nhau được sử dụng để đo Code Coverage tùy theo nền tảng. Đối với Android, tiêu chuẩn là JaCoCo (Java Code Coverage), tích hợp với Gradle và hỗ trợ cả kiểm thử đơn vị và kiểm thử công cụ. Đối với iOS, XCCov được sử dụng, được tích hợp trong Xcode.
JaCoCo tạo báo cáo ở định dạng HTML, XML và CSV. Báo cáo HTML làm nổi bật trực quan các dòng: xanh lục — đã thực thi, đỏ — bị bỏ qua, vàng — thực thi một phần. Báo cáo XML tương thích với SonarQube và các hệ thống phân tích mã khác. JaCoCo hỗ trợ lọc lớp: có thể loại trừ mã được tạo, databinding và BuildConfig.
// build.gradle — cấu hình JaCoCo
android {
buildTypes {
debug {
testCoverageEnabled = true
}
}
}
// Tạo báo cáo JaCoCo
task jacocoTestReport(type: JacocoReport) {
dependsOn 'testDebugUnitTest'
reports {
xml.enabled = true
html.enabled = true
}
}
XCCov là công cụ tích hợp trong Xcode để đo độ phủ mã. Nó được kích hoạt thông qua Gather coverage data trong lược đồ kiểm thử. XCCov hỗ trợ độ phủ cho Swift và Objective-C, tạo báo cáo ở định dạng .xccovreport và tích hợp với CI qua xcodebuild -enableCodeCoverage YES. Dữ liệu được xuất ra bảng điều khiển và có thể xuất sang JSON.
Để giám sát độ phủ tập trung, các nền tảng như SonarQube (phân tích chất lượng mã + độ phủ), Codecov và Coveralls được sử dụng. Các dịch vụ này tổng hợp dữ liệu từ JaCoCo và XCCov, hiển thị xu hướng, Cổng chất lượng và tích hợp với GitHub/GitLab qua bình luận PR.
Cải thiện Code Coverage đòi hỏi một cách tiếp cận có hệ thống: không phải “tăng phần trăm” mà là bao phủ rủi ro. Bước đầu tiên là phân tích báo cáo JaCoCo hoặc XCCov — xác định các lớp màu đỏ (chưa được phủ). Ưu tiên: logic kinh doanh → kho lưu trữ → ViewModel → thành phần giao diện.
Phát triển hướng kiểm thử (TDD) tự động đảm bảo độ phủ cao vì kiểm thử được viết trước khi triển khai. Quy trình: đỏ (viết kiểm thử thất bại) → xanh lục (viết mã tối thiểu) → tái cấu trúc. TDD rèn luyện nhà phát triển, buộc họ phải bao phủ các trường hợp biên và tình huống ngoại lệ thường không được kiểm thử.
Một kiểm thử tham số hóa thay thế hàng chục kiểm thử thông thường. JUnit và XCTest hỗ trợ tham số hóa: @ParameterizedTest trong JUnit 5, XCTestCase với testPerformanceExample trong XCTest. Tham số hóa cho phép xác minh nhiều dữ liệu đầu vào mà không trùng lặp mã, mở rộng đáng kể độ phủ nhánh và điều kiện.
// Kiểm thử tham số hóa trong Kotlin với JUnit 5
@ParameterizedTest
@ValueSource(strings = ["user@test.com", "admin@test.com", "test@example.com"])
fun `validate email returns true for valid addresses`(email: String) {
val result = EmailValidator.isValid(email)
Assertions.assertTrue(result)
}
Sai lầm phổ biến nhất là chạy theo phần trăm mà không phân tích chất lượng kiểm thử. Nhóm bắt đầu viết kiểm thử để cho có: kiểm tra getter và setter, sao chép độ phủ ở các cấp độ khác nhau, kiểm thử các phương thức tầm thường. Điều này tạo ra phần trăm cao nhưng không cải thiện chất lượng thực tế.
Code Coverage cao có thể tạo ra cảm giác sai lầm rằng ứng dụng đã được kiểm thử tốt. Một kiểm thử có thể thực thi một dòng mã nhưng không xác minh tính đúng đắn của kết quả. Ví dụ: một kiểm thử gọi phương thức tính chiết khấu nhưng không kiểm tra số tiền — dòng được thực thi, độ phủ tăng lên, nhưng lỗi không được tìm thấy.
Một sai lầm điển hình là chỉ kiểm thử “đường dẫn hạnh phúc” (happy path) và bỏ qua các trường hợp biên: danh sách rỗng, giá trị null, số tối đa, định dạng không hợp lệ. Hầu hết các lỗi xảy ra tại các ranh giới và ngoại lệ. Branch coverage giúp xác định các nhánh bị bỏ sót nhưng không đảm bảo kiểm tra các giá trị biên.
Kiểm thử đột biến (Mutation Testing) là phương pháp đánh giá chất lượng kiểm thử trong đó các đột biến (lỗi nhân tạo) được đưa vào mã nguồn và kiểm tra xem kiểm thử có thất bại không. Pitest là công cụ kiểm thử đột biến phổ biến cho Java và Kotlin. Nếu kiểm thử không thất bại trước một đột biến, điều đó có nghĩa là chúng không xác minh điều kiện đó.
Pitest tạo ra các đột biến thể — các bản sao đã sửa đổi của mã nguồn, ví dụ, > được thay thế bằng >=, true bằng false, hoặc một lời gọi phương thức bị xóa. Sau đó, cho mỗi đột biến thể, các kiểm thử được chạy. Nếu kiểm thử vượt qua — đột biến thể sống sót, có nghĩa là kiểm thử không bao phủ kịch bản đó. Nếu kiểm thử thất bại — đột biến thể bị tiêu diệt, kiểm thử hợp lệ.
// build.gradle — cấu hình Pitest
plugins {
id 'info.solidsoft.pitest' version '1.15.0'
}
pitest {
targetClasses = ['com.example.app.*']
targetTests = ['com.example.app.*Test']
threads = 4
outputFormats = ['HTML', 'XML']
mutationThreshold = 80
coverageThreshold = 85
}
Pitest hỗ trợ nhiều loại đột biến: thay đổi toán tử điều kiện (== → !=, < → <=), xóa lời gọi phương thức, thay thế giá trị trả về (true → false), thay đổi phép tính số học (+ → -), đột biến tăng (i++ → i--). Càng nhiều loại đột biến bị tiêu diệt bởi kiểm thử, bộ kiểm thử càng đáng tin cậy.
Điểm đột biến mục tiêu là 80% trở lên. Điều này có nghĩa là 80% lỗi nhân tạo được phát hiện bởi kiểm thử. Độ phủ mã (Code Coverage) 90% không đảm bảo rằng kiểm thử tìm ra lỗi — kiểm thử đột biến cung cấp đánh giá khách quan hơn. Pitest có thể được tích hợp vào CI như một Cổng chất lượng, chặn bản dựng nếu điểm đột biến giảm dưới ngưỡng.
Để kiểm soát tự động Code Coverage trong CI/CD, Cổng chất lượng (Quality Gate) được sử dụng — các giá trị ngưỡng mà khi vi phạm, bản dựng bị đánh dấu là không ổn định hoặc bị từ chối. SonarQube cho phép cấu hình Cổng chất lượng dựa trên sự kết hợp của các chỉ số: độ phủ (≥80%), số lỗi, lỗ hổng bảo mật và mã trùng lặp.
Trong GitHub Actions, Code Coverage được tích hợp thông qua các bước hành động: chạy kiểm thử với độ phủ → tải báo cáo lên Codecov → kiểm tra ngưỡng. Codecov tự động bình luận trên PR với sự khác biệt về độ phủ, hiển thị dòng nào đã thay đổi và ảnh hưởng đến phần trăm tổng thể như thế nào. Nếu độ phủ giảm, PR bị chặn cho đến khi viết thêm kiểm thử.
# GitHub Actions — tải độ phủ lên Codecov
- name: Run Tests with Coverage
run: ./gradlew testDebugUnitTest jacocoTestReport
- name: Upload to Codecov
uses: codecov/codecov-action@v4
with:
files: ./app/build/reports/jacoco/jacocoTestReport.xml
flags: unittests
fail_ci_if_error: true
- name: Check Coverage Threshold
run: |
coverage=$(grep -oP 'branchCoverage="\K[0-9.]+' report.xml)
if (( $(echo "$coverage < 80" | bc -l) )); then
echo "Coverage $coverage% is below 80% threshold"
exit 1
fi
Báo cáo HTML từ JaCoCo và XCCov chứa tô sáng trực quan độ phủ: xanh lục — dòng đã thực thi, đỏ — chưa thực thi. SonarQube hiển thị thêm độ phủ ở cấp tệp, lớp, phương thức và dòng, cũng như lịch sử thay đổi độ phủ theo sprint. Điều này giúp đưa ra quyết định về tái cấu trúc và thêm kiểm thử.
Câu hỏi thường gặp
Đối với các dự án di động, độ phủ 70–80% cho logic kinh doanh và 50–60% cho thành phần giao diện được coi là tốt. Trên 80%, chi phí kiểm thử bắt đầu vượt quá lợi ích. Điều quan trọng cần nhớ là phần trăm không phải là mục tiêu mà là một chỉ báo, và các mô-đun khác nhau có thể có các mức mục tiêu khác nhau.
Line Coverage cho thấy bao nhiêu dòng mã được thực thi. Branch Coverage cho thấy bao nhiêu nhánh (if-else, switch) được kiểm thử. Một dòng có if có thể được thực thi, nhưng chỉ nhánh true có thể được kiểm thử, còn false thì không. Branch Coverage nghiêm ngặt hơn và phát hiện nhiều kịch bản bỏ sót hơn.
Trong CI/CD, độ phủ được tích hợp thông qua Cổng chất lượng (Quality Gate): bản dựng bị chặn nếu độ phủ dưới ngưỡng. Cho Android, sử dụng JaCoCo + SonarQube; cho iOS — xcodebuild -enableCodeCoverage với phân tích .xccovreport. GitHub Actions có các hành động có sẵn cho Codecov.
Có, JaCoCo hỗ trợ Jetpack Compose thông qua cơ chế độ phủ JVM tiêu chuẩn. Tuy nhiên, mã Compose chứa nhiều biểu thức lambda được tạo mà JaCoCo có thể không bao phủ hoàn toàn. Nên loại trừ mã Compose được tạo khỏi báo cáo thông qua bộ lọc.
Độ phủ giả xảy ra khi kiểm thử thực thi mã nhưng không xác minh kết quả. Giải pháp: viết các kiểm tra assert cho mọi kịch bản quan trọng, sử dụng kiểm thử đột biến (Pitest) để xác minh chất lượng kiểm thử, phân tích không chỉ phần trăm mà còn nhánh nào được bao phủ.
Tóm tắt
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm