Come abbinare correttamente i vararg in Mockito


152

Ho cercato di prendere in giro un metodo con parametri vararg usando 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));

Questo non funziona, tuttavia se lo faccio invece:

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

Funziona, nonostante ciò abbia omesso completamente l'argomento varargs quando ho messo a tacere il metodo.

Qualche indizio?


il fatto che l'ultimo esempio funzioni è piuttosto banale poiché corrisponde al caso in cui i parametri varargs zero sono passati.
topchef

Risposte:


235

Mockito 1.8.1 ha introdotto anyVararg () matcher :

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

Vedi anche la storia per questo: https://code.google.com/archive/p/mockito/issues/62

Modifica la nuova sintassi dopo la deprecazione:

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

26
anyVararg()ha Object come tipo di restituzione. Per renderlo compatibile con qualsiasi tipo di argomento (ad es. String ..., Integer ..., ecc.), Esegui un casting esplicito. Ad esempio, se hai doSomething(Integer number, String ... args), puoi fare il codice mock / stub con qualcosa del genere when(mock).doSomething(eq(1), (String) anyVarargs()). Questo dovrebbe occuparsi dell'errore di compilazione.
Psycho Punch,

15
per info anyVararg è ora deprecato: "@deprecated a partire dalla 2.1.0 usa any ()"
alexbt

5
Matchersè ora deprecato per evitare uno scontro di nomi con la org.hamcrest.Matchersclasse e sarà probabilmente rimosso in mockito v3.0. Usa ArgumentMatchersinvece.
JonyD,

31

Una caratteristica in qualche modo priva di documenti: se si desidera sviluppare un Matcher personalizzato che corrisponda agli argomenti vararg, è necessario che sia implementato org.mockito.internal.matchers.VarargMatcheraffinché funzioni correttamente. È un'interfaccia marcatore vuota, senza la quale Mockito non confronterà correttamente gli argomenti quando invoca un metodo con varargs usando il tuo Matcher.

Per esempio:

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

Sulla base della risposta di Eli Levine ecco una soluzione più generica:

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

}

Quindi puoi usarlo con i matcher array di hamcrest in questo modo:

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

(Ovviamente le importazioni statiche renderanno questo più leggibile.)


Bello. Questo dovrebbe essere integrato in Mockito IMO.
Bryant,

Ho presentato un problema contro Hamcrest per aggiungere qualcosa del genere. Vedi github.com/mockito/mockito/issues/356
Segna il

È per Mockito 1? Ottengo vari errori di compilazione quando provo a compilare contro 2.10.
Frans,

@Frans sembra che la versione 2 fosse ancora in beta quando ho scritto questa risposta, quindi sì, probabilmente è stata scritta per Mockito v1.10.19 o giù di lì. ( github.com/mockito/mockito/releases ) È probabilmente aggiornabile ... MrGreen
Peter Westmacott

3

Ho usato il codice nella risposta di Peter Westmacott, tuttavia con Mockito 2.2.15 ora puoi fare quanto segue:

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

dove arg1, arg2, arg3sono i varargs.


1

Basandosi sulla risposta di topchef,

Per 2.0.31-beta ho dovuto usare Mockito.anyVararg invece di Matchers.anyVararrg:

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

3
per info anyVararg è ora deprecato: "@deprecated a partire dalla 2.1.0 usa any ()"
alexbt

0

Nel mio caso la firma del metodo che voglio catturare il suo argomento è:

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

In questo caso è necessario eseguire il cast in modo esplicito dell'array di byte :

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

Sto usando la versione mockito 1.10.19


0

Puoi anche passare in rassegna gli argomenti:

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

ad esempio controlla i loro tipi e li cast in modo appropriato, aggiungere a un elenco o altro.


0

Adattando la risposta da @topchef,

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

Per i documenti java per Mockito 2.23.4, Mockito.any () "Corrisponde a qualsiasi cosa, inclusi valori null e varargs."


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.