Menyuntikkan Mockito mengolok-olok kacang Spring

284

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 @Autowiredanotasi pada bidang anggota pribadi.

Saya telah mempertimbangkan untuk menggunakan ReflectionTestUtils.setFieldtetapi 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-argnilai ke sesuatu yang tidak valid tidak ada kesalahan terjadi ketika memulai konteks aplikasi.

teh celup
sumber
4
Silakan lihat di makhluk kecil kecil ini: bitbucket.org/kubek2k/springockito/wiki/Home
kubek2k
Ini adalah pendekatan yang sangat bersih - saya suka itu!
teko
2
Anda memiliki saya di Springockito-anotasi.
yihtserns
2
Bagi mereka yang menggunakan musim semi 4. *, pada Januari 2015 ini tampaknya tidak berfungsi dengan versi musim semi mockito terbaru dan proyek tampaknya tidak aktif.
Murali

Jawaban:

130

Cara terbaik adalah:

<bean id="dao" class="org.mockito.Mockito" factory-method="mock"> 
    <constructor-arg value="com.package.Dao" /> 
</bean> 

Pembaruan
Dalam file konteks mock ini harus didaftar sebelum bidang yang diotomasi otomatis tergantung pada yang dinyatakan.

amra
sumber
Saya mendapatkan kesalahan: "Kesalahan membuat kacang dengan nama 'mockito': definisi kacang abstrak"
tttppp
4
@ amra: spring dosn't menyimpulkan jenis objek yang dikembalikan dalam kasus ini ... stackoverflow.com/q/6976421/306488
lisak
7
Tidak tahu mengapa jawaban ini terangkat begitu tinggi, kacang yang dihasilkan tidak dapat autowired karena memiliki jenis yang salah.
azerole
4
Ini dapat diautowiri jika terdaftar pertama kali dalam file konteks (sebelum bidang autowired yang bergantung padanya dideklarasikan).
Ryan Walls
3
Pada musim semi 3.2, urutan biji tidak lagi penting. Lihat bagian berjudul "Metode Pabrik Generik" di posting blog ini: spring.io/blog/2012/11/07/…
Ryan Walls
110
@InjectMocks
private MyTestObject testObject;

@Mock
private MyDependentObject mockedObject;

@Before
public void setup() {
        MockitoAnnotations.initMocks(this);
}

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.

Greg Beauchamp
sumber
1
Bagaimana saya mematikan metode tertentu mockedObject?
Jim Holden
@Teinacher when (mockedObject.execute) .thenReturn (objToReturn); Anda dapat menempatkannya di metode sebelum atau di dalam metode pengujian Anda.
chaostheory
40
FYI: Pendekatan ini tidak akan berfungsi, jika saya ingin Autowiring parsial dan mengejek parsial di MyTestObject.
raksja
9
Saya tidak tahu mengapa ini tidak dipilih lebih tinggi. Jika saya melihat lagi jawaban yang berisi XML, saya akan melemparkan.
MarkOfHall
3
Mengapa Anda tidak menggunakan Mockito.spy(...)ini mockedObjectsaja? Dan kemudian gunakan when(mockedObject.execute).thenReturn(objToReturn)atau doReturn(objToReturn).when(mockedObject).execute(). Yang kedua tidak menggunakan metode nyata. Anda juga dapat memeriksa Mockito.doCallRealMethod()dokumentasi
Tomasz Przybylski
63

Saya punya solusi yang sangat sederhana menggunakan Spring Java Config dan Mockito:

@Configuration
public class TestConfig {

    @Mock BeanA beanA;
    @Mock BeanB beanB;

    public TestConfig() {
        MockitoAnnotations.initMocks(this); //This is a key
    }

    //You basically generate getters and add @Bean annotation everywhere
    @Bean
    public BeanA getBeanA() {
        return beanA;
    }

