Gereksiz Stubbing istisnası nasıl çözülür


107

Kodum aşağıdaki gibidir,

@RunWith(MockitoJUnitRunner.class)
public class MyClass {

    private static final String code ="Test";

    @Mock
     private MyClassDAO dao;

    @InjectMocks
     private MyClassService Service = new MyClassServiceImpl();

    @Test
     public void testDoSearch() throws Exception {
         final String METHOD_NAME = logger.getName().concat(".testDoSearchEcRcfInspections()");
         CriteriaDTO dto = new CriteriaDTO();
         dto.setCode(code);
         inspectionService.searchEcRcfInspections(dto);
         List<SearchCriteriaDTO> summaryList = new ArrayList<SearchCriteriaDTO>();
         inspectionsSummaryList.add(dto);
         when(dao.doSearch(dto)).thenReturn(inspectionsSummaryList);//got error in this line
         verify(dao).doSearchInspections(dto);

      }
}

İstisnanın altına düşüyorum

org.mockito.exceptions.misusing.UnnecessaryStubbingException: 
Unnecessary stubbings detected in test class: Test
Clean & maintainable test code requires zero unnecessary code.
Following stubbings are unnecessary (click to navigate to relevant line of code):
  1. -> at service.Test.testDoSearch(Test.java:72)
Please remove unnecessary stubbings or use 'silent' option. More info: javadoc for UnnecessaryStubbingException class.
  at org.mockito.internal.exceptions.Reporter.formatUnncessaryStubbingException(Reporter.java:838)
  at org.mockito.internal.junit.UnnecessaryStubbingsReporter.validateUnusedStubs(UnnecessaryStubbingsReporter.java:34)
  at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:49)
  at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:103)
  at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
  at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

Lütfen nasıl çözeceğime yardım et

Yanıtlar:


118

Değiştir @RunWith(MockitoJUnitRunner.class)ile @RunWith(MockitoJUnitRunner.Silent.class).


45
Hoşgeldiniz. OP'nin neden bu kodu değiştirmesi gerektiğini açıklamak için cevabınızı güncellemeniz iyi olacaktır . Bu onların ve gelecekteki ziyaretçilerin anlamasına yardımcı olacaktır.
Bugs

5
Btw, öyle @RunWith(MockitoJUnitRunner.Silent.class)ve SESSİZ değil
fgysin Monica'yı eski durumuna döndürmek

6
@RunWith(MockitoJUnitRunner.Silent::class)
Juan Saravia

12
Bu cevabın neden bir açıklama olmadan olumlu oy vermeye devam ettiğinden emin değilim. Diğer cevaplar daha anlamlı ve doğrudur.
Yogesh

10
Bu sorunu çözmez, sadece hata mesajını bastırır ve ayrıca sınıftaki diğer tüm testleri (varsa) etkiler.
Fencer

113

İlk önce test mantığınızı kontrol etmelisiniz. Genellikle 3 vaka vardır. İlk olarak, yanlış yöntemle dalga geçiyorsunuz (bir yazım hatası yaptınız veya birisi test edilen kodu değiştirdi, böylece alay edilen yöntem artık kullanılmıyor). İkinci olarak, bu yöntem çağrılmadan önce testiniz başarısız oluyor. Üçüncüsü, kodun herhangi bir yerinde if / switch dalı mantığınız yanlış olur, böylece alay edilen yöntem çağrılmaz.

Bu ilk durumsa, kodda kullanılan için her zaman alay edilen yöntemi değiştirmek istersiniz. İkinci ve üçüncü duruma göre değişir. Genellikle bu taklidi, faydası yoksa silmelisiniz. Ancak bazen parametrik testlerde bu farklı yolu izlemesi veya daha önce başarısız olması gereken belirli durumlar vardır. Sonra bu testi iki veya daha fazla ayrı sınava bölebilirsiniz, ancak bu her zaman iyi görünmüyor. Muhtemelen 3 bağımsız değişken içeren 3 test yöntemi, test etmenizi okunamaz hale getirebilir. Bu durumda JUnit 4 için bu istisnayı,

@RunWith(MockitoJUnitRunner.Silent.class) 

ek açıklama veya kural yaklaşımı kullanıyorsanız

