Как правилно да съпоставите varargs в 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() има Object като тип на връщане. За да го направите съвместим с всякакви типове променливи аргументи (напр. 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

Донякъде недокументирана функция: Ако искате да разработите персонализиран съпоставител, който съвпада с 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

Надграждане на отговора на Eli Levine тук е по-общо решение:

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) Вероятно може да се актуализира... :- д - person Peter Westmacott; 20.02.2018

Използвах кода в отговора на Peter Westmacott, но с 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-бета трябваше да използвам 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() "Съвпада с всичко, включително null и 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-регистратор:

да тествам:

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