Apakah Anda lebih suka membuat barang pribadi internal / publik untuk pengujian, atau menggunakan semacam peretasan seperti PrivateObject?

26

Saya cukup pemula dalam pengujian kode, dan pernah menjadi assertpelacur sebelumnya. Satu hal yang mengkhawatirkan saya dalam unit testing adalah bahwa sering kali mengharuskan Anda untuk membuat public(atau setidaknya internal) bidang yang privateseharusnya, untuk melepaskan readonlymereka, membuat privatemetode protected virtualsebagai gantinya, dll ...

Baru-baru ini saya menemukan bahwa Anda dapat menghindari ini dengan menggunakan hal-hal seperti kelas PrivateObject untuk mengakses apa pun dalam objek melalui refleksi. Tapi ini membuat tes Anda kurang dapat dipertahankan (hal-hal akan gagal pada eksekusi daripada waktu kompilasi, itu akan rusak oleh perubahan nama sederhana, lebih sulit untuk debug ...). Apa pendapat Anda tentang ini? Apa praktik terbaik dalam pengujian unit terkait pembatasan akses?

sunting: pertimbangkan misalnya bahwa Anda memiliki kelas dengan cache di file pada disk, dan dalam tes Anda, Anda ingin menulis ke memori sebagai gantinya.

Zonko
sumber
2
Orang-orang python hanya memiliki publik. Mereka bahagia. Kenapa khawatir? Mengapa tidak membuat semuanya publik?
S.Lott
12
Karena itu jauh dari praktik terbaik di sebagian besar bahasa teratas. Jawaban sebenarnya adalah dengan menggunakan antarmuka yang baik dan semacamnya sehingga Anda dapat menggunakan objek tiruan untuk melakukan pengujian.
Rig
2
@Rig: Python bukan bahasa top? Menarik.
S.Lott
7
"itu jauh dari praktik terbaik di sebagian besar bahasa teratas" tidak secara logis menyiratkan (atau bahkan menyinggung itu) "Python bukan bahasa utama".
Mike Nakis
1
Tepatnya, ini adalah bahasa top tetapi sebagian besar bahasa top bukan Python dan mendukung pengaturan ruang lingkup yang sesuai. Saya tetap pada pendapat saya. Ada pola yang dirancang untuk membuat perangkat lunak yang sangat dapat diuji sambil memastikan variabel mempertahankan ruang lingkup. Bahkan programmer Python cenderung meniru ruang lingkup dengan awalan dari apa yang saya lihat.
Rig

Jawaban:

36

Anda seharusnya tidak pernah harus melakukannya

membuat public(atau setidaknya internal) bidang yang akan menjadi privatesebaliknya, untuk un- readonlymereka, make privatemetode protected virtualsebagai gantinya

Terutama un-readonlying bidang dapat membuat objek abadi berubah, dan itu akan menjadi bencana.

Pengujian Anda harus mempertimbangkan objek Anda sebagai kotak hitam ( Wikipedia ), yang berarti bahwa mereka hanya harus peduli dengan antarmuka publik dari objek, bukan dengan detail implementasi mereka.

Jika suatu objek tidak dapat diuji secara memadai menggunakan antarmuka publiknya, maka Anda perlu menemukan cara untuk memberikan ekstensi formal dan berguna untuk antarmuka yang akan memfasilitasi pengujian. Sebagai contoh, sistem pendaftaran dengan hanya sepasang metode Daftar / Deregister mungkin akan mendapat manfaat dari metode IsRegistered bahkan jika itu tidak diperlukan untuk aplikasi yang ada; tetap, ini adalah ekstensi formal dan berguna untuk antarmuka, dan itu akan, secara kebetulan, mengakomodasi pengujian.

Yang penting adalah bahwa mengubah implementasi objek seharusnya tidak mengharuskan Anda untuk mengubah unit test. Secara teori Anda harus dapat menulis unit test satu kali, dan kemudian meminta beberapa programmer untuk mengkode beberapa implementasi yang sama sekali berbeda dari objek yang akan diuji untuk bekerja dengan unit test.

Mike Nakis
sumber
2
Persis! Jika Anda tidak dapat mengujinya tanpa refleksi atau peretasan, maka Anda mungkin memiliki kesalahan dalam API atau desain Anda.
Falcon
Apakah Anda mengatakan bahwa membuat privatemetode protected virtualuntuk membantu mengejek dalam tes dianggap praktik buruk?
Robula
1
@Robula Di buku saya, ya. Saya bahkan tidak menulis tes saya dalam paket yang sama dengan kode produksi, karena saya tidak dapat mengakses anggota non-publik. Saya juga tidak menggunakan ejekan. Tentu saja jika Anda harus menggunakan tiruan, maka Anda harus melakukan apa pun untuk memfasilitasi mengejek, jadi virtual yang dilindungi mungkin merupakan kompromi praktis dalam banyak situasi. Tetapi idealnya suatu desain tidak perlu untuk mengejek, hanya implementasi alternatif, dan idealnya pengujian adalah pengujian kotak-hitam, bukan pengujian kotak-putih, sehingga segala sesuatu diuji dalam integrasi, tanpa tiruan.
Mike Nakis
13

