Как разрешить исключение Unneccessary Stubbing

104

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

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

Пожалуйста, помогите мне как решить

VHS
источник

Ответы:

118

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

Сумит
источник
44
Добро пожаловать. Было бы неплохо обновить свой ответ, чтобы объяснить, почему они OP должны заменять такой код. Это поможет понять их и будущим посетителям.
Ошибки
5
@RunWith(MockitoJUnitRunner.Silent.class)
Между прочим
6
В Котлине:@RunWith(MockitoJUnitRunner.Silent::class)
Хуан Саравиа
11
Не уверен, почему этот ответ продолжает получать голоса без объяснения причин. Другие ответы более содержательны и точны.
Йогеш
10
Это не решает проблему, а просто подавляет сообщение об ошибке, а также повлияет на все другие тесты (если таковые имеются) в классе.
Fencer
105

Сначала вы должны проверить свою тестовую логику. Обычно бывает 3 случая. Во-первых, вы высмеиваете неправильный метод (вы допустили опечатку или кто-то изменил проверенный код, так что имитируемый метод больше не используется). Во-вторых, ваш тест завершился ошибкой до вызова этого метода. В-третьих, ваша логика ошибается, если ветвь if / switch где-то в коде, так что фиктивный метод не вызывается.

Если это первый случай, вы всегда хотите изменить фиктивный метод на тот, который используется в коде. Со вторым и третьим зависит. Обычно вы должны просто удалить этот макет, если он не нужен. Но иногда в параметризованных тестах есть определенные случаи, когда они должны идти по другому пути или терпеть неудачу раньше. Затем вы можете разделить этот тест на два или несколько отдельных, но это не всегда хорошо выглядит. 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-jupiterpackage.

@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
class JUnit5MockitoTest {
}
Dcortez
источник
3
@MockitoSettings (strictness = Strictness.LENIENT) - самый простой способ отрегулировать строгость в моей настройке. Спасибо!
Мэтт
7
Этот ответ дает хороший обзор возможностей. Однако вы также можете установить снисходительную строгость в каждом конкретном случае, используя Mockito.lenient().when(...): для этого конкретного вопроса это будетMockito.lenient().when(dao.doSearch(dto)).thenReturn(inspectionsSummaryList);
neXus
Определите ExtendWith в суперклассе и MockitoSettings в подклассах при работе с тестовыми иерархиями. Надеюсь, это сэкономит время кому-нибудь за мой счет.
miracle_the_V 05
38

Молчать - это не решение. Вам нужно исправить свой макет в своем тесте. См. Официальную документацию здесь .

Ненужные заглушки - это заглушенные вызовы методов, которые никогда не были реализованы во время выполнения теста (см. Также 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
 ...

Обратите внимание, что один из заглушенных методов никогда не был реализован в тестируемом коде во время выполнения теста. Блуждающая заглушка может быть недосмотром разработчика, артефактом копирования-вставки или эффектом непонимания теста / кода. В любом случае разработчик получает ненужный тестовый код. Чтобы кодовая база оставалась чистой и поддерживаемой, необходимо удалить ненужный код. В противном случае тесты труднее читать и рассуждать.

Чтобы узнать больше об обнаружении неиспользуемых заглушек, см. MockitoHint.

Стефан ГРИЛЬОН
источник
14
Есть много ситуаций, когда вы пишете 8–9 тестов для аналогичной установки @BeforeEach, когда возвращаемый элемент из одной заглушки не используется из-за бизнес-логики в нескольких тестах. Вы можете либо (A) разбить его на несколько тестов и эффективно скопировать / вставить раздел \ @BeforeEach за вычетом одного элемента (B) Скопировать / вставить единственную строку, в которой Mockito собирается эмо, в 6 тестов, которые ее используют, и получить ее. не в 2, которые этого не делают, или (C) Используйте беззвучный режим. Я предпочитаю использовать молчание / предупреждение. Это не сломанный тест.
RockMeetHardplace
1
@RockMeetHardplace, Silent - это не решение , быстро вы увидите меньше копий / вставок, но при поддержке ваших тестов новыми людьми в вашем проекте это будет проблематично. Если книжный магазин Mockito делает это, то не зря.
Стефан ГРИЛЛОН
2
@sgrillon: Но эта система обнаруживает множество ложных срабатываний. То есть он говорит, что что-то не используется, но это явно не так, поскольку удаление заглушки прерывает выполнение. Дело не в том, что тестовый код нельзя улучшить, а в том, что жизненно важная строка заглушки никогда не должна определяться как «ненужная». Следовательно, важно иметь возможность отключить эту проверку, она слишком рьяна.
Кариган
1
@Carighan, если ваш макет определен как неправильный, возможно, это не то, что вы думаете. Это дает вам ОК, хотя может быть ошибка.
Стефан ГРИЛЬОН
@sgrillon, извини, что так и не ответил на это. Оказывается, раньше была ошибка, когда в зависимости от порядка выполнения теста он генерировал «ложные совпадения», когда заглушки, которые использовались в одном тесте, но перезаписывались в другом, вызывали его. Однако, насколько я могу судить, это давно исправлено.
Кариган
32

Для меня ни предложения, @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());

