8 Praktik Terbaik Pengujian Otomatis untuk Pengalaman Pengujian yang Positif

Diterbitkan: 2022-03-11

Tidak heran banyak pengembang melihat pengujian sebagai kejahatan yang diperlukan yang menghabiskan waktu dan energi: Pengujian bisa menjadi membosankan, tidak produktif, dan sepenuhnya terlalu rumit.

Pengalaman pertama saya dengan pengujian sangat buruk. Saya bekerja di tim yang memiliki persyaratan cakupan kode yang ketat. Alur kerjanya adalah: mengimplementasikan fitur, men-debug-nya, dan menulis tes untuk memastikan cakupan kode penuh. Tim tidak memiliki pengujian integrasi, hanya pengujian unit dengan banyak tiruan yang diinisialisasi secara manual, dan sebagian besar pengujian unit menguji pemetaan manual sepele saat menggunakan perpustakaan untuk melakukan pemetaan otomatis. Setiap pengujian mencoba menegaskan setiap properti yang tersedia, sehingga setiap perubahan merusak lusinan pengujian.

Saya tidak suka mengerjakan tes karena dianggap sebagai beban yang memakan waktu. Namun, saya tidak menyerah. Pengujian kepercayaan menyediakan dan otomatisasi pemeriksaan setelah setiap perubahan kecil menggelitik minat saya. Saya mulai membaca dan berlatih, dan belajar bahwa tes, jika dilakukan dengan benar, bisa bermanfaat dan menyenangkan.

Dalam artikel ini, saya membagikan delapan praktik terbaik pengujian otomatis yang saya harap sudah saya ketahui sejak awal.

Mengapa Anda Membutuhkan Strategi Tes Otomatis

Pengujian otomatis sering kali berfokus pada masa depan, tetapi ketika Anda menerapkannya dengan benar, Anda langsung mendapat manfaat. Menggunakan alat yang membantu Anda melakukan pekerjaan dengan lebih baik dapat menghemat waktu dan membuat pekerjaan Anda lebih menyenangkan.

Bayangkan Anda sedang mengembangkan sistem yang mengambil pesanan pembelian dari ERP perusahaan dan menempatkan pesanan tersebut dengan vendor. Anda memiliki harga barang yang dipesan sebelumnya di ERP, tetapi harga saat ini mungkin berbeda. Anda ingin mengontrol apakah akan memesan dengan harga yang lebih rendah atau lebih tinggi. Anda memiliki preferensi pengguna yang disimpan, dan Anda sedang menulis kode untuk menangani fluktuasi harga.

Bagaimana Anda memeriksa bahwa kode berfungsi seperti yang diharapkan? Anda mungkin akan:

  1. Buat pesanan dummy dalam contoh ERP pengembang (dengan asumsi Anda mengaturnya sebelumnya).
  2. Jalankan aplikasi Anda.
  3. Pilih pesanan itu dan mulai proses penempatan pesanan.
  4. Mengumpulkan data dari database ERP.
  5. Minta harga saat ini dari API vendor.
  6. Ganti harga dalam kode untuk menciptakan kondisi tertentu.

Anda berhenti di breakpoint dan dapat melangkah selangkah demi selangkah untuk melihat apa yang akan terjadi untuk satu skenario, tetapi ada banyak kemungkinan skenario:

Preferensi harga ERP harga penjual Haruskah kita memesan?
Izinkan harga lebih tinggi Izinkan harga yang lebih rendah
Salah Salah 10 10 benar
(Di sini akan ada tiga kombinasi preferensi lagi, tetapi harganya sama, jadi hasilnya sama.)
benar Salah 10 11 benar
benar Salah 10 9 Salah
Salah benar 10 11 Salah
Salah benar 10 9 benar
benar benar 10 11 benar
benar benar 10 9 benar

Dalam kasus bug, perusahaan dapat kehilangan uang, merusak reputasinya, atau keduanya. Anda perlu memeriksa beberapa skenario dan mengulangi loop pemeriksaan beberapa kali. Melakukannya secara manual akan membosankan. Tetapi tes ada di sini untuk membantu!

