Як вирішити непотрібне виняток "Стубінг"


101

Мій код, як показано нижче,

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

      }
}

Я потрапляю нижче винятку

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)

Будь ласка, допоможіть мені вирішити проблему

Відповіді:


120

Замінити @RunWith(MockitoJUnitRunner.class)на @RunWith(MockitoJUnitRunner.Silent.class).


44
Ласкаво просимо. варто було б оновити свою відповідь, щоб пояснити, чому вони повинні замінити такий код. Це допоможе їм та майбутнім відвідувачам зрозуміти.
Баги

5
До речі, це @RunWith(MockitoJUnitRunner.Silent.class)і НЕ SILENT
fgysin відновило Моніка

6
У Котліні:@RunWith(MockitoJUnitRunner.Silent::class)
Хуан Саравія

9
Не впевнений, чому ця відповідь постійно піднімається "за" без пояснень. Інші відповіді є більш значущими та точними.
Йогеш

8
Це не вирішує проблему, а просто придушує повідомлення про помилку, а також впливає на всі інші тести (якщо такі є) у класі.
Фехтувальник

99

Спочатку слід перевірити логіку тесту. Зазвичай буває 3 випадки. По-перше, ви знущаєтесь над неправильним методом (ви зробили помилку або хтось змінив перевірений код, так що знущаний метод більше не використовується). По-друге, перед викликом цього методу ваш тест провалився. По-третє, ваша логіка потрапляє неправильно, якщо гілка / перемикати десь у коді, так що знущаний метод не викликається.

Якщо це перший випадок, ви завжди хочете змінити глузливий метод на той, що використовується в коді. З другим і третім це залежить. Зазвичай вам слід просто видалити цей макет, якщо він не має ніякої користі. Але іноді в параметризованих тестах трапляються певні випадки, котрі повинні пройти інший шлях або провалитися раніше. Потім ви можете розділити цей тест на два або більше окремих, але це не завжди добре виглядає. 3 методи тестування з можливо 3 постачальниками аргументів можуть зробити ваш тест нечитабельним. У цьому випадку для JUnit 4 ви виключаєте цей виняток ні з одним, ні з іншим

@RunWith(MockitoJUnitRunner.Silent.class) 

анотація або якщо ви використовуєте підхід правила

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

або (однакова поведінка)

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

Для тестів JUnit 5 ви можете вимкнути цей виняток, використовуючи анотацію, надану в mockito-junit-jupiterпакеті.

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

3
@MockitoSettings (strictness = Strictness.LENIENT) - це найпростіший спосіб налаштування строгості в моїй установці. Дякую!
Matt

4
Ця відповідь дає хороший огляд можливостей. Однак ви також можете встановити м’яку суворість для кожного конкретного випадку, використовуючи Mockito.lenient().when(...); для цього конкретного питання це було бMockito.lenient().when(dao.doSearch(dto)).thenReturn(inspectionsSummaryList);
neXus

Визначте ExtendWith у суперкласі та MockitoSettings у підкласах, коли йдеться про тестові ієрархії. Сподіваюся, це економить час для когось на мій рахунок.
miracle_the_V

34

Без звуку - це не рішення. Вам потрібно виправити свою фігуру під час тесту. Дивіться офіційну документацію тут .

Непотрібні заглушки - це виклики методів, які ніколи не реалізовувались під час виконання тесту (див. Також MockitoHint), приклад:

//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
 ...

Зверніть увагу на те, що під час виконання тесту в коді, що тестується, ніколи не реалізовувались один із методів затухання. Блудне затягування може бути недоглядом розробника, артефактом copy-paste або ефектом, який не розуміє тест / код. У будь-якому випадку розробник отримує непотрібний тестовий код. Для того, щоб підтримувати базу коду чистою та ремонтопридатною, необхідно видалити непотрібний код. В іншому випадку тести важче читати і міркувати про них.

Щоб дізнатись більше про виявлення невикористаних заглушень, див. MockitoHint.


13
Є багато ситуацій, коли ви пишете 8-9 тестів проти подібної установки @BeforeEach, коли повернутий елемент з одного заглушки не використовується через бізнес-логіку для декількох тестів. Ви можете (A) розбити його на кілька тестів і ефективно скопіювати / вставити розділ \ @BeforeEach мінус один елемент (B) Скопіювати / вставити один рядок, який Mockito є емо, до 6 тестів, які використовують його і мають не в 2, які не роблять, або (C) Використовуйте тихий. Я вважаю за краще використовувати мовчання / попередження. Це не зірваний тест.
RockMeetHardplace

1
@RockMeetHardplace, Silent - це не рішення , швидко ви бачите менше копіювання / вставлення, але при підтримці тестів новими людьми у вашому проекті це буде проблематично. Якщо книгарня Mockito це робить, це не дарма.
Стефан ГРІЛОН