Излишне говорить, что вам лучше смотреть на тестовый код, но нам нужно было скомпилировать материал и запустить тесты в первую очередь;)

щедрый
источник
8
ПО МОЕМУ МНЕНИЮ. Это самый полезный ответ, который я нашел, вместо того, чтобы отключить весь тестовый класс.
priyeshdkr
1
Поскольку я хотел подавить только 1 насмешку, это лучший ответ для меня. Не совсем ответ для OP.
Ханс Воутерс,
25
 when(dao.doSearch(dto)).thenReturn(inspectionsSummaryList);//got error in this line
 verify(dao).doSearchInspections(dto);

whenЗдесь настраивает свой макет , чтобы сделать что - то. Однако вы больше не используете этот макет после этой строки (кроме выполнения a verify). Mockito предупреждает вас, что whenстрочка бессмысленна. Возможно, вы допустили логическую ошибку?

john16384
источник
Спасибо за вашу помощь
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();
    }
}

Я бы предпочел рассмотреть возможность рефакторинга ваших тестов, чтобы при необходимости заглушить их.

Railomaya
источник
1

Если вместо этого вы используете этот стиль:

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

замените его на:

@Rule
public MockitoRule rule = MockitoJUnit.rule().silent();
Грег
источник
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)
}

затем я просто вызвал реальный метод, упомянутый в инструкции when, чтобы имитировать метод.

изменения сделаны, как показано ниже 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)
}

теперь работает.

Викас Кумар
источник
1

Заменить

@RunWith(MockitoJUnitRunner.class)

с участием

@RunWith(MockitoJUnitRunner.Silent.class)

или удалить@RunWith(MockitoJUnitRunner.class)

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

Накул Гоял
источник
0

В случае большого проекта сложно исправить каждое из этих исключений. При этом использование Silentне рекомендуется. Я написал сценарий для удаления всех ненужных заглушек, учитывая их список.

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

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

Mohitmayank
источник
-1

Если вы используете any () при насмешке, вам нужно заменить @RunWith (MockitoJUnitRunner.class) на @RunWith (MockitoJUnitRunner.Silent.class).

Абду АССУМАН
источник
Это просто ложь. any()отлично работает с обычным раннером при правильном использовании.
Матфей Рид
-1

Когда вы создаете макет и этот макет не используется, он выдает исключение неиспользуемой заглушки. В вашем случае этот макет на самом деле не называется. Следовательно, это вызывает эту ошибку. Следовательно, relpace, @RunWith(MockitoJUnitRunner.class)с помощью @RunWith(MockitoJUnitRunner.Silent.class)которого можно удалить ошибку. Если вы все еще хотите использовать, @RunWith(MockitoJUnitRunner.class)попробуйте отладить свою логику, если функция, которую вы высмеяли, действительно вызывается или нет.

Шивам Кохли
источник
Это не добавляет ничего, чего не было в существующих ответах.
Матфей Рид