Bagaimana saya bisa menganjurkan pengujian unit pada kode pribadi?

15

Saya mencoba menganjurkan pengujian unit di kelompok kerja saya, tetapi keberatan yang sering saya dapatkan adalah bahwa itu harus digunakan hanya untuk API yang diekspor secara eksternal (yang hanya merupakan bagian minimal dan tidak kritis dari sistem kami), dan bukan pada internal dan pribadi kode (yang sekarang hanya memiliki pengujian fungsional).

Sementara saya berpikir bahwa unit test dapat dan harus diterapkan ke semua kode, bagaimana saya bisa meyakinkan rekan kerja saya?

Wizard79
sumber
3
Jika Anda memiliki metode pribadi yang Anda rasa perlu untuk diuji, itu sering merupakan tanda bahwa kode Anda melanggar SRP & ada kelas lain di sana yang berteriak untuk diekstraksi dan diuji dengan caranya sendiri.
Paddyslacker
@Addaddlacker: Saya merasa bahwa semua kode perlu diuji. Saya tidak melihat mengapa unit kode yang mengikuti prinsip tanggung jawab tunggal tidak boleh dikenai pengujian unit ...
Wizard79
4
@lorenzo, Anda melewatkan poin saya; mungkin saya tidak membuatnya dengan sangat baik. Jika Anda mengekstrak metode pribadi ini ke kelas lain sekarang mereka harus dapat diakses dari kelas asli Anda. Karena metodenya sekarang bersifat publik, mereka harus diuji. Saya tidak menyiratkan bahwa mereka tidak boleh diuji, saya menyiratkan bahwa jika Anda merasa perlu untuk langsung menguji metode, kemungkinan mereka tidak boleh pribadi.
Paddyslacker
@Paddyslacker: Saya merasa perlu untuk langsung menguji juga metode pribadi. Menurut Anda mengapa mereka tidak harus pribadi?
Wizard79
6
Dengan menguji metode pribadi Anda melanggar abstraksi. Anda harus menguji status dan / atau perilaku, bukan implementasi, dalam pengujian unit. Contoh / skenario Anda harus dapat memverifikasi apa hasil dari kode pribadi hingga - jika Anda menemukan itu sulit, maka seperti kata Paddyslacker itu bisa berarti bahwa Anda melanggar SRP. Namun bisa juga berarti bahwa Anda belum menyaring contoh untuk benar-benar mewakili apa yang dilakukan kode Anda.
FinnNk

Jawaban:

9

Rekan kerja Anda mungkin membingungkan tes unit yang sebenarnya dengan tes integrasi. Jika produk Anda (atau memiliki) API, pengujian integrasi dapat diprogram sebagai NUnit test case. Beberapa orang secara keliru percaya bahwa itu adalah unit test.

Anda dapat mencoba meyakinkan rekan kerja Anda dengan yang berikut (Saya yakin Anda sudah mengetahui hal ini, semua yang saya katakan adalah menunjukkan hal itu kepada rekan kerja Anda mungkin membantu):

  • Cakupan tes . Ukur persentase cakupan tes yang sebenarnya dari tes integrasi tersebut. Ini adalah pemeriksaan realitas bagi mereka yang tidak pernah menjalankan cakupan tes. Karena sulit untuk menjalankan semua jalur logis ketika input berada beberapa lapis, cakupan pengujian berada di antara 20% dan 50%. Untuk mendapatkan lebih banyak liputan, rekan kerja Anda perlu menulis unit-test yang nyata dan terisolasi.
  • Konfigurasi . Menyebarkan perangkat lunak yang sama dalam pengujian dan mungkin Anda dapat menunjukkan kepada rekan kerja Anda betapa sulitnya untuk menjalankan tes mereka di lingkungan yang berbeda. Jalur ke berbagai file, string koneksi DB, URL layanan jarak jauh, dll - semuanya bertambah.
  • Waktu eksekusi . Kecuali jika tes tersebut adalah unit test yang benar dan dapat berjalan di memori, mereka akan membutuhkan banyak waktu untuk berjalan.
azheglov
sumber
12

Alasan untuk menggunakan pengujian unit pada kode internal / pribadi persis sama dengan untuk API yang didukung secara eksternal:

  • Mereka mencegah bug dari berulang (unit test membentuk bagian dari suite tes regresi Anda).
  • Mereka mendokumentasikan (dalam format yang dapat dieksekusi!) Bahwa kode berfungsi.
  • Mereka memberikan definisi yang dapat dieksekusi tentang apa artinya "kode bekerja".
  • Mereka menyediakan cara otomatis untuk menunjukkan bahwa kode memang cocok dengan spesifikasi (seperti yang didefinisikan oleh poin di atas).
  • Mereka menunjukkan bagaimana unit / kelas / modul / fungsi / metode gagal di hadapan input yang tidak terduga.
  • Mereka memberikan contoh tentang cara menggunakan unit ini, yang merupakan dokumentasi yang bagus untuk anggota tim baru.