@Rule
public MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.LENIENT);

veya (aynı davranış)

@Rule
public MockitoRule rule = MockitoJUnit.rule().silent();

JUnit 5 testleri için, mockito-junit-jupiterpakette sağlanan ek açıklamayı kullanarak bu istisnayı susturabilirsiniz .

@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
class JUnit5MockitoTest {
}

3
@MockitoSettings (tightness = Strictness.LENIENT), kurulumumdaki katılığı ayarlamanın en basit yoludur. Teşekkürler!
Matt

7
Bu cevap, olasılıklara iyi bir genel bakış sağlar. Bununla birlikte, esnek katılığı, kullanarak vaka bazında da ayarlayabilirsiniz Mockito.lenient().when(...); bu özel soru içinMockito.lenient().when(dao.doSearch(dto)).thenReturn(inspectionsSummaryList);
neXus

Test hiyerarşileriyle uğraşırken, alt sınıflarda ExtendWith ve alt sınıflarda MockitoSettings'i tanımlayın. Umarım bu benim masraflarımdan birine zaman kazandırır.
miracle_the_V

40

Sessizlik bir çözüm değil. Testinde taklidini düzeltmen gerekiyor. Resmi belgelere buradan bakın .

Gereksiz saplamalar, test yürütme sırasında asla gerçekleştirilemeyen saplama yöntem çağrılarıdır (ayrıca bkz.MockitoHint), örnek:

//code under test:
 ...
 String result = translator.translate("one")
 ...

 //test:
 ...
 when(translator.translate("one")).thenReturn("jeden"); // <- stubbing realized during code execution
 when(translator.translate("two")).thenReturn("dwa"); // <- stubbing never realized
 ...

Test yürütme sırasında test edilen kodda hiçbir zaman stublanmış yöntemlerden birinin gerçekleştirilmediğine dikkat edin. Başıboş saplama, geliştiricinin bir gözetiminden, kopyala-yapıştır yapısından veya testi / kodu anlamayan etkiden kaynaklanıyor olabilir. Her iki durumda da, geliştirici gereksiz test koduyla sonuçlanır. Kod tabanını temiz ve sürdürülebilir tutmak için, gereksiz kodların kaldırılması gerekir. Aksi takdirde testleri okumak ve akıl yürütmek daha zordur.

Kullanılmayan saplamaları tespit etme hakkında daha fazla bilgi için MockitoHint'e bakın.


16
Benzer bir @BeforeEach kurulumuna karşı 8-9 test yazdığınızda, bir saplamadan dönen öğenin bir avuç testteki iş mantığı nedeniyle kullanılmadığı birçok durum vardır. Ya (A) birden fazla teste ayırabilir ve \ @BeforeEach bölümünü eksi bir öğeyi kopyalayıp / yapıştırabilirsiniz (B) Mockito'nun emo olduğu tek satırı kopyalayıp, onu kullanan 6 teste yapıştırın ve buna sahip olun 2'de değil veya (C) Sessiz kullanın. Sessiz / uyarmayı tercih ederim. Bozuk bir test değil.
RockMeetHardplace

1
@RockMeetHardplace, Silent bir çözüm değil , hızlı bir şekilde daha az kopyala / yapıştır görüyorsunuz ancak testlerinizi projenizde yeni kişiler tarafından sürdürürken bu sorunlu olacaktır. Mockito kitabevi bunu yaparsa, boşuna değildir.
Stéphane GRILLON

2
@sgrillon: Ancak bu sistem birçok yanlış pozitif tespit ediyor. Yani, bir şeyin kullanılmadığını söylüyor, ancak saplamanın kaldırılması yürütmeyi kesintiye uğrattığı için açıkça değil. Sorun test kodunun iyileştirilemeyeceği değil, hayati bir saplama çizgisinin asla "gereksiz" olarak algılanmaması gerektiğidir . Bu nedenle, bu çeki devre dışı bırakmanın önemi çok heveslidir.
Carighan

1
@Carighan, taklidin yanlış tespit edilirse, düşündüğünüz gibi olmayabilir. Bu size bir hata olduğunda bir OK testi verir.
Stéphane GRILLON

