Bagaimana Anda menguji unit encoder?

9

Saya punya sesuatu seperti ini:

public byte[] EncodeMyObject(MyObject obj)

Saya telah menguji unit seperti ini:

byte[] expectedResults = new byte[3]{ 0x01, 0x02, 0xFF };
Assert.IsEqual(expectedResults, EncodeMyObject(myObject));

EDIT: Dua cara yang saya lihat diusulkan adalah:

1) Menggunakan nilai yang diharapkan hardcoded, seperti contoh di atas.

2) Menggunakan dekoder untuk mendekode array byte yang disandikan dan membandingkan objek input / output.

Masalah yang saya lihat dengan metode 1 adalah sangat rapuh dan membutuhkan banyak nilai kode keras.

Masalah dengan metode 2 adalah bahwa pengujian encoder tergantung pada decoder yang bekerja dengan benar. Jika encoder / decoder rusak secara merata (di tempat yang sama), maka pengujian dapat menghasilkan false positive.

Ini mungkin satu-satunya cara untuk menguji metode jenis ini. Jika itu masalahnya maka baiklah. Saya mengajukan pertanyaan untuk melihat apakah ada strategi yang lebih baik untuk jenis pengujian ini. Saya tidak dapat mengungkapkan internal encoder tertentu yang sedang saya kerjakan. Saya bertanya secara umum bagaimana Anda akan menyelesaikan masalah jenis ini, dan saya merasa internal tidak penting. Asumsikan bahwa objek input yang diberikan akan selalu menghasilkan array byte keluaran yang sama.

ConditionRacer
sumber
4
Bagaimana cara myObjectberalih dari myObjectke { 0x01, 0x02, 0xFF }? Bisakah algoritma itu dipecah dan diuji? Alasan saya bertanya adalah saat ini, sepertinya Anda memiliki tes yang membuktikan bahwa satu benda ajaib menghasilkan benda ajaib lain. Satu-satunya kepercayaan Anda adalah bahwa satu input menghasilkan satu output. Jika Anda dapat memecah algoritma, Anda bisa mendapatkan kepercayaan lebih lanjut dalam algoritma, dan kurang bergantung pada input dan output magis.
Anthony Pegram
3
@Codism Bagaimana jika encoder dan decoder rusak di tempat yang sama?
ConditionRacer
2
Tes, menurut definisi, melakukan sesuatu dan memeriksa untuk melihat apakah Anda mendapatkan hasil yang diharapkan, yang merupakan tes Anda. Tentu saja Anda harus memastikan bahwa Anda melakukan tes yang cukup seperti itu untuk memastikan Anda menggunakan semua kode Anda dan menutupi kasus tepi dan keanehan lainnya.
Blrfl
1
@ Justin984, well, sekarang kita akan lebih dalam. Saya tidak akan mengekspos internal pribadi itu sebagai anggota API Encoder, tentu saja tidak. Saya akan menghapusnya sepenuhnya dari Encoder. Atau lebih tepatnya, Encoder akan mendelegasikan ke tempat lain, ketergantungan . Jika ini adalah pertarungan antara metode monster yang tidak bisa diuji atau sekelompok kelas penolong, aku memilih kelas penolong setiap saat. Tetapi sekali lagi, saya membuat inferensi informasi kode Anda pada saat ini, karena saya tidak bisa melihatnya. Tetapi jika Anda ingin mendapatkan kepercayaan pada tes Anda, memiliki metode yang lebih kecil melakukan lebih sedikit hal adalah cara untuk sampai ke sana.
Anthony Pegram
1
@ Justin984 Jika spesifikasi berubah, Anda mengubah output yang diharapkan dalam pengujian Anda dan sekarang gagal. Kemudian, Anda mengubah logika encoder untuk lulus. Tampak persis bagaimana TDD seharusnya bekerja dan itu hanya akan gagal ketika seharusnya. Saya tidak melihat bagaimana ini membuatnya rapuh.
Daniel Kaplan

