Mockito: Mencoba memata-matai metode memanggil metode asli

350

Saya menggunakan Mockito 1.9.0. Saya ingin mengejek perilaku untuk metode tunggal kelas dalam tes JUnit, jadi saya punya

final MyClass myClassSpy = Mockito.spy(myInstance);
Mockito.when(myClassSpy.method1()).thenReturn(myResults);

Masalahnya adalah, pada baris kedua, myClassSpy.method1()sebenarnya dipanggil, menghasilkan pengecualian. Satu-satunya alasan saya menggunakan ejekan adalah agar nanti, kapan pun myClassSpy.method1()dipanggil, metode sebenarnya tidak akan dipanggil dan myResultsobjek akan dikembalikan.

MyClassadalah antarmuka dan myInstancemerupakan implementasi dari itu, jika itu penting.

Apa yang harus saya lakukan untuk memperbaiki perilaku memata-matai ini?

Dave
sumber

Jawaban:

609

Izinkan saya mengutip dokumentasi resmi :

Gotcha penting pada memata-matai benda nyata!

Terkadang tidak mungkin untuk digunakan saat (Objek) untuk mematikan mata-mata. Contoh:

List list = new LinkedList();
List spy = spy(list);

// Impossible: real method is called so spy.get(0) throws IndexOutOfBoundsException (the list is yet empty)
when(spy.get(0)).thenReturn("foo");

// You have to use doReturn() for stubbing
doReturn("foo").when(spy).get(0);

Dalam kasus Anda itu berlangsung seperti:

doReturn(resulstIWant).when(myClassSpy).method1();
Tomasz Nurkiewicz
sumber
27
Bagaimana jika saya menggunakan metode ini dan yang asli saya MASIH dipanggil? Mungkinkah ada masalah dengan parameter yang saya lewati? Inilah keseluruhan tes: metode pastebin.com/ZieY790P send dipanggil
Evgeni Petrov
26
@ EvgeniPetrov jika metode asli Anda masih dipanggil mungkin karena metode asli Anda final. Mockito tidak mengejek metode final, dan tidak bisa memperingatkan Anda tentang mengejek metode final.
MarcG
1
apakah ini juga mungkin untuk doThrow ()?
Gobliins
1
ya, sayangnya metode statis tidak dapat digerakkan dan "tidak dapat dimata-matai". Apa yang saya lakukan untuk berurusan dengan metode statis adalah membungkus metode di sekitar panggilan statis dan menggunakan doNothing atau doReturn pada metode itu. Dengan objek lajang atau skala, saya memindahkan daging logika ke kelas abstrak dan itu memberi saya kemampuan untuk memiliki kelas uji alternatif objek yang dapat saya buat sebagai mata-mata.
Andrew Norman
24
Dan bagaimana jika metode TIDAK final dan TIDAK statis masih dipanggil?
X-HuMan
27

Kasus saya berbeda dari jawaban yang diterima. Saya mencoba untuk mengejek metode paket-pribadi untuk contoh yang tidak hidup dalam paket itu

package common;

public class Animal {
  void packageProtected();
}

package instances;

class Dog extends Animal { }

dan kelas tes

package common;

public abstract class AnimalTest<T extends Animal> {
  @Before
  setup(){
    doNothing().when(getInstance()).packageProtected();
  }

  abstract T getInstance();
}

package instances;

class DogTest extends AnimalTest<Dog> {
  Dog getInstance(){
    return spy(new Dog());
  }

  @Test
  public void myTest(){}
}

Kompilasi itu benar, tetapi ketika mencoba untuk mengatur tes, itu memanggil metode yang sebenarnya.

Menyatakan metode yang dilindungi atau publik memperbaiki masalah, itu bukan solusi bersih.

Maragues
sumber
2
Saya mengalami masalah yang sama, tetapi tes dan metode paket-pribadi berada di paket yang sama. Saya pikir mungkin Mockito memiliki masalah dengan metode paket-pribadi pada umumnya.
Dave
22

Dalam kasus saya, menggunakan Mockito 2.0, saya harus mengubah semua any()parameter nullable()untuk mematikan panggilan sebenarnya.

ejaenv
sumber
2
Jangan biarkan 321 sebagai jawaban teratas membuat Anda kecewa, ini memecahkan masalah saya :) Saya telah berjuang dengan ini selama beberapa jam!
Chris Kessel
3
Ini jawaban untuk saya. Untuk membuatnya lebih mudah bagi mereka yang mengikuti ketika mengejek metode Anda, sintaksnya adalah: foo = Mockito.spy(foo); Mockito.doReturn(someValue).when(foo).methodToPrevent(nullable(ArgumentType.class));
Stryder
Dengan Mockito 2.23.4 saya dapat mengonfirmasi ini tidak perlu, itu berfungsi dengan baik anydan eqkorek api.
vmaldosan
2
Mencoba tiga pendekatan berbeda pada versi 2.23.4 lib: any (), eq () dan nullable (). Hanya kemudian bekerja
ryzhman
Hai, solusi Anda sangat bagus dan bekerja untuk saya juga. Terima kasih
Dhiren Solanki
16

