Bagaimana Anda mendeteksi masalah ketergantungan dengan tes unit saat Anda menggunakan benda tiruan?

98

Anda memiliki kelas X dan Anda menulis beberapa unit test yang memverifikasi perilaku X1. Ada juga kelas A yang menganggap X sebagai ketergantungan.

Ketika Anda menulis tes unit untuk A, Anda mengejek X. Dengan kata lain, saat pengujian unit A, Anda mengatur (mendalilkan) perilaku dari tiruan X menjadi X1. Waktu berlalu, orang menggunakan sistem Anda, perlu berubah, X berevolusi: Anda memodifikasi X untuk menunjukkan perilaku X2. Jelas, tes unit untuk X akan gagal dan Anda harus menyesuaikannya.

Tapi bagaimana dengan A? Tes unit untuk A tidak akan gagal ketika perilaku X diubah (karena mengejek X). Bagaimana mendeteksi bahwa hasil A akan berbeda ketika dijalankan dengan X "asli" (dimodifikasi)?

Saya mengharapkan jawaban di sepanjang baris: "Itu bukan tujuan pengujian unit", tetapi nilai apa yang dimiliki unit testing? Apakah itu benar-benar hanya memberitahu Anda bahwa ketika semua tes lulus, Anda belum memperkenalkan perubahan yang melanggar? Dan ketika beberapa perilaku kelas berubah (secara sukarela atau tidak), bagaimana Anda dapat mendeteksi (lebih disukai dengan cara otomatis) semua konsekuensinya? Bukankah seharusnya kita lebih fokus pada pengujian integrasi?

bvgheluwe
sumber
36
Selain semua jawaban yang disarankan, saya harus mengatakan saya menerima masalah dengan pernyataan berikut: "Apakah itu benar-benar hanya memberi tahu Anda bahwa ketika semua tes lulus, Anda belum memperkenalkan perubahan yang melanggar?" Jika Anda benar-benar berpikir menghilangkan rasa takut akan refactoring tidak ada artinya, Anda berada di jalur cepat menuju penulisan kode yang tidak dapat
dipertahankan
5
Pengujian unit memberi tahu Anda apakah unit kode Anda berperilaku seperti yang Anda harapkan. Tidak lebih atau kurang. Mengejek dan menguji ganda memberikan lingkungan buatan, terkendali bagi Anda untuk menggunakan unit kode Anda (dalam isolasi) untuk melihat apakah itu memenuhi harapan Anda. Tidak lebih atau kurang.
Robert Harvey
2
Saya yakin premis Anda salah. Ketika Anda menyebutkan X1Anda mengatakan bahwa Xmengimplementasikan antarmuka X1. Jika Anda mengubah antarmuka X1ke X2tiruan yang Anda gunakan dalam tes lain tidak boleh dikompilasi lagi, maka Anda terpaksa memperbaiki tes itu juga. Perubahan perilaku kelas seharusnya tidak menjadi masalah. Bahkan, kelas Anda Aseharusnya tidak bergantung pada detail implementasi (yang akan Anda ubah dalam kasus itu). Jadi tes unit Amasih benar, dan mereka memberi tahu Anda bahwa Aberfungsi memberikan implementasi antarmuka yang ideal.
Bakuriu
5
Saya tidak tahu tentang Anda, tetapi ketika saya harus bekerja pada basis kode tanpa tes, saya takut mati saya akan memecahkan sesuatu. Dan mengapa? Karena itu terjadi begitu sering sehingga sesuatu pecah ketika itu tidak dimaksudkan. Dan memberkati hati penguji kita, mereka tidak bisa menguji segalanya. Atau bahkan dekat. Tetapi unit test akan dengan senang hati menjalani rutinitas yang membosankan setelah rutinitas yang membosankan.
corsiKa

Jawaban:

125

Ketika Anda menulis unit test untuk A, Anda mengejek X

Apakah kamu? Saya tidak, kecuali saya benar-benar harus. Saya harus jika:

  1. X lambat, atau
  2. X memiliki efek samping

Jika tak satu pun dari ini berlaku, maka unit test saya Aakan menguji Xjuga. Melakukan hal lain akan mengambil tes isolasi ke ekstrim yang tidak masuk akal.

Jika Anda memiliki bagian dari kode Anda menggunakan ejekan bagian lain dari kode Anda, maka saya setuju: apa gunanya tes unit tersebut? Jadi jangan lakukan ini. Biarkan tes-tes itu menggunakan dependensi nyata karena mereka membentuk tes yang jauh lebih berharga seperti itu.