Frank Shearar
sumber
8

Jika maksud Anda pribadi seperti yang saya kira Anda maksudkan, maka tidak - Anda tidak boleh mengujinya. Anda seharusnya hanya unit yang menguji perilaku / keadaan yang dapat diamati. Anda mungkin kehilangan poin di balik siklus "red-green-refactor" dari TDD (dan jika Anda tidak melakukan tes terlebih dahulu maka prinsip yang sama berlaku). Setelah tes ditulis dan lulus, Anda tidak ingin itu berubah saat melakukan refactoring. Jika Anda dipaksa untuk menguji fungsionalitas pribadi unit maka itu mungkin berarti bahwa pengujian unit di sekitar fungsionalitas publik cacat. Jika sulit dan rumit untuk menulis tes di sekitar kode publik maka mungkin kelas Anda melakukan terlalu banyak atau masalah Anda tidak jelas.

Lebih buruk lagi, seiring waktu tes unit Anda akan menjadi bola dan rantai memperlambat Anda tanpa menambahkan nilai apa pun (mengubah implementasi, misalnya optimasi atau penghapusan duplikasi, seharusnya tidak berpengaruh pada tes unit). Akan tetapi, kode internal harus diuji unit karena perilaku / keadaan dapat diamati (hanya dengan cara terbatas).

Ketika saya pertama kali melakukan pengujian unit saya menarik segala macam trik untuk menguji unit barang-barang pribadi tetapi sekarang, dengan beberapa tahun di bawah ikat pinggang saya, saya melihatnya lebih buruk daripada membuang-buang waktu.

Inilah sedikit contoh konyol, tentu saja dalam kehidupan nyata Anda akan memiliki lebih banyak tes daripada ini:

Katakanlah Anda memiliki kelas yang mengembalikan daftar string yang diurutkan - Anda harus memeriksa bahwa hasilnya diurutkan, bukan bagaimana sebenarnya mengurutkan daftar itu. Anda bisa memulai implementasi Anda dengan algoritma tunggal yang hanya mengurutkan daftar. Setelah selesai, pengujian Anda tidak perlu diubah jika Anda kemudian mengubah algoritma penyortiran Anda. Pada titik ini Anda memiliki tes tunggal (dengan asumsi bahwa penyortiran tertanam di kelas Anda):

  1. Apakah hasil saya diurutkan?

Sekarang katakan Anda ingin dua algoritma (mungkin satu lebih efisien dalam beberapa keadaan tetapi tidak yang lain), maka masing-masing algoritma dapat (dan umumnya, harus) disediakan oleh kelas yang berbeda dan kelas Anda mengambil dari mereka - Anda dapat memeriksa ini terjadi untuk skenario yang Anda pilih menggunakan ejekan, tetapi tes asli Anda masih valid dan karena kami hanya memverifikasi perilaku yang dapat diamati, tidak perlu diubah. Anda berakhir dengan 3 tes:

  1. Apakah hasil saya diurutkan?
  2. Diberikan skenario (katakanlah daftar awal hampir diurutkan untuk memulai dengan) adalah panggilan yang dibuat ke kelas yang mengurutkan string menggunakan algoritma X?
  3. Diberikan skenario (daftar awal dalam urutan acak) adalah panggilan yang dilakukan ke kelas yang mengurutkan string menggunakan algoritma Y?

Alternatifnya adalah mulai menguji kode pribadi di dalam kelas Anda - Anda tidak mendapatkan apa-apa dari ini - tes di atas memberi tahu saya segala sesuatu yang perlu saya ketahui sejauh menyangkut pengujian unit. Dengan menambahkan tes pribadi Anda membuat jaket lurus, berapa banyak pekerjaan jika Anda tidak hanya memeriksa bahwa hasilnya diurutkan tetapi juga bagaimana itu diurutkan?

Tes (dari jenis ini) hanya boleh berubah ketika perilaku berubah, mulai menulis tes terhadap kode pribadi dan itu keluar jendela.

FinnNk
sumber
1
Mungkin ada kesalahpahaman tentang arti "pribadi". Dalam sistem kami 99% dari kode adalah "pribadi", maka kami memiliki API kecil untuk mengotomatisasi / mengendalikan salah satu komponen sistem secara remote. Maksud saya unit menguji kode dari semua modul lainnya.
Wizard79
4

inilah alasan lain: dalam kasus hipotetis saya harus memilih antara unit yang menguji API eksternal vs bagian pribadi, saya akan memilih bagian pribadi.

Jika setiap bagian pribadi dicakup oleh pengujian, API yang terdiri dari bagian-bagian pribadi ini harus ditutup hampir 100% juga, kecuali hanya lapisan atas. Tapi itu mungkin lapisan tipis.

