Pengantar pengujian

Diterbitkan: 2021-06-16

Selamat datang di seri blog baru kami tentang segala sesuatu yang berhubungan dengan pengujian . Mudah-mudahan, posting blog ini akan memberi Anda gambaran umum tentang bagaimana kami menulisnya di sini di Mediatoolkit dan mengapa. Sebelum kita masuk ke contoh, mari kita mulai dengan beberapa definisi dasar, ide, dan manfaat dari tes menulis.

Apa jenis tes di luar sana?

Tidak semua tes dibuat sama. Ada berbagai jenis tes untuk tujuan yang berbeda. Meskipun posting ini berfokus terutama pada unit test , kita harus menyadari perbedaan dan memahami manfaat masing-masing.

1. Tes manual

Pengujian manual melibatkan pengujian yang dilakukan secara manual oleh seorang penguji. Ini biasanya dilakukan oleh Quality Assurance atau oleh pengembang sendiri . QA harus dilihat sebagai garis pertahanan terakhir, bukan penguji aplikasi utama (QA melakukan pengujian manual sebagai cara utama untuk pengujian UI adalah pengecualian yang valid untuk kasus ini).

Pengembang biasanya menulis tes manual ketika mereka ingin menjalankan beberapa hal secara lokal dan melihat bagaimana sistem berperilaku tanpa mendorong kode apa pun ke tahap staging, atau produksi. Namun, tujuan dari tes ini bukanlah kekokohan kode. Satu-satunya peran tes ini adalah untuk menangkap bug dan memastikan kualitas produk Anda .

2. Tes integrasi

Sistem terdiri dari beberapa komponen, atau setidaknya seharusnya begitu. Tes integrasi memeriksa API publik dari komponen ini. Bukan hanya REST API, tetapi semua API yang diekspos secara publik, seperti komunikasi topik Kafka Anda.

Jika suatu komponen "mengatakan" itu mengeluarkan pesan tentang topik dalam format tertentu, itu adalah API atau kontrak publiknya. Pengujian integrasi memeriksa apakah beberapa komponen yang dikembangkan secara individual dapat berkomunikasi dan apakah kontrak mereka cocok sebagai sebuah grup.

Jika Anda hanya menguji komponen ini satu per satu, perilaku individualnya mungkin berfungsi dengan benar, tetapi ketika Anda mencoba menghubungkannya dalam kelompok yang lebih besar, Anda menemukan bahwa kontraknya berbeda .

3. Tes ujung ke ujung (E2E)

Pengujian ujung ke ujung memastikan aliran pengalaman pengguna dari produk akhir . Kompleksitas sistem saat ini sulit ditutupi dengan pengujian. Banyak sistem saling mengandalkan dan tes E2E memastikan bahwa produk melakukan apa yang diharapkan . QA memvalidasi kebenaran produk Anda dengan menelusuri alur pengguna akhir dan memeriksa apakah semua sistem berperilaku seperti yang diharapkan.

4. Tes unit

Tes unit adalah tulang punggung dari setiap perangkat lunak yang dapat diandalkan . Mereka membuat dasar untuk tes lain. Mereka menguji unit individu.

Pengembang mungkin salah mengartikan definisi unit dengan metode tunggal. Tes unit harus menguji perilaku komponen yang mungkin terdiri dari beberapa kelas berbeda. Metode publik yang dapat diakses oleh komponen lain perlu diuji, jangan menguji metode atau kelas yang dilindungi. Itu adalah detail implementasi, bukan bagian dari API publik Anda .

Mengapa kita harus menguji?

Jika kita menulis tes yang baik dan sering menulisnya, kita dapat memastikan kualitas produk kita sebelum terlihat terang.

Seiring bertambahnya usia sistem kami, manfaat pengujian menjadi semakin jelas. Lingkungan kami menjadi dapat diandalkan, mereka menghemat waktu pengembangan dan banyak kesulitan ketika ada masalah yang tak terhindarkan. Rekan-rekan Anda akan berterima kasih ketika mereka dapat mengintip pengujian Anda dan memahami apa yang harus dan tidak boleh dilakukan oleh kode Anda tanpa harus menjalankan sesuatu secara manual .

Kode yang kuat

