Mockito: Metodu gözetlemeye çalışmak orijinal metodu çağırıyor


352

Mockito 1.9.0 kullanıyorum. Ben bir JUnit testinde bir sınıfın tek bir yöntemi için alay davranış istiyorum, bu yüzden var

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

Sorun, ikinci satırda myClassSpy.method1()aslında çağrılıyor ve bir istisna ile sonuçlanıyor. Sahte kullanmamın tek nedeni, daha sonra, her myClassSpy.method1()çağrıldığında, gerçek yöntem çağrılmayacak ve myResultsnesne döndürülecektir.

MyClassbir arayüzdür ve önemliyse myInstancebunun bir uygulamasıdır.

Bu casusluk davranışını düzeltmek için ne yapmam gerekir?


Yanıtlar:


610

Resmi belgeleri teklif edeyim :

Gerçek nesneleri casusluk konusunda önemli yakaladım!

Bazen casusluk casusları için (Nesne) ne zaman kullanmak imkansızdır. Misal:

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

Senin durumunda böyle bir şey gider:

doReturn(resulstIWant).when(myClassSpy).method1();

27
Bu yöntemi kullanırsam ve orijinal yöntemim STILL çağrılırsa ne olur? Geçirdiğim parametrelerle ilgili bir sorun olabilir mi? İşte tüm test: pastebin.com/ZieY790P send yöntemi çağrılıyor
Evgeni Petrov

26
@EvgeniPetrov orijinal yönteminiz hala çağrılıyorsa, muhtemelen orijinal yönteminiz son olduğu için. Mockito nihai yöntemlerle alay etmez ve nihai yöntemlerin alay konusu hakkında sizi uyaramaz.
MarcG

1
Bu doThrow () için de mümkün mü?
Gobliins

1
evet, maalesef statik yöntemler hareketsizdir ve "casusluk yapamaz" dır. Statik yöntemler ile başa çıkmak için ne statik çağrı etrafında bir yöntem sarmak ve bu yöntemde bir doNothing veya doReturn kullanmaktır. Singleton veya scala nesneleriyle, mantığın etini soyut bir sınıfa taşıyorum ve bu bana bir casus oluşturabileceğim nesnenin alternatif bir test sınıfı impl yeteneğine sahip olmamı sağlıyor.
Andrew Norman

24
Peki ya NOT final ve NOT statik yöntemi hala çağrılıyorsa?
X-HuMan

27

Davam kabul edilen cevaptan farklıydı. Bu pakette yaşamamış bir örnek için bir paket-özel yöntem alay etmeye çalışıyordum

package common;

public class Animal {
  void packageProtected();
}

package instances;

class Dog extends Animal { }

ve test sınıfları

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(){}
}

Derleme doğrudur, ancak testi kurmaya çalıştığında bunun yerine gerçek yöntemi çağırır.

Korumalı veya halka açık bir yöntem beyan etmek , temiz bir çözüm olmadığından sorunu çözer.


2
Benzer bir sorunla karşılaştım, ancak test ve paket-özel yöntem aynı pakette idi. Belki de Mockito'nun paket-özel yöntemlerle ilgili sorunları var.
Dave

22

Benim durumumda, Mockito 2.0 kullanarak , gerçek aramayı saplamak için tüm any()parametreleri değiştirmek zorunda kaldım nullable().


2
Bu 321 oy üst cevap seni almak izin vermeyin, bu benim sorunum çözüldü :) Ben birkaç saat bu ile mücadele ediyorum!
Chris Kessel

3
Benim için cevap buydu. Yönteminizi alay ederken takip edenler için daha kolay hale getirmek için sözdizimi: foo = Mockito.spy(foo); Mockito.doReturn(someValue).when(foo).methodToPrevent(nullable(ArgumentType.class));
Stryder

Mockito 2.23.4 ile, bunun ile para cezası çalışır bu gerekli değildir teyit edebilir anyve eqmatchers.
vmaldosan

2
2.23.4 lib sürümünde üç farklı yaklaşım denendi: any (), eq () ve nullable (). Sadece daha sonra çalıştı
ryzhman

Merhaba, çözümünüz gerçekten güzel ve benim için de çalıştı. Teşekkürler
Dhiren Solanki

16

Tomasz Nurkiewicz'in cevabı tüm hikayeyi anlatmıyor gibi görünüyor!

NB Mockito sürümü: 1.10.19.

Ben çok Mockito'lu bir yeniyim, bu yüzden aşağıdaki davranışı açıklayamıyorum: bu cevabı geliştirebilecek bir uzman varsa, lütfen çekinmeyin.

Burada söz konusu olan yöntem, getContentStringValueolduğu DEĞİL final ve DEĞİL static .

Bu hat yapar orijinal yöntemini çağırın getContentStringValue:

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

Bu hat gelmez orijinal yöntemini çağırın getContentStringValue:

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

Yanıtlayamadığım nedenlerden dolayı isA(), amaçlanan (?) "Yöntemi çağırmayın" davranışının doReturnbaşarısız olmasına neden olur.

Yöntem imzalar at atalım burada yer: ikisi de vardır staticyöntemleri Matchers. Her ikisi de Javadoc tarafından geri dönmek için söyleniyor null, ki bu da başınızı kendi içinde döndürmek biraz zor. Muhtemelen Classparametre olarak iletilen nesne incelenir, ancak sonuç asla hesaplanmaz veya atılmaz. Olduğu göz önüne alındığında nullherhangi bir sınıf için duramaz ve imzaları, çağrılacak değil alay yöntem için umuyordum olmadığını isA( ... )ve any( ... )sadece dönüş nulljenerik parametresi yerine * <T>?

