Mockito: Suntikkan objek nyata ke dalam bidang @Autowired pribadi

191

Saya menggunakan Mockito @Mockdan @InjectMocksanotasi untuk menyuntikkan dependensi ke bidang pribadi yang dijelaskan dengan Spring @Autowired:

@RunWith(MockitoJUnitRunner.class)
public class DemoTest {
    @Mock
    private SomeService service;

    @InjectMocks
    private Demo demo;

    /* ... */
}

dan

public class Demo {

    @Autowired
    private SomeService service;

    /* ... */
}

Sekarang saya ingin juga menyuntikkan benda nyata ke @Autowiredbidang pribadi (tanpa setter). Apakah ini mungkin atau apakah mekanisme terbatas pada menyuntikkan Mock saja?

pengguna2286693
sumber
5
Biasanya ketika Anda mengejek sesuatu, itu menyiratkan bahwa Anda tidak terlalu peduli dengan objek konkret; Anda hanya benar-benar peduli tentang perilaku objek yang diejek. Mungkin Anda ingin melakukan tes integrasi? Atau, dapatkah Anda memberikan alasan mengapa Anda ingin benda-benda yang diolok-olok dan hidup bersama?
Makoto
2
Yah, saya berurusan dengan kode warisan dan akan butuh banyak waktu ketika (...). KemudianReturn (...) pernyataan untuk mengatur tiruan hanya untuk mencegah beberapa NPE dan sejenisnya. Di sisi lain, objek nyata dapat digunakan dengan aman untuk ini. Jadi akan sangat berguna untuk memiliki opsi untuk menyuntikkan benda nyata bersama dengan cemoohan. Bahkan jika ini mungkin bau kode, saya menganggapnya masuk akal dalam kasus khusus ini.
user2286693
Jangan lupa MockitoAnnotations.initMocks(this);dalam @Beforemetodenya. Saya tahu ini tidak terkait langsung dengan pertanyaan awal, tetapi untuk siapa pun yang datang kemudian, itu perlu ditambahkan untuk membuat runnable ini.
Cuga
7
@Cuga: jika Anda menggunakan pelari Mockito untuk JUnit, ( @RunWith(MockitoJUnitRunner.class)), Anda tidak memerlukan sambunganMockitoAnnotations.initMocks(this);
Clint Eastwood
1
Terima kasih - saya tidak pernah tahu itu dan selalu menetapkan keduanya
Cuga

Jawaban:

306

Gunakan @Spyanotasi

@RunWith(MockitoJUnitRunner.class)
public class DemoTest {
    @Spy
    private SomeService service = new RealServiceImpl();

    @InjectMocks
    private Demo demo;

    /* ... */
}

Mockito akan mempertimbangkan semua bidang yang memiliki @Mockatau @Spyanotasi sebagai kandidat potensial untuk disuntikkan ke dalam instance yang dianotasi dengan @InjectMocksanotasi. Dalam 'RealServiceImpl'contoh kasus di atas akan disuntikkan ke dalam 'demo'

Untuk lebih jelasnya lihat

Rumah Mockito

@Mengintai

@Mengejek

Dev Blanked
sumber
9
+1: Berfungsi untuk saya ... kecuali untuk objek String. Mockito mengeluh:Mockito cannot mock/spy following: - final classes - anonymous classes - primitive types
Adrian Pronk
Terima kasih Ini bekerja untuk saya juga :) Untuk membuat proksi menggunakan tiruan lain untuk implementasi nyata menggunakan mata
Swarit Agarwal
Terima kasih! Itulah tepatnya yang saya butuhkan!
nterry
2
Dalam kasus saya, Mockito tidak menyuntikkan Spy. Itu menyuntikkan Mock sekalipun. Bidang ini pribadi dan tanpa penyetel.
Vituel
8
BTW, tidak perlu new RealServiceImpl(), @Spy private SomeService service;membuat objek nyata menggunakan konstruktor default sebelum memata-matai itu.
parxier
20

Selain jawaban @Dev Blanked, jika Anda ingin menggunakan kacang yang sudah ada yang dibuat oleh Spring kode dapat dimodifikasi untuk:

@RunWith(MockitoJUnitRunner.class)
public class DemoTest {

