Saya menulis parser dan sebagai bagian dari itu, saya memiliki Expander
kelas yang "memperluas" pernyataan kompleks tunggal menjadi beberapa pernyataan sederhana. Misalnya, itu akan memperluas ini:
x = 2 + 3 * a
ke:
tmp1 = 3 * a
x = 2 + tmp1
Sekarang saya sedang berpikir tentang bagaimana menguji kelas ini, khususnya bagaimana mengatur tes. Saya bisa secara manual membuat pohon sintaks input:
var input = new AssignStatement(
new Variable("x"),
new BinaryExpression(
new Constant(2),
BinaryOperator.Plus,
new BinaryExpression(new Constant(3), BinaryOperator.Multiply, new Variable("a"))));
Atau saya bisa menulisnya sebagai string dan menguraikannya:
var input = new Parser().ParseStatement("x = 2 + 3 * a");
Opsi kedua jauh lebih sederhana, lebih pendek dan mudah dibaca. Tapi itu juga memperkenalkan denpendensi Parser
, yang berarti bug di Parser
bisa gagal dalam tes ini. Jadi, tes akan berhenti menjadi unit test Expander
, dan saya kira secara teknis menjadi tes integrasiParser
dan Expander
.
Pertanyaan saya adalah: apakah boleh mengandalkan sebagian besar (atau sepenuhnya) pada tes integrasi semacam ini untuk menguji Expander
kelas ini ?
Parser
dapat gagal dalam beberapa pengujian lain tidak menjadi masalah jika Anda terbiasa melakukan hanya pada nol kegagalan, sebaliknya itu berarti Anda memiliki lebih banyak cakupanParser
. Apa yang saya lebih suka khawatirkan adalah bug dalamParser
dapat membuat tes ini berhasil padahal seharusnya gagal . Tes unit ada untuk menemukan bug, setelah semua - tes rusak ketika tidak tetapi harus dilakukan.Jawaban:
Anda akan menemukan diri Anda menulis lebih banyak tes, perilaku yang jauh lebih rumit, menarik, dan berguna, jika Anda dapat melakukannya dengan sederhana. Jadi opsi itu melibatkan
cukup valid. Itu tergantung pada komponen lain. Tetapi semuanya tergantung pada lusinan komponen lainnya. Jika Anda mengejek sesuatu dalam satu inci dari kehidupannya, Anda mungkin tergantung pada banyak fitur mengejek dan alat uji.
Pengembang terkadang terlalu fokus pada kemurnian tes unit mereka , atau mengembangkan tes unit dan tes unit saja , tanpa modul, integrasi, stres atau jenis tes lainnya. Semua formulir itu valid dan bermanfaat, dan semuanya merupakan tanggung jawab pengembang yang tepat - bukan hanya T / A atau personel operasi yang berada di jalur pipa.
Salah satu pendekatan yang saya gunakan adalah memulai dengan level run yang lebih tinggi ini, kemudian menggunakan data yang dihasilkan dari mereka untuk membangun ekspresi bentuk-panjang, terendah-umum-denominator dari tes. Misalnya ketika Anda membuang struktur data dari yang
input
dihasilkan di atas, maka Anda dapat dengan mudah membangun:jenis tes yang menguji pada tingkat yang paling rendah. Dengan begitu Anda mendapatkan campuran yang bagus: Sejumlah tes primitif yang paling dasar (tes unit murni), tetapi belum menghabiskan satu minggu menulis tes pada tingkat primitif itu. Itu memberi Anda sumber daya waktu yang dibutuhkan untuk menulis lebih banyak, lebih sedikit pengujian atom menggunakan
Parser
sebagai penolong. Hasil akhir: Lebih banyak tes, lebih banyak cakupan, lebih banyak sudut dan kasus menarik lainnya, kode lebih baik dan jaminan kualitas yang lebih tinggi.sumber
Tentu saja tidak apa-apa!
Anda selalu memerlukan uji fungsional / integrasi yang menggunakan jalur kode lengkap. Dan jalur kode lengkap dalam hal ini berarti termasuk evaluasi kode yang dihasilkan. Itu adalah Anda menguji bahwa parsing
x = 2 + 3 * a
menghasilkan kode yang jika dijalankan dengana = 5
akan diaturx
ke17
dan jika dijalankan dengana = -2
akan ditetapkanx
ke-4
.Di bawah ini, Anda harus melakukan tes unit untuk bit yang lebih kecil asalkan itu benar-benar membantu debug kode . Tes berbutir halus Anda akan memiliki, semakin tinggi probabilitas bahwa setiap perubahan kode perlu mengubah tes juga, karena perubahan antarmuka internal. Tes semacam itu hanya memiliki sedikit nilai jangka panjang dan menambah pekerjaan pemeliharaan. Jadi ada titik pengembalian yang berkurang dan Anda harus berhenti sebelum itu.
sumber
Tes unit memungkinkan Anda untuk menunjukkan item tertentu yang rusak dan di mana dalam kode mereka pecah. Jadi mereka bagus untuk pengujian berbutir halus. Tes unit yang baik akan membantu mengurangi waktu debugging.
Namun, dari pengalaman saya, tes unit jarang cukup baik untuk benar-benar memverifikasi operasi yang benar. Jadi tes integrasi juga membantu memverifikasi rantai atau urutan operasi. Tes integrasi membuat Anda menjadi bagian dari jalan melalui pengujian fungsional. Seperti yang Anda tunjukkan, karena kerumitan tes integrasi, lebih sulit untuk menemukan titik spesifik dalam kode di mana tes pecah. Ini juga memiliki kerapuhan yang lebih dalam bahwa kegagalan di mana saja dalam rantai akan menyebabkan tes gagal. Namun Anda masih memiliki rantai itu dalam kode produksi, jadi menguji rantai yang sebenarnya masih membantu.
Idealnya Anda memiliki keduanya, tetapi bagaimanapun juga, umumnya memiliki tes otomatis lebih baik daripada tidak memiliki tes.
sumber
Lakukan banyak tes pada parser dan ketika parser melewati tes, simpan output tersebut ke file untuk mengejek parser dan menguji komponen lainnya.
sumber