Dan jika beberapa orang marah dengan Anda menyebut tes ini, "tes unit", maka sebut saja "tes otomatis" dan mulai menulis tes otomatis yang bagus.

David Arno
sumber
94
@ Longv, Tidak, tes unit seharusnya bertindak sebagai unit, yaitu dijalankan secara terpisah, dari tes lain . Node dan grafik dapat mengambil kenaikan. Jika saya dapat menjalankan tes ujung ke ujung yang terisolasi dan efek samping gratis dalam waktu singkat, itu adalah tes unit. Jika Anda tidak menyukai definisi itu, sebut itu tes otomatis dan berhenti menulis tes omong kosong agar sesuai dengan semantik konyol.
David Arno
9
@ Davidvidno Sayangnya ada definisi yang sangat luas dari terisolasi. Beberapa menginginkan "unit" untuk memasukkan tingkat menengah dan basis data. Mereka dapat mempercayai apa pun yang mereka suka tetapi kemungkinan pada perkembangan ukuran berapa pun, roda akan terlepas dalam waktu yang cukup singkat karena manajer pembangunan akan membuangnya. Secara umum, jika mereka terisolasi ke perakitan (atau setara), itu bagus. NB jika Anda kode ke antarmuka maka jauh lebih mudah untuk menambahkan ejekan dan DI nanti.
Robbie Dee
13
Anda menganjurkan untuk jenis tes yang berbeda daripada menjawab pertanyaan. Yang merupakan poin yang valid, tetapi ini adalah cara yang cukup curang untuk melakukannya.
Phil Frost
17
@ PhilFrost, untuk mengutip diri saya sendiri, " Dan jika beberapa orang marah dengan Anda memanggil tes ini," unit test ", maka sebut saja mereka" tes otomatis "dan lanjutkan dengan menulis tes otomatis yang bagus. " Tulis tes yang bermanfaat, bukan tes konyol yang hanya memenuhi beberapa definisi kata secara acak. Atau sebagai alternatif, terimalah bahwa Anda mungkin salah mengartikan "unit test" dan bahwa Anda lebih banyak menggunakan ejekan karena Anda salah. Bagaimanapun, Anda akan berakhir dengan tes yang lebih baik.
David Arno
30
Saya dengan @DavidArno untuk yang ini; strategi pengujian saya berubah setelah menonton ceramah ini oleh Ian Cooper: vimeo.com/68375232 . Untuk membuatnya menjadi satu kalimat: Jangan menguji kelas . Uji perilaku . Tes Anda seharusnya tidak memiliki pengetahuan tentang kelas / metode internal yang digunakan untuk menerapkan perilaku yang diinginkan; mereka hanya harus mengetahui permukaan publik API / perpustakaan Anda dan mereka harus mengujinya. Jika tes memiliki terlalu banyak pengetahuan maka Anda menguji detail implementasi dan tes Anda menjadi rapuh, ditambah dengan implementasi Anda, dan sebenarnya hanya jangkar di leher Anda.
Richiban
79

Anda membutuhkan keduanya. Tes unit untuk memverifikasi perilaku masing-masing unit Anda, dan beberapa tes integrasi untuk memastikan mereka terhubung dengan benar. Masalah dengan hanya mengandalkan pengujian integrasi adalah ledakan kombinatorial yang dihasilkan dari interaksi antara semua unit Anda.

Katakanlah Anda memiliki kelas A, yang membutuhkan 10 tes unit untuk sepenuhnya mencakup semua jalur. Kemudian Anda memiliki kelas B lainnya, yang juga membutuhkan 10 unit tes untuk mencakup semua jalur yang dapat dilalui kode. Sekarang katakanlah dalam aplikasi Anda, Anda perlu memberi makan output A ke B. Sekarang kode Anda dapat mengambil 100 jalur berbeda dari input A ke output B.

Dengan tes unit, Anda hanya perlu 20 tes unit + 1 tes integrasi untuk sepenuhnya mencakup semua kasus.

Dengan tes integrasi, Anda akan membutuhkan 100 tes untuk mencakup semua jalur kode.

Berikut adalah video yang sangat bagus tentang kelemahan mengandalkan tes integrasi hanya Uji Terpadu JB Rainsberger Are A Scam HD