Jawaban:

1

Anda berada dalam situasi yang menjengkelkan di sana. Jika Anda memiliki format statis yang Anda encode, metode pertama Anda akan menjadi cara untuk pergi. Jika itu hanya format Anda sendiri, dan tidak ada orang lain yang harus memecahkan kode dari metode kedua adalah cara untuk pergi. Tetapi Anda tidak benar-benar cocok dengan salah satu kategori tersebut.

Apa yang saya lakukan adalah mencoba memecah hal-hal berdasarkan tingkat abstraksi.

Jadi saya akan mulai dengan sesuatu pada level bit, yang akan saya uji seperti

bitWriter = new BitWriter();
bitWriter.writeInt(42, bits = 7);
assertEqual( bitWriter.data(), {0x42} )

Jadi idenya adalah bahwa penulis bit tahu bagaimana menulis jenis bidang yang paling primitif, seperti int.

Tipe yang lebih kompleks akan diimplementasikan menggunakan dan menguji sesuatu seperti:

bitWriter = new BitWriter();
writeDate(bitWriter, new Datetime(2001, 10, 4));

bitWriter2 = new BitWriter();
bitWriter2.writeInt(2001, 12)
bitWriter2.writeInt(10, 4)
bitWriter2.writeInt(4, 6)

assertEquals(bitWriter.data(), bitWriter2.data() )

Perhatikan bahwa ini menghindari segala pengetahuan tentang bagaimana bit aktual dikemas. Itu diuji oleh tes sebelumnya, dan untuk tes ini kita akan menganggapnya berfungsi.

Kemudian pada tingkat abstraksi berikutnya yang kita miliki

bitWriter = new BitWriter();
encodeObject(bitWriter, myObject);


bitWriter2 = new BitWriter();
bitWriter2.writeInt(42, 32)
writeDate(bitWriter2, new Datetime(2001, 10, 4));
writeVarString(bitWriter2, "alphanumeric");

assertEquals(bitWriter.data(), bitWriter2.data() )

jadi, sekali lagi, kami tidak mencoba untuk memasukkan pengetahuan tentang bagaimana varstrings atau tanggal atau angka sebenarnya dikodekan. Dalam pengujian ini, kami hanya tertarik pada pengkodean yang dihasilkan oleh encodeObject.

Hasil akhirnya adalah bahwa jika format untuk tanggal diubah, Anda harus memperbaiki tes yang sebenarnya melibatkan tanggal, tetapi semua kode dan tes lainnya tidak peduli dengan bagaimana tanggal sebenarnya dikodekan dan setelah Anda memperbarui kode untuk membuat pekerjaan itu, semua tes itu akan lulus dengan baik.

Winston Ewert
sumber
Saya suka ini. Saya kira inilah yang dikatakan beberapa komentator lain tentang memecahnya menjadi potongan-potongan kecil. Itu tidak sepenuhnya menghindari masalah ketika spesifikasi berubah, tetapi membuatnya lebih baik.
ConditionRacer
6

Tergantung. Jika pengkodean adalah sesuatu yang benar-benar diperbaiki, di mana setiap implementasi seharusnya membuat output yang persis sama, tidak masuk akal untuk memeriksa apa pun selain memverifikasi bahwa input contoh memetakan dengan tepat output yang diharapkan. Itu adalah ujian yang paling jelas, dan mungkin juga yang paling mudah untuk ditulis.

Jika ada ruang gerak dengan output alternatif, seperti dalam standar MPEG (misalnya ada operator tertentu yang dapat Anda terapkan pada input, tetapi Anda bebas untuk menukar upaya pengkodean versus kualitas output atau ruang penyimpanan), maka lebih baik untuk menerapkan mendefinisikan strategi decoding ke output dan memverifikasi bahwa itu sama dengan input - atau, jika encodingnya lossy, itu cukup dekat dengan input asli. Itu lebih sulit untuk diprogram, tetapi melindungi Anda dari segala peningkatan di masa depan yang mungkin dilakukan pada pembuat enkode Anda.