Pengujian memungkinkan Anda membuat konteks apa pun tanpa panggilan ke API yang tidak stabil. Mereka menghilangkan kebutuhan untuk mengklik berulang melalui antarmuka lama dan lambat yang terlalu umum dalam sistem ERP lama. Yang harus Anda lakukan adalah menentukan konteks untuk unit atau subsistem dan kemudian setiap debugging, pemecahan masalah, atau penjelajahan skenario terjadi secara instan—Anda menjalankan pengujian dan Anda kembali ke kode Anda. Preferensi saya adalah menyiapkan keybinding di IDE saya yang mengulangi uji coba saya sebelumnya, memberikan umpan balik langsung dan otomatis saat saya membuat perubahan.

1. Pertahankan Sikap yang Benar

Dibandingkan dengan debugging manual dan pengujian mandiri, pengujian otomatis lebih produktif sejak awal, bahkan sebelum kode pengujian dilakukan. Setelah Anda memeriksa apakah kode Anda berperilaku seperti yang diharapkan—dengan menguji secara manual atau mungkin, untuk modul yang lebih kompleks, dengan menelusurinya dengan debugger selama pengujian—Anda dapat menggunakan pernyataan untuk menentukan apa yang Anda harapkan untuk kombinasi parameter input apa pun.

Dengan lulus tes, Anda hampir siap untuk berkomitmen, tetapi tidak cukup. Bersiaplah untuk memperbaiki kode Anda karena versi kerja pertama biasanya tidak elegan. Apakah Anda akan melakukan refactoring itu tanpa tes? Itu patut dipertanyakan karena Anda harus menyelesaikan semua langkah manual lagi, yang bisa mengurangi semangat Anda.

Bagaimana dengan masa depan? Saat melakukan pemfaktoran ulang, pengoptimalan, atau penambahan fitur, pengujian membantu memastikan bahwa modul masih berperilaku seperti yang diharapkan setelah Anda mengubahnya, sehingga menanamkan kepercayaan yang bertahan lama dan memungkinkan pengembang merasa lebih siap untuk menangani pekerjaan yang akan datang.

Adalah kontraproduktif untuk menganggap tes sebagai beban atau sesuatu yang hanya membuat peninjau kode atau prospek senang. Pengujian adalah alat yang kami sebagai pengembang mendapat manfaat darinya. Kami suka ketika kode kami berfungsi dan kami tidak suka menghabiskan waktu untuk tindakan berulang atau memperbaiki kode untuk mengatasi bug.

