Code Coverage trong phát triển di động: khái niệm, chỉ số và cách đo lường

Tác giả: IT Sectr Đã đăng: 2026-04-09 Thời gian đọc: 9 phút

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 — chỉ số đo phần trăm mã được thực thi bởi kiểm thử
  • Chỉ số độ phủ bao gồm dòng (line), nhánh (branch), hàm, điều kiện và đường dẫn
  • Công cụ: JaCoCo cho Android, XCCov cho iOS, SonarQube để phân tích mã
  • Độ phủ mục tiêu 70–80% — cân bằng giữa chất lượng và chi phí kiểm thử
  • Tích hợp CI/CD cho phép chặn bản dựng khi độ phủ giảm dưới ngưỡng

Code Coverage là gì

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.

Tại sao nên đo độ phủ

Độ 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.

Lầm tưởng về Code Coverage

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ả.

Chỉ số độ phủ mã

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ử.

Độ phủ dòng (Line Coverage)

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.

Độ phủ nhánh (Branch Coverage)

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
LinePhần trăm dòng mã được thực thiThấp
BranchPhần trăm nhánh được thực thi (if/else, switch)Trung bình
FunctionPhần trăm hàm và phương thức được gọiThấp
ConditionPhần trăm biểu thức con logic (&&, ||)Cao

Độ phủ đường dẫn (Path Coverage)

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ó.

Công cụ đo độ phủ

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 cho Android

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.

groovy
// 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 cho iOS

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.

SonarQube và Codecov

Để 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ách cải thiện độ phủ mã

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.

Chiến lược TDD

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ử.

Kiểm thử tham số hóa

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.

kotlin
// 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 khi làm việc với Code Coverage

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ế.

Cảm giác an toàn sai lầm

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.

Bỏ qua các trường hợp biên

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 — xác minh chất lượng kiểm thử

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 đó.

Nguyên lý hoạt động của Pitest

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ệ.

groovy
// 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
}

Các loại đột biến

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.

Mục tiêu Điểm đột biến

Đ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.

Tích hợp Code Coverage vào CI/CD

Để 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.

Thiết lập Cổng chất lượng trong GitHub Actions

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ử.

yaml
# 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 và trực quan hóa

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

Bao nhiêu phần trăm Code Coverage được coi là tốt?

Đố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.

Sự khác biệt giữa Line và Branch Coverage là gì?

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.

Làm thế nào để tích hợp Code Coverage vào CI/CD?

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ó thể đo độ phủ cho Jetpack Compose không?

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.

Làm thế nào để tránh độ phủ giả?

Độ 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

  • Code Coverage — chỉ số cho thấy phần trăm mã được thực thi bởi kiểm thử, nhưng không đảm bảo không có lỗi
  • Line và Branch coverage — các chỉ số chính; Branch nghiêm ngặt hơn và phát hiện các nhánh chưa được kiểm thử
  • JaCoCo — công cụ tiêu chuẩn cho Android, XCCov — cho iOS, cả hai tích hợp với Gradle và Xcode
  • Độ phủ mục tiêu 70–80% cho logic kinh doanh — cân bằng tối ưu giữa chất lượng và chi phí
  • TDD và tham số hóa — phương pháp hiệu quả để tăng độ phủ mà không trùng lặp kiểm thử
  • SonarQube và Codecov — nền tảng giám sát tập trung và Cổng chất lượng trong CI/CD
  • Nguyên tắc chính: không chạy theo phần trăm, mà bao phủ các rủi ro quan trọng và các trường hợp biên

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.

Thảo luận dự án

Đọc thêm