Kilian Foth
sumber
2
Misalkan Anda menggunakan dekoder dan membandingkan nilai. Bagaimana jika encoder dan decoder keduanya pecah di tempat yang sama? Pengkodean disandi secara salah, dan pengurai kodenya salah, tetapi objek input / output benar karena proses itu dilakukan secara salah dua kali.
ConditionRacer
@ Justin984 kemudian gunakan apa yang disebut "vektor uji", tahu pasangan input / output yang dapat Anda gunakan secara tepat untuk menguji encoder dan decoder
ratchet freak
@ scratchet freak Itu membuat saya kembali menguji dengan nilai yang diharapkan. Yang baik-baik saja, itulah yang saya lakukan saat ini, tetapi agak rapuh jadi saya ingin melihat apakah ada cara yang lebih baik.
ConditionRacer
1
Selain dengan hati-hati membaca standar dan membuat test case untuk setiap aturan, hampir tidak ada cara untuk menghindari encoder dan decoder yang mengandung bug yang sama. Sebagai contoh, mari kita asumsikan bahwa "ABC" harus diterjemahkan ke "xyz" tetapi encoder tidak mengetahuinya dan decoder Anda juga tidak akan mengerti "xyz" jika pernah menemukan itu. Testcases buatan tangan tidak mengandung urutan "ABC", karena programmer tidak menyadari aturan itu, dan juga tes dengan pengkodean / decoding string acak akan salah lulus karena baik encoder dan decoder mengabaikan masalah.
user281377
1
Untuk membantu menangkap bug yang memengaruhi decoder dan encoder yang Anda tulis sendiri karena tidak ada pengetahuan, lakukan upaya untuk mendapatkan output encoder dari vendor lain; dan juga mencoba menguji output encoder Anda pada decoder pihak ketiga. Tidak ada alternatif di sekitarnya.
rwong
3

Uji itu encode(decode(coded_value)) == coded_valuedan decode(encode(value)) == value. Anda dapat memberikan input acak ke tes jika Anda mau.

Mungkin saja encoder dan decoder rusak dengan cara yang gratis, tetapi sepertinya tidak mungkin kecuali Anda memiliki kesalahpahaman konseptual standar pengkodean. Melakukan tes hardcode dari encoder dan decoder (seperti yang sudah Anda lakukan) harus waspada terhadap hal itu.

Jika Anda memiliki akses ke implementasi lain dari ini yang diketahui berfungsi, Anda setidaknya dapat menggunakannya untuk mendapatkan keyakinan bahwa implementasi Anda baik bahkan jika menggunakannya dalam unit test tidak mungkin dilakukan.

Michael Shaw
sumber
Saya setuju bahwa kesalahan encoder / decoder komplementer tidak mungkin secara umum. Dalam kasus khusus saya, kode untuk kelas encoder / decoder dihasilkan oleh alat lain berdasarkan aturan dari database. Jadi kesalahan komplementer bisa terjadi sesekali.
ConditionRacer
Bagaimana bisa ada 'kesalahan gratis'? Itu menyiratkan bahwa ada spesifikasi eksternal untuk bentuk yang disandikan, dan karenanya dekoder eksternal.
kevin cline
Saya tidak mengerti penggunaan kata eksternal. Tetapi ada spesifikasi untuk bagaimana data dikodekan dan juga decoder. Kesalahan komplementer adalah ketika encoder dan decoder beroperasi dengan cara yang saling melengkapi tetapi menyimpang dari spesifikasi. Saya punya contoh di komentar di bawah pertanyaan asli.
ConditionRacer
Jika encoder seharusnya mengimplementasikan ROT13 tetapi secara tidak sengaja melakukan ROT14 dan decoder juga, maka decode (encode ('a')) == 'a' tetapi encoder masih rusak. Untuk hal-hal yang jauh lebih rumit dari itu, kemungkinan besar hal semacam itu tidak akan terjadi, tetapi secara teoritis itu bisa terjadi.
Michael Shaw
@MichaelShaw hanya sepotong hal sepele, encoder dan decoder untuk ROT13 adalah sama; ROT13 adalah kebalikannya sendiri. Jika Anda menerapkan ROT14 karena kesalahan, maka decode(encode(char))tidak akan sama char(itu akan sama char+2).
Tom Marthenal
2