    @Bean
    public BeanB getBeanB() {
        return beanB;
    }
}
Piotr Gwiazda
sumber
4
Untuk beberapa alasan dengan pendekatan ini, pegas mencoba untuk membuat kacang yang sebenarnya (bukan tiruan) dan tersedak ... Apa yang saya lakukan salah?
Daniel Gruszczyk
1
Saya memiliki masalah yang sama
Korobko Alex
3
Bukan musim semi melainkan mockito mencoba membuat instance kacang yang sebenarnya jika Anda mengejek kelas. Jika Anda memiliki kacang yang harus diejek dalam tes, mereka harus merupakan implementasi dari antarmuka, dan disuntikkan melalui antarmuka itu. Jika Anda kemudian mengejek antarmuka (bukan kelas), mockito tidak akan mencoba untuk membuat instance kelas itu.
Daniel Gruszczyk
7
Apa intinya? Mengapa menambahkan bidang dan konstruktor beranotasi dengan initMocks? Mengapa tidak hanya return Mockito.mock(BeanA.class)di getBeanA? Dengan cara ini lebih sederhana dan ada sedikit kode. Apa yang saya lewatkan?
Oleg
1
@Oleg sepertinya Anda punya solusi sendiri, yang mungkin harus Anda posting sebagai jawaban, sehingga komunitas dapat memberikan suara untuknya.
Dawood ibn Kareem
48

Diberikan:

@Service
public class MyService {
    @Autowired
    private MyDAO myDAO;

    // etc
}

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.

@ContextConfiguration(classes = { MvcConfiguration.class })
@RunWith(SpringJUnit4ClassRunner.class)
public class MyServiceTest {
    @Autowired
    private MyService myService;

    private MyDAO myDAOMock;

    @Before
    public void before() {
        myDAOMock = Mockito.mock(MyDAO.class);
        ReflectionTestUtils.setField(myService, "myDAO", myDAOMock);
    }

    // etc
}

Harap dicatat bahwa sebelum Musim Semi 4.3.1, metode ini tidak akan berfungsi dengan layanan di belakang proxy (dijelaskan dengan @Transactional, atau Cacheable, 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.setFielddefault sekarang)

Paul Croarkin
sumber
Double @RunWith (SpringJUnit4ClassRunner.class) dan saya menggunakan anotasi yang berbeda untuk kelas uji (pelari yang sama) tetapi pendekatan ini bekerja untuk saya, terima kasih.
user1317422
1
Saya sangat terinspirasi oleh "Harap dicatat bahwa sebelum Spring 4.3.1, metode ini tidak akan berfungsi dengan layanan di belakang proxy (misalnya, dengan @Transactional, atau Cacheable, misalnya). Ini telah diperbaiki oleh SPR-14050". Saya hanya mengalami masalah ini persis dan tidak mendapatkan petunjuk sampai menemukan kata-kata ini. TERIMA KASIH BANYAK!
snowfox
1
Solusi ini menangani ketika Anda telah menghubungkan seluruh konteks aplikasi dan, untuk tujuan pengujian, ingin menyuntikkan tiruan dalam kacang acak dalam konteks Anda. Saya menggunakan jawaban ini untuk mengejek kacang klien pura-pura untuk menghindari panggilan REST ke modul lain dalam tes Modul. Saya hanya mendapatkan anotasi InjectMock untuk bekerja ketika Anda menyuntikkan ejekan dalam kacang yang akan Anda uji, bukan kacang yang dibuat oleh Spring Application Configuration.
Andreas Lundgren
1
Hampir sepanjang hari berusaha untuk membuat @MockBean bekerja tanpa mengatur ulang konteksnya dan kemudian saya menemukan permata ini. Persis apa yang saya butuhkan, tepuk tangan.
Matt R
Berhasil, meskipun berhati-hatilah bahwa bidang yang diganti mungkin tidak disetel ulang karena caching dan beberapa tes yang tidak terkait dapat rusak. Misalnya dalam pengujian saya, saya mengganti penyandi kata sandi dengan sandi tiruan dan beberapa tes lainnya gagal karena kegagalan otorisasi.
alextsil
36

