Как правильно подобрать вараргов в Мокито

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 при создании метода.

Есть какие-нибудь подсказки?

qualidafial
источник
тот факт, что последний пример работает, довольно тривиален, поскольку он соответствует случаю, когда передаются нулевые параметры varargs.
topchef

Ответы:

235

В Mockito 1.8.1 введено сопоставление anyVararg () :

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

Также смотрите историю этого: https://code.google.com/archive/p/mockito/issues/62

Изменить новый синтаксис после устаревания:

when(a.b(anyInt(), anyInt(), ArgumentMatchers.<String>any())).thenReturn(b);
topchef
источник
26
anyVararg()имеет Object в качестве возвращаемого типа. Чтобы сделать его совместимым с любыми типами var arg (например, String ..., Integer ... и т. Д.), Выполните явное приведение. Например, если у вас есть, doSomething(Integer number, String ... args)вы можете сделать код макета / заглушки с чем-то вроде when(mock).doSomething(eq(1), (String) anyVarargs()). Это должно позаботиться об ошибке компиляции.
Психо Панч
15
для информации anyVararg устарела: «@deprecated с 2.1.0 используйте any ()»
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/issues/356
Марк
Это для Мокито 1? Я получаю различные ошибки компиляции при попытке компиляции с 2.10.
Франс
@ Франц, похоже, когда я написал этот ответ, версия 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-бета я должен был использовать Mockito.anyVararg вместо Matchers.anyVararrg:

when(a.b(anyInt(), anyInt(), Mockito.<String>anyVararg())).thenReturn(b);
NPike
источник
3
для информации anyVararg устарела: «@deprecated с 2.1.0 используйте any ()»
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 () «Совпадает с чем угодно, включая нули и переменные».

Craig
источник