@sgrillon, üzgünüm sana bu konuda asla geri dönmedim. Test yürütme sırasına bağlı olarak, bir testte kullanılan ancak başka bir testte üzerine yazılan stub'ların onu tetikleyeceği "yanlış isabetler" oluşturduğu, bununla ilgili bir hata olduğu ortaya çıktı. Anladığım kadarıyla uzun süredir sabit.
Carighan

34

Benim için ne @Rulene de @RunWith(MockitoJUnitRunner.Silent.class)öneriler işe yaradı. Mockito-core 2.23.0'a yükselttiğimiz eski bir projeydi.

Şunları UnnecessaryStubbingExceptionkullanarak kurtulabiliriz :

Mockito.lenient().when(mockedService.getUserById(any())).thenReturn(new User());

onun yerine:

when(mockedService.getUserById(any())).thenReturn(new User());

Söylemeye gerek yok, test koduna bakmayı tercih etmelisiniz, ancak her şeyden önce derlenen ve testleri çalıştırmaya ihtiyacımız vardı;)


8
BENİM NACİZANE FİKRİME GÖRE. Bu, tüm test sınıfını susturmak yerine burada bulduğum en faydalı cevap.
priyeshdkr

1
Sadece 1 alay etmeyi bastırmak istediğim için, bu benim için en iyi cevap. Yine de OP için bir cevap değil.
Hans Wouters

25
 when(dao.doSearch(dto)).thenReturn(inspectionsSummaryList);//got error in this line
 verify(dao).doSearchInspections(dto);

whenBurada bir şeyler yapmak site taklidinin yapılandırır. Ancak bu taklidi bu satırdan sonra herhangi bir şekilde kullanmıyorsunuz (a yapmak dışında verify). Mockito, whenbu nedenle çizginin anlamsız olduğu konusunda sizi uyarır . Belki bir mantık hatası yaptınız?


Yardımınız için teşekkürler
VHS

Her ikisine de ihtiyacım var, ne zaman ve ne zaman daha ileri gideceğimi öneren ifadeler
VHS

2
ServiceDoğru tepki verip vermediğini görmek için test sınıfınızda ( ) bir işlev çağırın . Bunu hiç yapmadınız, peki burada neyi test ediyorsunuz?
john16384

3

Yığın izlemenizin bir kısmına baktığınızda, dao.doSearch()başka bir yere saplıyormuşsunuz gibi görünür . Daha çok aynı yöntemin taslaklarını tekrar tekrar oluşturmak gibi.

Following stubbings are unnecessary (click to navigate to relevant line of code):
  1. -> at service.Test.testDoSearch(Test.java:72)
Please remove unnecessary stubbings or use 'silent' option. More info: javadoc for UnnecessaryStubbingException class.

Örneğin aşağıdaki Test Sınıfını düşünün:

@RunWith(MockitoJUnitRunner.class)
public class SomeTest {
    @Mock
    Service1 svc1Mock1;

    @Mock
    Service2 svc2Mock2;

    @InjectMock
    TestClass class;

    //Assume you have many dependencies and you want to set up all the stubs 
    //in one place assuming that all your tests need these stubs.

    //I know that any initialization code for the test can/should be in a 
    //@Before method. Lets assume there is another method just to create 
    //your stubs.

    public void setUpRequiredStubs() {
        when(svc1Mock1.someMethod(any(), any())).thenReturn(something));
        when(svc2Mock2.someOtherMethod(any())).thenReturn(somethingElse);
    }

    @Test
    public void methodUnderTest_StateUnderTest_ExpectedBehavior() {
        // You forget that you defined the stub for svcMock1.someMethod or 
        //thought you could redefine it. Well you cannot. That's going to be 
        //a problem and would throw your UnnecessaryStubbingException.
       when(svc1Mock1.someMethod(any(),any())).thenReturn(anyThing);//ERROR!
       setUpRequiredStubs();
    }
}

Gerektiğinde testlerinizi yeniden düzenlemeyi tercih ederim.


2

Eh, Benim durumumda mockito hata sonrasında gerçek bir yöntemi çağırmak için bana söylüyordu whenya wheneversaplama. Az önce alay ettiğimiz koşulları çağırmadığımız için, Mockito bunu gereksiz saplamalar veya kod olarak bildiriyordu.