Uji dengan persyaratan .

Jika persyaratan hanya 'meng-encode ke byte stream yang ketika di-decode menghasilkan objek yang setara.', Maka cukup uji encoder dengan decoding. Jika Anda menulis encoder dan decoder, maka cukup uji bersama-sama. Mereka tidak dapat memiliki "kesalahan pencocokan". Jika mereka bekerja bersama, maka tes akan berlalu.

Jika ada persyaratan lain untuk aliran data, maka Anda harus mengujinya dengan memeriksa data yang disandikan.

Jika format yang dikodekan sudah ditentukan sebelumnya, maka Anda harus memverifikasi data yang dikodekan terhadap hasil yang diharapkan, seperti yang Anda lakukan, atau (lebih baik) mendapatkan dekoder referensi yang dapat dipercaya untuk melakukan verifikasi. Penggunaan dekoder referensi menghilangkan kemungkinan Anda telah salah menafsirkan spesifikasi format.

kevin cline
sumber
1

Bergantung pada kerangka kerja pengujian dan paradigma yang Anda gunakan, Anda masih bisa menggunakan pola Arrange Act Assert untuk ini seperti yang Anda katakan.

[TestMethod]
public void EncodeMyObject_ForValidInputs_Encodes()
{
    //Arrange object under test
    MyEncoder encoderUnderTest = new MyEncoder();
    MyObject validObject = new MyOjbect();
    //arrange object for condition under test

    //Act
    byte[] actual = encoderUnderTest.EncodeMyObject(myObject);

    //Assert
    byte[] expected = new byte[3]{ 0x01, 0x02, 0xFF };
    Assert.IsEqual(expected, actual);
}

Anda harus mengetahui persyaratan untuk EncodeMyObject()dan dapat menggunakan pola ini untuk menguji masing-masing kriteria yang valid dan tidak valid, dengan mengatur masing-masing kriteria tersebut dan meng-hardcoding hasil yang diharapkan expected, demikian pula untuk decoder.

Karena yang diharapkan adalah hard coded, ini akan rapuh jika Anda punya perubahan besar.

Anda mungkin dapat mengotomatisasi dengan sesuatu yang didorong oleh parameter (lihat Pex ) atau jika Anda sedang melakukan DDD atau BDD melihat gerkin / mentimun .

StuperUser
sumber
1

Anda harus memutuskan apa yang penting bagi Anda.

Apakah penting bagi Anda bahwa Obyek selamat dari perjalanan pulang pergi, dan format kawat yang tepat tidak terlalu penting? Atau apakah format kawat yang tepat merupakan bagian penting dari fungsi encoder dan decoder Anda?

Jika yang pertama, maka pastikan saja benda-benda itu selamat dari perjalanan pulang pergi. Jika encoder dan decoder keduanya rusak dengan cara yang saling melengkapi, Anda tidak terlalu peduli.

Jika yang terakhir, maka Anda perlu menguji bahwa format kawat seperti yang Anda harapkan untuk input yang diberikan. Ini berarti menguji format secara langsung, atau menggunakan implementasi referensi. Tetapi setelah menguji dasar-dasarnya, Anda mungkin mendapatkan nilai dari tes bolak-balik tambahan, yang seharusnya lebih mudah ditulis dalam volume.

Bill Michell
sumber