Mari kita asumsikan saya menulis metode ekstensi dalam C # untuk byte
array yang mengkodekannya menjadi string hex, sebagai berikut:
public static class Extensions
{
public static string ToHex(this byte[] binary)
{
const string chars = "0123456789abcdef";
var resultBuilder = new StringBuilder();
foreach(var b in binary)
{
resultBuilder.Append(chars[(b >> 4) & 0xf]).Append(chars[b & 0xf]);
}
return resultBuilder.ToString();
}
}
Saya bisa menguji metode di atas menggunakan NUnit sebagai berikut:
[Test]
public void TestToHex_Works()
{
var bytes = new byte[] { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef };
Assert.AreEqual("0123456789abcdef", bytes.ToHex());
}
Jika saya menggunakan bagian Extensions.ToHex
dalam proyek saya, mari kita asumsikan dalam Foo.Do
metode sebagai berikut:
public class Foo
{
public bool Do(byte[] payload)
{
var data = "ES=" + payload.ToHex() + "ff";
// ...
return data.Length > 5;
}
// ...
}
Maka semua tes Foo.Do
akan tergantung pada keberhasilan TestToHex_Works
.
Menggunakan fungsi bebas di C ++ hasilnya akan sama: tes yang menguji metode yang menggunakan fungsi bebas akan tergantung pada keberhasilan tes fungsi gratis.
Bagaimana saya bisa menangani situasi seperti itu? Bisakah saya menyelesaikan dependensi tes ini? Apakah ada cara yang lebih baik untuk menguji cuplikan kode di atas?
unit-testing
static-methods
Akira
sumber
sumber
Then all tests of Foo.Do will depend on the success of TestToHex_works
- Jadi? Anda tidak memiliki kelas yang bergantung pada keberhasilan kelas lain?toHex
(atau menukar implementasi). Terlepas dari itu semuanya baik-baik saja. Kode Anda yang dikonversi menjadi hex diuji, sekarang ada kode lain yang menggunakan kode yang diuji sebagai utilitas untuk mencapai tujuannya sendiri.Jawaban:
Iya. Itu sebabnya Anda memiliki tes untuk
TextToHex
. Jika tes tersebut lulus, fungsi tersebut memenuhi spesifikasi yang ditentukan dalam tes tersebut. JadiFoo.Do
bisa dengan aman menyebutnya dan tidak khawatir. Sudah tertutup.Anda bisa menambahkan antarmuka, menjadikan metode metode instan dan menyuntikkannya
Foo
. Maka Anda bisa mengejekTextToHex
. Tetapi sekarang Anda harus menulis tiruan, yang dapat berfungsi secara berbeda. Jadi, Anda akan memerlukan tes "integrasi" untuk menyatukan keduanya untuk memastikan bagian-bagiannya benar-benar bekerja sama. Apa yang telah dicapai selain membuat segalanya lebih rumit?Gagasan bahwa unit test harus menguji bagian-bagian kode Anda secara terpisah dari bagian-bagian lain adalah kekeliruan. "Unit" dalam tes unit adalah unit eksekusi yang terisolasi. Jika dua tes dapat dijalankan secara bersamaan tanpa mempengaruhi satu sama lain, maka keduanya dijalankan secara terpisah dan demikian juga dengan unit test. Fungsi statis yang cepat, tidak memiliki pengaturan yang rumit dan tidak memiliki efek samping seperti contoh Anda baik-baik saja untuk digunakan langsung dalam unit test. Jika Anda memiliki kode yang lambat, rumit untuk diatur atau memiliki efek samping, maka tiruan berguna. Mereka harus dihindari di tempat lain.
sumber
Yah, saya tidak melihat ketergantungan di sini. Setidaknya bukan jenis yang memaksa kita untuk melakukan satu tes sebelum yang lain. Ketergantungan yang kami bangun di antara tes (tidak peduli jenisnya) adalah satu keyakinan .
Kami membuat kode (uji-pertama atau tidak) dan kami memastikan tes lulus. Kemudian, kita berada dalam posisi membangun lebih banyak kode. Semua kode yang dibangun di atas yang pertama ini dibangun di atas kepercayaan dan kepastian. Ini kurang lebih apa yang @DavidArno jelaskan (sangat baik) dalam jawabannya.
Tes unit harus dijalankan dalam urutan apa pun, kapan saja, di lingkungan apa pun, dan secepat mungkin. Apakah
TestToHex_Works
dieksekusi yang pertama atau yang terakhir seharusnya tidak membuat Anda khawatir.Jika
TestToHex_Works
gagal karena kesalahan dalamToHex
, semua tes bergantungToHex
akan berakhir dengan hasil yang berbeda dan gagal (idealnya). Kuncinya di sini adalah mendeteksi hasil yang berbeda. Kami melakukannya membuat unit test menjadi deterministik. Seperti yang Anda lakukan di siniTes unit lebih lanjut yang mengandalkan
ToHex
juga harus bersifat deterministik. Jika semuanyaToHex
berjalan dengan baik, hasilnya harus menjadi yang diharapkan. Jika Anda mendapatkan yang berbeda, ada yang salah, di suatu tempat dan ini yang Anda inginkan dari unit test, untuk mendeteksi perubahan halus ini dan gagal dengan cepat.sumber
Agak sulit dengan contoh sekecil itu, tapi mari kita pertimbangkan kembali apa yang kita lakukan:
Mengapa Anda menulis metode ekstensi untuk melakukan ini? Kecuali jika Anda menulis pustaka untuk menyandikan array byte ke dalam string, ini tidak mungkin menjadi persyaratan yang Anda coba penuhi. Apa yang Anda lakukan di sini berusaha memenuhi persyaratan "Validasi beberapa payload yang merupakan array byte" - jadi unit test yang Anda implementasikan harus "Diberikan payload X yang valid, metode saya mengembalikan true" dan "Diberikan sebuah payload tidak valid Y, metode saya mengembalikan false ".
Jadi, ketika Anda awalnya menerapkan ini, Anda mungkin melakukan hal-hal "byte-to-hex" dalam metode. Dan itu tidak masalah. Kemudian nanti Anda mendapatkan beberapa persyaratan lain (mis. "Tampilkan payload sebagai string hex") yang, saat Anda mengimplementasikannya, Anda sadar juga mengharuskan Anda untuk mengkonversi array byte ke string hex. Pada saat itu Anda membuat metode ekstensi Anda, refactor metode yang lama, dan menyebutnya dari kode baru Anda. Tes Anda untuk fungsionalitas validasi tidak boleh berubah. Tes Anda untuk menampilkan payload harus sama apakah kode byte-ke-hex inline atau dalam metode statis. Metode statis adalah detail implementasi yang tidak perlu Anda pedulikan saat menulis tes. Kode dalam metode statis akan diuji oleh konsumennya. Saya tidak akan
sumber
Persis. Dan ini adalah salah satu masalah dengan metode statis, yang lain adalah bahwa OOP adalah alternatif yang jauh lebih baik dalam kebanyakan situasi.
Ini juga salah satu alasan Ketergantungan Injeksi digunakan.
Dalam kasus Anda, Anda dapat memilih konverter konkret yang Anda masukkan ke kelas yang membutuhkan konversi beberapa nilai menjadi heksadesimal. sebuah antarmuka , dilaksanakan oleh kelas beton ini, akan menentukan kontrak yang diperlukan untuk mengkonversi nilai-nilai.
Anda tidak hanya dapat menguji kode Anda dengan lebih mudah, tetapi Anda juga dapat menukar implementasi nanti (karena ada banyak cara lain yang memungkinkan untuk mengubah nilai menjadi heksadesimal, beberapa dari mereka menghasilkan output yang berbeda).
sumber
multiply
metode, Anda akan mengejek ini? Yang berarti untuk melakukan refactor (dan menggunakan operator * sebagai gantinya), Anda harus mengubah setiap deklarasi tiruan tunggal dalam kode Anda? Tidak terlihat seperti kode mudah bagi saya. Yang saya katakan adalahtoHex
sangat sederhana dan tidak memiliki efek samping sama sekali jadi saya tidak melihat alasan untuk mengejek atau mematikan ini. Itu terlalu banyak biaya untuk sama sekali tanpa keuntungan. Tidak mengatakan kita tidak harus menyuntikkannya, tapi itu tergantung penggunaan.String
danint
juga? Atau kelas utilitas dasar dari perpustakaan standar?