Eternal21
sumber
1
Saya yakin bukan kebetulan bahwa tanda tanya tentang kemanjuran tes integrasi telah berjalan seiring dengan tes unit yang mencakup setiap lapisan lagi.
Robbie Dee
16
Benar, tetapi tes 20 unit Anda tidak perlu diejek. Jika Anda memiliki 10 tes A yang mencakup semua A dan 10 tes Anda yang mencakup semua B dan juga tes ulang 25% dari A sebagai bonus ini tampaknya antara "baik" dan hal yang baik. Mengejek A dalam tes B tampaknya aktif bodoh (kecuali benar-benar ada alasan mengapa A adalah masalah, misalnya itu database atau membawa dalam web panjang hal-hal lain)
Richard Tingle
9
Saya tidak setuju dengan gagasan bahwa uji integrasi tunggal sudah cukup jika Anda ingin cakupan penuh. Reaksi B terhadap keluaran A akan bervariasi berdasarkan keluaran; jika mengubah parameter dalam A mengubah outputnya, maka B mungkin tidak menanganinya dengan benar.
Matthieu M.
3
@ Eternal21: Maksud saya adalah bahwa kadang-kadang, masalahnya bukan pada perilaku individu, tetapi dalam interaksi yang tidak terduga. Yaitu, ketika lem antara A dan B berperilaku tak terduga dalam beberapa skenario. Jadi A dan B bekerja sesuai dengan spesifikasi, dan case yang berhasil bekerja, tetapi pada beberapa input ada bug dalam kode lem ...
Matthieu M.
1
@ MatthieuM. Saya berpendapat ini berada di luar cakupan Unit Testing. Kode lem dapat diuji unit sendiri sementara interaksi antara A dan B melalui kode lem adalah Tes Integrasi. Ketika ditemukan kasus tepi atau bug yang spesifik, mereka dapat ditambahkan ke tes unit kode lem dan akhirnya diverifikasi dalam pengujian Integrasi.
Andrew T Finnell
72

Ketika Anda menulis tes unit untuk A, Anda mengejek X. Dengan kata lain, saat pengujian unit A, Anda mengatur (mendalilkan) perilaku dari tiruan X menjadi X1. Waktu berlalu, orang menggunakan sistem Anda, perlu berubah, X berevolusi: Anda memodifikasi X untuk menunjukkan perilaku X2. Jelas, tes unit untuk X akan gagal dan Anda harus menyesuaikannya.

Woah, tunggu sebentar. Implikasi dari tes untuk X gagal terlalu penting untuk ditutup-tutupi seperti itu.

Jika mengubah implementasi X dari X1 ke X2 mematahkan tes unit untuk X, itu menunjukkan bahwa Anda telah membuat perubahan mundur yang tidak kompatibel ke kontrak X.

X2 bukan X, dalam arti Liskov , jadi Anda harus memikirkan cara lain untuk memenuhi kebutuhan pemegang saham Anda (seperti memperkenalkan spesifikasi Y yang baru, yang diterapkan oleh X2).

Untuk wawasan lebih dalam, lihat Pieter Hinjens: Akhir Versi Perangkat Lunak , atau Rich Hickey Simple Made Easy .

Dari perspektif A, ada prasyarat bahwa kolaborator menghormati kontrak X. Dan pengamatan Anda secara efektif bahwa tes terisolasi untuk A tidak memberi Anda jaminan bahwa A mengakui kolaborator yang melanggar kontrak X.

Ulasan Tes Terpadu adalah Penipuan ; pada level tinggi, Anda diharapkan memiliki tes terisolasi sebanyak yang Anda perlukan untuk memastikan bahwa X2 mengimplementasikan kontrak X dengan benar, dan sebanyak mungkin tes terisolasi yang Anda perlukan untuk memastikan bahwa A melakukan hal yang benar dengan memberikan tanggapan yang menarik dari X, dan sejumlah kecil tes terintegrasi untuk memastikan bahwa X2 dan A menyepakati apa yang X maksudkan.

Terkadang Anda akan melihat perbedaan ini dinyatakan sebagai tes soliter vs sociabletes; lihat Jay Fields Bekerja Efektif dengan Unit Tes .

Bukankah seharusnya kita lebih fokus pada pengujian integrasi?