Baru-baru ini, saya mengerjakan refactoring di basis kode saya dan meminta IDE saya untuk membersihkan yang tidak digunakan using arahan. Yang mengejutkan saya, pengujian menunjukkan beberapa kegagalan dalam sistem pelaporan email saya. Namun, itu adalah kegagalan yang valid—proses pembersihan menghapus beberapa using arahan dalam kode Razor (HTML + C#) saya untuk template email, dan akibatnya mesin template tidak dapat membuat HTML yang valid. Saya tidak menyangka bahwa operasi kecil seperti itu akan merusak pelaporan email. Pengujian membantu saya menghindari menghabiskan berjam-jam menangkap bug di seluruh aplikasi tepat sebelum dirilis, ketika saya berasumsi bahwa semuanya akan berfungsi.

Tentu saja, Anda harus tahu cara menggunakan alat dan tidak memotong jari pepatah Anda. Tampaknya mendefinisikan konteks itu membosankan dan bisa lebih sulit daripada menjalankan aplikasi, bahwa pengujian memerlukan terlalu banyak pemeliharaan untuk menghindari menjadi basi dan tidak berguna. Ini adalah poin yang valid dan kami akan mengatasinya.

2. Pilih Jenis Tes yang Tepat

Pengembang sering tumbuh untuk tidak menyukai tes otomatis karena mereka mencoba untuk mengejek selusin dependensi hanya untuk memeriksa apakah mereka dipanggil oleh kode. Atau, pengembang menghadapi pengujian tingkat tinggi dan mencoba mereproduksi setiap status aplikasi untuk memeriksa semua variasi dalam modul kecil. Pola-pola ini tidak produktif dan membosankan, tetapi kita dapat menghindarinya dengan memanfaatkan jenis pengujian yang berbeda seperti yang dimaksudkan. (Lagi pula, tes harus praktis dan menyenangkan!)

Pembaca perlu mengetahui apa itu pengujian unit dan bagaimana cara menulisnya, dan terbiasa dengan pengujian integrasi—jika tidak, ada baiknya berhenti sejenak di sini untuk mempercepatnya.

Ada lusinan jenis pengujian, tetapi lima jenis umum ini membuat kombinasi yang sangat efektif:

Satu set ilustrasi dasar yang menggambarkan pengujian unit, pengujian integrasi, pengujian fungsional, pengujian kenari, dan pengujian beban.
Lima Jenis Tes Umum

  • Tes unit digunakan untuk menguji modul yang terisolasi dengan memanggil metodenya secara langsung. Dependensi tidak sedang diuji, oleh karena itu, mereka diejek.
  • Tes integrasi digunakan untuk menguji subsistem. Anda masih menggunakan panggilan langsung ke metode modul itu sendiri, tetapi di sini kami peduli dengan dependensi, jadi jangan gunakan dependensi tiruan—hanya modul dependen nyata (produksi). Anda masih dapat menggunakan database dalam memori atau server web tiruan karena ini adalah tiruan infrastruktur.
  • Pengujian fungsional adalah pengujian untuk seluruh aplikasi, juga dikenal sebagai pengujian ujung ke ujung (E2E). Anda tidak menggunakan panggilan langsung. Sebagai gantinya, semua interaksi melewati API atau antarmuka pengguna—ini adalah pengujian dari perspektif pengguna akhir. Namun, infrastruktur masih diejek.
  • Tes Canary mirip dengan tes fungsional tetapi dengan infrastruktur produksi dan serangkaian tindakan yang lebih kecil. Mereka digunakan untuk memastikan bahwa aplikasi yang baru digunakan berfungsi.
  • Uji beban mirip dengan uji kenari tetapi dengan infrastruktur pementasan nyata dan serangkaian tindakan yang lebih kecil, yang diulang berkali-kali.

Tidak selalu perlu bekerja dengan kelima jenis pengujian dari awal. Dalam kebanyakan kasus, Anda bisa pergi jauh dengan tiga tes pertama.

Kami akan memeriksa secara singkat kasus penggunaan setiap jenis untuk membantu Anda memilih yang tepat untuk kebutuhan Anda.

Tes Unit

Ingat contoh dengan harga dan preferensi penanganan yang berbeda. Ini adalah kandidat yang baik untuk pengujian unit karena kami hanya peduli tentang apa yang terjadi di dalam modul, dan hasilnya memiliki konsekuensi bisnis yang penting.

Modul memiliki banyak kombinasi parameter input yang berbeda, dan kami ingin mendapatkan nilai pengembalian yang valid untuk setiap kombinasi argumen yang valid. Tes unit bagus dalam memastikan validitas karena menyediakan akses langsung ke parameter input fungsi atau metode dan Anda tidak perlu menulis lusinan metode pengujian untuk mencakup setiap kombinasi. Dalam banyak bahasa, Anda dapat menghindari duplikasi metode pengujian dengan mendefinisikan metode, yang menerima argumen yang diperlukan untuk kode Anda dan hasil yang diharapkan. Kemudian, Anda dapat menggunakan alat pengujian untuk memberikan kumpulan nilai dan ekspektasi yang berbeda untuk metode berparameter tersebut.

Tes Integrasi

Tes integrasi cocok untuk kasus ketika Anda tertarik pada bagaimana modul berinteraksi dengan dependensinya, modul lain, atau infrastruktur. Anda masih menggunakan panggilan metode langsung tetapi tidak ada akses ke submodul, jadi mencoba menguji semua skenario untuk semua metode input dari semua submodul tidak praktis.

Biasanya, saya lebih suka memiliki satu skenario sukses dan satu skenario kegagalan per modul.

Saya suka menggunakan tes integrasi untuk memeriksa apakah wadah injeksi ketergantungan berhasil dibuat, apakah pipa pemrosesan atau perhitungan mengembalikan hasil yang diharapkan, atau apakah data kompleks dibaca dan dikonversi dengan benar dari database atau API pihak ketiga.

Tes Fungsional atau E2E

Pengujian ini memberi Anda kepercayaan diri terbesar bahwa aplikasi Anda berfungsi karena mereka memverifikasi bahwa aplikasi Anda setidaknya dapat dimulai tanpa kesalahan waktu proses. Memang sedikit lebih sulit untuk mulai menguji kode Anda tanpa akses langsung ke kelasnya, tetapi begitu Anda memahami dan menulis beberapa tes pertama, Anda akan merasa bahwa itu tidak terlalu sulit.

Jalankan aplikasi dengan memulai proses dengan argumen baris perintah, jika diperlukan, lalu gunakan aplikasi seperti yang akan dilakukan calon pelanggan Anda: dengan memanggil titik akhir API atau menekan tombol. Ini tidak sulit, bahkan dalam kasus pengujian UI: Setiap platform utama memiliki alat untuk menemukan elemen visual di UI.

Tes Kenari

Pengujian fungsional memberi tahu Anda jika aplikasi Anda berfungsi di lingkungan pengujian, tetapi bagaimana dengan lingkungan produksi? Misalkan Anda bekerja dengan beberapa API pihak ketiga dan Anda ingin memiliki dasbor statusnya atau ingin melihat bagaimana aplikasi Anda menangani permintaan yang masuk. Ini adalah kasus penggunaan umum untuk tes kenari.

Mereka beroperasi dengan bertindak singkat pada sistem kerja tanpa menyebabkan efek samping pada sistem pihak ketiga. Misalnya, Anda dapat mendaftarkan pengguna baru atau memeriksa ketersediaan produk tanpa melakukan pemesanan.

Tujuan pengujian canary adalah untuk memastikan bahwa semua komponen utama bekerja bersama dalam lingkungan produksi, tidak gagal karena, misalnya, masalah kredensial.

Tes beban

Tes beban mengungkapkan apakah aplikasi Anda akan terus berfungsi saat banyak orang mulai menggunakannya. Mereka mirip dengan tes kenari dan fungsional tetapi tidak dilakukan di lingkungan lokal atau produksi. Biasanya, lingkungan pementasan khusus digunakan, yang mirip dengan lingkungan produksi.

Penting untuk diperhatikan bahwa pengujian ini tidak menggunakan layanan pihak ketiga yang sebenarnya, yang mungkin tidak menyenangkan dengan pengujian beban eksternal dari layanan produksi mereka dan akibatnya mungkin dikenakan biaya tambahan.

3. Pisahkan Jenis Pengujian

Saat merancang rencana pengujian otomatis Anda, setiap jenis pengujian harus dipisahkan agar dapat berjalan secara independen. Meskipun ini membutuhkan pengaturan ekstra, hal ini bermanfaat karena pengujian pencampuran dapat menimbulkan masalah.

Tes ini memiliki perbedaan:

  • Niat dan konsep dasar (jadi memisahkannya menjadi preseden yang baik bagi orang berikutnya yang melihat kode tersebut, termasuk "masa depan Anda").
  • Waktu eksekusi (jadi menjalankan pengujian unit terlebih dahulu memungkinkan siklus pengujian yang lebih cepat saat pengujian gagal).
  • Dependensi (jadi lebih efisien untuk memuat hanya yang diperlukan dalam tipe pengujian).
  • Infrastruktur yang dibutuhkan.
  • Bahasa pemrograman (dalam kasus tertentu).
  • Posisi dalam pipa continuous integration (CI) atau di luarnya.

Penting untuk dicatat bahwa dengan sebagian besar bahasa dan tumpukan teknologi, Anda dapat mengelompokkan, misalnya, semua pengujian unit bersama-sama dengan subfolder yang dinamai berdasarkan modul fungsional. Ini nyaman, mengurangi gesekan saat membuat modul fungsional baru, lebih mudah untuk pembuatan otomatis, menghasilkan lebih sedikit kekacauan, dan merupakan satu lagi cara untuk menyederhanakan pengujian.

4. Jalankan Tes Anda Secara Otomatis

Bayangkan situasi di mana Anda telah menulis beberapa tes, tetapi setelah menarik repo Anda beberapa minggu kemudian, Anda melihat tes tersebut tidak lagi lulus.

Ini adalah pengingat yang tidak menyenangkan bahwa pengujian adalah kode dan, seperti bagian kode lainnya, pengujian tersebut perlu dipertahankan. Waktu terbaik untuk ini adalah tepat sebelum Anda berpikir bahwa Anda telah menyelesaikan pekerjaan Anda dan ingin melihat apakah semuanya masih berjalan sebagaimana mestinya. Anda memiliki semua konteks yang diperlukan dan Anda dapat memperbaiki kode atau mengubah tes yang gagal dengan lebih mudah daripada rekan kerja Anda yang mengerjakan subsistem yang berbeda. Tetapi momen ini hanya ada di pikiran Anda, jadi cara paling umum untuk menjalankan tes adalah secara otomatis setelah push ke cabang pengembangan atau setelah membuat permintaan tarik.

Dengan cara ini, cabang utama Anda akan selalu dalam status valid, atau setidaknya Anda akan memiliki indikasi yang jelas tentang statusnya. Pembuatan dan pengujian pipeline otomatis—atau pipeline CI—membantu:

  • Pastikan kode dapat dibangun.
  • Hilangkan potensi masalah "Ini berfungsi pada mesin saya" .
  • Berikan instruksi yang dapat dijalankan tentang cara menyiapkan lingkungan pengembangan.

Mengonfigurasi pipeline ini membutuhkan waktu, tetapi pipeline dapat mengungkapkan berbagai masalah sebelum mencapai pengguna atau klien, bahkan saat Anda adalah satu-satunya developer.

Setelah berjalan, CI juga mengungkapkan isu-isu baru sebelum mereka memiliki kesempatan untuk berkembang dalam ruang lingkup. Karena itu, saya lebih suka mengaturnya tepat setelah menulis tes pertama. Anda dapat meng-host kode Anda di repositori pribadi di GitHub dan mengatur Tindakan GitHub. Jika repo Anda bersifat publik, Anda memiliki lebih banyak opsi daripada GitHub Actions. Misalnya, rencana pengujian otomatis saya berjalan di AppVeyor, untuk proyek dengan database dan tiga jenis pengujian.

Saya lebih suka menyusun saluran pipa saya untuk proyek produksi sebagai berikut:

  1. Kompilasi atau transpilasi
  2. Tes unit: cepat dan tidak memerlukan ketergantungan
  3. Setup dan inisialisasi database atau layanan lainnya
  4. Tes integrasi: mereka memiliki dependensi di luar kode Anda, tetapi lebih cepat daripada tes fungsional
  5. Tes fungsional: ketika langkah-langkah lain telah berhasil diselesaikan, jalankan seluruh aplikasi

Tidak ada tes kenari atau tes beban. Karena spesifikasi dan persyaratannya, mereka harus dimulai secara manual.

5. Tulis Hanya Tes yang Diperlukan

Menulis tes unit untuk semua kode adalah strategi umum, tetapi terkadang ini membuang waktu dan energi, dan tidak memberi Anda kepercayaan diri. Jika Anda terbiasa dengan konsep "piramida pengujian", Anda mungkin berpikir bahwa semua kode Anda harus dicakup dengan pengujian unit, dengan hanya sebagian yang dicakup oleh pengujian tingkat yang lebih tinggi lainnya.

Saya tidak melihat kebutuhan untuk menulis tes unit yang memastikan bahwa beberapa dependensi tiruan dipanggil dalam urutan yang diinginkan. Melakukan itu memerlukan pengaturan beberapa ejekan dan memverifikasi semua panggilan, tetapi itu masih tidak memberi saya kepercayaan diri bahwa modul berfungsi. Biasanya, saya hanya menulis tes integrasi yang menggunakan dependensi nyata dan hanya memeriksa hasilnya; itu memberi saya keyakinan bahwa saluran dalam modul yang diuji berfungsi dengan baik.

Secara umum, saya menulis tes yang membuat hidup saya lebih mudah saat menerapkan fungsionalitas dan mendukungnya nanti.

Untuk sebagian besar aplikasi, menargetkan cakupan kode 100% menambah banyak pekerjaan yang membosankan dan menghilangkan kesenangan dari bekerja dengan pengujian dan pemrograman secara umum. Seperti yang dikatakan oleh Cakupan Uji Martin Fowler:

Cakupan pengujian adalah alat yang berguna untuk menemukan bagian basis kode yang belum diuji. Cakupan tes tidak banyak berguna sebagai pernyataan numerik tentang seberapa bagus tes Anda.

Jadi saya sarankan Anda menginstal dan menjalankan penganalisis cakupan setelah menulis beberapa tes. Laporan dengan baris kode yang disorot akan membantu Anda lebih memahami jalur eksekusinya dan menemukan tempat-tempat terbuka yang harus dicakup. Juga, melihat getter, setter, dan fasad Anda, Anda akan melihat mengapa cakupan 100% tidak menyenangkan.

6. Mainkan Lego

Dari waktu ke waktu, saya melihat pertanyaan seperti, "Bagaimana saya bisa menguji metode pribadi?" Anda tidak. Jika Anda menanyakan pertanyaan itu, ada sesuatu yang salah. Biasanya, itu berarti Anda melanggar Prinsip Tanggung Jawab Tunggal, dan modul Anda tidak melakukan sesuatu dengan benar.

Refactor modul ini dan tarik logika yang menurut Anda penting ke dalam modul terpisah. Tidak ada masalah dengan menambah jumlah file, yang akan mengarah pada kode yang terstruktur sebagai bata Lego: sangat mudah dibaca, dapat dipelihara, dapat diganti, dan dapat diuji.

Di sebelah kiri ada tumpukan persegi panjang. Yang paling atas diberi label OrderProcessor dan beberapa yang di bawahnya diberi label Access Order Data, Price Check, dan Place Order. Sebuah panah menunjuk dari tumpukan kiri ke kanan, di mana OrderProcessor adalah bata Lego menyamping, dengan bata dalam berbagai tahap pemasangan dan pelepasannya, termasuk OrderDataProvider, PriceChecker, dan OrderPlacer.
Memfaktorkan ulang modul agar menyerupai bata Lego.

Penataan kode yang benar lebih mudah diucapkan daripada dilakukan. Berikut adalah dua saran:

Pemrograman Fungsional

Perlu dipelajari tentang prinsip dan ide pemrograman fungsional. Kebanyakan bahasa utama, seperti C, C++, C#, Java, Assembly, JavaScript, dan Python, memaksa Anda untuk menulis program untuk mesin. Pemrograman fungsional lebih cocok untuk otak manusia.

Ini mungkin tampak berlawanan dengan intuisi pada awalnya, tetapi pertimbangkan ini: Komputer akan baik-baik saja jika Anda meletakkan semua kode Anda dalam satu metode, menggunakan potongan memori bersama untuk menyimpan nilai sementara, dan menggunakan cukup banyak instruksi lompatan. Selain itu, kompiler dalam tahap optimasi terkadang melakukan ini. Namun, otak manusia tidak mudah menangani pendekatan ini.

Pemrograman fungsional memaksa Anda untuk menulis fungsi murni tanpa efek samping, dengan tipe yang kuat, dengan cara yang ekspresif. Dengan begitu, lebih mudah untuk bernalar tentang suatu fungsi karena satu-satunya hal yang dihasilkannya adalah nilai baliknya. Episode podcast Pemrograman Throwdown Pemrograman Fungsional Dengan Adam Gordon Bell akan membantu Anda memperoleh pemahaman dasar, dan Anda dapat melanjutkan dengan episode Corecursive Bahasa Pemrograman Tuhan Dengan Philip Wadler dan Teori Kategori Dengan Bartosz Milewski. Dua yang terakhir sangat memperkaya persepsi saya tentang pemrograman.

Pengembangan Berbasis Tes

Saya sarankan menguasai TDD. Cara terbaik untuk belajar adalah dengan berlatih. String Calculator Kata adalah cara yang bagus untuk berlatih dengan kode kata. Menguasai kata akan memakan waktu tetapi pada akhirnya akan memungkinkan Anda untuk sepenuhnya menyerap ide TDD, yang akan membantu Anda membuat kode yang terstruktur dengan baik yang menyenangkan untuk dikerjakan dan juga dapat diuji.

Satu catatan peringatan: Terkadang Anda akan melihat puritan TDD mengklaim bahwa TDD adalah satu-satunya cara yang tepat untuk memprogram. Menurut pendapat saya, ini hanyalah alat lain yang berguna di kotak peralatan Anda, tidak lebih.

Terkadang, Anda perlu melihat bagaimana menyesuaikan modul dan proses dalam hubungannya satu sama lain dan tidak tahu data dan tanda tangan apa yang digunakan. Dalam kasus seperti itu, tulis kode hingga dikompilasi, lalu tulis tes untuk memecahkan masalah dan men-debug fungsionalitas.

Dalam kasus lain, Anda tahu input dan output yang Anda inginkan, tetapi tidak tahu bagaimana menulis implementasi dengan benar karena logika yang rumit. Untuk kasus tersebut, lebih mudah untuk mulai mengikuti prosedur TDD dan membangun kode Anda langkah demi langkah daripada menghabiskan waktu memikirkan implementasi yang sempurna.

7. Buat Tes Sederhana dan Fokus

Sangat menyenangkan bekerja di lingkungan kode yang tertata rapi tanpa gangguan yang tidak perlu. Itulah mengapa penting untuk menerapkan prinsip SOLID, KISS, dan DRY untuk pengujian—menggunakan refactoring saat dibutuhkan.

Terkadang saya mendengar komentar seperti, "Saya benci bekerja di basis kode yang sangat teruji karena setiap perubahan mengharuskan saya untuk memperbaiki lusinan pengujian." Itu masalah pemeliharaan tinggi yang disebabkan oleh tes yang tidak fokus dan mencoba menguji terlalu banyak. Prinsip "Lakukan satu hal dengan baik" juga berlaku untuk tes: "Uji satu hal dengan baik"; setiap tes harus relatif singkat dan hanya menguji satu konsep. "Uji satu hal dengan baik" tidak berarti bahwa Anda harus dibatasi pada satu pernyataan per pengujian: Anda dapat menggunakan lusinan jika Anda menguji pemetaan data yang tidak sepele dan penting.

Fokus ini tidak terbatas pada satu tes atau jenis tes tertentu. Bayangkan berurusan dengan logika rumit yang Anda uji menggunakan pengujian unit, seperti memetakan data dari sistem ERP ke struktur Anda, dan Anda memiliki uji integrasi yang mengakses API ERP tiruan dan mengembalikan hasilnya. Dalam hal ini, penting untuk mengingat apa yang sudah tercakup dalam pengujian unit Anda sehingga Anda tidak menguji pemetaan lagi dalam pengujian integrasi. Biasanya, itu cukup untuk memastikan hasilnya memiliki bidang identifikasi yang benar.

Dengan kode terstruktur seperti batu bata Lego dan tes terfokus, perubahan logika bisnis seharusnya tidak menyakitkan. Jika perubahan bersifat radikal, Anda cukup membuang file dan pengujian terkaitnya, dan membuat implementasi baru dengan pengujian baru. Dalam kasus perubahan kecil, Anda biasanya mengubah satu hingga tiga tes untuk memenuhi persyaratan baru dan membuat perubahan pada logika. Tidak apa-apa untuk mengubah tes; Anda dapat menganggap praktik ini sebagai pembukuan entri ganda.

Cara lain untuk mencapai kesederhanaan meliputi:

  • Muncul dengan konvensi untuk penataan file uji, penataan konten uji (biasanya struktur Atur-Bertindak-Tegaskan), dan uji penamaan; kemudian, yang paling penting, mengikuti aturan ini secara konsisten.
  • Mengekstrak blok kode besar ke metode seperti "mempersiapkan permintaan" dan membuat fungsi pembantu untuk tindakan berulang.
  • Menerapkan pola builder untuk konfigurasi data pengujian.
  • Menggunakan (dalam tes integrasi) wadah DI yang sama dengan yang Anda gunakan di aplikasi utama sehingga setiap instantiasi akan menjadi sepele seperti TestServices.Get() tanpa membuat dependensi secara manual. Dengan begitu, akan mudah untuk membaca, memelihara, dan menulis tes baru karena Anda sudah memiliki helper yang berguna.

Jika Anda merasa ujian menjadi terlalu rumit, berhentilah dan pikirkan. Baik modul atau pengujian Anda perlu difaktorkan ulang.

8. Gunakan Alat untuk Membuat Hidup Anda Lebih Mudah

Anda akan menghadapi banyak tugas yang membosankan saat pengujian. Misalnya, menyiapkan lingkungan pengujian atau objek data, mengonfigurasi rintisan dan tiruan untuk dependensi, dan sebagainya. Untungnya, setiap tumpukan teknologi dewasa berisi beberapa alat untuk membuat tugas-tugas ini jauh lebih tidak membosankan.

Saya sarankan Anda menulis seratus tes pertama Anda jika Anda belum melakukannya, lalu luangkan waktu untuk mengidentifikasi tugas yang berulang dan pelajari tentang alat terkait pengujian untuk tumpukan teknologi Anda.

Untuk inspirasi, berikut adalah beberapa alat yang dapat Anda gunakan:

  • Pelari uji. Cari sintaks yang ringkas dan kemudahan penggunaan. Dari pengalaman saya, untuk .NET, saya merekomendasikan xUnit (meskipun NUnit juga merupakan pilihan yang solid). Untuk JavaScript atau TypeScript, saya menggunakan Jest. Cobalah untuk menemukan kecocokan terbaik untuk tugas dan pola pikir Anda karena alat dan tantangan berkembang.
  • Perpustakaan mengejek . Mungkin ada tiruan tingkat rendah untuk dependensi kode, seperti antarmuka, tetapi ada juga tiruan tingkat tinggi untuk API web atau database. Untuk JavaScript dan TypeScript, tiruan tingkat rendah yang disertakan dalam Jest tidak masalah. Untuk .NET. Saya menggunakan Moq, meskipun NSubstitute juga bagus. Adapun tiruan API web, saya menikmati menggunakan WireMock.NET. Ini dapat digunakan sebagai pengganti API untuk memecahkan masalah dan men-debug penanganan respons. Ini juga sangat andal dan cepat dalam pengujian otomatis. Basis data dapat diejek menggunakan rekanan dalam memori mereka. EfCore di .NET menyediakan opsi seperti itu.
  • Perpustakaan generasi data . Utilitas ini mengisi objek data Anda dengan data acak. Mereka berguna ketika, misalnya, Anda hanya peduli tentang beberapa bidang dari objek transfer data besar (jika itu; mungkin Anda hanya ingin menguji kebenaran pemetaan). Anda dapat menggunakannya untuk pengujian dan juga sebagai data acak untuk ditampilkan pada formulir atau untuk mengisi database Anda. Untuk tujuan pengujian, saya menggunakan AutoFixture di .NET.
  • perpustakaan otomatisasi UI . Ini adalah pengguna otomatis untuk pengujian otomatis: Mereka dapat menjalankan aplikasi Anda, mengisi formulir, mengklik tombol, membaca label, dan sebagainya. Untuk menavigasi semua elemen aplikasi Anda, Anda tidak perlu berurusan dengan mengklik dengan koordinat atau pengenalan gambar; platform utama memiliki alat untuk menemukan elemen yang dibutuhkan berdasarkan jenis, pengenal, atau data sehingga Anda tidak perlu mengubah pengujian dengan setiap desain ulang. Mereka kuat, jadi setelah Anda membuatnya bekerja untuk Anda dan CI (terkadang Anda mengetahui bahwa semuanya hanya berfungsi di mesin Anda ), mereka akan terus bekerja. Saya senang menggunakan FlaUI untuk .NET dan Cypress untuk JavaScript dan TypeScript.
  • Perpustakaan pernyataan . Sebagian besar test runner menyertakan alat asersi, tetapi ada kasus di mana alat independen dapat membantu Anda menulis pernyataan kompleks menggunakan sintaks yang lebih bersih dan lebih mudah dibaca, seperti Fluent Assertions untuk .NET. Saya terutama menyukai fungsi untuk menegaskan bahwa koleksi adalah sama terlepas dari urutan item atau alamatnya di memori.

Semoga Arus Bersamamu

Kebahagiaan sangat erat kaitannya dengan apa yang disebut pengalaman "aliran" yang dijelaskan secara rinci dalam buku Flow: The Psychology of Optimal Experience . Untuk mencapai pengalaman aliran itu, Anda harus terlibat dalam aktivitas dengan serangkaian tujuan yang jelas dan dapat melihat kemajuan Anda. Tugas harus menghasilkan umpan balik langsung, yang idealnya adalah pengujian otomatis. Anda juga perlu mencapai keseimbangan antara tantangan dan keterampilan, yang tergantung pada setiap individu. Tes, terutama ketika didekati dengan TDD, dapat membantu memandu Anda dan menanamkan kepercayaan diri. Mereka membantu Anda menetapkan tujuan tertentu, dengan setiap ujian yang lulus menjadi indikator kemajuan Anda.

Pendekatan yang tepat untuk pengujian dapat membuat Anda lebih bahagia dan lebih produktif, dan pengujian mengurangi kemungkinan kelelahan. Kuncinya adalah melihat pengujian sebagai alat (atau perangkat) yang dapat membantu Anda dalam rutinitas pengembangan harian Anda, bukan sebagai langkah yang memberatkan untuk pembuktian kode Anda di masa mendatang.

Pengujian adalah bagian penting dari pemrograman yang memungkinkan insinyur perangkat lunak untuk meningkatkan cara mereka bekerja, memberikan hasil terbaik, dan menggunakan waktu mereka secara optimal. Mungkin yang lebih penting, tes dapat membantu pengembang lebih menikmati pekerjaan mereka, sehingga meningkatkan semangat dan motivasi mereka.