Jawaban oleh Tomasz Nurkiewicz tampaknya tidak menceritakan keseluruhan cerita!

NB Mockito versi: 1.10.19.

Saya seorang pemula Mockito, jadi tidak bisa menjelaskan perilaku berikut: jika ada seorang ahli di luar sana yang dapat meningkatkan jawaban ini, silakan saja.

Metode yang dimaksud di sini, getContentStringValueadalah TIDAK final dan TIDAK static .

Baris ini memang memanggil metode asli getContentStringValue:

doReturn( "dummy" ).when( im ).getContentStringValue( anyInt(), isA( ScoreDoc.class ));

Baris ini tidak memanggil metode asli getContentStringValue:

doReturn( "dummy" ).when( im ).getContentStringValue( anyInt(), any( ScoreDoc.class ));

Untuk alasan yang tidak dapat saya jawab, menggunakan isA()penyebab perilaku (?) "Jangan panggil metode" doReturngagal.

Mari kita lihat tanda tangan metode yang terlibat di sini: keduanya adalah staticmetode Matchers. Keduanya dikatakan oleh Javadoc untuk kembali null, yang agak sulit untuk membuat kepala Anda berputar. Agaknya Classobjek diteruskan sebagai parameter diperiksa tetapi hasilnya tidak pernah dihitung atau dibuang. Mengingat bahwa nulldapat berdiri untuk kelas apa pun dan bahwa Anda berharap metode yang diejek tidak dapat dipanggil, tidak dapat tanda tangan isA( ... )dan any( ... )hanya kembali nulldaripada parameter generik *<T> ?

Bagaimanapun:

public static <T> T isA(java.lang.Class<T> clazz)

public static <T> T any(java.lang.Class<T> clazz)

Dokumentasi API tidak memberikan petunjuk tentang hal ini. Tampaknya juga mengatakan perlunya perilaku "jangan panggil metode" semacam itu "sangat jarang". Secara pribadi saya menggunakan teknik ini sepanjang waktu : biasanya saya menemukan bahwa mengejek melibatkan beberapa baris yang "mengatur adegan" ... diikuti dengan memanggil metode yang kemudian "memainkan" adegan dalam konteks tiruan yang telah Anda tampilkan .. ... dan saat Anda mengatur pemandangan dan alat peraga, hal terakhir yang Anda inginkan adalah para aktor memasuki panggung kiri dan mulai memerankan hati mereka ...

Tapi ini jauh melampaui nilai gajiku ... Aku mengundang penjelasan dari pendeta tinggi Mockito yang lewat ...

* Apakah "parameter umum" istilah yang tepat?

mike rodent
sumber
Saya tidak tahu apakah ini menambah kejelasan atau lebih lanjut membingungkan masalah ini, tetapi perbedaan antara isA () dan any () adalah bahwa isA benar-benar melakukan pengecekan tipe, sedangkan sembarang () keluarga metode dibuat hanya untuk menghindari tipe casting dari argumen.
Kevin Welker
@KevinWelker Terima kasih. Dan memang nama-nama metode tidak kurang dalam kualitas cukup jelas. Namun saya lakukan, dan betapapun ringannya, mengambil masalah dengan desainer Mockito jenius karena tidak mendokumentasikan secara memadai. Tidak diragukan lagi saya perlu membaca buku lain tentang Mockito. PS sebenarnya tampaknya ada sangat sedikit sumber daya untuk mengajarkan "perantara Mockito"!
mike rodent
1
Sejarahnya adalah bahwa metode anyXX diciptakan pertama sebagai cara untuk berurusan dengan typecasting saja. Kemudian ketika disarankan mereka menambahkan pengecekan argumen, mereka tidak ingin memecah pengguna API yang ada, sehingga mereka menciptakan keluarga isA (). Mengetahui bahwa semua metode () seharusnya melakukan pengecekan tipe selama ini, mereka menunda mengubah mereka sampai mereka memperkenalkan perubahan melanggar lainnya pada overhaul Mockito 2.X (yang belum saya coba). Dalam 2.x +, metode anyX () adalah alias untuk metode isA ().
Kevin Welker
Terima kasih. Ini adalah jawaban penting bagi kita yang melakukan beberapa pembaruan pustaka sekaligus, karena kode yang digunakan untuk menjalankan tiba-tiba dan diam-diam gagal.
Dex Stakker
6