Sekali lagi, lihat tes terintegrasi adalah penipuan - Rainsberger menjelaskan secara terperinci lingkaran umpan balik positif yang umum (dalam pengalamannya) untuk proyek yang mengandalkan tes terintegrasi (note spelling). Singkatnya, tanpa tes terisolasi / soliter memberikan tekanan pada desain , kualitasnya menurun, menyebabkan lebih banyak kesalahan dan tes yang lebih terintegrasi ....

Anda juga akan memerlukan (beberapa) tes integrasi. Selain kompleksitas yang diperkenalkan oleh banyak modul, mengeksekusi tes ini cenderung memiliki hambatan lebih dari tes terisolasi; lebih efisien untuk melakukan iterasi pada cek sangat cepat ketika pekerjaan sedang berlangsung, menyimpan cek tambahan ketika Anda berpikir Anda "selesai".

VoiceOfUnasonason
sumber
8
Ini harus menjadi jawaban yang diterima. Pertanyaannya menguraikan situasi di mana perilaku kelas telah dimodifikasi dengan cara yang tidak kompatibel, tetapi secara lahiriah terlihat identik. Masalahnya di sini terletak pada desain aplikasi, bukan tes unit. Cara keadaan ini akan tertangkap dalam pengujian menggunakan uji integrasi antara dua kelas.
Nick Coad
1
"Tanpa tes terisolasi / soliter yang memberikan tekanan pada desain, kualitasnya menurun". Saya pikir ini adalah poin penting. Bersamaan dengan pemeriksaan perilaku, unit-test memiliki efek samping untuk memaksa Anda memiliki desain yang lebih modular.
MickaëlG
Saya kira ini semua benar, tetapi bagaimana ini membantu saya jika ketergantungan eksternal memperkenalkan perubahan yang tidak kompatibel ke belakang pada kontrak X? Mungkin kelas yang berkinerja I / O di perpustakaan merusak kompatibilitas, dan kami mengejek X karena kami tidak ingin unit test di CI bergantung pada I / O yang berat. Saya pikir OP meminta untuk menguji ini, dan saya tidak mengerti bagaimana ini menjawab pertanyaan. Bagaimana cara menguji ini?
gerrit
15

Mari saya mulai dengan mengatakan bahwa premis inti dari pertanyaan itu cacat.

Anda tidak pernah menguji (atau mengejek) implementasi, Anda menguji (dan mengejek) antarmuka .

Jika saya memiliki kelas X nyata yang mengimplementasikan antarmuka X1, saya dapat menulis tiruan XM yang juga sesuai dengan X1. Maka kelas A saya harus menggunakan sesuatu yang mengimplementasikan X1, yang bisa berupa kelas X atau mock XM.

Sekarang, misalkan kita mengubah X untuk mengimplementasikan antarmuka baru X2. Yah, jelas kode saya tidak lagi dikompilasi. A membutuhkan sesuatu yang mengimplementasikan X1, dan yang sudah tidak ada lagi. Masalah telah diidentifikasi dan dapat diperbaiki.

Misalkan alih-alih mengganti X1, kami hanya memodifikasinya. Sekarang kelas A sudah siap. Namun, mock XM tidak lagi mengimplementasikan antarmuka X1. Masalah telah diidentifikasi dan dapat diperbaiki.


Seluruh dasar untuk pengujian dan mengejek unit adalah Anda menulis kode yang menggunakan antarmuka. Pengguna antarmuka tidak peduli bagaimana kode diterapkan, hanya saja kontrak yang sama dipatuhi (input / output).

Ini rusak ketika metode Anda memiliki efek samping, tapi saya pikir itu dapat dengan aman dikecualikan sebagai "tidak dapat diuji atau diejek unit".

