Os matchers do Mockito são métodos estáticos e chamadas para esses métodos, que substituem argumentos durante as chamadas para when
e verify
.
Os correspondentes Hamcrest (versão arquivada) (ou correspondentes no estilo Hamcrest) são instâncias de objetos de uso geral sem estado e que implementam Matcher<T>
e expõem um método matches(T)
que retorna true se o objeto corresponder aos critérios do Correspondente. Eles pretendem estar livres de efeitos colaterais e geralmente são usados em afirmações como a abaixo.
/* Mockito */ verify(foo).setPowerLevel(gt(9000));
/* Hamcrest */ assertThat(foo.getPowerLevel(), is(greaterThan(9000)));
Existem correspondências Mockito, separadas das correspondências no estilo Hamcrest, para que as descrições das expressões correspondentes se ajustem diretamente às invocações de métodos : As correspondências Mockito retornam T
onde os métodos correspondentes do Hamcrest retornam objetos Matcher (do tipo Matcher<T>
).
Matchers Mockito são invocadas através de métodos estáticos, como eq
, any
, gt
, e startsWith
no org.mockito.Matchers
e org.mockito.AdditionalMatchers
. Também existem adaptadores que foram alterados nas versões do Mockito:
- Para o Mockito 1.x,
Matchers
algumas chamadas em destaque (como intThat
ou argThat
) são correspondências Mockito que aceitam diretamente correspondências Hamcrest como parâmetros. ArgumentMatcher<T>
extended org.hamcrest.Matcher<T>
, que foi usado na representação interna do Hamcrest e era uma classe base do combinador Hamcrest em vez de qualquer tipo de combinador Mockito.
- Para o Mockito 2.0+, o Mockito não tem mais uma dependência direta do Hamcrest.
Matchers
chama formulado como intThat
ou argThat
quebra de ArgumentMatcher<T>
objetos que não são mais implementados, org.hamcrest.Matcher<T>
mas são usados de maneiras semelhantes. Adaptadores Hamcrest como argThat
e intThat
ainda estão disponíveis, mas foram movidos para MockitoHamcrest
ele.
Independentemente de os matchers serem do tipo Hamcrest ou simplesmente do tipo Hamcrest, eles podem ser adaptados da seguinte forma:
/* Mockito matcher intThat adapting Hamcrest-style matcher is(greaterThan(...)) */
verify(foo).setPowerLevel(intThat(is(greaterThan(9000))));
Na declaração acima: foo.setPowerLevel
é um método que aceita um int
. is(greaterThan(9000))
retorna a Matcher<Integer>
, que não funcionaria como setPowerLevel
argumento. A correspondência Mockito intThat
embrulha que Hamcrest-style Matcher e retorna um int
modo que pode aparecer como um argumento; Correspondentes de Mockito como agrupariam gt(9000)
toda a expressão em uma única chamada, como na primeira linha do código de exemplo.
O que os jogadores fazem / retornam
when(foo.quux(3, 5)).thenReturn(true);
Quando não estiver usando correspondentes de argumento, o Mockito registra seus valores de argumento e os compara com seus equals
métodos.
when(foo.quux(eq(3), eq(5))).thenReturn(true); // same as above
when(foo.quux(anyInt(), gt(5))).thenReturn(true); // this one's different
Quando você chama um matcher como any
ou gt
(maior que), o Mockito armazena um objeto correspondente que faz com que o Mockito pule essa verificação de igualdade e aplique sua correspondência de escolha. No caso, argumentCaptor.capture()
ele armazena um correspondente que salva seu argumento para uma inspeção posterior.
Os correspondentes retornam valores fictícios como zero, coleções vazias ou null
. Mockito tenta retornar um valor fictício seguro e apropriado, como 0 para anyInt()
ou any(Integer.class)
ou vazio List<String>
para anyListOf(String.class)
. Por causa da exclusão de tipo, no entanto, o Mockito não possui informações de tipo para retornar qualquer valor, exceto null
para any()
or argThat(...)
, o que pode causar uma NullPointerException se tentar "descompactar automaticamente" um null
valor primitivo.
Matchers gostam eq
e gt
aceitam valores de parâmetros; idealmente, esses valores devem ser calculados antes do início da verificação / remoção. Chamar uma zombaria no meio da zombaria de outra chamada pode interferir com o stub.
Os métodos correspondentes não podem ser usados como valores de retorno; não há como expressar thenReturn(anyInt())
ou thenReturn(any(Foo.class))
no Mockito, por exemplo. O Mockito precisa saber exatamente qual instância retornar nas chamadas stubbing e não escolherá um valor de retorno arbitrário para você.
Detalhes da implementação
Os correspondentes são armazenados (como correspondentes do objeto no estilo Hamcrest) em uma pilha contida em uma classe chamada ArgumentMatcherStorage . MockitoCore e Matchers possuem uma instância ThreadSafeMockingProgress , que estaticamente contém um ThreadLocal que contém instâncias MockingProgress. É este MockingProgressImpl que contém um ArgumentMatcherStorageImpl concreto . Conseqüentemente, o estado de simulação e de correspondência é estático, mas com escopo de segmento consistente entre as classes Mockito e Matchers.
A maioria das chamadas matcher só adicionar a esta pilha, com uma exceção para matchers como and
, or
, enot
. Isso corresponde perfeitamente (e depende) da ordem de avaliação do Java , que avalia os argumentos da esquerda para a direita antes de chamar um método:
when(foo.quux(anyInt(), and(gt(10), lt(20)))).thenReturn(true);
[6] [5] [1] [4] [2] [3]
Isso vai:
- Adicione
anyInt()
à pilha.
- Adicione
gt(10)
à pilha.
- Adicione
lt(20)
à pilha.
- Retirar
gt(10)
e lt(20)
e adicionar and(gt(10), lt(20))
.
- Chamada
foo.quux(0, 0)
, que (a menos que seja stubada de outra forma) retorna o valor padrão false
. Internamente, Mockito marca quux(int, int)
como a chamada mais recente.
- Chamada
when(false)
, que descarta seu argumento e se prepara para o método de stub quux(int, int)
identificado em 5. Os únicos dois estados válidos são com tamanho de pilha 0 (igualdade) ou 2 (correspondências), e há duas correspondências na pilha (etapas 1 e 4), portanto Mockito stubs o método com um any()
matcher para seu primeiro argumento e and(gt(10), lt(20))
para o segundo argumento e limpa a pilha.
Isso demonstra algumas regras:
Mockito não pode dizer a diferença entre quux(anyInt(), 0)
e quux(0, anyInt())
. Ambos se parecem com uma chamada quux(0, 0)
com um int matcher na pilha. Conseqüentemente, se você usar um correspondente, precisará corresponder a todos os argumentos.
A ordem de chamada não é apenas importante, é o que faz tudo funcionar . Extrair matchers para variáveis geralmente não funciona, porque geralmente altera a ordem das chamadas. Extrair matchers para métodos, no entanto, funciona muito bem.
int between10And20 = and(gt(10), lt(20));
/* BAD */ when(foo.quux(anyInt(), between10And20)).thenReturn(true);
// Mockito sees the stack as the opposite: and(gt(10), lt(20)), anyInt().
public static int anyIntBetween10And20() { return and(gt(10), lt(20)); }
/* OK */ when(foo.quux(anyInt(), anyIntBetween10And20())).thenReturn(true);
// The helper method calls the matcher methods in the right order.
A pilha muda com frequência suficiente para que Mockito não possa policiá-la com muito cuidado. Ele só pode verificar a pilha quando você interage com o Mockito ou com uma farsa, e precisa aceitar jogadores sem saber se eles são usados imediatamente ou abandonados acidentalmente. Em teoria, a pilha sempre deve estar vazia fora de uma chamada para when
ou verify
, mas o Mockito não pode verificar isso automaticamente. Você pode verificar manualmente com Mockito.validateMockitoUsage()
.
Em uma chamada para when
, Mockito realmente chama o método em questão, que lançará uma exceção se você tiver stubado o método para lançar uma exceção (ou exigir valores diferentes de zero ou não nulos).
doReturn
e doAnswer
(etc) não invocam o método real e geralmente são uma alternativa útil.
Se você tivesse chamado um método simulado no meio do stub (por exemplo, para calcular uma resposta para um eq
matcher), o Mockito verificaria o comprimento da pilha nessa chamada e provavelmente falharia.
Se você tentar fazer algo ruim, como stubbing / verificação de um método final , o Mockito chamará o método real e também deixará matchers extras na pilha . ofinal
chamada do método pode não gerar uma exceção, mas você pode obter uma InvalidUseOfMatchersException dos correspondentes dispersos quando você interagir com uma simulação.
Problemas comuns
InvalidUseOfMatchersException :
Verifique se todos os argumentos têm exatamente uma chamada de correspondência, se você usa correspondências, e se você não usou uma correspondência fora de um when
verify
chamada ou . Os correspondentes nunca devem ser usados como valores de retorno stubbed ou campos / variáveis.
Verifique se você não está chamando de simulação como parte do fornecimento de um argumento de correspondência.
Verifique se você não está tentando stub / verificar um método final com um matcher. É uma ótima maneira de deixar um matcher na pilha e, a menos que seu método final gere uma exceção, talvez seja a única vez que você percebe que o método que está zombando é final.
NullPointerException com argumentos primitivos: (Integer) any()
retorna nulo enquanto any(Integer.class)
retorna 0; isso pode causar um NullPointerException
se você estiver esperando um em int
vez de um número inteiro. De qualquer forma, prefira anyInt()
, que retornará zero e também pulará a etapa de boxe automático.
NullPointerException ou outras exceções: as chamadas para when(foo.bar(any())).thenReturn(baz)
realmente chamarão foo.bar(null)
, as quais você pode ter stubbed para lançar uma exceção ao receber um argumento nulo. Mudar para doReturn(baz).when(foo).bar(any())
ignora o comportamento stubbed .
Solução de problemas gerais
Use MockitoJUnitRunner , ou chame explicitamente validateMockitoUsage
em seu tearDown
ou@After
método (o que o corredor faria por você automaticamente). Isso ajudará a determinar se você usou mal os matchers.
Para fins de depuração, adicione chamadas validateMockitoUsage
diretamente ao seu código. Isso será lançado se você tiver algo na pilha, o que é um bom aviso de um sintoma ruim.
Apenas uma pequena adição à excelente resposta de Jeff Bowman, pois encontrei essa pergunta ao procurar uma solução para um dos meus próprios problemas:
Se uma chamada para um método corresponder a mais de uma
when
chamada treinada de uma farsa , a ordem daswhen
chamadas é importante e deve ser da mais ampla à mais específica. A partir de um dos exemplos de Jeff:é a ordem que garante o resultado (provavelmente) desejado:
Se você inverter as chamadas when, o resultado será sempre
true
.fonte