Могу ли я манипулировать порядком сопоставителей мокито? - PullRequest
0 голосов
/ 21 марта 2019

Некоторый контекст

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

when(mock.method(1, 2, 3));
when(mock.method(eq(1), eq(2), eq(3)));

Я говорю о втором стиле.

Из-за того, как работает Mockito, важен порядок, в котором называются сопоставители. Внутри Mockito будет регистрировать совпадения в стеке, выполняя их по мере необходимости.

Чего я пытаюсь достичь

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

Например:

public String callOnMock(int argument2) {
    return mock.call(eq(1), argument2, argThat(i -> i >= 3));
}

который будет использоваться так:

when(callOnMock(eq(2)).thenReturn("result");

проблема

Это не работает, потому что Mockito регистрирует эти сопоставители в неправильном порядке:

  1. eq(2)
  2. eq(1)
  3. argThat(i -> i >= 3)

пока должно быть

  1. eq(1)
  2. eq(2)
  3. argThat(i -> i >= 3)

Есть ли способ для меня манипулировать порядком, в котором регистрируются эти совпадения?

Теперь я знаю, что org.mockito.AdditionalMatchers имеет методы, которые управляют внутренним стеком, позволяя комбинировать сопоставители (and, or, not), так что по крайней мере внутри ядра Mockito это возможно.

Можно ли явным образом вставлять и выдвигать совпадения?

Ответы [ 3 ]

1 голос
/ 22 марта 2019

Используйте Supplier:

public String callOnMock(Supplier<Integer> argument2) {
    return mock.call(eq(1), argument2.get(), argThat(i -> i >= 3));
}

when(callOnMock(() -> eq(2)).thenReturn("result");
0 голосов
/ 24 марта 2019

Я думаю, что есть несколько способов добиться желаемого поведения.

1.Управлять порядком совпадений в стеке

Это не тот путь, по которому можно идти!

Кажется, что matcherStack является внутренним для Mockito.
У них есть метод pullLocalizedMatchers из стека и reportMatcher метод для добавления ArgumentMatcher в стек.К ним можно получить доступ через

org.mockito.internal.progress.ThreadSafeMockingProgress
    .mockingProgress()
    .getArgumentMatcherStorage()

Таким образом, теоретически вы можете выбрать этот путь, но решение будет хрупким, потому что вы возитесь с внутренностями Mockito.Они могут измениться без уведомления в последующих версиях Mockito.

К счастью, есть пара альтернатив.

2.Управляйте порядком, в котором в первую очередь регистрируются сопоставители

, используя функциональный интерфейс Java 8 Supplier (Это соответствует этому ответу , заданному @ToYonos)

Сопоставители автоматически регистрируются Mockito при вызове методов, их создающих (eq, argThat, any, isNotNull, ...).Но вы можете отложить вызов этих методов, передав Supplier для каждого из этих сопоставителей.Затем удобный метод управляет порядком, в котором он выполняет этих поставщиков.

public String callOnMock(Supplier<Integer> argument2) {
    return mock.call(eq(1), argument2.get(), argThat(i -> i >= 3));
}

when(callOnMock(() -> eq(2))).thenReturn("result");

Использование его выглядит несколько иначе, чем обычный стиль Mockito.

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

callOnMock(() -> AdditionalMatchers.and(isNotNull(), eq(2)))

будет работать,
, но это не будет:

public Supplier<Integer> and(int matcher1, int matcher2){
   return () -> AdditionalMatchers.and(matcher1, matcher2);
}

callOnMock(and(isNotNull(), eq(2)))

Это накладывает некоторую ответственность на пользователя ваших методов.Они должны убедиться, что никто из участников не будет вызван случайно.

3.Управление порядком, в котором макеты ожидают совпадений

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

public interface MockDelegate {
    String call(Integer i1, Integer i0, Integer i2);
}

@Mock
private MockDelegate delegate;

@Before
public void setUp() {
    when(mock.call(any(), any(), any()))
            .thenAnswer(invocation -> delegate.call(
                    invocation.getArgument(1), // this delegates the call
                    invocation.getArgument(0), // but flips the first two arguments
                    invocation.getArgument(2)
            ));
}

public String callOnMock(int argument2) {
    return delegate.call(argument2, eq(1), argThat(i -> i >= 3));
}

Это можно использовать с обычными сопоставителями стиля Mockito:

when(callOnMock(eq(2))).thenReturn("result");
0 голосов
/ 21 марта 2019

Попробуйте:

public String callOnMock(int argument2) {
    return mock.call(eq(1), eq(argument2), argThat(i -> i >= 3));
}

и назовите его следующим образом:

when(callOnMock(2)).thenReturn("result");
...