Vlad274
sumber
11
Jawaban ini membuat banyak asumsi yang tidak perlu berlaku. Pertama, secara kasar mengasumsikan bahwa kita berada di C # atau Java (atau, lebih tepatnya, bahwa kita menggunakan bahasa yang dikompilasi, bahwa bahasa tersebut memiliki antarmuka, dan bahwa X mengimplementasikan antarmuka; tidak ada yang perlu benar). Kedua, mengasumsikan bahwa setiap perubahan pada perilaku atau "kontrak" X memerlukan perubahan pada antarmuka (sebagaimana dipahami oleh kompiler) yang diimplementasikan oleh X. Ini jelas tidak benar, bahkan jika kita berada di Jawa atau C #; Anda dapat mengubah implementasi metode tanpa mengubah tanda tangannya.
Mark Amery
6
@MarkAmery Memang benar bahwa terminologi "antarmuka" lebih spesifik untuk C # atau Java, tapi saya pikir intinya adalah asumsi "kontrak" perilaku (dan jika itu tidak dikodifikasi maka secara otomatis mendeteksi ini tidak mungkin). Anda juga sepenuhnya benar bahwa suatu implementasi dapat diubah tanpa perubahan pada kontrak. Tetapi perubahan implementasi tanpa perubahan antarmuka (atau kontrak) tidak akan berdampak pada konsumen mana pun. Jika perilaku A tergantung pada bagaimana antarmuka (atau kontrak) diimplementasikan, maka tidak mungkin untuk (bermakna) pengujian unit.
Vlad274
1
"Anda juga sepenuhnya benar bahwa suatu implementasi dapat diubah tanpa mengubah kontrak" - sementara juga benar, ini bukan poin yang saya coba buat. Sebaliknya, saya menegaskan perbedaan antara kontrak (pemahaman programmer tentang apa yang harus dilakukan objek, mungkin ditentukan dalam dokumentasi) dan antarmuka (daftar tanda tangan metode, dipahami oleh kompiler) dan mengatakan bahwa kontrak dapat diubah tanpa mengubah antarmuka. Semua fungsi dengan tanda tangan yang sama dapat dipertukarkan dari perspektif sistem tipe, tetapi tidak pada kenyataannya!
Mark Amery
4
@MarkAmery: Saya tidak berpikir Vlad menggunakan kata "antarmuka" dalam arti yang sama seperti Anda menggunakannya; cara saya membaca jawabannya, itu tidak berbicara tentang antarmuka dalam arti sempit C # / Java (yaitu seperangkat tanda tangan metode) tetapi dalam arti umum kata, seperti yang digunakan misalnya dalam istilah "antarmuka pemrograman aplikasi" atau bahkan " antarmuka pengguna". [...]
Ilmari Karonen
6
@IlmariKaronen Jika Vlad menggunakan "antarmuka" berarti "kontrak" daripada dalam arti C # / Java yang sempit, maka pernyataan "Sekarang, misalkan kita mengubah X untuk mengimplementasikan antarmuka X2 yang baru. Nah, jelas kode saya tidak lagi dikompilasi. " benar-benar salah, karena Anda dapat mengubah kontrak tanpa mengubah tanda tangan metode apa pun. Tapi jujur, saya pikir masalahnya di sini adalah bahwa Vlad tidak menggunakan salah satu makna secara konsisten, tetapi menyatukan mereka - yang menyebabkan para klaim bahwa setiap perubahan pada kontrak X1 akan menyebabkan kesalahan kompilasi tanpa memperhatikan bahwa itu salah .
Mark Amery
9

Secara bergiliran menjawab pertanyaan Anda:

nilai apa yang dimiliki unit testing saat itu

Mereka murah untuk menulis dan menjalankan dan Anda mendapatkan umpan balik awal. Jika Anda melanggar X, Anda akan segera mengetahui apakah Anda memiliki tes yang bagus. Jangan pernah mempertimbangkan untuk menulis tes integrasi kecuali Anda telah menguji semua layer Anda (ya, bahkan pada basis data).

Apakah itu benar-benar hanya memberi tahu Anda bahwa ketika semua tes lulus, Anda belum memperkenalkan perubahan yang melanggar

Memiliki tes yang lulus sebenarnya bisa memberi tahu Anda sangat sedikit. Anda mungkin tidak memiliki cukup tes tertulis. Anda mungkin belum menguji cukup skenario. Cakupan kode dapat membantu di sini tetapi itu bukan peluru perak. Anda mungkin memiliki tes yang selalu lulus. Oleh karena itu merah menjadi langkah pertama yang sering diabaikan dari merah, hijau, refactor.

Dan ketika beberapa perilaku kelas berubah (secara sukarela atau tidak), bagaimana Anda bisa mendeteksi (lebih disukai dengan cara otomatis) semua konsekuensi

Lebih banyak pengujian - meskipun alat menjadi lebih baik dan lebih baik. TETAPI Anda harus mendefinisikan perilaku kelas dalam suatu antarmuka (lihat di bawah). NB akan selalu ada tempat untuk pengujian manual di atas piramida pengujian.

Bukankah seharusnya kita lebih fokus pada pengujian integrasi?