Di sisi lain ketika hanya menguji API, akan sangat sulit untuk sepenuhnya mencakup semua jalur kode yang mungkin.

stijn
sumber
+1 "di sisi lain ..." Tetapi jika tidak ada yang lain, tambahkan tes di mana kegagalan akan paling menyakitkan.
Tony Ennis
2

Sulit untuk membuat orang menerima pengujian unit karena sepertinya buang-buang waktu ("kita bisa meng-coding proyek penghasil uang lain!") Atau rekursif ("Dan kemudian kita harus menulis kasus uji untuk kasus uji!") Saya bersalah karena mengatakan keduanya.

Pertama kali Anda menemukan bug, Anda harus menghadapi kenyataan bahwa Anda tidak sempurna (seberapa cepat kita programmer lupa!) Dan Anda berkata, "Hmmm."


Aspek lain dari pengujian unit adalah bahwa kode harus ditulis agar dapat diuji. Menyadari bahwa Some Code mudah diuji dan Some Code tidak membuat programmer yang baik menjadi "Hmmm."


Apakah Anda bertanya kepada rekan kerja Anda mengapa pengujian unit hanya berguna untuk API yang menghadap eksternal?


Salah satu cara untuk menunjukkan nilai pengujian unit adalah menunggu bug buruk terjadi dan kemudian menunjukkan bagaimana pengujian unit dapat mencegahnya. Itu bukan untuk menggosoknya di wajah mereka, itu untuk, dalam pikiran mereka, memindahkan pengujian unit dari Menara Gading Teoritis ke realitas di dalam parit.

Cara lain adalah menunggu sampai kesalahan yang sama terjadi dua kali . "Uhhh, wah Bos, kami menambahkan kode untuk menguji nol setelah masalah minggu lalu, tetapi pengguna memasukkan hal kosong kali ini!"


Menurut contoh. Tulis unit test untuk kode ANDA, lalu tunjukkan nilai pada bos Anda. Kemudian lihat apakah bos akan memanggil pizza untuk makan siang suatu hari dan memberikan presentasi.


Akhirnya, saya tidak bisa memberi tahu Anda tentang kelegaan yang saya rasakan ketika kita akan mendorong untuk mendorong dan saya mendapatkan bilah hijau dari tes unit.

Tony Ennis
sumber
2

Ada dua macam kode pribadi: Kode swasta yang dipanggil oleh kode umum (atau kode pribadi yang dipanggil oleh kode pribadi yang dipanggil oleh kode umum (atau ...)) dan kode pribadi yang tidak tidak akhirnya dipanggil oleh masyarakat kode.

Yang pertama sudah diuji melalui tes untuk kode publik. Yang terakhir tidak bisa dipanggil sama sekali dan karenanya harus dihapus, tidak diuji.

Perhatikan bahwa ketika Anda melakukan TDD tidak mungkin ada kode pribadi yang belum diuji.

Jörg W Mittag
sumber
Dalam sistem kami 99% dari kode adalah jenis ketiga : privat, tidak dipanggil oleh kode publik, dan penting untuk sistem (hanya sebagian kecil dari sistem kami memiliki eksternal, API publik).
Wizard79
1
"Perhatikan bahwa ketika Anda melakukan TDD tidak mungkin ada kode pribadi yang belum diuji." <- hapus suatu test case, tanpa mengetahui bahwa tes itu adalah satu-satunya tes untuk mencakup cabang tertentu. OK, itu lebih "belum diuji" kode, tetapi cukup mudah untuk melihat refactoring sepele kemudian mengubah kode itu ... hanya test suite Anda tidak lagi menutupinya.
Frank Shearar
2

Pengujian unit adalah semua tentang pengujian unit kode Anda. Terserah Anda untuk menentukan apa itu unit. Rekan kerja Anda mendefinisikan unit sebagai elemen API.

Bagaimanapun, pengujian API harus menghasilkan kode pribadi juga. Jika Anda menetapkan cakupan kode sebagai indikator kemajuan pengujian unit, Anda akan berakhir dalam menguji semua kode Anda. Jika beberapa bagian kode belum tercapai, berikan tiga pilihan kepada rekan kerja Anda:

  • menentukan kasus uji lain untuk menutupi bagian itu,
  • menganalisis kode untuk membenarkan mengapa tidak dapat dicakup dalam konteks pengujian unit tetapi harus dicakup dalam situasi lain,
  • menghapus kode mati yang belum tercakup tidak dibenarkan.
mouviciel
sumber
Dalam sistem kami, API hanya merupakan bagian minimal, yang memungkinkan otomatisasi / kendali jarak jauh untuk aplikasi pihak ketiga. Menguji hanya akun API untuk cakupan kode 1% ...
Wizard79