Як правильно зіставити вараги в Mockito


152

Я намагався змусити знущатися над методом з параметрами vararg за допомогою Mockito:

interface A {
  B b(int x, int y, C... c);
}

A a = mock(A.class);
B b = mock(B.class);

when(a.b(anyInt(), anyInt(), any(C[].class))).thenReturn(b);
assertEquals(b, a.b(1, 2));

Це не працює, однак якщо я це роблю замість цього:

when(a.b(anyInt(), anyInt())).thenReturn(b);
assertEquals(b, a.b(1, 2));

Це працює, незважаючи на те, що я повністю опустив аргумент varargs під час заглушення методу.

Будь-які підказки?


той факт, що останній приклад працює досить тривіально, оскільки він відповідає випадку, коли передано нульові параметри varargs.
топчеф

Відповіді:


235

Mockito 1.8.1 представив будь-який відповідникVararg () :

when(a.b(anyInt(), anyInt(), Matchers.<String>anyVararg())).thenReturn(b);

Також перегляньте історію цього: https://code.google.com/archive/p/mockito/isissue/62

Відредагуйте новий синтаксис після депресії:

when(a.b(anyInt(), anyInt(), ArgumentMatchers.<String>any())).thenReturn(b);

26
anyVararg()як об'єкт повернення має Object. Щоб зробити його сумісним з будь-якими типами аргументів (наприклад, String ..., Integer ..., і т.д.), виконайте явну трансляцію. Наприклад, якщо у вас є, doSomething(Integer number, String ... args)ви можете зробити макет / заглушку коду з чимось подібним when(mock).doSomething(eq(1), (String) anyVarargs()). Це повинно піклуватися про помилку компіляції.
Psycho Punch

15
для інформації anyVararg тепер застаріло: "@deprecked станом на 2.1.0 використовуйте будь-який ()"
alexbt

5
Matchersтепер застаріло, щоб уникнути зіткнення імені з org.hamcrest.Matchersкласом і, ймовірно, буде видалено в mockito v3.0. Використовуйте ArgumentMatchersзамість цього.
JonyD

31

Дещо недокументована функція: Якщо ви хочете розробити спеціальний Matcher, який відповідає аргументам vararg, вам потрібно його реалізувати, org.mockito.internal.matchers.VarargMatcherщоб він працював правильно. Це порожній маркерний інтерфейс, без якого Mockito не буде правильно порівнювати аргументи під час виклику методу з varargs за допомогою вашого Matcher.

Наприклад:

class MyVarargMatcher extends ArgumentMatcher<C[]> implements VarargMatcher {
    @Override public boolean matches(Object varargArgument) {
        return /* does it match? */ true;
    }
}

when(a.b(anyInt(), anyInt(), argThat(new MyVarargMatcher()))).thenReturn(b);

7

Опираючись на відповідь Елі Левіна, тут є більш загальним рішенням:

import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.mockito.ArgumentMatcher;
import org.mockito.internal.matchers.VarargMatcher;

import static org.mockito.Matchers.argThat;

public class VarArgMatcher<T> extends ArgumentMatcher<T[]> implements VarargMatcher {

    public static <T> T[] varArgThat(Matcher<T[]> hamcrestMatcher) {
        argThat(new VarArgMatcher(hamcrestMatcher));
        return null;
    }

    private final Matcher<T[]> hamcrestMatcher;

    private VarArgMatcher(Matcher<T[]> hamcrestMatcher) {
        this.hamcrestMatcher = hamcrestMatcher;
    }

    @Override
    public boolean matches(Object o) {
        return hamcrestMatcher.matches(o);
    }

    @Override
    public void describeTo(Description description) {
        description.appendText("has varargs: ").appendDescriptionOf(hamcrestMatcher);
    }

}

Тоді ви можете використовувати його з відповідниками масивів hamcrest таким чином:

verify(a).b(VarArgMatcher.varArgThat(
            org.hamcrest.collection.IsArrayContaining.hasItemInArray("Test")));

(Очевидно статичний імпорт зробить це читабельніше.)


Приємно. Це має бути вбудовано в Mockito IMO.
Брайант

Я подав заяву проти Hamcrest, щоб додати щось подібне. Дивіться github.com/mockito/mockito/isissue/356
Марк

Це для Mockito 1? Я отримую різні помилки компіляції при спробі компілювати проти 2.10.
Франс

@Frans, схоже, що випуск 2 все ще був у бета-версії, коли я писав цю відповідь, так що так, ймовірно, це було написано для Mockito v1.10.19 або після цього. ( github.com/mockito/mockito/releases ) Це, ймовірно, оновлення ... :-D
Пітер Вестмакот

3

Я використовував код у відповіді Пітера Вестмакотта, однак за допомогою Mockito 2.2.15 ви можете зробити наступне:

verify(a).method(100L, arg1, arg2, arg3)

де arg1, arg2, arg3вараги.


1

Спираючись на відповідь топчефа,

Для версії 2.0.31-beta мені довелося використовувати Mockito.anyVararg замість Matchers.anyVararrg:

when(a.b(anyInt(), anyInt(), Mockito.<String>anyVararg())).thenReturn(b);

3
для інформації anyVararg тепер застаріло: "@deprecked станом на 2.1.0 використовуйте будь-який ()"
alexbt

0

У моєму випадку підписом методу, який я хочу захопити його аргументом, є:

    public byte[] write(byte ... data) throws IOException;

У цьому випадку вам слід передати байтовий масив явно:

       when(spi.write((byte[])anyVararg())).thenReturn(someValue);

Я використовую версію mockito 1.10.19


0

Ви також можете переглядати аргументи:

Object[] args = invocation.getArguments(); 
for( int argNo = 0; argNo < args.length; ++argNo) { 
    // ... do something with args[argNo] 
}

наприклад, перевірте їх типи та додайте їх відповідним чином, додайте до списку чи будь-чого іншого.


0

Адаптуючи відповідь від @topchef,

Mockito.when(a.b(Mockito.anyInt(), Mockito.anyInt(), Mockito.any())).thenReturn(b);

Згідно з документами Java для Mockito 2.23.4, Mockito.any () "відповідає будь-чому, включаючи нулі та вараги."


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