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


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.
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);

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

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 () «Совпадает с чем угодно, включая нули и переменные».


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.