2
@sgrillon: Але ця система виявляє купу помилкових спрацьовувань. Тобто там написано, що щось не використовується, але це явно ні, оскільки видалення заглушки порушує виконання. Справа не в тому, що тестовий код неможливо вдосконалити, це в тому, що життєво важлива лінія заглушення ніколи не повинна розглядатися як "непотрібна". Звідси важливість можливості відключити цю перевірку, вона дуже гостра.
Каріган

@Carighan, якщо ваш знущання виявлено як неправильний, можливо, це не те, що ви думаєте. Це дає вам тест OK, хоча може бути помилка.
Стефан ГРІЛЛОН,

@sgrillon, вибачте, я ніколи не звертався до вас із цього приводу. Виявляється, раніше з цим була помилка, коли в залежності від порядку виконання тесту вона генерувала "помилкові звернення", коли заглушки, які використовувались в одному тесті, але перезаписували в іншому, могли б це викликати. Це вже давно виправлено, наскільки я можу зрозуміти.
Каріган

27

Для мене ні пропозиції, @Ruleні @RunWith(MockitoJUnitRunner.Silent.class)пропозиції не спрацювали. Це був застарілий проект, де ми перейшли на mockito-core 2.23.0.

Ми могли б позбутися UnnecessaryStubbingException, використовуючи:

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

замість:

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

Само собою зрозуміло, що вам краще дивитись на тестовий код, але нам потрібно було скомпілювати матеріали та виконати тести, перш за все;)


6
ІМХО. Це найбільш корисна відповідь тут, яку я знайшов замість того, щоб замовкати весь клас тесту.
priyeshdkr

Оскільки я хотів придушити лише одне глузування, це найкраща відповідь для мене. Насправді це не відповідь на ОП.
Ганс Воутерс,

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

whenТут налаштовує свій макет , щоб зробити що - то. Тим не менш, ви більше не використовуєте цей макет після цього рядка (крім виконання a verify). Mockito попереджає вас, що whenлінія тому безглузда. Можливо, ви допустили логічну помилку?


Дякуємо за допомогу
VHS

Мені потрібні як коли, так і підтверджуючі заяви, люб’язно підкажіть, як рухатися далі
VHS

2
Викличте функцію у вашому тестовому класі ( Service), щоб перевірити, чи вона правильно реагує. Ви зовсім цього не робили, то що ви тут тестуєте?
john16384

3

Дивлячись на частину трасування стека, здається, ви заглушаєте dao.doSearch()деінде. Більш схоже на неодноразове створення заглушок того самого методу.

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.

Розглянемо, наприклад, наступний тест-клас:

@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();
    }
}

Я волів би розглянути можливість рефакторингу ваших тестів, щоб заглушити там, де це необхідно.


2

Якщо ви використовуєте цей стиль замість цього:

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

замініть його на:

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

2

Замініть

@RunWith(MockitoJUnitRunner.class)

з

@RunWith(MockitoJUnitRunner.Silent.class)

або видалити@RunWith(MockitoJUnitRunner.class)

або просто прокоментуйте небажані глузливі дзвінки (відображаються як несанкціоноване заглушення).


1

У мене було, UnnecessaryStubbingExceptionколи я намагався використовувати whenметоди на об’єкті-шпигуні. Mockito.lenient()замовчував виняток, але результати тестування були неправильними.

У випадку шпигунських об'єктів, потрібно викликати методи безпосередньо.

@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

Ну, у моєму випадку помилка Mockito казала мені викликати власне метод після whenабо wheneverзаглушки. Оскільки ми не посилалися на умови, з яких ми просто знущалися, Mockito повідомляв про це як про непотрібні заглушки чи код.

Ось як це було, коли з’являлася помилка:

@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)
}

тоді я просто назвав фактичний метод, згаданий в операторі коли, щоб знущатися над методом.

внесені зміни наведені нижче 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)
}

це працює зараз.


0

У випадку великого проекту важко виправити кожен із цих винятків. У той же час використання Silentне рекомендується. Я написав сценарій для видалення всіх непотрібних заглушень з урахуванням їхнього списку.

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

Нам просто потрібно скопіювати вставний mvnрезультат і написати список цих винятків, використовуючи регулярний вираз, і нехай сценарій подбає про все інше.


0

Якщо ви використовуєте будь-який () під час знущань, вам доведеться перекласти @RunWith (MockitoJUnitRunner.class) з @RunWith (MockitoJUnitRunner.Silent.class).


0

Коли ви створюєте макет, а цей макет не використовується, він видає невикористаний виняток заглушення. У вашому випадку насмішка насправді не називається. Тому вона викидає цю помилку. Отже, relpace, за @RunWith(MockitoJUnitRunner.class)допомогою @RunWith(MockitoJUnitRunner.Silent.class)якого буде видалено помилку. Якщо ви все одно хочете використовувати, @RunWith(MockitoJUnitRunner.class)спробуйте налагодити свою логіку, якщо функція, над якою ви знущалися, насправді викликається чи ні.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.