Neyse:

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

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

API belgeleri bu konuda herhangi bir ipucu vermez. Ayrıca, bu tür "yöntem çağırma" davranışına duyulan ihtiyacın "çok nadir" olduğu da anlaşılmaktadır. Şahsen ben bu tekniği her zaman kullanıyorum : tipik olarak alaycı "sahneyi ayarlamak" birkaç satır içerir bulmak ... daha sonra sahnelemek sahte bağlamında sahneyi "oynar" bir yöntem çağırdıktan sonra .. sahne ve sahne kurarken istediğiniz son şey oyuncuların sahneye sola girip kalplerini dışarı atmaya başlaması ...

Ama bu maaş notumun çok ötesinde ... Geçen Mockito papazlarının açıklamalarına davet ediyorum ...

* "jenerik parametre" doğru terim mi?


Bunun netlik ekleyip eklemediğini bilmiyorum, ama isA () ve any () arasındaki fark, isA'nın gerçekten kontrol etmesidir, oysa herhangi bir () ailesi sadece türün dökümünü önlemek için oluşturulmuştur. argüman.
Kevin Welker

@KevinWelker Teşekkürler. Gerçekten de yöntem adları belli bir açıklayıcı nitelikte değildir. Bununla birlikte, ancak hafif bir şekilde, dahi Mockito tasarımcılarıyla yeteri kadar belgelemediği için sorun yaşıyorum. Şüphesiz Mockito hakkında başka bir kitap okumam gerekiyor. PS aslında "ara Mockito" öğretmek için çok az kaynak var gibi görünüyor!
mike kemirgen

1
Tarihçe, anyXX yöntemlerinin ilk olarak yalnızca daktiloyla başa çıkmanın bir yolu olarak yaratıldığıdır. Daha sonra argüman kontrolünü eklemeleri önerildiğinde, mevcut API'nın kullanıcılarını kırmak istemediler, bu yüzden isA () ailesini oluşturdular. Any () yöntemlerinin tür denetimini baştan başa yapmış olması gerektiğini bilerek, Mockito 2.X revizyonunda (henüz denemedim) diğer kırılma değişikliklerini tanıtana kadar bunları değiştirmeyi ertelediler. 2.x + 'da, anyX () yöntemleri isA () yöntemleri için takma adlardır.
Kevin Welker

Teşekkür ederim. Bu, aynı anda birkaç kütüphane güncellemesi yapanlarımız için çok önemli bir cevaptır, çünkü eskiden çalıştırılan kod aniden ve sessizce başarısız olur.
Dex Stakker

6

Casuslarla ilgili sorunlara neden olabilecek bir olası senaryo, bahar çekirdeklerini (yay test çerçevesi ile) veya test sırasında nesnelerinize proxy yapan başka bir çerçeveyi test ettiğinizde ortaya çıkar .

Misal

@Autowired
private MonitoringDocumentsRepository repository

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

Yukarıdaki kodda hem Spring hem de Mockito, MonitoringDocumentsRepository nesnesinizi proxy yapmaya çalışacak, ancak Spring ilk olacak ve findMonitoringDocuments yönteminin gerçek çağrısına neden olacak. Kod nesnesine bir casus yerleştirdikten hemen sonra hata ayıklarsak, hata ayıklayıcı içinde şöyle görünür:

repository = MonitoringDocumentsRepository$$EnhancerBySpringCGLIB$$MockitoMock$

@SpyBean kurtarmaya

Bunun yerine @Autowiredkullandığımız açıklama @SpyBeanek açıklama, biz sorun yukarıda çözecek, SpyBean açıklama da iğne yapmak depo nesne ama öncelikle Mockito tarafından proxy edilecek ve bu iç ayıklayıcıya benzeyecek

repository = MonitoringDocumentsRepository$$MockitoMock$$EnhancerBySpringCGLIB$

ve işte kod:

@SpyBean
private MonitoringDocumentsRepository repository

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

1

Casusun orijinal yöntemi çağırması için başka bir neden buldum.

Birinin bir finalsınıfı alay etme fikri vardı ve MockMakerşunları buldu :

Bu, mevcut mekanizmamızdan farklı çalıştığından ve bunun farklı sınırlamaları olduğundan ve deneyim ve kullanıcı geri bildirimi toplamak istediğimizden, bu özelliğin kullanılabilir olması için açıkça etkinleştirilmesi gerekiyordu; mockito uzatma mekanizması ile src/test/resources/mockito-extensions/org.mockito.plugins.MockMakertek bir satır içeren dosya oluşturularak yapılabilir :mock-maker-inline

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

Birleştirip dosyayı makineme getirdikten sonra testlerim başarısız oldu.

Sadece çizgiyi (veya dosyayı) kaldırmak zorunda kaldım ve spy()çalıştım.


benim durumumda nedeni bu, son bir yöntem alay etmeye çalışıyordu ama kafa karıştırıcı net bir hata mesajı olmadan gerçek olanı aramaya devam etti.
Beşar Ali Labadi

1

Partiye biraz geç ama yukarıdaki çözümler benim için işe yaramadı, bu yüzden 0.02 $ paylaşmak

Mokcito sürümü: 1.10.19

MyClass.java

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

Test.java

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

Aşağıdaki benim için işe yaramadı (gerçek yöntem çağrıldı):

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

Takip edilenler:

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

0

Bir sınıftan bir yöntemin çağrılmadığından emin olmanın bir yolu, yöntemi bir kukla ile geçersiz kılmaktır.

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.