it-swarm-id.com

Apakah statis secara universal "jahat" untuk pengujian unit dan jika demikian mengapa Resharper merekomendasikannya?

Saya telah menemukan bahwa hanya ada 3 cara untuk menguji unit dependensi (mock/stub) yang statis di C # .NET:

Mengingat bahwa dua di antaranya tidak gratis dan satu belum mencapai rilis 1.0, mengejek hal-hal statis tidak terlalu mudah.

Apakah itu membuat metode statis dan "jahat" seperti itu (dalam pengertian pengujian unit)? Dan jika demikian, mengapa penyelamat ingin saya membuat sesuatu yang bisa statis, statis? (Dengan asumsi penyelamat tidak juga "jahat".)

Klarifikasi: Saya berbicara tentang skenario ketika Anda ingin unit menguji suatu metode dan metode itu memanggil metode statis dalam berbeda unit/kelas. Dengan sebagian besar definisi pengujian unit, jika Anda membiarkan metode yang sedang diuji memanggil metode statis di unit/kelas lain maka Anda tidak pengujian unit, Anda integrasi pengujian. (Berguna, tetapi bukan tes unit.)

87
Vaccano

Melihat jawaban lain di sini, saya pikir mungkin ada beberapa kebingungan antara metode statis yang tahan keadaan statis atau menyebabkan efek samping (yang menurut saya seperti ide yang sangat buruk), dan metode statis yang hanya mengembalikan nilai.

Metode statis yang tahan dan tidak menimbulkan efek samping harus mudah diuji unit. Sebenarnya, saya menganggap metode semacam itu sebagai bentuk pemrograman fungsional "orang miskin"; Anda memberikan metode objek atau nilai, dan mengembalikan objek atau nilai. Tidak ada lagi. Saya tidak melihat bagaimana metode seperti itu secara negatif akan mempengaruhi pengujian unit sama sekali.

108
Robert Harvey

Anda tampaknya membingungkan statis data dan statis metode. Resharper, jika saya ingat dengan benar, merekomendasikan membuat metode private di dalam kelas statis jika dapat dibuat demikian - saya yakin ini menghasilkan manfaat kinerja yang kecil. Itu tidak merekomendasikan membuat "apa pun yang bisa" statis!

Tidak ada yang salah dengan metode statis dan mudah diuji (selama tidak mengubah data statis). Sebagai contoh, pikirkan perpustakaan Matematika, yang merupakan kandidat yang baik untuk kelas statis dengan metode statis. Jika Anda memiliki metode (dibuat-buat) seperti ini:

public static long Square(int x)
{
    return x * x;
}

maka ini sangat dapat diuji dan tidak memiliki efek samping. Anda hanya memeriksa bahwa ketika Anda lulus, katakanlah, 20 Anda mendapatkan kembali 400. Tidak masalah.

27
Dan Diplo

Jika pertanyaan sebenarnya di sini adalah "Bagaimana saya menguji kode ini?":

public class MyClass
{
   public void MethodToTest()
   {
       //... do something
       MyStaticClass.StaticMethod();
       //...more
   }
}

Kemudian, cukup refactor kode dan menyuntikkan seperti biasa panggilan ke kelas statis seperti ini:

public class MyClass
{
   private readonly IExecutor _externalExecutor;
   public MyClass(_IExecutor executor)
   {
       _exeternalExecutor = executor;
   }

   public void MethodToTest()
   {
       //... do something
       _exetrnalExecutor.DoWork();
       //...more
   }
}

public class MyStaticClassExecutor : IExecutor
{
    public void DoWork()
    {
        MyStaticClass.StaticMethod();
    }
}
18
Sunny

Statika tidak selalu jahat, tetapi mereka dapat membatasi pilihan Anda ketika datang ke pengujian unit dengan palsu/mengejek/bertopik.

Ada dua pendekatan umum untuk mengejek.

Yang pertama (tradisional - dilaksanakan oleh RhinoMocks, Moq, NMock2; mengolok-olok manual dan bertopik juga di kamp ini) bergantung pada jahitan uji dan injeksi ketergantungan. Misalkan Anda sedang menguji beberapa kode statis dan memiliki dependensi. Yang sering terjadi dalam kode yang dirancang dengan cara ini adalah statika membuat dependensi mereka sendiri, membalikkan inversi dependensi . Anda segera menemukan bahwa Anda tidak dapat menyuntikkan antarmuka tiruan ke dalam kode yang sedang diuji yang dirancang dengan cara ini.

Yang kedua (mengejek apa pun - diimplementasikan oleh TypeMock, JustMock dan Moles) bergantung pada .NET Profiling API . Itu dapat mencegat salah satu instruksi CIL Anda dan mengganti potongan kode Anda dengan yang palsu. Ini memungkinkan TypeMock dan produk lain di kamp ini untuk mengolok-olok apa pun: statika, kelas tertutup, metode pribadi - hal-hal yang tidak dirancang untuk dapat diuji.

