web-gelistirme-sc.com

Mockito: Yönteme göz atmaya çalışmak orijinal yöntemi çağırıyor

Mockito 1.9.0 kullanıyorum. Bir JUnit testindeki bir sınıfın tek bir metodu için davranışlarla alay etmek istiyorum.

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

Sorun şu ki, ikinci satırda myClassSpy.method1() aslında çağrılıyor ve bir istisna ortaya çıkıyor. Alaycı kullanmamın tek nedeni, daha sonra, myClassSpy.method1() ne zaman çağrılırsa, gerçek yöntem çağrılmaz ve myResults nesnesi döndürülür. 

MyClass bir arayüzdür ve myInstance bunun bir uygulamasıdır, eğer önemliyse.

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

256
Dave

Teklif vereyim resmi evraklar :

Gerçek nesneleri casuslukla ilgili önemli şeyler!

Bazen casusları inatlamak için (Object) ne zaman kullanmak imkansız. Örnek: 

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

Davam kabul edilen cevaptan farklıydı. Paket içinde yaşamayan bir örnek için özel paket yöntemiyle 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 kamu yöntemini bildirmek, sorunu çözmez, temiz bir çözüm değildir.

24
Maragues

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

NB Mockito versiyonu: 1.10.19.

Ben bir Mockito yenisiyim, bu yüzden şu davranışı açıklayamam: Bu cevabı geliştirebilecek bir uzman varsa, lütfen çekinmeyin.

Burada söz konusu olan yöntem, getContentStringValue,DEĞILfinal veDEĞILstatic şeklindedir.

Bu satır yapar orjinal yöntemi çağırır getContentStringValue:

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

Bu satır'orjinal yöntemi çağırmaz getContentStringValue:

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

Cevaplayamadığım nedenlerden dolayı, isA() işlevinin kullanılması, doReturn işlevinin amaçlanan (?) "Yöntem çağırmayın" davranışının başarısız olmasına neden olur.

Burada yer alan yöntem imzalarına bakalım: ikisi de static __ Matchers yöntemidir. Her ikisi de Javadoc tarafından null 'a döndüğünü söylüyor, bu da kafanızı kendi içinde gezdirmek için biraz zor. Muhtemelen, parametre incelenirken geçen Class nesnesi ancak sonuç hiçbir zaman hesaplanmadı veya atılmadı. null herhangi bir sınıfa dayanabileceğini ve çağrılan yöntemin çağrılmamasını umduğunu göz önüne alındığında, isA( ... ) ve any( ... ) imzaları genel bir parametre yerine null döndürmez mi? * <T>?

Neyse:

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

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

API dokümantasyonu bu konuda hiçbir ipucu vermez. Aynı zamanda, böyle bir "yöntem çağrı yapma" davranışına duyulan ihtiyacın "çok nadir" olduğu söyleniyor. Şahsen ben bu tekniği kullanıyorum her zaman : tipik olarak alaycılığın, "sahneyi" ayarlayan birkaç satır içerdiğini ve ardından sahtekarlık bağlamında sahneyi "çalan" bir yöntemi çağırarak buldum. sahnelemeye başladınız ... ve sahneyi kurarken ve sahneden istediğin son şey, oyuncuların soldaki sahneye girmeleri ve kalplerini harekete geçirmeleri ...

Ama bu maaş derecemin ötesinde bir şey ... Geçen her Mockito rahipinden gelen açıklamaları davet ediyorum ...

* "genel parametre" doğru terimdir?

14
mike rodent

Benim durumumda Mockito 2.0 kullanarak, gerçek çağrıyı engellemek için tüm any () parametrelerini nullable () olarak değiştirmek zorunda kaldım.

12
ejaenv

Casuslarla ilgili sorunlara yol açabilecek bir başka olası senaryo, bahar fasulyesi (bahar testi çerçevesiyle) veya başka bir test sırasında nesnelerinizi proksize eden çerçeve test ederken.

Örnek

@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 nesnesini proxy'ye almaya çalışacak, ancak Spring ilk olarak bulunacak ve bu, findMonitoringDocuments yönteminin gerçek çağrısına neden olacaktır. Kodumuzu depo nesnesine bir casus koyduktan hemen sonra hata ayıklarsak, hata ayıklayıcı içinde şöyle görünecektir:

repository = MonitoringDocumentsRepository$$EnhancerBySpringCGLIB$$MockitoMock$

@SpyBean kurtarmaya

Eğer bunun yerine @Autowired notunu kullanırsak, @SpyBean notunu kullanırız, yukarıdaki problemi çözeceğiz, SpyBean notu ayrıca depo nesnesini de enjekte edecektir, ancak ilk önce Mockito tarafından proxy'ye alınacak ve debugger içinde görünecek

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 daha buldum.

Birisi final sınıfıyla alay etme fikri vardı ve MockMaker hakkında bulundu:

Bu, mevcut mekanizmamıza göre farklı şekilde çalıştığından ve bunun farklı sınırlamaları olduğundan ve deneyim ve kullanıcı geribildirimleri toplamak istediğimiz için, bu özelliğin erişilebilir olması için açıkça etkinleştirilmesi gerekiyordu; Tek bir satır içeren src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker dosyasını oluşturarak mockito uzatma mekanizması aracılığıyla 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 o dosyayı makineme getirdikten sonra testlerim başarısız oldu.

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

0
Matruskan

Scala kullanıcıları için cevap: doReturn 'ı ilk koymak bile işe yaramaz! Bkz. bu gönderi .

0
Nick Resnick

Bir sınıftan bir yöntemin çağrılmadığından emin olmanın bir yolu, yöntemi bir kukla ile değiştirmek. 

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