İşte hata geldiği zamanki gibiydi:

@Test
fun `should return error when item list is empty for getStockAvailability`() {
    doAnswer(
        Answer<Void> { invocation ->
            val callback =
                invocation.arguments[1] as GetStockApiCallback<StockResultViewState.Idle, StockResultViewState.Error>
            callback.onApiCallError(stockResultViewStateError)
            null
        }
    ).whenever(stockViewModelTest)
        .getStockAvailability(listOf(), getStocksApiCallBack)
}

daha sonra, yöntemle alay etmek için when ifadesinde belirtilen gerçek yöntemi çağırdım.

yapılan değişiklikler aşağıdaki gibidir stockViewModelTest.getStockAvailability(listOf(), getStocksApiCallBack)

@Test
fun `should return error when item list is empty for getStockAvailability`() {
    doAnswer(
        Answer<Void> { invocation ->
            val callback =
                invocation.arguments[1] as GetStockApiCallback<StockResultViewState.Idle, StockResultViewState.Error>
            callback.onApiCallError(stockResultViewStateError)
            null
        }
    ).whenever(stockViewModelTest)
        .getStockAvailability(listOf(), getStocksApiCallBack)
    //called the actual method here
    stockViewModelTest.getStockAvailability(listOf(), getStocksApiCallBack)
}

şuan çalışıyor.


1

Bunun yerine bu stili kullanıyorsanız:

@Rule
public MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);

şununla değiştir:

@Rule
public MockitoRule rule = MockitoJUnit.rule().silent();

1

Ben UnnecessaryStubbingExceptionkullandığım çalıştığında whenbir casus nesne üzerinde yöntemleri. Mockito.lenient()istisnayı susturdu ancak test sonuçları doğru değildi.

Casus nesneler söz konusu olduğunda, yöntemlerin doğrudan çağrılması gerekir.

@ExtendWith(MockitoExtension.class)
@RunWith(JUnitPlatform.class)
class ArithmTest {

    @Spy
    private Arithm arithm;

    @Test
    void testAddition() {

        int res = arithm.add(2, 5);

        // doReturn(7).when(arithm).add(2, 5);
        assertEquals(res, 7);
    }
}

1

Değiştir

@RunWith(MockitoJUnitRunner.class)

ile

@RunWith(MockitoJUnitRunner.Silent.class)

veya kaldır@RunWith(MockitoJUnitRunner.class)

veya sadece istenmeyen alaycı çağrıları yorumlayın (yetkisiz stubing olarak gösterilir).


0

Büyük bir proje olması durumunda, bu istisnaların her birini düzeltmek zordur. Aynı zamanda kullanarakSilent tavsiye edilmez. Bunların bir listesi verilen tüm gereksiz izleri kaldırmak için bir komut dosyası yazdım.

https://gist.github.com/cueo/da1ca49e92679ac49f808c7ef594e75b

Sadece mvnçıktıyı kopyalayıp yapıştırmamız ve bu istisnaların listesini regex kullanarak yazmamız ve betiğin gerisini halletmesine izin vermemiz gerekiyor.


-1

Bir sahte oluşturduğunuzda ve bu sahte kullanılmadığında, kullanılmayan saplama istisnası atar. Senin durumunda bu sahte aslında çağrılmaz. Bu nedenle o hatayı atıyor. Bu nedenle, relpace @RunWith(MockitoJUnitRunner.class)ile @RunWith(MockitoJUnitRunner.Silent.class)hata çıkarıyoruz. Hala kullanmak @RunWith(MockitoJUnitRunner.class)istiyorsanız, alay ettiğiniz işlev gerçekten çağrılıyorsa veya çağrılmıyorsa mantığınızda hata ayıklamayı deneyin.


Bu, mevcut cevaplarda kapsanmayan hiçbir şeyi eklemez.
Matthew

-3

Alay ederken herhangi () kullanırsanız, @RunWith(MockitoJUnitRunner.class)ile yeniden boşluk bırakmanız gerekir @RunWith(MockitoJUnitRunner.Silent.class).


Bu tamamen yanlıştır. any()doğru kullanıldığında sıradan koşucu ile mükemmel çalışır.
Matthew
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.