Unit menguji perilaku tanpa menyambung ke detail implementasi

16

Dalam ceramahnya TDD, di mana semuanya salah , Ian Cooper mendorong niat asli Kent Beck di balik pengujian unit dalam TDD (untuk menguji perilaku, bukan metode kelas khusus) dan berpendapat untuk menghindari menggabungkan tes untuk implementasi.

Dalam hal perilaku seperti save X to some data sourcedalam sistem dengan seperangkat layanan dan repositori yang khas, bagaimana kita dapat menguji unit penyimpanan beberapa data pada tingkat layanan, melalui repositori, tanpa menyambungkan tes ke detail implementasi (seperti memanggil metode tertentu )? Apakah menghindari sambungan semacam ini sebenarnya tidak sepadan dengan usaha / buruk dalam beberapa cara?

Andy Hunt
sumber
1
Jika Anda ingin menguji bahwa data disimpan dalam repositori, maka tes harus benar-benar pergi dan memeriksa repositori untuk melihat apakah data ada di sana, bukan? Atau apakah saya melewatkan sesuatu?
Pertanyaan saya lebih lanjut tentang menghindari menggabungkan tes ke detail implementasi seperti memanggil metode tertentu di repositori, atau benar-benar jika itu adalah sesuatu yang harus dilakukan.
Andy Hunt

Jawaban:

8

Contoh spesifik Anda adalah kasus yang biasanya harus Anda uji dengan memeriksa apakah metode tertentu dipanggil, karena saving X to data sourceberarti berkomunikasi dengan ketergantungan eksternal , sehingga perilaku yang harus Anda uji adalah bahwa komunikasi tersebut terjadi seperti yang diharapkan .

Namun, ini bukan hal yang buruk. Antarmuka batas antara aplikasi Anda dan dependensi eksternalnya bukan detail implementasi , pada kenyataannya, mereka didefinisikan dalam arsitektur sistem Anda; yang berarti bahwa batas seperti itu tidak akan berubah (atau jika harus, itu akan menjadi jenis perubahan yang paling jarang). Dengan demikian, menggabungkan tes Anda ke repositoryantarmuka seharusnya tidak menyebabkan Anda terlalu banyak kesulitan (jika ya, pertimbangkan apakah antarmuka tersebut tidak mencuri tanggung jawab dari aplikasi).

Sekarang, pertimbangkan hanya aturan bisnis aplikasi, dipisahkan dari UI, database, dan layanan eksternal lainnya. Ini adalah Anda harus bebas untuk mengubah struktur dan perilaku kode. Di sinilah pengujian kopling dan detail implementasi akan memaksa Anda untuk mengubah lebih banyak kode uji daripada kode produksi, bahkan ketika tidak ada perubahan dalam perilaku keseluruhan aplikasi. Di sinilah pengujian Statebukannya Interactionmembantu kami lebih cepat.

PS: Bukan maksud saya untuk mengatakan apakah pengujian oleh Negara atau Interaksi adalah satu-satunya cara yang sebenarnya dari TDD - saya percaya itu masalah menggunakan alat yang tepat untuk pekerjaan yang tepat.

MichelHenrich
sumber
Ketika Anda menyebutkan "berkomunikasi dengan ketergantungan eksternal", apakah Anda mengacu pada ketergantungan eksternal sebagai yang eksternal terhadap unit yang sedang diuji, atau yang eksternal ke sistem secara keseluruhan?
Andy Hunt
Yang saya maksud dengan "ketergantungan eksternal" adalah apa pun yang dapat dianggap sebagai plug-in ke aplikasi Anda. Dengan aplikasi, maksud saya aturan bisnis, agnostik dari segala jenis detail seperti kerangka mana yang digunakan untuk kegigihan atau UI. Saya pikir Paman Bob dapat menjelaskannya dengan lebih baik, seperti dalam pembicaraan ini: youtube.com/watch?v=WpkDN78P884
MichelHenrich
Saya pikir ini adalah pendekatan yang ideal, seperti ceramah, untuk menguji berdasarkan "fitur" atau "perilaku", dan satu tes per fitur atau perilaku (atau permutasi satu, yaitu parameter yang bervariasi). Namun, jika saya memiliki 1 tes "bahagia" untuk fitur, untuk melakukan TDD, itu berarti saya akan memiliki komit raksasa tunggal (dan tinjauan kode) untuk fitur itu, yang merupakan ide yang buruk. Bagaimana ini bisa dihindari? Tulis sebagian dari fitur itu sebagai tes dan semua kode yang terkait dengannya, lalu secara bertahap tambahkan sisa fitur dalam komit berikutnya?
jordan
Saya benar-benar ingin melihat contoh dunia nyata dari tes yang digabungkan dengan implementasi.
PositiveGuy
7

Penafsiran saya tentang pembicaraan itu adalah:

  • komponen uji, bukan kelas.
  • menguji komponen melalui port antarmuka mereka.

