Симуляция сервиса с использованием Mockito 2 приводит к ошибке заглушки - PullRequest
0 голосов
/ 02 сентября 2018

Я пытаюсь смоделировать поведение класса, используя Mockito. Это работало с использованием Mockito 1.x. Переход на JUnit 5 и Mockito 2, похоже, больше не работает.

@ExtendWith(MockitoExtension.class)
public class MockitoExample {

  static abstract class TestClass {
    public abstract int booleanMethod(boolean arg);
  }

  @Mock
  TestClass testClass;

  @BeforeEach
  public void beforeEach() {
    when(testClass.booleanMethod(eq(true))).thenReturn(1);
    when(testClass.booleanMethod(eq(false))).thenReturn(2);
  }

  @Test
  public void test() {
    assertEquals(1,testClass.booleanMethod(true));
    assertEquals(2,testClass.booleanMethod(false));
  }
}

Предполагается, что фиктивный TestClass показывает поведение, протестированное в тестовом методе.

Я получаю ошибку:

org.mockito.exceptions.misusing.PotentialStubbingProblem: 

  Strict stubbing argument mismatch. Please check:
   - this invocation of 'booleanMethod' method:
      testClass.booleanMethod(false);
      -> at org.oneandone.ejbcdiunit.mockito_example.MockitoExample.beforeEach(MockitoExample.java:30)
   - has following stubbing(s) with different arguments:
      1. testClass.booleanMethod(false);
        -> at org.oneandone.ejbcdiunit.mockito_example.MockitoExample.beforeEach(MockitoExample.java:29)
  Typically, stubbing argument mismatch indicates user mistake when writing tests.
  Mockito fails early so that you can debug potential problem easily.
  However, there are legit scenarios when this exception generates false negative signal:
    - stubbing the same method multiple times using 'given().will()' or 'when().then()' API
      Please use 'will().given()' or 'doReturn().when()' API for stubbing.
    - stubbed method is intentionally invoked with different arguments by code under test
      Please use default or 'silent' JUnit Rule (equivalent of Strictness.LENIENT).
  For more information see javadoc for PotentialStubbingProblem class.

В обоих случаях аргумент false, похоже, совпадает, хотя я явно совпал с true.

Это ошибка в Mockito 2.17 или недоразумение. Как / можно использовать Mockito 2.x для имитации вызовов с различными логическими аргументами?

Пример также можно найти на github. Но верный запуск теста начнется только с использованием

mvn test -Dtest=MockitoExample

Выполнение теста с использованием Mockito 2.21 приводит к тем же результатам.

Ответы [ 4 ]

0 голосов
/ 10 ноября 2018

Мокито 1 и 2 не имеют одинакового уровня "строгости".
Кроме того, при использовании Mockito 2 с JUnit 4 или 5 уровень по умолчанию все равно будет другим.

Подводя итог:

3 уровня строгости:

  • LENIENT: минимальная строгость
  • WARN: дополнительные предупреждения выводятся на консоль
  • STRICT_STUBS: обеспечивает чистые тесты, выдавая исключение в случае возможного неправильного использования, но также может давать некоторые ложные срабатывания.

Эффективный уровень по умолчанию в соответствии с используемыми API:

  • Mockito 1: LENIENT
  • Mockito 2 с JUnit 4: WARN
  • Mockito 2 с JUnit 5 (MockitoExtension.class): STRICT_STUBS
  • Mockito 3: запланировано на STRICT_STUBS.

Подробнее

В документации Mockito об этом очень ясно сказано:

Strictness Javadoc состояния:

Настраивает "строгость" Мокито во время сеанса насмешки. сеанс обычно отображается на один вызов метода тестирования. взыскательность проводит более чистые тесты и повышает производительность. Самый простой способ усилить строгость использует поддержку MUKITO JUnit (MockitoRule или MockitoJUnitRunner). Если вы не можете использовать поддержку JUnit MockitoSession - это путь.

Как уровень строгости влияет на поведение теста (насмешливый сессия)

1. Strictness.LENIENT - без дополнительного поведения. По умолчанию Mockito 1.x.Рекомендуется, только если вы не можете использовать STRICT_STUBS или WARN.

2. Strictness.WARN - помогает поддерживать чистоту тестов и улучшает возможность отладки. Сообщает о предупреждениях консоли о неиспользуемых заглушках и заглушках несоответствие аргумента (см. org.mockito.quality.MockitoHint). По умолчанию Поведение Mockito 2.x при использовании JUnitRule или MockitoJUnitRunner. Рекомендуется, если вы не можете использовать STRICT_STUBS.