Sebelum kita mulai berbicara tentang "kode yang kuat", bagaimana kita mendefinisikannya? Apa yang membuat kode kuat? Apakah itu berarti kita harus memprogram secara defensif dan memikirkan bagaimana pengembang lain dapat menyalahgunakan kode kita?

Kode yang kuat selalu sederhana dan bersih. Kesederhanaan itu kuat, kerumitan itu rapuh . Kami harus menangani input yang tidak valid, tetapi itu tidak berarti bahwa kami perlu memprogram secara defensif dan tidak mempercayai tim kami.

Hukum Gall : Sebuah sistem kompleks yang bekerja selalu ditemukan telah berevolusi dari sistem sederhana yang bekerja.

Proposisi terbalik juga tampaknya benar: sistem kompleks yang dirancang dari awal tidak pernah berfungsi dan tidak dapat dibuat berfungsi. Anda harus memulai dari awal, dimulai dengan sistem sederhana yang berfungsi.

Refactor dengan aman

Saat kode Anda tercakup oleh pengujian, Anda tidak lagi takut mengubah kode yang ada . Setelah setiap perubahan, Anda dapat menjalankan pengujian dan memastikan Anda tidak merusaknya. Saat Anda menjalani tes, Anda tidak perlu memprogram secara defensif.

Refactoring tanpa tes akan menuruni lereng licin yang akan berakhir di malam tanpa tidur dan bekerja pada hari Minggu. Topik ini terlalu luas untuk dibahas di sini dan layak mendapatkan posting blog tersendiri di masa mendatang.

Bagaimana kita tidak menulis tes?

Sama pentingnya untuk mengetahui bagaimana TIDAK menulis unit test , seperti juga cara menulisnya.

  • Menulis tes yang mencetak hasil pemanggilan metode bukanlah tes karena kami tidak memvalidasi hasil yang diinginkan .
  • Jika pengujian Anda membaca data dari file di folder Dokumen Anda, itu bukan pengujian unit yang sebenarnya karena pengujian tidak boleh bergantung pada lingkungan.
  • Pengembang mana pun harus dapat memeriksa kode Anda dan menjalankan pengujian dengan sukses tanpa melakukan hal lain.
  • Setiap tes unit harus independen dari tes lainnya . Itu menyiratkan bahwa urutan eksekusi pengujian Anda juga tidak menjadi masalah.
  • Menjalankan tes beberapa kali harus selalu berakhir dengan hasil yang sama jika kita tidak mengubah kode apa pun.
  • Tes unit harus menguji perilaku , bukan panggilan metode individual. Tidak setiap kelas dan metode perlu diuji.

Perilaku adalah sesuatu yang menghasilkan nilai nyata yang dibutuhkan pengguna sistem Anda. Apakah pengguna Anda perlu tahu apakah productFactory.create() membuat objek yang sama saat dipanggil dua kali atau jika repositori Anda dipanggil dengan beberapa parameter? Mungkin tidak, tetapi masih banyak pengembang yang menulis tes semacam ini.

Jika pengujian Anda terlihat seperti itu, mereka digabungkan erat dengan implementasi Anda. Setiap kali Anda ingin mengubah detail implementasi, Anda perlu memperbarui pengujian, meskipun perilakunya sama. Pengujian Anda harus berubah hanya ketika perilaku berubah, bukan detail implementasi . Dengan kata lain, uji apa yang dilakukan kode Anda, bukan bagaimana melakukannya.

Bagaimana kita menulis tes?

Pengujian kami harus mengikuti praktik kode terbaik , pengujian tersebut harus independen terhadap lingkungan dan harus dijalankan dengan cepat .

Penting untuk menjaga waktu eksekusi tes sesingkat mungkin. Setiap tes tidak boleh memakan waktu lebih dari beberapa milidetik . Ketika pengujian memakan waktu terlalu lama untuk dijalankan, orang cenderung melewatkannya dan hanya mengandalkan server CI mereka, seperti Jenkins untuk membuat keributan ketika tidak dapat membangun executable penerapan mereka.

Setiap tes terdiri dari 3 bagian 'A' (Pola AAA) :

  1. Mengatur
  2. bertindak
  3. Menegaskan

1. Atur