Ada perdebatan yang sedang berlangsung antara dua aliran pemikiran. Seseorang berkata, ikuti Prinsip-prinsip SOLID dan desain untuk testabilitas (yang sering termasuk statika mudah). Yang lain mengatakan, beli TypeMock dan jangan khawatir.

15
azheglov

Lihat ini: "Metode Statis Mati untuk Testabilitas" . Ringkasan singkat argumen:

Untuk menguji unit Anda perlu mengambil sepotong kecil kode Anda, rewire dependensi dan mengujinya secara terpisah. Ini sulit dengan metode statis, tidak hanya dalam kasus mereka mengakses keadaan global tetapi bahkan jika mereka hanya memanggil metode statis lainnya.

14
Rafał Dowgird

Kebenaran sederhana yang jarang diakui adalah bahwa jika sebuah kelas berisi ketergantungan kompiler-terlihat pada kelas lain, itu tidak bisa diuji secara terpisah dari kelas itu. Anda dapat memalsukan sesuatu yang terlihat seperti tes, dan akan muncul pada laporan seolah-olah itu adalah tes.

Tapi itu tidak akan memiliki sifat penentu utama dari suatu tes; gagal ketika ada yang salah, melewati saat mereka benar.

Ini berlaku untuk semua panggilan statis, panggilan konstruktor, dan referensi apa pun untuk metode atau bidang yang tidak diwarisi dari kelas dasar atau antarmuka . Jika nama kelas muncul dalam kode, itu adalah ketergantungan kompiler-terlihat, dan Anda tidak dapat menguji tanpa itu. Setiap bongkahan yang lebih kecil adalah bukan unit yang dapat diuji valid. Setiap upaya untuk memperlakukannya seolah-olah akan memiliki hasil yang tidak lebih bermakna daripada menulis sedikit utilitas untuk memancarkan XML yang digunakan oleh kerangka pengujian Anda untuk mengatakan 'lulus uji'.

Mengingat bahwa, ada tiga opsi:

  1. mendefinisikan pengujian unit untuk menguji unit yang terdiri dari sebuah kelas dan itu dependensi hard-coded. Ini berfungsi, asalkan Anda menghindari ketergantungan sirkular.

  2. jangan pernah membuat dependensi waktu kompilasi antara kelas yang Anda bertanggung jawab untuk pengujian. Ini berfungsi, asalkan Anda tidak keberatan dengan gaya kode yang dihasilkan.

  3. jangan uji unit, melainkan tes integrasi. Yang berfungsi, asalkan tidak bertentangan dengan hal lain yang Anda perlukan untuk menggunakan pengujian integrasi istilah.

5
soru

Tidak ada dua cara tentang itu. Saran ReSharper dan beberapa fitur berguna dari C # tidak akan digunakan sesering jika Anda menulis tes unit atom terisolasi untuk semua kode Anda.

Misalnya, jika Anda memiliki metode statis dan Anda harus mematikannya, Anda tidak bisa kecuali Anda menggunakan kerangka kerja isolasi berbasis profil. Pemecahan masalah yang kompatibel dengan panggilan adalah mengubah bagian atas metode untuk menggunakan notasi lambda. Sebagai contoh:

SEBELUM:

    public static DBConnection ConnectToDB( string dbName, string connectionInfo ) {
    }

SETELAH:

    public static Func<string, string, DBConnection> ConnectToDB (dbName, connectionInfo ) {
    };

Keduanya kompatibel dengan panggilan. Penelepon tidak harus berubah. Tubuh fungsi tetap sama.

Kemudian dalam kode Unit-Test Anda, Anda dapat mematikan panggilan ini seperti itu (dengan asumsi itu dalam kelas yang disebut Database):

        Database.ConnectToDB = (dbName, connectionInfo) => { return null|whatever; }

Berhati-hatilah untuk menggantinya dengan nilai asli setelah Anda selesai. Anda dapat melakukannya melalui percobaan/akhirnya atau, dalam pembersihan unit-tes Anda, tes yang dipanggil setelah setiap tes, tulis kode seperti ini:

    [TestCleanup]
    public void Cleanup()
    {
        typeof(Database).TypeInitializer.Invoke(null, null);
    }

yang akan memanggil kembali initializer statis kelas Anda.

Fungsi Lambda tidak terlalu kaya dalam dukungan seperti metode statis biasa, sehingga pendekatan ini memiliki efek samping yang tidak diinginkan:

  1. Jika metode statis adalah metode ekstensi, Anda harus mengubahnya ke metode non-ekstensi terlebih dahulu. Resharper dapat melakukan ini untuk Anda secara otomatis.
  2. Jika salah satu tipe data dari metode statis adalah Majelis interop tertanam, seperti untuk Office, Anda harus membungkus metode, membungkus jenis atau mengubahnya untuk mengetikkan 'objek'.
  3. Anda tidak dapat lagi menggunakan alat refactoring perubahan tanda tangan Resharper.

