Saya ingin menguji kelas abstrak. Tentu, saya bisa secara manual menulis tiruan yang mewarisi dari kelas.
Bisakah saya melakukan ini menggunakan kerangka kerja mengejek (saya menggunakan Mockito) alih-alih membuat kerajinan tangan tiruan saya? Bagaimana?
java
unit-testing
mocking
abstract-class
mockito
ripper234
sumber
sumber
SomeAbstract spy = spy(SomeAbstract.class);
mock(MyAbstractClass.class, withSettings().useConstructor(arg1, arg2).defaultAnswer(CALLS_REAL_METHODS))
Jawaban:
Saran berikut memungkinkan Anda menguji kelas abstrak tanpa membuat subclass "nyata" - Mock adalah subclass.
gunakan
Mockito.mock(My.class, Mockito.CALLS_REAL_METHODS)
, lalu tiru metode abstrak apa saja yang dipanggil.Contoh:
Catatan: Keindahan solusi ini adalah Anda tidak harus menerapkan metode abstrak, asalkan tidak pernah diminta.
Menurut pendapat jujur saya, ini lebih rapi daripada menggunakan mata-mata, karena mata-mata membutuhkan contoh, yang berarti Anda harus membuat subkelas kelas abstrak Anda yang tidak dapat dipakai.
sumber
Jika Anda hanya perlu menguji beberapa metode konkret tanpa menyentuh abstrak apa pun, Anda dapat menggunakan
CALLS_REAL_METHODS
(lihat jawaban Morten ), tetapi jika metode konkret yang diuji memanggil beberapa abstrak, atau metode antarmuka yang tidak diterapkan, ini tidak akan berfungsi - Mockito akan mengeluh "Tidak dapat memanggil metode nyata di antarmuka java."(Ya, ini desain yang buruk, tetapi beberapa kerangka kerja, misalnya Tapestry 4, semacam memaksakannya pada Anda.)
Solusinya adalah membalikkan pendekatan ini - gunakan perilaku mengejek biasa (yaitu, semuanya diejek / di-stub) dan gunakan
doCallRealMethod()
untuk secara eksplisit memanggil metode konkret yang sedang diuji. MisalnyaDiperbarui untuk menambahkan:
Untuk metode non-void, Anda harus menggunakan
thenCallRealMethod()
, misalnya:Kalau tidak, Mockito akan mengeluh "Kegagalan yang belum selesai terdeteksi."
sumber
Anda dapat mencapai ini dengan menggunakan mata-mata (gunakan versi terbaru Mockito 1.8+).
sumber
Kerangka kerja mengejek dirancang untuk membuatnya lebih mudah untuk mengejek dependensi kelas yang Anda uji. Saat Anda menggunakan kerangka kerja mengejek untuk mengejek kelas, sebagian besar kerangka kerja secara dinamis membuat subkelas, dan mengganti implementasi metode dengan kode untuk mendeteksi kapan metode dipanggil dan mengembalikan nilai palsu.
Saat menguji kelas abstrak, Anda ingin menjalankan metode non-abstrak dari Subject Under Test (SUT), jadi kerangka kerja mengejek bukanlah yang Anda inginkan.
Bagian dari kebingungan adalah bahwa jawaban untuk pertanyaan yang Anda tautkan dengan kerajinan tangan merupakan tiruan yang meluas dari kelas abstrak Anda. Saya tidak akan menyebut kelas seperti itu pura-pura. Mock adalah kelas yang digunakan sebagai pengganti ketergantungan, diprogram dengan harapan, dan dapat ditanyakan untuk melihat apakah harapan tersebut terpenuhi.
Sebagai gantinya, saya sarankan mendefinisikan subclass non-abstrak dari kelas abstrak Anda dalam pengujian Anda. Jika itu menghasilkan terlalu banyak kode, maka itu mungkin pertanda bahwa kelas Anda sulit diperluas.
Solusi alternatif adalah membuat test case Anda sendiri abstrak, dengan metode abstrak untuk membuat SUT (dengan kata lain, case test akan menggunakan pola desain Metode Templat ).
sumber
Coba gunakan jawaban khusus.
Sebagai contoh:
Ini akan mengembalikan tiruan untuk metode abstrak dan akan memanggil metode nyata untuk metode konkret.
sumber
Apa yang benar-benar membuat saya merasa tidak enak tentang mengejek kelas abstrak adalah fakta, bahwa konstruktor default YourAbstractClass () tidak dipanggil (missing super () di mock) atau tampaknya tidak ada cara apa pun di Mockito untuk menginisialisasi default properti tiruan (misalnya properti Daftar dengan ArrayList kosong atau LinkedList).
Kelas abstrak saya (pada dasarnya kode sumber kelas dihasilkan) TIDAK memberikan injeksi setter dependensi untuk elemen daftar, atau konstruktor di mana ia menginisialisasi elemen daftar (yang saya coba tambahkan secara manual).
Hanya atribut kelas yang menggunakan inisialisasi default: Daftar pribadi dep1 = new ArrayList; Daftar pribadi dep2 = ArrayList baru
Jadi tidak ada cara untuk mengejek kelas abstrak tanpa menggunakan implementasi objek nyata (misalnya definisi kelas dalam dalam kelas unit test, metode abstrak utama) dan memata-matai objek nyata (yang melakukan inisialisasi bidang yang tepat).
Sayang sekali bahwa hanya PowerMock akan membantu di sini lebih lanjut.
sumber
Dengan asumsi kelas tes Anda berada dalam paket yang sama (di bawah sumber root berbeda) seperti kelas Anda yang sedang diuji, Anda bisa membuat tiruan:
dan panggil metode yang ingin Anda uji seperti halnya metode lainnya.
Anda perlu memberikan harapan untuk setiap metode yang dipanggil dengan harapan pada metode konkret yang memanggil metode super - tidak yakin bagaimana Anda akan melakukannya dengan Mockito, tapi saya percaya itu mungkin dengan EasyMock.
Semua ini dilakukan adalah menciptakan contoh konkret
YouClass
dan menyelamatkan Anda dari upaya menyediakan implementasi kosong dari setiap metode abstrak.Selain itu, saya sering merasa berguna untuk mengimplementasikan kelas abstrak dalam pengujian saya, di mana ia berfungsi sebagai contoh implementasi yang saya uji melalui antarmuka publiknya, meskipun hal ini bergantung pada fungsionalitas yang disediakan oleh kelas abstrak.
sumber
Anda dapat memperluas kelas abstrak dengan kelas anonim dalam pengujian Anda. Misalnya (menggunakan Junit 4):
sumber
Mockito memungkinkan mengejek kelas abstrak dengan menggunakan
@Mock
anotasi:Kerugiannya adalah tidak dapat digunakan jika Anda membutuhkan parameter konstruktor.
sumber
Anda dapat membuat instance kelas anonim, menyuntikkan tiruan Anda dan kemudian menguji kelas itu.
Perlu diingat bahwa visibilitas harus
protected
untuk propertimyDependencyService
kelas abstrakClassUnderTest
.sumber
PowerMock
Whitebox.invokeMethod(..)
dapat berguna dalam kasus ini.sumber