    @Inject
    private ApplicationContext ctx;

    @Spy
    private SomeService service;

    @InjectMocks
    private Demo demo;

    @Before
    public void setUp(){
        service = ctx.getBean(SomeService.class);
    }

    /* ... */
}

Dengan cara ini Anda tidak perlu mengubah kode Anda (tambahkan konstruktor lain) hanya untuk membuat tes bekerja.

Yoaz Menda
sumber
2
@Ada Bisakah Anda menguraikan?
Yoaz Menda
1
Dari perpustakaan mana ini berasal, saya hanya dapat melihat InjectMocks di org.mockito
Sameer
1
@sameer import org.mockito.InjectMocks;
Yoaz Menda
Menambahkan komentar untuk membatalkan Aada. Ini berhasil untuk saya. Saya tidak bisa "hanya tidak menggunakan injeksi bidang" seperti yang disarankan oleh jawaban lain, jadi saya harus memastikan Autowiredbidang dari dependensi dibuat dengan benar.
Scrambo
1
Saya sudah mencoba ini. tetapi saya menerima pengecualian null pointer dari metode setup
Akhil Surapuram
3

Mockito bukan kerangka kerja DI dan bahkan kerangka kerja DI mendorong suntikan konstruktor melalui suntikan lapangan.
Jadi, Anda cukup mendeklarasikan konstruktor untuk menetapkan dependensi kelas yang diuji:

@Mock
private SomeService serviceMock;

private Demo demo;

/* ... */
@BeforeEach
public void beforeEach(){
   demo = new Demo(serviceMock);
}

Menggunakan Mockito spyuntuk kasus umum adalah saran yang mengerikan. Itu membuat kelas tes rapuh, tidak lurus dan rentan kesalahan: Apa yang benar-benar diejek? Apa yang benar-benar diuji?
@InjectMocksdan @Spyjuga merusak keseluruhan desain karena mendorong kelas yang membengkak dan tanggung jawab campuran di kelas.
Harap baca spy()javadoc sebelum menggunakannya secara membabi buta (penekanan bukan milikku):

Menciptakan mata-mata dari objek nyata. Mata-mata itu memanggil metode nyata kecuali jika itu tidak bisa dilakukan. Mata-mata nyata harus digunakan dengan hati-hati dan kadang-kadang , misalnya ketika berhadapan dengan kode warisan.

Seperti biasa Anda akan membaca partial mock warning: Pemrograman berorientasi objek menangani kompleksitas dengan membagi kompleksitas menjadi objek SRPy yang terpisah, spesifik. Bagaimana tiruan sebagian cocok dengan paradigma ini? Yah, itu tidak ... Partial mock biasanya berarti bahwa kompleksitas telah dipindahkan ke metode berbeda pada objek yang sama. Dalam kebanyakan kasus, ini bukan cara Anda ingin merancang aplikasi Anda.

Namun, ada beberapa kasus yang jarang terjadi ketika tiruan parsial berguna: berurusan dengan kode yang tidak dapat Anda ubah dengan mudah (antarmuka pihak ke-3, refactoring sementara kode legacy dll.) kode yang dirancang.

davidxxx
sumber
0

Di Musim Semi ada utilitas khusus yang dipanggil ReflectionTestUtilsuntuk tujuan ini. Ambil contoh spesifik dan menyuntikkan ke lapangan.


@Spy
..
@Mock
..

@InjectMock
Foo foo;

@BeforeEach
void _before(){
   ReflectionTestUtils.setField(foo,"bar", new BarImpl());// `bar` is private field
}
takacsot
sumber
-1

Saya tahu ini adalah pertanyaan lama, tetapi kami dihadapkan dengan masalah yang sama ketika mencoba menyuntikkan Strings. Jadi kami menemukan ekstensi JUnit5 / Mockito yang sesuai dengan yang Anda inginkan: https://github.com/exabrial/mockito-object-injection

EDIT:

@InjectionMap
 private Map<String, Object> injectionMap = new HashMap<>();

 @BeforeEach
 public void beforeEach() throws Exception {
  injectionMap.put("securityEnabled", Boolean.TRUE);
 }

 @AfterEach
 public void afterEach() throws Exception {
  injectionMap.clear();
 }
Jonathan S. Fisher
sumber