Как правильно сопоставить варарги в Mockito

Я пытался издеваться над методом с параметрами 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 при заглушке метода.

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


person qualidafial    schedule 13.04.2010    source источник
comment
Тот факт, что последний пример работает, довольно тривиален, поскольку он соответствует случаю, когда переданы нулевые параметры varargs.   -  person topchef    schedule 14.04.2010


Ответы (10)


Представлен 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);
person topchef    schedule 14.04.2010
comment
anyVararg() имеет объект в качестве возвращаемого типа. Чтобы сделать его совместимым с любыми типами аргументов var (например, String ..., Integer ... и т. Д.), Выполните явное приведение типов. Например, если у вас есть doSomething(Integer number, String ... args), вы можете создать код-заглушку с помощью чего-то вроде when(mock).doSomething(eq(1), (String) anyVarargs()). Это должно позаботиться об ошибке компиляции. - person Psycho Punch; 11.12.2012
comment
для информации anyVararg устарел: @deprecated с версии 2.1.0 используйте any () - person alexbt; 13.11.2016
comment
Matchers теперь устарел, чтобы избежать конфликта имен с классом org.hamcrest.Matchers и, вероятно, будет удален в mockito v3.0. Вместо этого используйте ArgumentMatchers. - person JonyD; 13.07.2017
comment
anyVararg() также устарел в ArgumentMatchers. - person SQB; 05.07.2021

Несколько недокументированная функция: если вы хотите разработать собственный 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);
person Eli Levine    schedule 03.11.2011

Основываясь на ответе Эли Левина, вот более общее решение:

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

(Очевидно, статический импорт сделает это более читаемым.)

person Peter Westmacott    schedule 30.06.2015
comment
Хороший. Это должно быть встроено в Mockito IMO. - person bryant; 23.09.2015
comment
Я подал жалобу на Hamcrest, чтобы добавить что-то вроде этого. См. github.com/mockito/mockito/issues/356. - person Mark; 15.02.2016
comment
Это для Mockito 1? Я получаю различные ошибки компиляции при компиляции против 2.10. - person Frans; 15.02.2018
comment
@Frans, похоже, что версия 2 все еще находилась в стадии бета-тестирования, когда я писал этот ответ, так что да, он, вероятно, был написан для Mockito v1.10.19 или около того. (github.com/mockito/mockito/releases) Вероятно, его можно обновить ...: - D - person Peter Westmacott; 20.02.2018

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

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

где arg1, arg2, arg3 - varargs.

person Mark    schedule 14.11.2016

Основываясь на ответе topchef,

Для 2.0.31-beta мне пришлось использовать Mockito.anyVararg вместо Matchers.anyVararrg:

when(a.b(anyInt(), anyInt(), Mockito.<String>anyVararg())).thenReturn(b);
person NPike    schedule 29.04.2016
comment
для информации anyVararg устарел: @deprecated с версии 2.1.0 используйте any () - person alexbt; 13.11.2016

В моем случае сигнатура метода, аргумент которого я хочу зафиксировать, такова:

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

В этом случае вы должны явно привести к массиву байтов:

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

Я использую версию mockito 1.10.19

person artronics    schedule 03.05.2016

Вы также можете перебрать аргументы:

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

например, проверьте их типы и соответствующим образом приведите их, добавьте в список или что-то еще.

person Richard Whitehead    schedule 10.02.2017

Адаптируя ответ от @topchef,

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

Согласно java-документации для Mockito 2.23.4, Mockito.any () «Соответствует чему угодно, включая нули и varargs».

person Craig    schedule 25.01.2019

Вы можете сделать это, передав захват ArgumentCaptor и затем получив varargs в виде списка с помощью getAllValues, см. https://stackoverflow.com/a/55621731/11342928

person Clarke Lacher    schedule 10.04.2019

Поскольку другие ответы имеют смысл и делают тесты очевидными, я все же рекомендую тестировать, как если бы метод не принимал vararg, а вместо этого использовал обычные четко определенные параметры. Это помогает в ситуациях, когда используются переопределенные методы в связи с возможными неоднозначными параметрами, например SLF4J-logger:

тестировать:

jobLogger.info("{} finished: {} tasks processed with {} failures, took {}", jobName, count, errors, duration);

У этого есть куча переопределений, и важный метод объявлен так:

Logger.info(String, Object...)

проверка:

verify(loggerMock).info(anyString(), anyString(), anyInt(), anyInt(), anyString());

доказательство того, что указанное выше работает, поскольку errors является целым числом, а не длинным, поэтому следующее не будет выполняться:

verify(loggerMock).info(anyString(), anyString(), anyInt(), anyLong(), anyString());

Таким образом, вы можете легко использовать when() вместо verify()-stuff для установки требуемого возвращаемого значения.

И это, вероятно, показывает больше намерений и более читабельно. Здесь также можно использовать захваты, и таким образом они становятся намного доступнее.

Протестировано с Mockito 2.15

person sjngm    schedule 14.06.2021