Saya ingin menyuntikkan objek mockito mock ke dalam pegas (3+) kacang untuk keperluan pengujian unit dengan JUnit. Ketergantungan kacang saya saat ini disuntikkan dengan menggunakan @Autowired
anotasi pada bidang anggota pribadi.
Saya telah mempertimbangkan untuk menggunakan ReflectionTestUtils.setField
tetapi contoh kacang yang ingin saya suntikkan sebenarnya adalah proxy dan karenanya tidak menyatakan bidang anggota pribadi dari kelas target. Saya tidak ingin membuat setter publik untuk dependensi karena saya kemudian akan memodifikasi antarmuka saya murni untuk keperluan pengujian.
Saya telah mengikuti beberapa saran yang diberikan oleh komunitas Spring tetapi mock tidak dibuat dan kabel otomatis gagal:
<bean id="dao" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="com.package.Dao" />
</bean>
Kesalahan yang saya temui saat ini adalah sebagai berikut:
...
Caused by: org...NoSuchBeanDefinitionException:
No matching bean of type [com.package.Dao] found for dependency:
expected at least 1 bean which qualifies as autowire candidate for this dependency.
Dependency annotations: {
@org...Autowired(required=true),
@org...Qualifier(value=dao)
}
at org...DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(D...y.java:901)
at org...DefaultListableBeanFactory.doResolveDependency(D...y.java:770)
Jika saya menetapkan constructor-arg
nilai ke sesuatu yang tidak valid tidak ada kesalahan terjadi ketika memulai konteks aplikasi.
sumber
Jawaban:
Cara terbaik adalah:
Pembaruan
Dalam file konteks mock ini harus didaftar sebelum bidang yang diotomasi otomatis tergantung pada yang dinyatakan.
sumber
Ini akan menyuntikkan benda yang diejek ke dalam kelas tes. Dalam hal ini akan menyuntikkan mockedObject ke dalam testObject. Ini disebutkan di atas tetapi di sini adalah kode.
sumber
mockedObject
?Mockito.spy(...)
inimockedObject
saja? Dan kemudian gunakanwhen(mockedObject.execute).thenReturn(objToReturn)
ataudoReturn(objToReturn).when(mockedObject).execute()
. Yang kedua tidak menggunakan metode nyata. Anda juga dapat memeriksaMockito.doCallRealMethod()
dokumentasiSaya punya solusi yang sangat sederhana menggunakan Spring Java Config dan Mockito:
sumber
initMocks
? Mengapa tidak hanyareturn Mockito.mock(BeanA.class)
digetBeanA
? Dengan cara ini lebih sederhana dan ada sedikit kode. Apa yang saya lewatkan?Diberikan:
Anda dapat memiliki kelas yang sedang diuji dimuat melalui autowiring, mengejek ketergantungan dengan Mockito, dan kemudian menggunakan Spring's ReflectionTestUtils untuk menyuntikkan mock ke dalam kelas yang diuji.
Harap dicatat bahwa sebelum Musim Semi 4.3.1, metode ini tidak akan berfungsi dengan layanan di belakang proxy (dijelaskan dengan
@Transactional
, atauCacheable
, misalnya). Ini telah diperbaiki oleh SPR-14050 .Untuk versi sebelumnya, solusinya adalah membuka bungkusan proksi, seperti dijelaskan di sana: Anotasi transaksional menghindari layanan yang diejek (yang secara
ReflectionTestUtils.setField
default sekarang)sumber
Jika Anda menggunakan Spring Boot 1.4, ia memiliki cara yang luar biasa untuk melakukan ini. Cukup gunakan merek baru
@SpringBootTest
di kelas Anda dan@MockBean
di lapangan dan Spring Boot akan membuat tiruan jenis ini dan itu akan menyuntikkannya ke dalam konteks (alih-alih menyuntikkan yang asli):Di sisi lain, jika Anda tidak menggunakan Spring Boot atau Anda menggunakan versi sebelumnya, Anda harus melakukan lebih banyak pekerjaan:
Buat
@Configuration
kacang yang menyuntikkan tiruan Anda ke dalam konteks Spring:Dengan menggunakan
@Primary
anotasi, Anda memberi tahu pegas bahwa kacang ini memiliki prioritas jika tidak ada kualifikasi yang ditentukan.Pastikan Anda membubuhi keterangan kelas
@Profile("useMocks")
untuk mengontrol kelas mana yang akan menggunakan tiruan dan yang mana yang akan menggunakan kacang asli.Terakhir, dalam pengujian Anda, aktifkan
userMocks
profil:Jika Anda tidak ingin menggunakan tiruan tetapi kacang asli, jangan aktifkan
useMocks
profil:sumber
web.xml
dan pengaturan AnnotationConfigWebApplicationContext. Harus menggunakan@WebAppConfiguration
alih-alih@WebIntegrationTest
dan@ContextHierarchy
dengan@ContextConfiguration
alih - alih@SpringApplicationConfiguration
.@Primary
anotasi untuk kasus saya, karena ada panggilan gagal di dalam@PostConstruct
yang saya ingin mengejek, tetapi@PostConstruct
kacang itu dibuat sebelum tiruan saya sehingga tidak menggunakan tiruan itu (sampai saya menambahkan@Primary
).Sejak 1.8.3 Mockito memiliki
@InjectMocks
- ini sangat berguna. Tes JUnit saya@RunWith
yangMockitoJUnitRunner
dan saya membangun@Mock
objek yang memenuhi semua dependensi untuk kelas yang diuji, yang semuanya disuntikkan ketika anggota swasta dijelaskan dengan@InjectMocks
.Aku
@RunWith
yangSpringJUnit4Runner
untuk tes integrasi hanya sekarang.Saya akan perhatikan bahwa sepertinya tidak bisa menyuntikkan
List<T>
dengan cara yang sama seperti Spring. Itu terlihat hanya untuk objek Mock yang memenuhiList
, dan tidak akan menyuntikkan daftar objek Mock. Solusinya bagi saya adalah dengan menggunakan@Spy
terhadap daftar instantiated secara manual, dan secara manual. Tambahkan objek tiruan ke daftar itu untuk pengujian unit. Mungkin itu disengaja, karena itu pasti memaksa saya untuk memperhatikan dengan seksama apa yang sedang diejek bersama.sumber
Pembaruan: Sekarang ada solusi yang lebih baik dan lebih bersih untuk masalah ini. Harap pertimbangkan jawaban lain terlebih dahulu.
Saya akhirnya menemukan jawaban untuk ini oleh ronen di blog-nya. Masalah yang saya alami adalah karena metode yang
Mockito.mock(Class c)
menyatakan tipe pengembalianObject
. Akibatnya Spring tidak dapat menyimpulkan jenis kacang dari jenis pengembalian metode pabrik.Solusi Ronen adalah membuat
FactoryBean
implementasi yang mengembalikan ejekan. TheFactoryBean
antarmuka memungkinkan musim semi untuk query jenis objek yang dibuat oleh kacang pabrik.Definisi kacang tiruan saya sekarang terlihat seperti:
sumber
Pada Spring 3.2, ini tidak lagi menjadi masalah. Spring sekarang mendukung Autowiring dari hasil metode pabrik generik. Lihat bagian berjudul "Metode Pabrik Generik" di posting blog ini: http://spring.io/blog/2012/11/07/spring-framework-3-2-rc1-new-testing-features/ .
Poin kuncinya adalah:
Yang berarti ini harus bekerja di luar kotak:
sumber
Kode di bawah ini berfungsi dengan autowiring - ini bukan versi terpendek tetapi berguna ketika seharusnya hanya bekerja dengan standar spring / mockito guci.
sumber
Jika Anda menggunakan pegas> = 3.0 , coba gunakan
@Configuration
anotasi Pegas untuk menentukan bagian dari konteks aplikasiJika Anda tidak ingin menggunakan @ImportResource, itu dapat dilakukan dengan cara sebaliknya:
Untuk informasi lebih lanjut, lihat referensi pegas-kerangka kerja: konfigurasi wadah berbasis Java
sumber
Mungkin bukan solusi yang sempurna, tetapi saya cenderung tidak menggunakan pegas untuk melakukan DI untuk unit test. dependensi untuk satu kacang (kelas yang diuji) biasanya tidak terlalu rumit jadi saya hanya melakukan injeksi langsung dalam kode tes.
sumber
Saya dapat melakukan hal berikut menggunakan Mockito:
sumber
Posting beberapa contoh berdasarkan pendekatan di atas
Dengan Musim Semi:
Tanpa Musim Semi:
sumber
Perbarui - jawaban baru di sini: https://stackoverflow.com/a/19454282/411229 . Jawaban ini hanya berlaku untuk orang-orang di versi Spring sebelum 3.2.
Saya sudah mencari beberapa saat untuk solusi yang lebih pasti untuk ini. Posting blog ini sepertinya mencakup semua kebutuhan saya dan tidak bergantung pada pemesanan pernyataan kacang. Semua pujian untuk Mattias Severson. http://www.jayway.com/2011/11/30/spring-integration-tests-part-i-creating-mock-objects/
Pada dasarnya, laksanakan FactoryBean
Selanjutnya perbarui konfigurasi pegas Anda dengan yang berikut:
sumber
Melihat laju perkembangan Springockito dan sejumlah masalah terbuka , saya akan sedikit khawatir untuk memperkenalkannya ke tumpukan test suite saya saat ini. Fakta bahwa rilis terakhir dilakukan sebelum rilis Spring 4 memunculkan pertanyaan seperti "Apakah mungkin untuk dengan mudah mengintegrasikannya dengan Spring 4?". Saya tidak tahu, karena saya tidak mencobanya. Saya lebih suka pendekatan Spring murni jika saya perlu mengejek kacang Spring dalam tes integrasi.
Ada opsi untuk memalsukan kacang Spring dengan fitur Musim Semi biasa. Anda perlu menggunakan
@Primary
,@Profile
dan@ActiveProfiles
anotasi untuk itu. Saya menulis posting blog tentang topik tersebut.sumber
Saya menemukan jawaban yang sama seperti teko teh untuk membuat MockFactory yang menyediakan tiruan. Saya menggunakan contoh berikut untuk membuat pabrik tiruan (karena tautan ke narkisr sudah mati): http://hg.randompage.org/java/src/407e78aa08a0/projects/bookmarking/backend/spring/src/test/java/ org / randompage / bookmarking / backend / testUtils / MocksFactory.java
Ini juga membantu mencegah Spring ingin menyelesaikan suntikan dari kacang tiruan.
sumber
^ ini berfungsi dengan baik jika dideklarasikan pertama / awal dalam file XML. Mockito 1.9.0 / Spring 3.0.5
sumber
Saya menggunakan kombinasi dari pendekatan yang digunakan dalam jawaban oleh Markus T dan implementasi pembantu sederhana
ImportBeanDefinitionRegistrar
yang mencari penjelasan kustom (@MockedBeans
) di mana orang dapat menentukan kelas mana yang harus diejek. Saya percaya bahwa pendekatan ini menghasilkan unit test singkat dengan beberapa kode boilerplate terkait dengan ejekan dihapus.Begini penampilan unit sampel dengan pendekatan itu:
Untuk mewujudkannya, Anda perlu mendefinisikan dua kelas pembantu sederhana - anotasi khusus (
@MockedBeans
) danImportBeanDefinitionRegistrar
implementasi kustom .@MockedBeans
definisi anotasi perlu dijelaskan dengan@Import(CustomImportBeanDefinitionRegistrar.class)
danImportBeanDefinitionRgistrar
kebutuhan untuk menambahkan definisi kacang tiruan untuk konfigurasi dalamregisterBeanDefinitions
metode itu.Jika Anda menyukai pendekatan ini, Anda dapat menemukan contoh implementasi di blogpost saya .
sumber
Saya mengembangkan solusi berdasarkan proposal Kresimir Nesek. Saya menambahkan penjelasan baru @EnableMockedBean untuk membuat kode sedikit lebih bersih dan modular.
Saya telah menulis posting yang menjelaskannya.
sumber
Saya akan menyarankan untuk memigrasi proyek Anda ke Spring Boot 1.4. Setelah itu Anda dapat menggunakan anotasi baru
@MockBean
untuk memalsukancom.package.Dao
sumber
Hari ini saya menemukan bahwa konteks musim semi di mana saya menyatakan sebelum kacang Mockito, gagal dimuat. Setelah menggerakkan SETELAH pengetikan, konteks aplikasi berhasil dimuat. Hati hati :)
sumber
Sebagai catatan, semua tes saya berfungsi dengan benar dengan hanya membuat fixture malas diinisialisasi, misalnya:
Saya kira alasannya adalah yang dijelaskan oleh Mattias di sini (di bagian bawah pos), bahwa solusinya adalah mengubah urutan biji dinyatakan - inisialisasi malas adalah "semacam" memiliki fixture yang diumumkan pada akhirnya.
sumber
Jika Anda menggunakan Injeksi Pengendali, pastikan variabel lokal Anda TIDAK "final"
sumber