Di C # Anda dapat menggunakan InternalsVisibleToAttributeuntuk memungkinkan rakitan pengujian Anda melihat internalkelas dalam rakitan yang Anda uji. Sepertinya Anda sudah tahu ini.

Dalam kebanyakan kasus, saya hanya tertarik untuk menguji API publik dari perakitan saya. Dalam kasus di mana saya ingin melakukan pengujian kotak putih pada beberapa unit, saya memindahkan kode ke internalkelas, dan menjadikannya publicmetode kelas itu. Lalu saya bisa kode tes unit untuk kelas itu.

Ini cocok untuk injeksi ketergantungan dan pola strategi. Anda bisa mengabstraksi metode menjadi antarmuka, menyuntikkan antarmuka ke konstruktor objek induk Anda, dan hanya menguji apakah delegasi induk tepat. Kemudian Anda dapat menguji bahwa objek anak berperilaku tepat. Ini membuat semuanya lebih modular.

Scott Whitlock
sumber
8

Dalam pengalaman saya, yang terbaik adalah tidak mengekspos internal kelas Anda khusus untuk ujian. Meskipun ini mungkin tampak untuk membuat tes lebih mudah untuk ditulis. Tes ini adalah klien pertama kelas Anda, jadi jika sulit untuk menguji kelas Anda melalui antarmuka publiknya, mungkin akan sulit untuk menggunakan kelas Anda di tempat lain juga.

Seberapa mudah kelas untuk menguji melalui API publik adalah umpan balik tentang betapa mudahnya kelas akan digunakan. Pikirkan tes sebagian sebagai cara untuk membuat prototipe penggunaan kelas Anda.

Coba pertimbangkan mengapa tes Anda perlu merasakan sesuatu tentang kelas yang tidak tersedia melalui API publik. Mungkin kelas Anda melakukan terlalu banyak dan perlu diuraikan menjadi sekelompok kelas yang bekerja sama? Kemudian Anda dapat mengejek beberapa kelas yang berkolaborasi untuk merasakan apa yang sedang dilakukan kelas.

Cobalah untuk menerapkan prinsip inversi dependensi dan memperkenalkan antarmuka antara kelas Anda yang dapat Anda tiru dalam tes Anda. Anda dapat menyediakan kolaborator untuk pengujian Anda dengan menggunakan inversi kontrol .

Juga pertimbangkan untuk menerapkan prinsip "kirim jangan tanya" untuk membantu menyusun antarmuka kelas Anda dengan cara yang kuat, longgar, di mana informasi yang tepat terbuka dan sisanya disembunyikan.

flamingpenguin
sumber
6

Secara pribadi saya lebih suka meninggalkan kode yang saya uji semurni mungkin dan jika kode pengujian perlu diretas (dan dalam C ++ pointer jahat dll) Saya senang melakukan itu.

Semut
sumber
1
Saya setuju, ini cara untuk melakukannya. Simpan keburukan dalam kode uji, di mana tidak ada salahnya.
Alan Delimon
@AlanDelimon Mungkinkah itu tidak melukai pikiran programmer pemeliharaan selanjutnya?
flamingpenguin
1
@ kode jelek jelek dapat merusak programmer di mana saja; tetapi kode tes yang jelek kemungkinan tidak akan banyak merugikan karena hanya akan memengaruhi orang-orang yang memodifikasi kelas dan tidak juga orang-orang yang hanya mengkonsumsinya.
Dan Neely
2
@flamingpenguin Mungkin, tetapi jika Anda harus meletakkan kode yang jelek di suatu tempat, mungkin juga dalam unit test di mana ia cenderung membutuhkan modifikasi dan menjadi bagian dari aplikasi.
Alan Delimon
5

Visibilitas kode Anda tidak ada hubungannya dengan tes unit Anda!

Anda menjadikan metode Anda pribadi bukan dengan tujuan untuk menyembunyikannya dari pengujian dan Anda tidak membuatnya terbuka untuk diekspos untuk pengujian, bukan? Ini adalah implementasi Anda yang penting di sini, bukan tes - server itu sebagai bukti kode Anda, tetapi itu bukan kode Anda .

Ada banyak cara untuk memungkinkan akses ke metode dan properti tersembunyi (pribadi atau internal). Banyak kerangka kerja pengujian dan IDE (misalnya, Visual Studio) mendukung Anda di sini dengan menghasilkan semua yang diperlukan untuk akses, Anda hanya perlu menulis kasus pengujian Anda. Jadi, mengapa khawatir? Lebih baik menjaga kode Anda tetap bersih dan rapi.

Alexander Galkin
sumber