Di bagian pengaturan pengujian kami, kami memastikan sistem kami dalam keadaan tertentu sebelum memanggil perilaku yang ingin kami uji . 'Sistem' bisa menjadi objek yang perlu kita atur dengan cara tertentu untuk menghasilkan perilaku, membuat file sementara atau hal-hal seperti itu.

Kode di bagian ini biasanya lebih besar dari gabungan dua lainnya .

Salah satu pola desain yang terbukti sangat berguna untuk menjaga bagian ini tetap kecil adalah Object Mother . Pola desain ini sangat mirip dengan Factory , tetapi memiliki metode yang lebih spesifik yang membangun objek yang telah dikonfigurasi sebelumnya untuk Anda. Sementara Factory standar dapat memiliki metode seperti createCar(carDescription) , ObjectMother akan memiliki metode seperti createRedFerrari() , createBlackTesla() atau createBrokenYugo() .

2. Bertindak

Bagian pengujian Anda ini harus memiliki satu baris . Baris ini menjalankan perilaku yang sedang diuji. Jika Anda mendapati diri Anda menulis lebih dari satu baris untuk bagian ini, Anda mungkin tidak memiliki enkapsulasi yang tepat untuk perilaku Anda . Klien Anda seharusnya tidak diharapkan untuk memanggil beberapa metode objek Anda dalam urutan tertentu, jadi mengapa pengujian Anda?

Baris ini adalah pemanggilan metode yang ingin kita uji. Jika metode ini mengembalikan hasil, Anda harus menyimpan nilai tersebut dalam variabel untuk memeriksa apakah itu adalah nilai yang diharapkan dalam langkah Tegaskan.

3. Tegaskan

Setelah kami menyiapkan sistem di bagian Atur dan menjalankan tindakan kami yang diuji di bagian Tindakan, kami perlu memvalidasi hasil tindakan. Kami biasanya memeriksa hasil metode di sini, tetapi terkadang, metode kami tidak mengembalikan nilai, tetapi masih menghasilkan efek samping. Jika kode kita diharapkan untuk mengubah status suatu objek, membuat file, atau menghapus sesuatu dari List , kita harus memeriksa apakah kode tersebut benar-benar melakukannya.

Rintisan vs Mock

Sebagian besar pengembang menggunakan istilah tiruan dan rintisan secara bergantian, tetapi ada perbedaan.

Sebuah rintisan tidak bisa gagal dalam ujian, tiruan bisa.

Rintisan berbasis negara bagian , mereka mengembalikan nilai hard-coded. “Hasil apa yang saya dapatkan?”

Ejekan berbasis perilaku , Anda menggunakannya untuk memverifikasi bagaimana perilaku Anda melewatinya. “Bagaimana saya mendapatkan hasilnya?”

Jika pengujian unit Anda dipenuhi dengan tiruan, pengujian Anda menjadi sangat rapuh, artinya setiap kali Anda mengubah salah satu detail implementasi, Anda perlu memperbarui semua panggilan tiruan Anda.

Uji hanya satu hal.

Kita harus mampu mengisolasi satu perilaku dan membuktikan bahwa itu berhasil . Jika perilaku itu harus bekerja secara berbeda dengan input yang berbeda, kita perlu menulis tes baru untuk masing-masing perilaku tersebut. Sulit untuk mengetahui mengapa pengujian kami gagal jika kami memiliki pengujian besar yang menguji banyak hal sekaligus. Selain itu, semakin sulit untuk menghapus fitur yang tidak kita perlukan lagi dan melihat fitur mana yang rusak saat kita menambahkan kode baru.

Tidak apa-apa untuk memiliki beberapa pernyataan di bagian akhir pengujian Anda selama itu tidak mengharuskan Anda memanggil perilaku yang sedang diuji beberapa kali. Jika itu masalahnya, akan sulit untuk menunjukkan dengan tepat perilaku yang salah dan memperbaikinya.

Ketika kami menyatakan beberapa perilaku dalam satu pengujian, kami tidak mendapatkan gambaran yang jelas tentang apa yang sebenarnya tidak berfungsi karena pengujian hanya akan melaporkan kegagalan pertama dan sisanya dilewati. Menjadi jauh lebih sulit untuk memahami perubahan apa yang diperlukan dan berapa banyak hal yang tidak berjalan seperti yang diharapkan.