3. Strictness.STRICT_STUBS - обеспечивает чистоту тестов, уменьшает дублирование тестов, улучшает возможность отладки. Лучшее сочетание гибкости и производительность. Настоятельно рекомендуется. Планируется по умолчанию для Mockito v3.Для получения подробной информации см. STRICT_STUBS.

Но независимо от того, что сгенерировано исключение, связанное с сообщением

"имеет следующие заглушки с разными аргументами"

кажется слишком строгой проверкой. Сообщение об исключении в некотором роде подтверждает это:

Однако существуют законные сценарии, когда это исключение создает ложное отрицательный сигнал:

...

  • метод-заглушка намеренно вызывается с различными аргументами тестируемым кодом

Так что запретить его по умолчанию кажется слишком много.
Так что, если вы используете JUnit 5, в качестве альтернативы STRICT_STUBS вы можете использовать WARNING, но вы обычно хотите избегать LENIENT, который слишком тихий.

В дополнение к MockitoExtension библиотека mockito-junit-jupiter предоставляет @MockitoSettings, который можно использовать как на уровне метода, так и на уровне класса.

Вот пример:

import java.util.List;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;

@ExtendWith(MockitoExtension.class)
public class FooTest {

    @MockitoSettings(strictness = Strictness.WARN)
    @Test
    void foo() throws Exception {
        List<String> strings = Mockito.mock(List.class);
        Mockito.when(strings.add("a"))
               .thenReturn(true);
        Mockito.when(strings.add("b"))
               .thenReturn(false);
    }

    @Test
    void fooKo() throws Exception {
        List<String> strings = Mockito.mock(List.class);
        Mockito.when(strings.add("a"))
               .thenReturn(true);
        Mockito.when(strings.add("b"))
               .thenReturn(false);

    }

}

fooKo() выдает исключение неправильного использования Mockito, в то время как foo() успешно, но выдает полезные предупреждения:

[MockitoHint] FooTest (see javadoc for MockitoHint):
[MockitoHint] 1. Unused -> at FooTest.foo(FooTest.java:19)
[MockitoHint] 2. Unused -> at FooTest.foo(FooTest.java:21)

В качестве другой альтернативы вы также можете использовать Mockito.lenient(), очень хорошо описанный aschoerk, чтобы применить снисходительную строгость для конкретного вызова. Кроме того, вы можете установить все ложные вызовы как снисходительные при создании экземпляра:

@Test
void foo() throws Exception {
    List<String> strings = Mockito.mock(List.class, Mockito.withSettings()
                                                           .lenient());
     ....
}
0 голосов
/ 03 сентября 2018

Поскольку первый ответ оказался неожиданным, я проверил следующее:

interface Poops {
    String get(boolean is);
}

@Test
void test1() {
    Poops a = mock(Poops.class);

    when(a.get(eq(true))).thenReturn("1");
    when(a.get(eq(false))).thenReturn("2");

    Assertions.assertEquals("1", a.get(true));
    Assertions.assertEquals("2", a.get(false));
}

Работает с Mockito 2.21.0.

Обновление : Кажется, проблема в расширении Jupiter Mockito, которое меняет настройку по умолчанию на Strictness.STRICT_STUBS.

0 голосов
/ 04 сентября 2018

Начиная с Mockito 2.20, можно также добавить lenient () локально

@ExtendWith(MockitoExtension.class)
public class MockitoExample {

  static abstract class TestClass {
    public abstract int booleanMethod(boolean arg);
  }

  @Mock
  TestClass testClass;

  @BeforeEach
  public void beforeEach() {
    lenient().when(testClass.booleanMethod(eq(true))).thenReturn(1);
    lenient().when(testClass.booleanMethod(eq(false))).thenReturn(2);
  }

  @Test
  public void test() {
    assertEquals(1,testClass.booleanMethod(true));
    assertEquals(2,testClass.booleanMethod(false));
  }
}
0 голосов
/ 02 сентября 2018

При строгих заглушках (стандартное поведение Mockito) вызов нескольких when с одним и тем же методом сбрасывает эту имитацию. Решение состоит в том, чтобы вызвать when один раз и иметь логику в Answer:

@BeforeEach
public void beforeEach() {
    when(testClass.booleanMethod(anyBoolean())).thenAnswer(invocationOnMock -> {
        if ((boolean) invocationOnMock.getArguments()[0]) {
            return 1;
        }
        return 2;
    });
}

В качестве альтернативы вы можете использовать снисходительный макет, но это не всегда хорошая идея - снисходительный макет допускает избыточную заглушку и облегчает вам ошибки в тесте, которые могут привести к незаметным ошибкам в «производственном» коде :

@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
public class MockitoExample {
...