Unit menguji komponen internal

14

Sejauh mana Anda menguji komponen internal / pribadi dari kelas / modul / paket / dll? Apakah Anda mengujinya sama sekali atau hanya menguji antarmuka ke dunia luar? Contoh internal ini adalah metode pribadi.

Sebagai contoh, bayangkan parser keturunan rekursif , yang memiliki beberapa prosedur internal (fungsi / metode) yang dipanggil dari satu prosedur pusat. Satu-satunya antarmuka ke dunia luar adalah prosedur pusat, yang mengambil string dan mengembalikan informasi yang diuraikan. Prosedur lain mem-parsing bagian-bagian berbeda dari string, dan mereka dipanggil baik dari prosedur pusat atau prosedur lainnya.

Secara alami, Anda harus menguji antarmuka eksternal dengan menyebutnya dengan string sampel dan membandingkannya dengan output yang diurai dengan tangan. Tetapi bagaimana dengan prosedur lainnya? Apakah Anda akan mengujinya secara terpisah untuk memeriksa apakah mereka mengurai substring dengan benar?

Saya dapat memikirkan beberapa argumen:

Pro :

  1. Lebih banyak pengujian selalu lebih baik, dan ini dapat membantu meningkatkan cakupan kode
  2. Beberapa komponen internal mungkin sulit untuk memberikan input spesifik (misalnya kasus tepi) dengan memberikan input ke antarmuka eksternal
  3. Pengujian lebih jelas. Jika komponen internal memiliki bug (tetap), sebuah uji kasus untuk komponen itu membuat jelas bahwa bug itu ada di komponen tertentu

Cons :

  1. Refactoring menjadi terlalu menyakitkan dan menghabiskan waktu. Untuk mengubah apa pun, Anda perlu menulis ulang tes unit, bahkan jika pengguna antarmuka eksternal tidak terpengaruh
  2. Beberapa bahasa dan kerangka kerja pengujian tidak mengizinkannya

Apa pendapat anda

imgx64
sumber
kemungkinan duplikat atau tumpang tindih yang signifikan dengan: programmers.stackexchange.com/questions/10832/…
azheglov

Jawaban:

8

Kasus: "modul" (dalam arti luas, yaitu sesuatu yang memiliki antarmuka publik dan mungkin juga beberapa bagian dalam pribadi) memiliki beberapa logika rumit / terlibat di dalamnya. Menguji hanya antarmuka modul akan menjadi semacam pengujian integrasi yang terkait dengan struktur bagian dalam modul, dan jika ada kesalahan ditemukan pengujian tersebut tidak akan melokalisasi bagian dalam / komponen yang tepat yang bertanggung jawab atas kegagalan.

Solusi: ubah sendiri komponen dalam yang rumit menjadi modul, uji unit mereka (dan ulangi langkah-langkah ini untuk mereka jika terlalu rumit) dan impor ke modul asli Anda. Sekarang Anda hanya memiliki satu set modul yang cukup sederhana untuk uji uniit (keduanya memeriksa apakah perilaku sudah benar dan memperbaiki kesalahan) dengan mudah, dan hanya itu.

catatan:

  • tidak perlu mengubah apa pun dalam pengujian "sub-modul" modul saat mengubah kontrak modul, kecuali jika "sub-modul" tidak lagi menawarkan layanan yang cukup untuk memenuhi kontrak baru / yang diubah.

  • tidak ada yang akan diumumkan secara publik yaitu kontrak modul akan disimpan dan enkapsulasi dipertahankan.

[Memperbarui]

Untuk menguji beberapa logika internal yang pintar dalam kasus-kasus ketika sulit untuk menempatkan bagian dalam objek (maksud saya anggota bukan modul / paket yang diimpor secara pribadi) ke dalam keadaan yang sesuai dengan hanya memasukkannya input melalui antarmuka publik objek:

  • hanya memiliki beberapa kode pengujian dengan teman (dalam istilah C ++) atau paket (Java) akses ke jeroan benar-benar mengatur keadaan dari dalam dan menguji perilaku yang Anda inginkan.

    • ini tidak akan merusak enkapsulasi lagi sambil memberikan akses langsung yang mudah ke bagian dalam untuk keperluan pengujian - jalankan saja pengujian sebagai "kotak hitam" dan kompilasi dalam rilis build.
mlvljr
sumber
dan tata letak daftar agak rusak; (
mlvljr
1
Jawaban yang bagus. Di .NET Anda dapat menggunakan [assembly: InternalsVisibleTo("MyUnitTestAssembly")]atribut di AssemblyInfo.csuntuk menguji internal. Rasanya seperti selingkuh.
Tidak ada yang
@ rmx Setelah sesuatu memenuhi semua kriteria yang diperlukan, maka itu bukan selingkuh meskipun memiliki kesamaan dengan cheat yang sebenarnya. Tetapi topik akses antar / modul benar-benar sedikit dibikin dalam bahasa arus utama modern.
mlvljr
2

Pendekatan kode berbasis FSM sedikit berbeda dari yang digunakan secara tradisional. Ini sangat mirip dengan apa yang dijelaskan di sini untuk pengujian perangkat keras (yang biasanya juga merupakan FSM).

Singkatnya, Anda membuat urutan input tes (atau serangkaian urutan input tes) yang seharusnya tidak hanya menghasilkan output tertentu, tetapi juga ketika menghasilkan output "buruk" tertentu memungkinkan mengidentifikasi komponen yang gagal dengan sifat kegagalan. Pendekatannya cukup scalable, semakin banyak waktu yang Anda habiskan untuk desain tes semakin baik tes itu.

Jenis pengujian ini lebih dekat dengan apa yang disebut "tes fungsional" tetapi menghilangkan kebutuhan untuk mengubah tes setiap kali Anda sedikit menyentuh suatu implementasi.

bobah
sumber
2

Yah - itu tergantung :-). Jika Anda mengikuti BDD (Behavior Driven Development) atau pendekatan ATDD (Acceptance Test Driven Development) maka pengujian antarmuka publik baik-baik saja (selama Anda mengujinya secara mendalam dengan berbagai input. Implementasi yang mendasarinya misalnya metode pribadi tidak sebenarnya penting.

Namun katakan Anda ingin bagian dari algoritma itu dieksekusi dalam kerangka waktu tertentu atau sepanjang kurva bigO tertentu (misalnya nlogn) lalu ya menguji masing-masing bagian masalah. Beberapa akan menyebutnya lebih dari pendekatan TDD / Unit Test tradisional.

Seperti semuanya, YMMV

Martijn Verburg
sumber
1

Membaginya menjadi beberapa bagian dengan makna fungsional, misalnya ParseQuotedString(), ParseExpression(), ParseStatement(), ParseFile()dan membuat mereka semua masyarakat. Seberapa mungkin sintaks Anda berubah begitu banyak sehingga ini menjadi tidak relevan?

DomQ
sumber
1
pendekatan ini dengan mudah menyebabkan enkapsulasi yang lebih lemah dan antarmuka yang lebih besar dan lebih sulit untuk digunakan / dipahami.
sara