Satu lagi skenario yang mungkin menyebabkan masalah dengan mata-mata adalah ketika Anda menguji kacang musim semi (dengan kerangka uji pegas) atau kerangka kerja lain yang memeriksa objek Anda selama pengujian .

Contoh

@Autowired
private MonitoringDocumentsRepository repository

void test(){
    repository = Mockito.spy(repository)
    Mockito.doReturn(docs1, docs2)
            .when(repository).findMonitoringDocuments(Mockito.nullable(MonitoringDocumentSearchRequest.class));
}

Dalam kode di atas baik Spring dan Mockito akan mencoba untuk mem-proxy objek MonitoringDocumentsRepository Anda, tetapi Spring akan menjadi yang pertama, yang akan menyebabkan panggilan nyata metode findMonitoringDocuments. Jika kita men-debug kode kita setelah meletakkan mata-mata pada objek repositori, maka akan terlihat seperti ini di dalam debugger:

repository = MonitoringDocumentsRepository$$EnhancerBySpringCGLIB$$MockitoMock$

@SpyBean untuk menyelamatkan

Jika bukan @Autowiredanotasi, kami menggunakan @SpyBeananotasi, kami akan menyelesaikan masalah di atas, anotasi SpyBean juga akan menyuntikkan objek repositori tetapi akan terlebih dahulu diproksi oleh Mockito dan akan terlihat seperti ini di dalam debugger

repository = MonitoringDocumentsRepository$$MockitoMock$$EnhancerBySpringCGLIB$

dan ini kodenya:

@SpyBean
private MonitoringDocumentsRepository repository

void test(){
    Mockito.doReturn(docs1, docs2)
            .when(repository).findMonitoringDocuments(Mockito.nullable(MonitoringDocumentSearchRequest.class));
}
Adrian Kapuscinski
sumber
1

Saya telah menemukan alasan lain bagi mata-mata untuk memanggil metode asli.

Seseorang memiliki ide untuk mengejek finalkelas, dan menemukan tentang MockMaker:

Karena ini bekerja secara berbeda dengan mekanisme kami saat ini dan ini memiliki keterbatasan yang berbeda dan karena kami ingin mengumpulkan pengalaman dan umpan balik pengguna, fitur ini harus diaktifkan secara eksplisit agar tersedia; itu dapat dilakukan melalui mekanisme ekstensi mockito dengan membuat file yang src/test/resources/mockito-extensions/org.mockito.plugins.MockMakerberisi satu baris:mock-maker-inline

Sumber: https://github.com/mockito/mockito/wiki/What%27s-new-in-Mockito-2#mock-the-unmockable-opt-in-mocking-of-final-classesmethods

Setelah saya bergabung dan membawa file itu ke mesin saya, tes saya gagal.

Saya hanya harus menghapus baris (atau file), dan spy()berhasil.

Matruskan
sumber
ini adalah alasan dalam kasus saya, saya mencoba untuk mengejek metode terakhir tetapi terus memanggil yang asli tanpa pesan kesalahan yang jelas yang membingungkan.
Bashar Ali Labadi
1

Agak terlambat ke pesta tetapi solusi di atas tidak bekerja untuk saya, jadi bagikan $ 0,02 saya

Versi Mokcito: 1.10.19

MyClass.java

private int handleAction(List<String> argList, String action)

Test.java

MyClass spy = PowerMockito.spy(new MyClass());

Berikut ini TIDAK bekerja untuk saya (metode aktual dipanggil):

1.

doReturn(0).when(spy , "handleAction", ListUtils.EMPTY_LIST, new String());

2.

doReturn(0).when(spy , "handleAction", any(), anyString());

3.

doReturn(0).when(spy , "handleAction", null, null);

Berikut BEKERJA:

doReturn(0).when(spy , "handleAction", any(List.class), anyString());
mencobaToLearn
sumber
0

Salah satu cara untuk memastikan metode dari kelas tidak dipanggil adalah dengan mengganti metode dengan dummy.

    WebFormCreatorActivity activity = spy(new WebFormCreatorActivity(clientFactory) {//spy(new WebFormCreatorActivity(clientFactory));
            @Override
            public void select(TreeItem i) {
                log.debug("SELECT");
            };
        });
Geoffrey Ritchey
sumber
-1

Jawaban untuk pengguna scala: Menempatkan doReturnlebih dulu tidak berhasil! Lihat posting ini .

Nick Resnick
sumber
ini bukan jawaban
Umpa