Membuat kode internal tetapi tersedia untuk pengujian unit dari proyek lain

129

Kami menempatkan semua unit test kami di proyek mereka sendiri. Kami menemukan bahwa kami harus membuat kelas tertentu untuk publik daripada internal hanya untuk tes unit. Apakah ada cara untuk menghindari keharusan melakukan ini. Apa implikasi ingatannya dengan membuat kelas terbuka alih-alih disegel?

leora
sumber
2
kemungkinan duplikat pengubah akses C # "internal" saat melakukan pengujian unit
Dour High Arch

Jawaban:

205

Jika Anda menggunakan .NET, atribut perakitan InternalsVisibleTo memungkinkan Anda membuat rakitan "teman". Ini adalah majelis khusus yang diberi nama kuat yang diizinkan untuk mengakses kelas internal dan anggota majelis lainnya.

Catatan, ini harus digunakan dengan hati-hati karena dengan ketat memasangkan majelis yang terlibat. Penggunaan umum untuk InternalsVisibleTo adalah untuk proyek pengujian unit. Itu mungkin bukan pilihan yang baik untuk digunakan dalam majelis aplikasi Anda yang sebenarnya, karena alasan yang disebutkan di atas.

Contoh:

[assembly: InternalsVisibleTo("NameAssemblyYouWantToPermitAccess")]
namespace NameOfYourNameSpace
{
Abu
sumber
23
Saya sarankan menempatkan # jika DEBUG di sekitar atribut, dan kemudian pengujian unit dalam debug. Dengan begitu Anda akan yakin bahwa atribut tidak diatur pada kode rilis.
steve cook
Ini hanya sebuah ide, saya tidak tahu .... Bagaimana dengan: # jika DEBUG kelas publik IniReader #else kelas internal IniReader #endif Mungkin tidak disarankan? Mengapa?
jmelhus
4
Nah, mengapa membatasi tes untuk debug build?
Marco Mp
2
Juga, hanya nitpick, tidak perlu ada majelis "teman" untuk dinamai kuat (mungkin praktik yang baik - bahkan jika selera pribadi saya mengatakan sebaliknya, tetapi tidak wajib).
Marco Mp
9
Tidak setuju. Jika saya sedang membangun komponen yang kompleks dengan API publik yang sangat tipis, tidak praktis dan tidak realistis untuk hanya menguji melalui API publik. Anda akan berakhir dengan bola lumpur yang tidak terawat. Sebagai gantinya, saya akan dengan hati-hati mendefinisikan unit internal dan mengujinya secara terpisah. Seperti yang dikatakan Jeremy D. Miller cukup sering: "Tes kecil sebelum Anda menguji besar".
Dennis Doomen
6

Jika ini adalah kelas internal maka itu tidak boleh digunakan dalam isolasi. Karena itu Anda tidak boleh benar-benar mengujinya selain menguji beberapa kelas lain yang memanfaatkan objek itu secara internal.

Sama seperti Anda tidak harus menguji anggota pribadi kelas, Anda tidak harus menguji kelas internal DLL. Kelas-kelas tersebut adalah detail implementasi dari beberapa kelas yang dapat diakses publik, dan karenanya harus dilakukan dengan baik melalui tes unit lainnya.

Idenya adalah Anda hanya ingin menguji perilaku kelas karena jika Anda menguji detail implementasi internal maka tes Anda akan rapuh. Anda harus dapat mengubah detail implementasi dari kelas apa pun tanpa merusak semua tes Anda.

Jika Anda benar-benar perlu menguji kelas itu, maka Anda mungkin ingin menguji kembali mengapa kelas itu internal.

Josh
sumber
2
Detail implementasi harus dilakukan sebagai bagian dari tes yang meliputi. Jangan mengintip variabel pribadi ... uji perilaku yang diharapkan. Jika tesnya benar .. semua pipa dan kabel internal harus diuji sebagai bagian dari itu. Terpilih.
Gishu
69
saya tidak perlu setuju dengan ini karena kelas-kelas ini adalah "publik" ke kelas-kelas lain di dalam DLL dan fungsionalitas kelas harus diuji secara independen
leora
27
Saya juga tidak setuju. Unit adalah unit dan harus diuji secara terpisah.
Sentinel
5
Pedoman umum yang bagus, tapi jangan sampai dogmatis. Tes melayani dua tujuan utama: 1) Regresi - pastikan Anda tidak merusak sesuatu, 2) Meningkatkan kecepatan pengembangan. Jika saya harus berdiri layanan besar setiap kali saya ingin menulis garis kode saya akan menghambat pengembangan. Jika saya memiliki bagian internal yang kompleks saya ingin dapat mengembangkan dan menguji secara terpisah. Sebaliknya, saya tidak ingin semuanya diuji secara terpisah (sehingga menurunkan nilai pengujian regresi, atau menggandakan kode uji), tetapi beberapa kode membenarkan isolasi. Mungkin kuncinya adalah membuatnya menjadi majelis terpisah.
Lee Jensen
2
OP di sini benar. Anda sedang menggabungkan tes Anda ke implementasi internal. Karenanya tim Anda selamanya menjadi budak untuk memperbaiki tes dan kode ejekan yang mengerikan. Uji API publik saja, baik API lib yang dipaket atau API yang terpapar jaringan. Semua kode harus dapat dieksekusi melalui pintu depan. Pengujian implementasi adalah mengapa TDD sebagian besar telah mati, itu hanya PITA besar untuk menguji secara harfiah setiap kelas dari seluruh aplikasi, daripada fokus pada memastikan perilaku sistem. Saya pikir hampir semua orang, termasuk buku "berwibawa", salah atau tidak memperjelasnya.
Luke Puplett
4

untuk keperluan dokumentasi

atau Anda dapat membuat instance kelas internal dengan menggunakan Type.GetTypemetode

contoh

//IServiceWrapper is public class which is 
//the same assembly with the internal class 
var asm = typeof(IServiceWrapper).Assembly;
//Namespace.ServiceWrapper is internal
var type = asm.GetType("Namespace.ServiceWrapper");
return (IServiceWrapper<T>)Activator
    .CreateInstance(type, new object[1] { /*constructor parameter*/ });

untuk tipe generik ada proses berbeda seperti di bawah ini:

var asm = typeof(IServiceWrapper).Assembly;
//note the name Namespace.ServiceWrapper`1
//this is for calling Namespace.ServiceWrapper<>
var type = asm.GetType("Namespace.ServiceWrapper`1");
var genType = type.MakeGenericType(new Type[1] { typeof(T) });
return (IServiceWrapper<T>)Activator
     .CreateInstance(genType, new object[1] { /*constructor parameter*/});
ktutnik
sumber
-5

Kelas dapat bersifat publik DAN disegel.

Tapi, jangan lakukan itu.

Anda dapat membuat alat untuk merefleksikan kelas internal, dan memancarkan kelas baru yang mengakses semuanya melalui refleksi. MSTest melakukan itu.

Sunting: Maksud saya, jika Anda tidak ingin menyertakan -semua-pengujian dalam perakitan asli Anda; ini juga berfungsi jika anggota bersifat pribadi.

TraumaPony
sumber
1
Tunggu apa? Anda mengatakan jangan membuat public sealed class? Apa alasanmu untuk permata itu?
naksir
@crush traumapony belum masuk sejak 2009.
Prof. Falken