Semakin banyak tes integrasi juga bukan jawabannya, mereka mahal untuk ditulis, dijalankan, dan dipelihara. Bergantung pada pengaturan build Anda, build manager Anda mungkin mengecualikan mereka sehingga membuatnya bergantung pada pengembang yang mengingat (tidak pernah menjadi hal yang baik!).

Saya telah melihat pengembang menghabiskan waktu berjam-jam untuk mencoba memperbaiki tes integrasi yang mereka temukan dalam lima menit jika mereka memiliki tes unit yang baik. Jika gagal, coba jalankan perangkat lunaknya - hanya itu yang akan dipedulikan pengguna akhir Anda. Tidak ada gunanya memiliki jutaan unit tes yang lulus jika seluruh kartu jatuh ketika pengguna menjalankan seluruh rangkaian.

Jika Anda ingin memastikan kelas A mengkonsumsi kelas X dengan cara yang sama, Anda harus menggunakan antarmuka daripada konkret. Kemudian perubahan yang melanggar lebih mungkin diambil pada waktu kompilasi.

Robbie Dee
sumber
9

Itu benar.

Tes unit ada untuk menguji fungsionalitas unit yang terisolasi, pemeriksaan sekilas yang berfungsi sebagaimana dimaksud dan tidak mengandung kesalahan bodoh.

Tes unit tidak ada untuk menguji apakah seluruh aplikasi berfungsi.

Apa yang banyak orang lupakan adalah bahwa unit test hanyalah cara tercepat dan paling kotor untuk memvalidasi kode Anda. Setelah Anda tahu bahwa rutinitas kecil Anda berfungsi, Anda harus menjalankan tes Integrasi juga. Pengujian unit dengan sendirinya hanya sedikit lebih baik daripada tidak ada pengujian.

Alasan kami memiliki unit test sama sekali adalah bahwa mereka seharusnya murah. Cepat untuk membuat dan menjalankan dan memelihara. Setelah Anda mulai mengubahnya menjadi tes integrasi minimal, Anda menjadi dunia yang penuh kesakitan. Anda mungkin harus melakukan tes Integrasi penuh dan mengabaikan pengujian unit sama sekali jika Anda akan melakukannya.

sekarang, beberapa orang berpikir bahwa sebuah unit bukan hanya fungsi dalam kelas, tetapi seluruh kelas itu sendiri (termasuk saya sendiri). Namun, yang dilakukan adalah menambah ukuran unit sehingga Anda mungkin memerlukan lebih sedikit pengujian integrasi, tetapi Anda masih membutuhkannya. Masih tidak mungkin memverifikasi program Anda melakukan apa yang seharusnya dilakukan tanpa paket uji integrasi lengkap.

dan kemudian, Anda masih harus menjalankan pengujian integrasi penuh pada sistem langsung (atau semi-live) untuk memastikan bahwa itu bekerja dengan kondisi yang digunakan pelanggan.

gbjbaanb
sumber
2

Tes unit tidak membuktikan kebenaran apa pun. Ini berlaku untuk semua tes. Biasanya pengujian unit dikombinasikan dengan desain berbasis kontrak (desain dengan kontrak adalah cara lain untuk mengatakannya) dan mungkin bukti kebenaran otomatis, jika kebenaran perlu diverifikasi secara teratur.

Jika Anda memiliki kontrak nyata, yang terdiri dari invarian kelas, prasyarat, dan kondisi pos, dimungkinkan untuk membuktikan kebenaran secara hierarkis, dengan mendasarkan kebenaran komponen tingkat yang lebih tinggi pada kontrak komponen tingkat yang lebih rendah. Ini konsep dasar di balik desain berdasarkan kontrak.