Jika Anda menggunakan Spring Boot 1.4, ia memiliki cara yang luar biasa untuk melakukan ini. Cukup gunakan merek baru @SpringBootTestdi kelas Anda dan @MockBeandi lapangan dan Spring Boot akan membuat tiruan jenis ini dan itu akan menyuntikkannya ke dalam konteks (alih-alih menyuntikkan yang asli):

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTests {

    @MockBean
    private RemoteService remoteService;

    @Autowired
    private Reverser reverser;

    @Test
    public void exampleTest() {
        // RemoteService has been injected into the reverser bean
        given(this.remoteService.someCall()).willReturn("mock");
        String reverse = reverser.reverseSomeCall();
        assertThat(reverse).isEqualTo("kcom");
    }

}

Di sisi lain, jika Anda tidak menggunakan Spring Boot atau Anda menggunakan versi sebelumnya, Anda harus melakukan lebih banyak pekerjaan:

Buat @Configurationkacang yang menyuntikkan tiruan Anda ke dalam konteks Spring:

@Configuration
@Profile("useMocks")
public class MockConfigurer {

    @Bean
    @Primary
    public MyBean myBeanSpy() {
        return mock(MyBean.class);
    }
}

Dengan menggunakan @Primaryanotasi, 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 userMocksprofil:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {Application.class})
@WebIntegrationTest
@ActiveProfiles(profiles={"useMocks"})
public class YourIntegrationTestIT {

    @Inject
    private MyBean myBean; //It will be the mock!


    @Test
    public void test() {
        ....
    }
}

Jika Anda tidak ingin menggunakan tiruan tetapi kacang asli, jangan aktifkan useMocksprofil:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {Application.class})
@WebIntegrationTest
public class AnotherIntegrationTestIT {

    @Inject
    private MyBean myBean; //It will be the real implementation!


    @Test
    public void test() {
        ....
    }
}
jfcorugedo
sumber
5
Jawaban ini harus menuju ke atas - Dukungan @MockBean di boot musim semi juga dapat digunakan tanpa boot-musim semi. Anda dapat menggunakannya hanya dalam unit test sehingga berfungsi untuk semua aplikasi pegas!
bedrin
2
@Profil penjelasan Anda dapat mengatur juga pada metode definisi kacang, untuk menghindari membuat kelas konfigurasi yang terpisah
marcin
Jawaban bagus! Saya membuat beberapa perubahan untuk membuatnya berfungsi dengan sekolah lama saya web.xmldan pengaturan AnnotationConfigWebApplicationContext. Harus menggunakan @WebAppConfigurationalih-alih @WebIntegrationTestdan @ContextHierarchydengan @ContextConfigurationalih - alih @SpringApplicationConfiguration.
UTF_or_Death
Saya harus menambahkan @Primaryanotasi untuk kasus saya, karena ada panggilan gagal di dalam @PostConstructyang saya ingin mengejek, tetapi @PostConstructkacang itu dibuat sebelum tiruan saya sehingga tidak menggunakan tiruan itu (sampai saya menambahkan @Primary).
helleye
19

Sejak 1.8.3 Mockito memiliki @InjectMocks- ini sangat berguna. Tes JUnit saya @RunWithyang MockitoJUnitRunnerdan saya membangun @Mockobjek yang memenuhi semua dependensi untuk kelas yang diuji, yang semuanya disuntikkan ketika anggota swasta dijelaskan dengan @InjectMocks.

Aku @RunWithyang SpringJUnit4Runneruntuk 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 memenuhi List, dan tidak akan menyuntikkan daftar objek Mock. Solusinya bagi saya adalah dengan menggunakan @Spyterhadap 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.

