Como combinar corretamente varargs no Mockito


152

Eu tenho tentado zombar de um método com parâmetros vararg usando o 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));

Isso não funciona, no entanto, se eu fizer isso:

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

Isso funciona, apesar de eu ter omitido completamente o argumento varargs ao remover o método.

Alguma pista?


o fato de que o último exemplo funciona é bastante trivial, pois corresponde ao caso em que zero parâmetros de varargs passaram.
Topchef

Respostas:


235

O Mockito 1.8.1 introduziu o matcher Vararg () :

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

Veja também o histórico para isso: https://code.google.com/archive/p/mockito/issues/62

Edite a nova sintaxe após a descontinuação:

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

26
anyVararg()tem Object como seu tipo de retorno. Para torná-lo compatível com qualquer tipo de var arg (por exemplo, String ..., Inteiro ..., etc.), faça uma conversão explícita. Por exemplo, se você tiver, doSomething(Integer number, String ... args)poderá executar o código de simulação / stub com algo parecido when(mock).doSomething(eq(1), (String) anyVarargs()). Isso deve resolver o erro de compilação.
Psycho Punch

15
para informações anyVararg agora está obsoleta: "@ obsoleto a partir do 2.1.0 use any ()"
alexbt 13/11/16

5
Matchersagora está obsoleto para evitar um conflito de nome com a org.hamcrest.Matchersclasse e provavelmente será removido no mockito v3.0. Use em ArgumentMatchersvez disso.
21417 JJDD em

31

Um recurso não documentado: se você deseja desenvolver um Matcher personalizado que corresponda aos argumentos vararg, é necessário implementá org.mockito.internal.matchers.VarargMatcher-lo para que funcione corretamente. É uma interface de marcador vazia, sem a qual o Mockito não comparará corretamente os argumentos ao invocar um método com varargs usando seu Matcher.

Por exemplo:

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

Com base na resposta de Eli Levine, aqui está uma solução mais genérica:

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

}

Em seguida, você pode usá-lo com os correspondentes de matriz do hamcrest assim:

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

(Obviamente, as importações estáticas tornarão isso mais legível.)


Agradável. Isso deve ser incorporado ao Mockito IMO.
Bryant

Eu registrei uma questão contra Hamcrest para adicionar algo assim. Veja github.com/mockito/mockito/issues/356
Marque

Isso é para Mockito 1? Eu recebo vários erros de compilação ao tentar compilar na versão 2.10.
Frans

@Frans parece que a versão 2 ainda estava em beta quando escrevi esta resposta, então sim, provavelmente foi escrita para o Mockito v1.10.19 ou por aí. ( Github.com/mockito/mockito/releases ) é provavelmente atualizável ... :-D
Peter Westmacott

3

Eu tenho usado o código na resposta de Peter Westmacott, no entanto, com o Mockito 2.2.15, agora você pode fazer o seguinte:

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

Onde arg1, arg2, arg3estão os varargs.


1

Com base na resposta do topchef,

Para o 2.0.31-beta, eu tive que usar o Mockito.anyVararg em vez do Matchers.anyVararrg:

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

3
para informações anyVararg agora está obsoleta: "@ obsoleto a partir do 2.1.0 use any ()"
alexbt 13/11/16

0

No meu caso, a assinatura do método que eu quero capturar seu argumento é:

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

Nesse caso, você deve converter a matriz de bytes explicitamente:

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

Estou usando a versão mockito 1.10.19


0

Você também pode fazer um loop sobre os argumentos:

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

por exemplo, verifique seus tipos e faça a conversão adequada, adicione a uma lista ou o que for.


0

Adaptando a resposta de @topchef,

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

De acordo com os documentos java do Mockito 2.23.4, Mockito.any () "Corresponde a qualquer coisa, incluindo valores nulos e variáveis".


Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.