Frank Hileman
sumber
Tes unit tidak membuktikan kebenaran apa pun . Tidak yakin saya mengerti ini, unit test pasti memeriksa hasilnya sendiri? Atau maksud Anda perilaku tidak dapat dibuktikan benar karena ini dapat mencakup beberapa lapisan?
Robbie Dee
7
@RobbieDee Saya kira, maksudnya ketika Anda menguji fac(5) == 120, Anda belum membuktikan bahwa fac()memang mengembalikan faktorial dari argumennya. Anda hanya membuktikan yang fac()mengembalikan faktorial lima ketika Anda masuk 5. Dan bahkan itu tidak pasti, karena fac()dapat kembali 42pada hari Senin pertama setelah gerhana total di Timbuktu ... Masalahnya di sini adalah, bahwa Anda tidak dapat membuktikan kepatuhan dengan memeriksa masing-masing input pengujian, Anda perlu memeriksa semua input yang mungkin, dan juga buktikan bahwa Anda belum melupakan (seperti membaca jam sistem).
cmaster
1
@RobbieDee Tes (termasuk tes unit) adalah pengganti yang buruk, sering kali terbaik yang tersedia, untuk tujuan sebenarnya, sebuah bukti yang diperiksa mesin. Pertimbangkan seluruh ruang keadaan unit yang diuji, termasuk ruang keadaan setiap komponen atau tiruan di dalamnya. Kecuali jika Anda memiliki ruang status yang sangat terbatas, pengujian tidak dapat mencakup ruang status itu. Cakupan lengkap akan menjadi bukti, tetapi ini hanya tersedia untuk ruang keadaan kecil, misalnya, menguji satu objek yang berisi byte tunggal atau integer 16 bit. Bukti otomatis jauh lebih berharga.
Frank Hileman
1
@ cmaster Anda merangkum perbedaan antara tes dan bukti dengan sangat baik. Terima kasih!
Frank Hileman
2

Saya menemukan tes sangat diejek jarang berguna. Sebagian besar waktu, saya akhirnya menerapkan kembali perilaku yang sudah dimiliki kelas asli, yang benar-benar mengalahkan tujuan mengejek.

Saya strategi yang lebih baik adalah memiliki pemisahan keprihatinan yang baik (mis. Anda dapat menguji Bagian A dari aplikasi Anda tanpa membawa bagian B hingga Z). Arsitektur yang begitu bagus sangat membantu untuk menulis tes yang bagus.

Juga, saya lebih dari mau menerima efek samping selama saya bisa mengembalikannya, misalnya jika metode saya memodifikasi data dalam db, biarkan saja! Selama saya bisa mengembalikan db ke kondisi sebelumnya, apa salahnya? Juga, ada manfaat yang bisa diperiksa oleh pengujian saya jika data tampak seperti yang diharapkan. DB dalam Memori atau versi uji spesifik dbs sangat membantu di sini (mis. Versi RavenDBs dalam memori).

Akhirnya, saya suka melakukan ejekan pada batas-batas layanan, mis. Jangan membuat panggilan http ke layanan b, tapi mari kita sela dan perkenalkan yang tepat

Sauer Kristen
sumber
1

Saya berharap orang-orang di kedua kubu akan memahami bahwa pengujian kelas dan pengujian perilaku tidak ortogonal.

Pengujian kelas dan pengujian unit digunakan secara bergantian dan mungkin tidak seharusnya demikian. Beberapa unit test kebetulan diimplementasikan di kelas. Itu semuanya. Pengujian unit telah terjadi selama beberapa dekade dalam bahasa tanpa kelas.

Adapun perilaku pengujian, sangat mungkin untuk melakukan ini dalam pengujian kelas menggunakan konstruk GWT.

Selain itu, apakah tes otomatis Anda berjalan di sepanjang kelas atau garis perilaku lebih tergantung pada prioritas Anda. Beberapa perlu prototipe dengan cepat dan mengeluarkan sesuatu sementara yang lain akan memiliki batasan cakupan karena gaya rumah. Banyak alasan. Keduanya merupakan pendekatan yang benar-benar valid. Anda membayar uang Anda, Anda mengambil pilihan Anda.

Jadi, apa yang harus dilakukan ketika kode rusak. Jika telah dikodekan ke antarmuka, maka konkret hanya perlu diubah (bersama dengan tes apa pun).

Namun, memperkenalkan perilaku baru tidak perlu membahayakan sistem sama sekali. Linux dkk penuh dengan fitur-fitur usang. Dan hal-hal seperti konstruktor (dan metode) dapat dengan senang hati kelebihan beban tanpa memaksa semua kode panggilan untuk berubah.

Di mana pengujian kelas menang adalah di mana Anda perlu membuat perubahan ke kelas yang belum diselami (karena kendala waktu, kompleksitas, atau apa pun). Jauh lebih mudah untuk memulai dengan kelas jika memiliki tes komprehensif.

Anjing Belgia
sumber
Di mana Anda memiliki konkresi saya akan menulis implementasi . Apakah ini merupakan calque dari bahasa lain (saya kira Prancis) atau adakah perbedaan penting antara konkresi dan implementasi ?
Peter Taylor
0

