Saya bertanya-tanya bagaimana cara menguji kelas abstrak, dan kelas yang memperluas kelas abstrak.
Haruskah saya menguji kelas abstrak dengan memperluasnya, mematikan metode abstrak, dan kemudian menguji semua metode konkret? Maka hanya menguji metode yang saya timpa, dan menguji metode abstrak dalam tes unit untuk objek yang memperpanjang kelas abstrak saya?
Haruskah saya memiliki test case abstrak yang dapat digunakan untuk menguji metode kelas abstrak, dan memperluas kelas ini dalam test case saya untuk objek yang memperluas kelas abstrak?
Perhatikan bahwa kelas abstrak saya memiliki beberapa metode konkret.
sumber
Ada dua cara di mana kelas dasar abstrak digunakan.
Anda mengkhususkan objek abstrak Anda, tetapi semua klien akan menggunakan kelas turunan melalui antarmuka dasarnya.
Anda menggunakan kelas dasar abstrak untuk memfaktorkan duplikasi dalam objek dalam desain Anda, dan klien menggunakan implementasi konkret melalui antarmuka mereka sendiri.!
Solusi Untuk 1 - Pola Strategi
Jika Anda memiliki situasi pertama, maka Anda sebenarnya memiliki antarmuka yang ditentukan oleh metode virtual di kelas abstrak yang diterapkan oleh kelas turunan Anda.
Anda harus mempertimbangkan membuat ini antarmuka nyata, mengubah kelas abstrak Anda menjadi konkret, dan mengambil contoh antarmuka ini dalam konstruktornya. Kelas turunan Anda kemudian menjadi implementasi dari antarmuka baru ini.
Ini berarti Anda sekarang dapat menguji kelas abstrak Anda sebelumnya menggunakan contoh tiruan dari antarmuka baru, dan setiap implementasi baru melalui antarmuka sekarang publik. Semuanya sederhana dan dapat diuji.
Solusi untuk 2
Jika Anda memiliki situasi kedua, maka kelas abstrak Anda berfungsi sebagai kelas pembantu.
Lihatlah fungsionalitas yang dikandungnya. Lihat apakah ada yang bisa didorong ke objek yang sedang dimanipulasi untuk meminimalkan duplikasi ini. Jika Anda masih memiliki sesuatu yang tersisa, lihat menjadikannya sebagai kelas pembantu yang implementasi konkret Anda ambil dalam konstruktor mereka dan menghapus kelas dasar mereka.
Ini lagi mengarah ke kelas konkret yang sederhana dan mudah diuji.
Sebagai aturan
Mendukung jaringan kompleks objek sederhana melalui jaringan objek kompleks sederhana.
Kunci kode yang dapat diuji yang dapat diperluas adalah blok bangunan kecil dan kabel independen.
Diperbarui: Bagaimana cara menangani campuran keduanya?
Dimungkinkan untuk memiliki kelas dasar yang melakukan kedua peran ini ... yaitu: ia memiliki antarmuka publik, dan telah melindungi metode pembantu. Jika ini masalahnya, maka Anda dapat memfaktorkan metode helper menjadi satu kelas (skenario2) dan mengubah pohon warisan menjadi pola strategi.
Jika Anda menemukan Anda memiliki beberapa metode yang diterapkan kelas dasar Anda secara langsung dan yang lainnya adalah virtual, maka Anda masih dapat mengubah pohon warisan menjadi pola strategi, tetapi saya juga akan menganggapnya sebagai indikator yang baik bahwa tanggung jawab tidak selaras dengan benar, dan mungkin perlu refactoring.
Pembaruan 2: Kelas Abstrak sebagai batu loncatan (2014/06/12)
Saya mengalami situasi di hari sebelumnya ketika saya menggunakan abstrak, jadi saya ingin mencari tahu mengapa.
Kami memiliki format standar untuk file konfigurasi kami. Alat khusus ini memiliki 3 file konfigurasi semua dalam format itu. Saya ingin kelas yang sangat diketik untuk setiap file pengaturan sehingga, melalui injeksi ketergantungan, kelas bisa meminta pengaturan yang diperhatikannya.
Saya menerapkan ini dengan memiliki kelas dasar abstrak yang tahu bagaimana mem-parsing format file pengaturan dan kelas turunan yang mengekspos metode yang sama, tetapi merangkum lokasi file pengaturan.
Saya bisa menulis "SettingsFileParser" yang dibungkus oleh 3 kelas, dan kemudian didelegasikan ke kelas dasar untuk mengekspos metode akses data. Saya memilih untuk tidak melakukan hal ini belum karena akan mengakibatkan 3 kelas turunan dengan lebih delegasi kode di dalamnya dari apa pun.
Namun ... saat kode ini berkembang dan konsumen dari masing-masing kelas pengaturan ini menjadi lebih jelas. Setiap pengaturan pengguna akan meminta beberapa pengaturan dan mengubahnya dalam beberapa cara (karena pengaturan adalah teks mereka dapat membungkusnya dalam objek untuk mengubahnya menjadi angka dll.). Ketika ini terjadi, saya akan mulai mengekstraksi logika ini ke dalam metode manipulasi data dan mendorongnya kembali ke kelas pengaturan yang diketik dengan kuat. Ini akan mengarah ke antarmuka tingkat yang lebih tinggi untuk setiap set pengaturan, yang pada akhirnya tidak lagi sadar berurusan dengan 'pengaturan'.
Pada titik ini, kelas pengaturan yang sangat diketik tidak lagi memerlukan metode "pengambil" yang mengekspos implementasi 'pengaturan' yang mendasarinya.
Pada saat itu saya tidak ingin lagi antarmuka publik mereka memasukkan metode accessor pengaturan; jadi saya akan mengubah kelas ini untuk merangkum kelas parser pengaturan bukannya berasal dari itu.
Kelas Abstrak karena itu: cara bagi saya untuk menghindari kode delegasi saat ini, dan penanda dalam kode untuk mengingatkan saya untuk mengubah desain nanti. Saya mungkin tidak pernah mencapainya, jadi mungkin tinggal sebentar ... hanya kode yang dapat memberi tahu.
Saya menemukan ini benar dengan aturan apa pun ... seperti "tanpa metode statis" atau "tanpa metode pribadi". Mereka mengindikasikan bau dalam kode ... dan itu bagus. Itu membuat Anda mencari abstraksi yang Anda lewatkan ... dan memungkinkan Anda terus memberikan nilai kepada pelanggan Anda dalam waktu yang bersamaan.
Saya membayangkan aturan seperti ini mendefinisikan lanskap, di mana kode yang dapat dipelihara tinggal di lembah. Saat Anda menambahkan perilaku baru, itu seperti hujan mendarat di kode Anda. Awalnya Anda meletakkannya di mana pun ia mendarat .. lalu Anda refactor untuk memungkinkan kekuatan desain yang baik untuk mendorong perilaku di sekitar sampai semuanya berakhir di lembah.
sumber
Apa yang saya lakukan untuk kelas dan antarmuka abstrak adalah sebagai berikut: Saya menulis tes, yang menggunakan objek seperti beton. Tetapi variabel tipe X (X adalah kelas abstrak) tidak diatur dalam tes. Kelas uji ini tidak ditambahkan ke suite tes, tetapi subkelasnya, yang memiliki metode pengaturan yang mengatur variabel ke implementasi konkret X. Dengan begitu saya tidak menduplikasi kode uji. Subkelas dari tes yang tidak digunakan dapat menambahkan lebih banyak metode uji jika diperlukan.
sumber
Untuk membuat tes unit khusus pada kelas abstrak, Anda harus menurunkannya untuk tujuan pengujian, test base.method () hasil dan perilaku yang diinginkan saat mewarisi.
Anda menguji suatu metode dengan memanggilnya jadi uji kelas abstrak dengan mengimplementasikannya ...
sumber
Jika kelas abstrak Anda berisi fungsionalitas konkret yang memiliki nilai bisnis, maka saya biasanya akan mengujinya secara langsung dengan membuat tes ganda yang mematikan data abstrak, atau dengan menggunakan kerangka kerja mengejek untuk melakukan ini untuk saya. Yang mana saya pilih sangat tergantung pada apakah saya perlu menulis implementasi khusus metode abstrak atau tidak.
Skenario yang paling umum di mana saya perlu melakukan ini adalah ketika saya menggunakan pola Metode Templat , seperti ketika saya sedang membangun semacam kerangka kerja yang dapat diperluas yang akan digunakan oleh pihak ke-3. Dalam hal ini, kelas abstrak adalah apa yang mendefinisikan algoritma yang ingin saya uji, sehingga lebih masuk akal untuk menguji basis abstrak daripada implementasi tertentu.
Namun, saya pikir penting bahwa tes ini harus fokus pada implementasi nyata dari logika bisnis nyata ; Anda tidak boleh menguji unit implementasi detail dari kelas abstrak karena Anda akan berakhir dengan tes rapuh.
sumber
salah satu caranya adalah dengan menulis test case abstrak yang sesuai dengan kelas abstrak Anda, kemudian menulis test case konkret yang mensubklasifikasikan case test abstrak Anda. lakukan ini untuk setiap subkelas konkret dari kelas abstrak asli Anda (yaitu hirarki kasus uji Anda mencerminkan hierarki kelas Anda). lihat Menguji antarmuka dalam buku penerima junit: http://safari.informit.com/9781932394238/ch02lev1sec6 .
juga lihat Testcase Superclass dalam pola xUnit: http://xunitpatterns.com/Testcase%20Superclass.html
sumber
Saya akan menentang tes "abstrak". Saya pikir tes adalah ide konkret dan tidak memiliki abstraksi. Jika Anda memiliki elemen umum, letakkan dalam metode pembantu atau kelas untuk digunakan semua orang.
Sedangkan untuk menguji kelas tes abstrak, pastikan Anda bertanya pada diri sendiri apa yang Anda uji. Ada beberapa pendekatan, dan Anda harus mencari tahu apa yang berhasil dalam skenario Anda. Apakah Anda mencoba menguji metode baru di subkelas Anda? Maka tes Anda hanya berinteraksi dengan metode itu. Apakah Anda menguji metode di kelas dasar Anda? Kemudian mungkin memiliki fixture terpisah hanya untuk kelas itu, dan uji setiap metode secara individual dengan tes sebanyak yang diperlukan.
sumber
Ini adalah pola yang biasanya saya ikuti ketika menyiapkan harness untuk menguji kelas abstrak:
Dan versi yang saya gunakan sedang diuji:
Jika metode abstrak dipanggil ketika saya tidak mengharapkannya, tes gagal. Ketika mengatur tes, saya dapat dengan mudah mematikan metode abstrak dengan lambdas yang melakukan penegasan, melempar pengecualian, mengembalikan nilai yang berbeda, dll.
sumber
Jika metode konkret memanggil salah satu metode abstrak yang strategi tidak akan berhasil, dan Anda ingin menguji perilaku setiap kelas anak secara terpisah. Kalau tidak, memperluasnya dan mematikan metode abstrak seperti yang telah Anda jelaskan seharusnya baik-baik saja, asalkan metode abstrak kelas beton dipisahkan dari kelas anak.
sumber
Saya kira Anda mungkin ingin menguji fungsionalitas dasar dari kelas abstrak ... Tapi Anda mungkin akan lebih baik dengan memperluas kelas tanpa mengesampingkan metode apa pun, dan membuat upaya minimum mengejek metode abstrak.
sumber
Salah satu motivasi utama untuk menggunakan kelas abstrak adalah untuk mengaktifkan polimorfisme dalam aplikasi Anda - yaitu: Anda dapat mengganti versi yang berbeda saat runtime. Bahkan, ini sangat banyak hal yang sama dengan menggunakan antarmuka kecuali kelas abstrak menyediakan beberapa pipa ledeng umum, sering disebut sebagai pola Templat .
Dari perspektif pengujian unit, ada dua hal yang perlu dipertimbangkan:
Interaksi kelas abstrak Anda dengan kelas terkait . Menggunakan kerangka pengujian tiruan sangat ideal untuk skenario ini karena ini menunjukkan bahwa kelas abstrak Anda bermain baik dengan orang lain.
Fungsi kelas turunan . Jika Anda memiliki logika khusus yang Anda tulis untuk kelas turunan Anda, Anda harus menguji kelas-kelas itu secara terpisah.
sunting: RhinoMocks adalah kerangka pengujian tiruan yang mengagumkan yang dapat menghasilkan objek tiruan saat runtime dengan secara dinamis berasal dari kelas Anda. Pendekatan ini dapat menghemat banyak jam dari kelas turunan kode tangan.
sumber
Pertama jika kelas abstrak berisi beberapa metode konkret saya pikir Anda harus melakukan ini dengan mempertimbangkan contoh ini
sumber
Jika kelas abstrak sesuai untuk implementasi Anda, uji (seperti yang disarankan di atas) kelas beton turunan. Asumsi Anda benar.
Untuk menghindari kebingungan di masa mendatang, ketahuilah bahwa kelas ujian konkret ini bukan tiruan, tetapi palsu .
Dalam istilah yang ketat, tiruan didefinisikan oleh karakteristik berikut:
sumber
Mengikuti jawaban @ patrick-desjardins, saya menerapkan abstrak dan kelas implementasinya bersama dengan
@Test
sebagai berikut:Kelas abstrak - ABC.java
Sebagai kelas abstrak tidak bisa dipakai, tetapi mereka bisa subclass , kelas beton DEF.java , adalah sebagai berikut:
@Test kelas untuk menguji metode abstrak maupun non-abstrak:
sumber