Tes penamaan

Satu hal yang membedakan tes bagus dari tes hebat adalah nama tesnya. Tes seharusnya tidak hanya memberi tahu kita apa yang mereka lakukan tetapi kapan mereka melakukannya.

Ada banyak pola penamaan bagus yang dapat Anda gunakan, jadi pilihlah yang menurut Anda paling deskriptif dan patuhi itu. Berikut adalah beberapa contoh nama tes yang bagus:

  • Layanan PendaftaranSeharusnya.membuatAkunBaruSaatEmailTidakDiambil
  • RegistrationServiceTest.whenEmailIsFree_createNewAccount
  • RegistrationServiceTest.if_freeEmail_when_userCreatesAccount_then_create

Saat Anda menulis pengujian unit, jauh lebih penting untuk mengomunikasikan apa yang Anda uji daripada mengikuti praktik penamaan metode terbaik. Misalnya, di Java, kami menggunakan camelCase saat menulis metode, tetapi sangat valid untuk menggunakan garis bawah (_) untuk memisahkan status dari tindakan dalam nama pengujian Anda.

Tes bersih

Tes yang Anda tulis harus mengikuti semua praktik kode bersih yang Anda terapkan pada kode Anda. Tes bukanlah warga negara kelas dua dan Anda perlu menerapkan tingkat kehati-hatian yang sama seperti yang Anda lakukan dengan sisa kode Anda agar dapat dibaca.

Definisi duplikasi kode dalam tes sangat penting. Prinsip KERING (Jangan Ulangi Diri Sendiri) berlaku untuk mengekstraksi perilaku yang berubah karena alasan yang sama. Tes berubah karena alasan yang berbeda, jadi bervariasi dalam mengekstraksi sesuatu dari tes Anda jika mereka benar-benar tidak berubah karena alasan yang sama. Spoiler waspada, mereka sering tidak.

if pernyataan tidak termasuk dalam tes. Pernyataan if memberitahu kita bahwa pengujian kita melakukan setidaknya dua hal yang berbeda dan kita akan lebih baik jika kita menulis ulang pengujian kita sebagai dua pengujian yang berbeda. Jika diberi nama dengan benar, akan lebih mudah untuk memahami apa yang dilakukan tes, dan apa saja perilaku yang berbeda.

Kapan kita harus menulis tes?

Dipandu oleh prinsip-prinsip TDD, kita harus menulis tes sebelum menulis kode baru .

Saat kami perlu menambahkan fitur baru, pertama-tama kami mendeskripsikan perilaku yang diinginkan sebagai pengujian baru. Kami membuat perubahan paling sedikit yang diperlukan untuk lulus tes itu tanpa merusak yang lain.

Jika tidak, semakin banyak waktu berlalu, semakin banyak kode yang kita miliki yang belum diuji dan kemungkinan munculnya bug atau overengineering meningkat .

Selain itu, pengujian menjadi lebih kompleks karena kami memiliki lebih banyak kode untuk diuji sekarang, dan yang biasanya terjadi adalah pengembang menyesuaikan pengujian dengan kode. Itu berarti kita menyesuaikan perilaku agar sesuai dengan apa yang dilakukan kode kita, bukan sebaliknya.

Ketika kita menulis tes lebih awal, definisi masalah menjadi lebih kecil dan lebih mudah untuk membungkus kepala kita di sekitar masalah seperti itu daripada perilaku yang lebih umum dan kompleks.

Pikiran terakhir

Sementara tes menulis mungkin tampak seperti hal opsional untuk dilakukan, sangat penting untuk memulai dengan dasar yang baik. Pengkodean sudah sulit, permudah diri Anda dan rekan tim Anda dengan menulis kode yang dapat diuji yang lebih mudah dibaca, dipahami, dan dipelihara . Terakhir, jika Anda mengalami kesulitan dalam membuat pengujian Anda sesuai dengan keinginan Anda, masalahnya lebih mungkin terjadi pada kode Anda daripada dalam pengujian Anda.

Tertarik untuk mengerjakan kode yang bersih dan dapat diuji?
Lihat posisi terbuka kami untuk Pengembang Backend Senior
atau kirimkan aplikasi terbuka kepada kami!