Kecuali jika antarmuka untuk X berubah, Anda tidak perlu mengubah unit test untuk A karena tidak ada yang terkait dengan A telah berubah. Sepertinya Anda benar-benar menulis unit test X dan A bersama-sama, tetapi menyebutnya unit test A:

Ketika Anda menulis tes unit untuk A, Anda mengejek X. Dengan kata lain, saat pengujian unit A, Anda mengatur (mendalilkan) perilaku dari tiruan X menjadi X1.

Idealnya, tiruan X harus mensimulasikan semua perilaku X yang mungkin, bukan hanya perilaku yang Anda harapkan dari X. Jadi, apa pun yang sebenarnya Anda implementasikan dalam X, A harus sudah mampu menanganinya. Jadi tidak ada perubahan ke X, selain mengubah antarmuka itu sendiri, akan berpengaruh pada unit test untuk A.

Sebagai contoh: Misalkan A adalah algoritma pengurutan, dan A menyediakan data untuk diurutkan. Mock of X harus memberikan nilai balik nol, daftar data kosong, elemen tunggal, beberapa elemen yang sudah diurutkan, beberapa elemen belum diurutkan, beberapa elemen diurutkan mundur, daftar dengan elemen yang sama diulang, nilai-nilai null saling terkait, sebuah ridiculously sejumlah besar elemen, dan juga harus membuang pengecualian.

Jadi mungkin X awalnya mengembalikan data yang diurutkan pada hari Senin dan daftar kosong pada hari Selasa. Tapi sekarang ketika X mengembalikan data yang tidak disortir pada hari Senin dan melempar pengecualian pada hari Selasa, A tidak peduli - skenario itu sudah tercakup dalam uji unit A.

Disk Moby
sumber
bagaimana jika X hanya mengganti nama indeks dalam array yang dikembalikan dari foobar ke foobarRandomNumber, bagaimana saya bisa menghitung dengan itu? jika Anda mengerti maksud saya, ini pada dasarnya masalah saya, saya mengganti nama kolom yang dikembalikan dari secondName ke nama keluarga, tugas klasik, tetapi pengujian saya tidak akan pernah tahu, karena ini mengejek. Saya hanya memiliki perasaan aneh seolah-olah banyak orang dalam pertanyaan ini tidak pernah benar-benar mencoba sesuatu seperti itu, sebelum berkomentar
FantomX1
Kompiler seharusnya mendeteksi perubahan ini dan memberi Anda kesalahan kompilator. Jika Anda menggunakan sesuatu seperti Javascript, maka saya sarankan untuk beralih ke jenis skrip atau menggunakan kompiler seperti Babel yang dapat mendeteksi hal ini.
Moby Disk
Bagaimana jika saya menggunakan array dalam PHP, atau dalam Java atau dalam Javascript, jika Anda mengubah indeks array atau menghapusnya, bukan dari kompiler bahasa yang akan memberi tahu Anda tentang hal itu, indeks mungkin bersarang pada angka yang dipikirkan ke-36. tingkat bersarang dari array, oleh karena itu saya pikir kompiler bukanlah solusi untuk ini.
FantomX1
-2

Anda harus melihat berbagai tes.

Unit test sendiri hanya akan menguji X. Mereka ada di sana untuk mencegah Anda mengubah perilaku X tetapi tidak mengamankan keseluruhan sistem. Mereka memastikan Anda dapat memperbaiki kelas Anda tanpa memperkenalkan perubahan perilaku. Dan jika Anda memecahkan X, Anda memecahkannya ...

Seharusnya A benar-benar mengejek X untuk tes unitnya dan tes dengan Mock harus terus berlalu bahkan setelah Anda mengubahnya.

Tetapi ada lebih dari satu level pengujian! Ada juga tes integrasi. Tes-tes ini ada untuk memvalidasi interaksi antara kelas-kelas. Tes ini biasanya memiliki harga yang lebih tinggi karena mereka tidak menggunakan ejekan untuk semuanya. Sebagai contoh, tes integrasi mungkin benar-benar menulis catatan ke dalam database, dan tes unit seharusnya tidak memiliki dependensi eksternal.

Juga jika X perlu memiliki perilaku baru, akan lebih baik untuk menyediakan metode baru yang akan memberikan hasil yang diinginkan

Heiko Hatzfeld
sumber