Penjelasan Cakupan Kode: Cakupan Jalur vs Cabang dan Arti 100% Sebenarnya
Cakupan memberi tahu Anda kode mana yang dijalankan pengujian Anda. Itu tidak memberi tahu Anda apakah tes tersebut memeriksa sesuatu. Kami mengukur modul kecil dengan tiga cara untuk menunjukkan perbedaannya.
- Cakupan kode mengukur seberapa banyak kode Anda berjalan selama pengujian.
- Cakupan cabang lebih ketat dibandingkan cakupan jalur, dan lebih berguna.
- Dalam pengujian kami, rangkaian dengan tanpa pernyataan masih mencapai cakupan 100%.
- Gunakan cakupan untuk menemukan kode yang belum teruji, bukan sebagai bukti kualitas.
Sebuah tim dengan bangga melaporkan 95% cakupan kode, lalu mengirimkan bug di baris yang “dicakup”. Ini bukanlah suatu kontradiksi. Cakupan menjawab satu pertanyaan sempit: apakah pengujian mengeksekusi kode ini? Apakah mereka memeriksa hasilnya adalah pertanyaan yang sama sekali berbeda.
Kami menggunakan Python dengan coverage.py 7.17 dan pytest 9.2. Setiap nomor dan laporan di bawah ini berasal dari kami sendiri. Ide-idenya berlaku dalam bahasa apa pun.
Apa itu cakupan kode?
Cakupan kode adalah persentase kode Anda yang berjalan saat pengujian otomatis Anda berjalan. Sebuah alat mencatat baris mana, dan secara opsional cabang mana, yang dieksekusi, lalu melaporkan apa yang terlewat.
Hal ini paling berguna sebaliknya. Cakupan yang rendah di suatu area memberi tahu Anda, dengan pasti, bahwa tes tidak berhasil. Cakupan yang tinggi memberi tahu Anda lebih sedikit.
Jenis pertanggungan
| Ketik | Pertanyaan yang dijawabnya |
|---|---|
| Fungsi | Apakah setiap fungsi dipanggil setidaknya satu kali? |
| Garis (pernyataan) | Apakah setiap baris berjalan setidaknya satu kali? |
| Cabang | Apakah masing-masing if berjalan dua arah, benar dan salah? |
| Kondisi | Apakah setiap bagian dari kondisi gabungan (a and b) mengambil kedua nilai tersebut? |
| MC/DC | Apakah setiap kondisi secara independen mengubah hasilnya? Digunakan dalam perangkat lunak yang kritis terhadap keselamatan. |
Tiap level lebih ketat dibandingkan level di atasnya. Sebagian besar tim melacak cakupan jalur dan cabang. Cakupan kondisi dan MC/DC muncul dalam standar penerbangan dan otomotif, yang mengkhususkan dukungan perkakas.
Kode yang kami ukur
def shipping_fee(total, is_member, express=False):
if total < 0:
raise ValueError("total cannot be negative")
fee = 0 if total >= 1000 else 50
if is_member:
fee = max(fee - 25, 0)
if express:
fee += 100
return fee
File pengujian pertama kami memiliki dua pengujian:
def test_regular_order():
assert shipping_fee(500, False) == 50
def test_member_express():
assert shipping_fee(500, True, express=True) == 125
Kami menjalankannya dengan pengukuran cabang diaktifkan:
coverage run --branch -m pytest test_shipping.py
coverage report -m
Membaca laporan
Name Stmts Miss Branch BrPart Cover Missing
---------------------------------------------------------
shipping.py 9 1 6 1 87% 3
- Stmts 9, Miss 1: sembilan pernyataan yang dapat dieksekusi, satu tidak pernah dijalankan.
- Cabang 6, BrBagian 1: tiga
ifkeputusan memberikan enam kemungkinan hasil. Satu keputusan hanya berjalan satu arah. - Hilang 3: baris 3,
raise ValueError, tidak pernah berjalan. Tidak ada tes yang lulus total negatif.
Perbaikannya adalah pengujian jalur kesalahan. Kami juga menambahkan tes untuk pesanan besar:
def test_free_over_1000():
assert shipping_fee(1000, False) == 0
def test_negative_total():
with pytest.raises(ValueError):
shipping_fee(-1, False)
Dengan kedua file tersebut, laporan menunjukkan 100% dan tidak ada baris yang hilang atau sebagian cabang. Jalur kesalahan adalah penemuan cakupan kesenjangan yang klasik, itulah sebabnya pengujian untuk masukan yang tidak valid, dirancang dengan teknik seperti partisi ekivalensi, sangat cocok dipadukan dengannya.
Apa yang disembunyikan laporan itu
Lihat laporan pertama lagi. Dikatakan hanya baris 3 yang hilang. Namun pada dua pengujian pertama, keduanya menggunakan total 500 buah, sehingga aturan pengiriman gratis untuk pesanan 1000 buah atau lebih tidak pernah diuji, dan laporan tersebut tidak menandainya.
Alasannya adalah fee = 0 if total >= 1000 else 50 berada pada satu baris. coverage.py mengukur cabang antar baris, sehingga ekspresi kondisional di dalam satu baris dihitung sebagai satu pernyataan yang dieksekusi. Jika aturan tersebut memiliki bug, seperti > dan bukannya >=, rangkaian pertama akan tetap menampilkan 87% tanpa petunjuk. Pengujian terhadap batas itu sendiri, seperti yang dijelaskan dalam analisis nilai batas, adalah hal yang menarik perhatiannya.
Cakupan 100% tanpa pemeriksaan
Selanjutnya, kami menulis tes yang sengaja dibuat buruk yang memanggil setiap jalur tetapi tidak menyatakan apa pun:
def test_runs_everything_but_checks_nothing():
shipping_fee(500, True, express=True)
shipping_fee(1000, False)
shipping_fee(500, False)
try:
shipping_fee(-1, False)
except ValueError:
pass
shipping.py 9 0 6 0 100%
Mencapai cakupan jalur dan cabang 100%. Fungsi tersebut dapat mengembalikan angka apa pun dan pengujian ini akan tetap lulus. Inilah batasan inti dari cakupan: ini mengukur pelaksanaan, bukan verifikasi.
Kiat: Saat meninjau permintaan penarikan, bacalah pernyataannya, bukan hanya perubahan cakupannya. Tes yang menambahkan cakupan tanpa pernyataan yang bermakna hanya akan menambah keyakinan palsu.
Untuk mengukur apakah pengujian benar-benar menangkap bug, gunakan pengujian mutasi. Alat seperti mutmut untuk Python, PIT untuk Java, atau Stryker untuk JavaScript membuat perubahan kecil pada kode Anda, seperti membalik >= ke >, dan memeriksa apakah pengujian gagal. Uji tanpa penegasan kami akan membuat setiap mutasi dapat bertahan.
Target apa yang harus ditetapkan
Tidak ada angka yang benar secara universal. Panduan praktis:
- Jangan mengejar 100%. Beberapa persen terakhir sering kali berarti menguji kode sepele atau menulis tes yang hanya menyentuh garis.
- Banyak tim menargetkan sekitar 70 hingga 85% secara keseluruhan, dengan ekspektasi yang lebih tinggi untuk logika bisnis inti.
- Perhatikan trennya, bukan angkanya. Liputan yang menurun pada setiap rilis adalah tanda peringatan.
- Memerlukan cakupan kode baru dalam permintaan penarikan daripada memaksakan kode lama sekaligus.
- Kecualikan hal-hal yang tidak penting, seperti kode yang dihasilkan, dengan aturan yang jelas.
Hukum Goodhart berlaku: ketika suatu ukuran menjadi target, maka ukuran tersebut tidak lagi menjadi ukuran yang baik. Target yang sulit mendorong pengujian seperti contoh tanpa pernyataan kami.
Alat menurut bahasa
| Bahasa | Alat umum |
|---|---|
| ular piton | cakupan.py, pytest-cov |
| JavaScript / Skrip Ketik | Istanbul (nyc), dibangun di Jest dan Vitest |
| Jawa | JaCoCo |
| .NET | Selimut |
| Pergi | pergi tes -tutup |
| Pelaporan | Codecov, SonarQube, Baju |
Cakupan di CI
Jalankan cakupan sebagai bagian dari pipa CI Anda, dan publikasikan laporan sehingga peninjau dapat melihat baris baru mana yang belum diuji. Dengan coverage.py, coverage report --fail-under=80 menggagalkan pembangunan di bawah ambang batas. Pertahankan ambang batas apa pun tetap sederhana dan gabungkan dengan aturan pada kode baru, sehingga satu file lama yang besar tidak memblokir setiap perubahan.
Di luar cakupan: pertanyaan yang lebih baik untuk ditanyakan
- Apakah pengujian mencakup bagian kode yang berisiko dan sering diubah?
- Apakah batasan dan jalur kesalahan diuji, bukan hanya jalur bahagia?
- Apakah pengujian akan gagal jika baris ini salah?
- Apakah pengujian cukup cepat dan stabil untuk dijalankan pada setiap perubahan?
Ini termasuk dalam strategi pengujian. Cakupan hanya membantu menjawab pertanyaan pertama.
Tidak termasuk kode dan membaca laporan HTML
Beberapa kode tidak layak untuk diuji secara langsung, seperti pembantu debug atau blok yang hanya berjalan ketika skrip dimulai dengan tangan. coverage.py memungkinkan Anda mengecualikan baris dengan komentar:
if __name__ == "__main__": # pragma: no cover
main()
Anda juga dapat mengatur pengecualian dan folder sumber yang akan diukur dalam konfigurasi, misalnya di pyproject.toml:
[tool.coverage.run]
branch = true
source = ["shop"]
[tool.coverage.report]
show_missing = true
exclude_also = ["if TYPE_CHECKING:"]
Gunakan pengecualian dengan hemat dan tinjau pengecualian tersebut seperti kode. Masing-masing adalah keputusan bahwa beberapa baris tidak akan pernah diperiksa. Mengecualikan penangan kesalahan yang sulit diuji hanya untuk menaikkan angka akan menggagalkan tujuan.
Untuk pekerjaan sehari-hari, laporan HTML lebih berguna daripada tabel terminal. Jalankan coverage html dan buka htmlcov/index.html. Setiap file ditampilkan dengan baris yang dieksekusi berwarna hijau, baris yang terlewat berwarna merah, dan sebagian cabang berwarna kuning, sehingga Anda dapat melihat sekilas mana if yang tidak pernah mengambil jalur lainnya. Banyak editor juga dapat menampilkan cakupan di margin, sehingga membuat celah terlihat saat Anda menulis kode, bukan setelahnya.
Cakupan sebelum refactoring
Cakupan juga merupakan pemeriksaan keamanan sebelum melakukan refactoring. Jika Anda berencana merestrukturisasi modul, lihat laporannya terlebih dahulu. Area merah tidak memiliki pengujian untuk memperingatkan Anda jika perilaku berubah, jadi tambahkan pengujian karakterisasi yang menangkap perilaku saat ini sebelum Anda memulai. Jika digunakan dengan cara ini, cakupan tidak lagi menjadi target dan hanya menjadi alat perencanaan yang praktis.
Laporan yang sama membantu ketika bug lolos ke produksi. Temukan baris tempat bug itu berada dan periksa apakah ada tes yang menjalankannya. Jika tidak, Anda telah menemukan celah yang layak untuk ditutup. Jika pengujian berhasil menjalankannya, masalahnya adalah pernyataan yang lemah, persis dengan situasi yang ditunjukkan oleh contoh tanpa pernyataan kami. Kedua jawaban tersebut memberi tahu Anda cara meningkatkan suite.
Penutup
Cakupan kode adalah senter, bukan nilai. Gunakan cakupan cabang untuk menemukan kode yang tidak disentuh oleh pengujian, terutama penanganan kesalahan, lalu tulis pengujian yang membuat pernyataan nyata. Jika Anda ingat satu hasil dari pengujian kami, jadikanlah sebagai berikut: cakupan 100% dicapai oleh pengujian yang tidak menghasilkan apa pun.
Pertanyaan yang sering diajukan
Apa cakupan kode dalam pengujian?
Ini adalah persentase kode yang dieksekusi saat pengujian otomatis dijalankan, diukur berdasarkan garis, cabang, atau kondisi. Ini menunjukkan tes apa yang disentuh, bukan apakah mereka memverifikasinya.
Apa perbedaan cakupan jalur dan cabang?
Cakupan jalur memeriksa apakah setiap jalur berjalan. Cakupan cabang memeriksa bahwa setiap keputusan berjalan dua arah. Cakupan cabang menemukan jalur yang belum teruji yang terlewatkan oleh cakupan jalur.
Apakah cakupan kode 100% bagus?
Tidak dengan sendirinya. Dalam pengujian kami, rangkaian tanpa pernyataan mencapai 100%. Bertujuan untuk pengujian yang bermakna pada kode penting daripada angka sempurna.
Berapa persentase cakupan kode yang baik?
Banyak tim yang menargetkan sekitar 70 hingga 85% secara keseluruhan, dengan lebih banyak pada logika kritis. Tren dan kualitas pernyataan lebih penting daripada angka pastinya.
Apa perbedaan pengujian mutasi dengan cakupan?
Pengujian mutasi sedikit mengubah kode Anda dan memeriksa apakah pengujian gagal. Ini mengukur apakah pengujian mendeteksi bug, yang tidak dapat dilakukan oleh cakupan.
Teruslah belajar
Butuh bantuan dengan pengujian, WordPress atau SEO?
Kami membantu tim mengirimkan perangkat lunak yang lebih baik dan mengembangkan situs mereka. Beritahu kami apa yang sedang Anda kerjakan dan kami akan menyarankan langkah selanjutnya yang jelas.
Panduan lebih lanjut tentang pengujian, AI & pengembangan modern
Menikmati artikel ini? Pilih topik dan langsung masuk ke tutorial terbaru kami.
Penjelasan Halusinasi AI: Mengapa Model Bahasa Memperbaiki Masalah dan Cara Menguranginya
Halusinasi AI menjelaskan: apa itu, mengapa model bahasa memproduksinya, tipe umum, cara menguranginya dengan landasan dan petunjuk, dan pemeriksaan…
Baca artikel →DevOpsPenjelasan Variabel Lingkungan: File .env, Rahasia, dan Konfigurasi Dilakukan dengan Benar
Variabel lingkungan dijelaskan dengan contoh Python yang kami jalankan: membaca dan mengaturnya, mengapa setiap nilai berupa string, file .env, pewa…
Baca artikel →Pengembangan perangkat lunakPenjelasan Pembatasan Kecepatan API: Algoritma, Header, dan Penanganan 429 Kesalahan
Pembatasan laju API dijelaskan dengan simulasi yang kami jalankan: keranjang token, jendela tetap, jendela geser, respons 429, header batas laju, pe…
Baca artikel →Otomatisasi pengujianMengejek dengan Python: unittest.mock, patch dan Kesalahan yang Membuat Tes Berbohong
Mengejek dengan Python dengan unittest.mock, menguji: Mock dan return_value, side_effect, menambal di mana nama digunakan, spesifikasi dan autospec,…
Baca artikel →Pengujian perangkat lunakPenjelasan Cakupan Kode: Cakupan Jalur vs Cabang dan Apa Arti 100% Sebenarnya
Cakupan kode dijelaskan dengan coverage.py yang sebenarnya dijalankan: cakupan garis, cabang dan kondisi, membaca laporan, mengapa cakupan 100% masi…
Baca artikel →Basis data & SQLDELETE vs TRUNCATE vs DROP dalam SQL: Perbedaan, Rollback dan Kapan Menggunakan Masing-Masing
DELETE vs TRUNCATE vs DROP dijelaskan dengan SQL yang diuji: apa yang dihapus, rollback, pemicu, reset identitas, kecepatan, dan perbedaan PostgreSQ…
Baca artikel →Basis data & SQLPenjelasan Tampilan SQL: Membuat, Menggunakan, dan Menghindari Kesalahan, Dengan Contoh
Tampilan SQL dijelaskan dengan kueri yang kami jalankan: membuat tampilan, hasil langsung, menyembunyikan kolom, kinerja, tampilan yang dapat diperb…
Baca artikel →Pengembangan perangkat lunakAntarmuka TypeScript vs Tipe: Perbedaan Nyata, Diuji Dengan Kompiler
Antarmuka TypeScript vs tipe, diperiksa dengan kompiler sebenarnya: apa yang bisa dilakukan keduanya, apa yang hanya bisa dilakukan satu orang, peng…
Baca artikel →Pengembangan perangkat lunakPenjelasan Python *args dan **kwargs: Fungsi Fleksibel Dengan Contoh Jelas
Python *args dan **kwargs dijelaskan dengan kode yang kami jalankan: pengepakan dan pembongkaran, urutan parameter, argumen hanya kata kunci dan pos…
Baca artikel →Pengembangan perangkat lunakFungsi Python Lambda: Sintaks, Kegunaan Sebenarnya, dan Kapan Tidak Digunakan
Fungsi python lambda dijelaskan dengan kode yang kami jalankan: sintaksis, pengurutan dengan kunci, peta dan filter, penutupan, jebakan pengikatan a…
Baca artikel →Kecerdasan buatanApa Itu Agen AI? Cara Kerjanya, Dengan Agent Loop Sederhana yang Dapat Anda Jalankan
Apa itu agen AI? Panduan sederhana tentang loop agen, alat, memori, dan pagar pembatas, dengan loop Python kecil yang kami jalankan, risiko nyata da…
Baca artikel →DevOpsPemantauan vs Observabilitas: Perbedaan Nyata, Dengan Log, Metrik, dan Jejak
Penjelasan pemantauan vs observabilitas: kegagalan yang diketahui dan tidak diketahui, log, metrik dan jejak, penelusuran insiden checkout, sinyal e…
Baca artikel →DevOpsVolume Docker vs Bind Mounts: Pertahankan Data Kontainer dengan Cara yang Benar
Volume Docker vs pengikatan pengikat dijelaskan: mengapa data kontainer menghilang, volume yang diberi nama, pengikatan pengikatan, tmpfs, contoh Tu…
Baca artikel →Otomatisasi pengujianTDD vs BDD: Perbedaan Utama, Satu Fitur Dibangun dengan Dua Cara dan Kapan Menggunakan Masing-masing
TDD vs BDD dijelaskan dengan satu fitur diskon yang dibuat dengan dua cara: refactor merah-hijau dengan kode yang kami jalankan, skenario Mengingat-…
Baca artikel →Pengembangan perangkat lunakGit merge vs rebase: perbedaan, contoh, dan kapan memakai masing-masing
Git merge vs rebase dijelaskan dengan keluaran log nyata, tabel perbandingan, aturan emas rebase, dan alur kerja tim yang aman.
Baca artikel →Pengembangan perangkat lunakList Comprehension Python: Sintaks, Contoh, dan Kapan Harus Dihindari
List comprehension Python dijelaskan: sintaks, filter, if-else, perulangan bersarang, waktu yang diukur, dan kapan perulangan for biasa lebih baik.
Baca artikel →Basis data & SQLPenjelasan SQL JOIN dengan Contoh (Inner, Left, Right, Full)
Pahami SQL JOIN langkah demi langkah dengan tabel dan query contoh: INNER, LEFT, RIGHT, FULL, CROSS, dan SELF, plus kesalahan umum dan tips performa.
Baca artikel →