Tetapi katakanlah Anda menghindari statika sama sekali, dan Anda mengonversinya menjadi metode instan. Itu masih tidak bisa dipermainkan kecuali metode ini virtual atau diimplementasikan sebagai bagian dari antarmuka.

Jadi pada kenyataannya, siapa pun yang menyarankan solusi untuk mematikan metode statis adalah menjadikannya metode instan, mereka juga akan menentang metode instan yang bukan virtual atau bagian dari antarmuka.

Jadi mengapa C # memiliki metode statis? Mengapa ini memungkinkan metode instance non-virtual?

Jika Anda menggunakan salah satu dari "Fitur" ini, maka Anda tidak dapat membuat metode yang terisolasi.

Jadi kapan Anda menggunakannya?

Gunakan mereka untuk kode apa pun yang Anda tidak harapkan ada orang yang ingin mematikannya. Beberapa contoh: metode Format () dari kelas String metode WriteLine () dari kelas Konsol metode Cosh () dari kelas Matematika

Dan satu hal lagi .. Kebanyakan orang tidak akan peduli tentang ini, tetapi jika Anda bisa tentang kinerja panggilan tidak langsung, itu alasan lain untuk menghindari metode contoh. Ada kasus ketika itu adalah hit kinerja. Itu sebabnya metode non-virtual ada di tempat pertama.

4
zumalifeguard

Saya melihat bahwa setelah lama tidak ada yang menyatakan fakta yang sangat sederhana. Jika resharper mengatakan kepada saya bahwa saya dapat membuat metode statis, itu berarti hal yang sangat besar bagi saya, saya dapat mendengar suaranya mengatakan kepada saya: "hei, Anda, potongan-potongan logika ini bukan TANGGUNG JAWAB kelas saat ini untuk ditangani, jadi harus tetap di luar di beberapa kelas pembantu atau sesuatu ".

3
g1ga
  1. Saya percaya itu sebagian karena metode statis "lebih cepat" untuk memanggil daripada metode contoh. (Dalam kutipan karena ini bau optimasi mikro) lihat http://dotnetperls.com/static-method
  2. Ini memberi tahu Anda bahwa itu tidak perlu keadaan, karena itu dapat dipanggil dari mana saja, menghapus overhead instatiation jika itu satu-satunya hal yang seseorang butuhkan.
  3. Jika saya ingin mengejeknya, maka saya pikir pada umumnya itu adalah praktik yang dideklarasikan pada antarmuka.
  4. Jika dideklarasikan pada antarmuka maka R # tidak akan menyarankan Anda membuatnya statis.
  5. Jika dinyatakan virtual, maka R # tidak akan menyarankan Anda membuatnya statis juga.
  6. Holding state (bidang) secara statis adalah sesuatu yang harus selalu dipertimbangkan dengan hati-hati . Keadaan statis dan benang bercampur seperti lithium dan air.

R # bukan satu-satunya alat yang akan membuat saran ini. Analisis Kode FxCop/MS juga akan melakukan hal yang sama.

Saya biasanya akan mengatakan bahwa jika metode ini statis, umumnya harus dapat diuji juga. Itu membawa beberapa pertimbangan desain dan mungkin lebih banyak diskusi daripada yang saya miliki di jari saya sekarang, jadi dengan sabar menunggu suara turun dan komentar ...;)

3
MIA

Jika metode statis dipanggil dari di dalam metode lain, tidak mungkin untuk mencegah atau mengganti panggilan semacam itu. Ini berarti, kedua metode ini membuat Unit tunggal; Tes unit apa pun menguji keduanya.

Dan jika metode statis ini berbicara ke Internet, menghubungkan basis data, menampilkan popup GUI atau mengonversi uji Unit menjadi kekacauan total, itu hanya dilakukan tanpa ada penyelesaian yang mudah. Metode yang memanggil metode statis seperti itu tidak dapat diuji tanpa refactoring, bahkan jika ia memiliki banyak kode komputasi murni yang akan sangat diuntungkan dengan menjadi unit yang diuji.

2
h22

Saya percaya bahwa Resharper memberi Anda petunjuk dan menerapkan pedoman pengkodean yang telah disiapkan. Ketika saya telah menggunakan Resharper dan telah memberi tahu saya bahwa suatu metode harus statis itu terikat pada metode pribadi yang tidak bertindak pada variabel contoh apa pun.

Sekarang untuk testablity skenario ini seharusnya tidak menjadi masalah karena Anda tidak harus menguji metode pribadi.

Sedangkan untuk pengujian metode statis yang bersifat publik maka pengujian unit menjadi sulit ketika metode statis menyentuh keadaan statis. Secara pribadi saya akan menjaga ini seminimal mungkin dan menggunakan metode statis sebagai fungsi murni sebanyak mungkin di mana setiap dependensi dilewatkan ke dalam metode yang dapat dikontrol melalui perlengkapan tes. Namun ini adalah keputusan desain.

0
aqwert