Doug Moscrop
sumber
Ya ini cara terbaik. Springockito tidak benar-benar menyuntikkan mock untuk alasan apa pun dalam kasus saya.
chaostheory
13

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 pengembalian Object. Akibatnya Spring tidak dapat menyimpulkan jenis kacang dari jenis pengembalian metode pabrik.

Solusi Ronen adalah membuat FactoryBeanimplementasi yang mengembalikan ejekan. The FactoryBeanantarmuka memungkinkan musim semi untuk query jenis objek yang dibuat oleh kacang pabrik.

Definisi kacang tiruan saya sekarang terlihat seperti:

<bean id="mockDaoFactory" name="dao" class="com.package.test.MocksFactory">
    <property name="type" value="com.package.Dao" />
</bean>
teh celup
sumber
1
Tautan yang diperbarui ke Solusi Ronen: narkisr.com/blog/2008/2647754885089732945
Jeff Martin
Saya tidak mengerti bahwa, metode pabrik memiliki objek tipe kembali ... Tetapi solusi amra memiliki tipe pengembalian generik sehingga Spring seharusnya mengenalinya ... Tetapi solusi amra tidak bekerja untuk saya
lisak
Baik solusi ini, musim semi tidak menyimpulkan jenis kacang yang dikembalikan dari pabrikBean karenanya Tidak ada kacang pencocokan jenis [com.package.Ltd] ...
lisak
1
Mesin jalan mundur: web.archive.org/web/20120806223839/http://…
Daniel Kaplan
Tautan ini sebenarnya masih berfungsi: javadevelopmentforthemasses.blogspot.com/2008/07/... Cukup nonaktifkan pengalihan tautan di browser Anda dan Anda akan melihatnya, alih-alih dipaksa melihat 404 di blog barunya.
kira
12

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:

Di Spring 3.2, tipe pengembalian generik untuk metode pabrik sekarang disimpulkan dengan benar, dan autowiring menurut jenis untuk mengejek harus bekerja seperti yang diharapkan. Akibatnya, solusi khusus seperti MockitoFactoryBean, EasyMockFactoryBean, atau Springockito kemungkinan tidak lagi diperlukan.

Yang berarti ini harus bekerja di luar kotak:

<bean id="dao" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="com.package.Dao" />
</bean>
Ryan Walls
sumber
9

Kode di bawah ini berfungsi dengan autowiring - ini bukan versi terpendek tetapi berguna ketika seharusnya hanya bekerja dengan standar spring / mockito guci.

<bean id="dao" class="org.springframework.aop.framework.ProxyFactoryBean">
   <property name="target"> <bean class="org.mockito.Mockito" factory-method="mock"> <constructor-arg value="com.package.Dao" /> </bean> </property>
   <property name="proxyInterfaces"> <value>com.package.Dao</value> </property>
</bean> 
Kamil
sumber
Bekerja untukku. Saya harus membuka bungkusan proksi dalam pengujian saya untuk memverifikasinya seperti yang dijelaskan di sini: forum.spring.io/forum/spring-projects/aop/…
Holgzn
9

Jika Anda menggunakan pegas> = 3.0 , coba gunakan @Configurationanotasi Pegas untuk menentukan bagian dari konteks aplikasi

@Configuration
@ImportResource("com/blah/blurk/rest-of-config.xml")
public class DaoTestConfiguration {

    @Bean
    public ApplicationService applicationService() {
        return mock(ApplicationService.class);
    }

}

Jika Anda tidak ingin menggunakan @ImportResource, itu dapat dilakukan dengan cara sebaliknya:

<beans>
    <!-- rest of your config -->

    <!-- the container recognize this as a Configuration and adds it's beans 
         to the container -->
    <bean class="com.package.DaoTestConfiguration"/>
</beans>

Untuk informasi lebih lanjut, lihat referensi pegas-kerangka kerja: konfigurasi wadah berbasis Java