Itu tidak disebutkan dalam pembicaraan, tetapi saya pikir konteks yang diasumsikan untuk saran adalah sesuatu seperti:

  • Anda sedang mengembangkan sistem untuk pengguna, bukan, katakanlah, perpustakaan atau kerangka kerja utilitas.
  • tujuan pengujian adalah untuk berhasil memberikan sebanyak mungkin dalam anggaran kompetitif.
  • komponen ditulis dalam bahasa tunggal, dewasa, mungkin diketik secara statis, seperti C # / Java.
  • komponen adalah urutan 10.000-50000 garis; proyek Maven atau VS, plugin OSGI, dll.
  • komponen ditulis oleh pengembang tunggal, atau tim yang terintegrasi erat.
  • Anda mengikuti terminologi dan pendekatan sesuatu seperti arsitektur heksagonal
  • port komponen adalah tempat Anda meninggalkan bahasa lokal, dan sistem tipenya, di belakang, beralih ke http / SQL / XML / byte / ...
  • membungkus setiap port adalah antarmuka yang diketik, dalam arti Java / C #, yang dapat mengimplementasikan implementasinya untuk beralih teknologi.

Jadi pengujian komponen adalah ruang lingkup terbesar yang mungkin di mana sesuatu masih bisa disebut unit testing. Ini agak berbeda dengan bagaimana beberapa orang, terutama akademisi, menggunakan istilah ini. Ini tidak seperti contoh dalam tutorial alat uji unit tipikal. Namun, itu cocok dengan asalnya dalam pengujian perangkat keras; papan dan modul adalah unit yang diuji, bukan kabel dan sekrup. Atau setidaknya Anda tidak membuat Boeing tiruan untuk menguji sekrup ...

Mengekstrapolasi dari itu, dan memasukkan beberapa pemikiran saya sendiri,

  • Setiap antarmuka akan berupa input, output, atau kolaborator (seperti database).
  • Anda menguji antarmuka input; panggil metode, nyatakan nilai kembali.
  • Anda mengejek antarmuka output; memverifikasi metode yang diharapkan dipanggil untuk test case yang diberikan.
  • Anda memalsukan kolaborator; memberikan implementasi yang sederhana namun berfungsi

Jika Anda melakukannya dengan benar dan bersih, Anda hampir tidak memerlukan alat mengejek; itu hanya digunakan beberapa kali per sistem.

Database umumnya kolaborator, sehingga dipalsukan daripada diejek. Ini akan menyakitkan untuk diterapkan dengan tangan; Untungnya hal-hal seperti itu sudah ada .

Pola pengujian dasar adalah melakukan beberapa urutan operasi (mis. Menyimpan dan memuat kembali dokumen); konfirmasi itu berfungsi. Ini sama dengan skenario pengujian lainnya; tidak ada perubahan implementasi (yang berfungsi) yang kemungkinan menyebabkan tes seperti itu gagal.

Pengecualian adalah di mana catatan database ditulis tetapi tidak pernah dibaca oleh sistem yang diuji; mis. log audit atau sejenisnya. Ini adalah output, dan karenanya harus diejek. Pola pengujian adalah melakukan beberapa urutan operasi; konfirmasi antarmuka audit dipanggil dengan metode dan argumen sebagaimana ditentukan.

Perhatikan bahwa bahkan di sini, asalkan Anda menggunakan alat mengejek tipe-aman seperti mockito , mengganti nama metode antarmuka tidak dapat menyebabkan kegagalan pengujian. Jika Anda menggunakan IDE dengan tes yang dimuat, itu akan menjadi refactored bersama dengan metode rename. Jika tidak, tes tidak akan dikompilasi.

soru
sumber
Bisakah Anda menjelaskan / memberi saya contoh konkret dari port antarmuka?
PositiveGuy
apa contoh dari antarmuka output. Bisakah Anda spesifik dalam kode? Sama dengan antarmuka input.
PositiveGuy
Antarmuka (dalam arti Java / C #) membungkus sebuah port, yang bisa berupa apa saja yang berbicara dengan dunia luar (d / b, socket, http, ....). Antarmuka keluaran adalah yang tidak memiliki metode dengan nilai pengembalian yang datang dari dunia luar melalui port, hanya pengecualian atau yang setara.
soru
Antarmuka input adalah sebaliknya, kolaborator adalah input dan output.
soru
1
Saya pikir Anda berbicara tentang pendekatan desain dan terminologi yang sama sekali berbeda dengan yang dijelaskan dalam video. Tetapi 90% dari waktu repositori (yaitu database) adalah kolaborator, bukan input atau output. Jadi antarmuka untuk itu adalah antarmuka kolaborasi.
soru
0

Saran saya adalah menggunakan pendekatan pengujian berbasis negara:

DIBERIKAN Kami memiliki tes DB dalam kondisi yang diketahui

KAPAN Layanan disebut dengan argumen X

KEMUDIAN Menegaskan bahwa DB telah berubah dari keadaan semula ke keadaan yang diharapkan dengan memanggil metode repositori read-only dan memeriksa nilai yang dikembalikan

Dengan melakukan itu, Anda tidak bergantung pada algoritme internal apa pun dari layanan ini, dan bebas untuk memperbaiki implementasinya tanpa harus mengubah tes.

Satu-satunya sambungan di sini adalah panggilan metode layanan dan panggilan repositori yang diperlukan untuk membaca data dari DB, yang baik-baik saja.

Elifarley
sumber