Markus T
sumber
Bagus Saya menggunakan ini ketika tes yang saya uji adalah @Autowired dalam kasus tes yang sebenarnya.
enkor
8

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.

Angelo Genovese
sumber
3
Saya mengerti pendekatan Anda. Namun, saya menemukan diri saya dalam situasi ini pada basis kode legacy yang besar yang tidak mudah untuk ini - belum.
Teko
1
Saya telah menemukan kombo Mockito / Spring sangat berguna ketika saya perlu menguji kode yang sangat bergantung pada aspek Spring / AOP (misalnya, ketika menguji aturan keamanan pegas). Meskipun seseorang dibenarkan dalam mengklaim bahwa tes tersebut harus menjadi tes integrasi.
Lars Tackmann
@ Lars - setuju - hal yang sama dapat dikatakan tentang tes yang saya hadapi.
teabot
7

Saya dapat melakukan hal berikut menggunakan Mockito:

<bean id="stateMachine" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="com.abcd.StateMachine"/>
</bean>
Alexander
sumber
1
Terima kasih atas jawabannya @Alexander. Bolehkah saya bertanya: apakah pemasangannya benar? Jika demikian, versi Spring / Mockito mana yang Anda gunakan?
teko
6

Posting beberapa contoh berdasarkan pendekatan di atas

Dengan Musim Semi:

@ContextConfiguration(locations = { "classpath:context.xml" })
@RunWith(SpringJUnit4ClassRunner.class)
public class TestServiceTest {
    @InjectMocks
    private TestService testService;
    @Mock
    private TestService2 testService2;
}

Tanpa Musim Semi:

@RunWith(MockitoJUnitRunner.class)
public class TestServiceTest {
    @InjectMocks
    private TestService testService = new TestServiceImpl();
    @Mock
    private TestService2 testService2;
}
Basu
sumber
2

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

package com.jayway.springmock;

import org.mockito.Mockito;
import org.springframework.beans.factory.FactoryBean;

/**
 * A {@link FactoryBean} for creating mocked beans based on Mockito so that they 
 * can be {@link @Autowired} into Spring test configurations.
 *
 * @author Mattias Severson, Jayway
 *
 * @see FactoryBean
 * @see org.mockito.Mockito
 */
public class MockitoFactoryBean<T> implements FactoryBean<T> {

    private Class<T> classToBeMocked;

    /**
     * Creates a Mockito mock instance of the provided class.
     * @param classToBeMocked The class to be mocked.
     */
    public MockitoFactoryBean(Class<T> classToBeMocked) {
        this.classToBeMocked = classToBeMocked;
    }

    @Override
    public T getObject() throws Exception {
        return Mockito.mock(classToBeMocked);
    }

    @Override
    public Class<?> getObjectType() {
        return classToBeMocked;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}

Selanjutnya perbarui konfigurasi pegas Anda dengan yang berikut:

<beans...>
    <context:component-scan base-package="com.jayway.example"/>

    <bean id="someDependencyMock" class="com.jayway.springmock.MockitoFactoryBean">
        <constructor-arg name="classToBeMocked" value="com.jayway.example.SomeDependency" />
    </bean>
</beans>
Ryan Walls
sumber
2

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, @Profiledan @ActiveProfilesanotasi untuk itu. Saya menulis posting blog tentang topik tersebut.

luboskrnac
sumber
1

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

<bean id="someFacade" class="nl.package.test.MockFactory">
    <property name="type" value="nl.package.someFacade"/>
</bean>

Ini juga membantu mencegah Spring ingin menyelesaikan suntikan dari kacang tiruan.

Renso Lohuis
sumber
1
<bean id="mockDaoFactory" name="dao" class="com.package.test.MocksFactory">
    <property name="type" value="com.package.Dao" />
</bean>

^ ini berfungsi dengan baik jika dideklarasikan pertama / awal dalam file XML. Mockito 1.9.0 / Spring 3.0.5

almondandapricot
sumber
1

Saya menggunakan kombinasi dari pendekatan yang digunakan dalam jawaban oleh Markus T dan implementasi pembantu sederhana ImportBeanDefinitionRegistraryang 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:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader=AnnotationConfigContextLoader.class)
public class ExampleServiceIntegrationTest {

    //our service under test, with mocked dependencies injected
    @Autowired
    ExampleService exampleService;

    //we can autowire mocked beans if we need to used them in tests
    @Autowired
    DependencyBeanA dependencyBeanA;

    @Test
    public void testSomeMethod() {
        ...
        exampleService.someMethod();
        ...
        verify(dependencyBeanA, times(1)).someDependencyMethod();
    }

    /**
     * Inner class configuration object for this test. Spring will read it thanks to
     * @ContextConfiguration(loader=AnnotationConfigContextLoader.class) annotation on the test class.
     */
    @Configuration
    @Import(TestAppConfig.class) //TestAppConfig may contain some common integration testing configuration
    @MockedBeans({DependencyBeanA.class, DependencyBeanB.class, AnotherDependency.class}) //Beans to be mocked
    static class ContextConfiguration {

        @Bean
        public ExampleService exampleService() {
            return new ExampleService(); //our service under test
        }
    }
}

Untuk mewujudkannya, Anda perlu mendefinisikan dua kelas pembantu sederhana - anotasi khusus ( @MockedBeans) dan ImportBeanDefinitionRegistrarimplementasi kustom . @MockedBeansdefinisi anotasi perlu dijelaskan dengan @Import(CustomImportBeanDefinitionRegistrar.class)dan ImportBeanDefinitionRgistrarkebutuhan untuk menambahkan definisi kacang tiruan untuk konfigurasi dalam registerBeanDefinitionsmetode itu.

Jika Anda menyukai pendekatan ini, Anda dapat menemukan contoh implementasi di blogpost saya .

Krešimir Nesek
sumber
1

Saya mengembangkan solusi berdasarkan proposal Kresimir Nesek. Saya menambahkan penjelasan baru @EnableMockedBean untuk membuat kode sedikit lebih bersih dan modular.

@EnableMockedBean
@SpringBootApplication
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes=MockedBeanTest.class)
public class MockedBeanTest {

    @MockedBean
    private HelloWorldService helloWorldService;

    @Autowired
    private MiddleComponent middleComponent;

    @Test
    public void helloWorldIsCalledOnlyOnce() {

        middleComponent.getHelloMessage();

        // THEN HelloWorldService is called only once
        verify(helloWorldService, times(1)).getHelloMessage();
    }

}

Saya telah menulis posting yang menjelaskannya.

Alfredo Diaz
sumber
1

Saya akan menyarankan untuk memigrasi proyek Anda ke Spring Boot 1.4. Setelah itu Anda dapat menggunakan anotasi baru @MockBeanuntuk memalsukancom.package.Dao

luboskrnac
sumber
0

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 :)

Daniele Dellafiore
sumber
1
Ada yang hilang. 8-) Anda pindah apa setelah mengejek?
Hans-Peter Störr
0

Sebagai catatan, semua tes saya berfungsi dengan benar dengan hanya membuat fixture malas diinisialisasi, misalnya:

<bean id="fixture"
      class="it.tidalwave.northernwind.rca.embeddedserver.impl.DefaultEmbeddedServer"
      lazy-init="true" /> <!-- To solve Mockito + Spring problems -->

<bean class="it.tidalwave.messagebus.aspect.spring.MessageBusAdapterFactory" />

<bean id="applicationMessageBus"
      class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="it.tidalwave.messagebus.MessageBus" />
</bean>

<bean class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="javax.servlet.ServletContext" />
</bean>

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.

Fabrizio Giudici
sumber
-1

Jika Anda menggunakan Injeksi Pengendali, pastikan variabel lokal Anda TIDAK "final"

RS
sumber