mockito callbacks e obtenção de valores de argumento

88

Não estou tendo sorte em fazer o Mockito capturar os valores dos argumentos da função! Estou zombando de um índice de mecanismo de pesquisa e, em vez de construir um índice, estou apenas usando um hash.

// Fake index for solr
Hashmap<Integer,Document> fakeIndex;

// Add a document 666 to the fakeIndex
SolrIndexReader reader = Mockito.mock(SolrIndexReader.class);

// Give the reader access to the fake index
Mockito.when(reader.document(666)).thenReturn(document(fakeIndex(666))

Não posso usar argumentos arbitrários porque estou testando os resultados das consultas (ou seja, quais documentos eles retornam). Da mesma forma, não quero especificar um valor específico e ter uma linha para cada documento!

Mockito.when(reader.document(0)).thenReturn(document(fakeIndex(0))
Mockito.when(reader.document(1)).thenReturn(document(fakeIndex(1))
....
Mockito.when(reader.document(n)).thenReturn(document(fakeIndex(n))

Eu olhei para a seção de retornos de chamada na página Usando o Mockito . Infelizmente, não é Java e eu não consegui fazer minha própria interpretação disso funcionar em Java.

EDITAR (para esclarecimento): Como faço para que o Mockito capture um argumento X e o transmita para minha função? Quero que o valor exato (ou ref) de X seja passado para a função.

Não quero enumerar todos os casos e o argumento arbitrário não funciona porque estou testando resultados diferentes para consultas diferentes.

A página Mockito diz

val mockedList = mock[List[String]]
mockedList.get(anyInt) answers { i => "The parameter is " + i.toString } 

Isso não é java, e eu não sei como traduzir para java ou passar o que aconteceu em uma função.

nflacco
fonte
Não tenho certeza se entendi exatamente o que está falhando para você. Sua chamada para Mockito.when(reader.document(666)).thenReturn(document(fakeIndex(666))deve configurar o objeto mock para você. O que acontece quando você liga reader.document(666)?
altamente cafeinado
O 666 funciona bem. No entanto, gostaria de passar um número específico X e obter o resultado de fakeIndex (X). Tenho um grande número de documentos em potencial para testar quanto a consultas e não quero inserir todos eles.
nflacco

Respostas:

103

Nunca usei o Mockito, mas quero aprender, então aqui vai. Se alguém menos sem noção do que eu responder, tente a resposta primeiro!

Mockito.when(reader.document(anyInt())).thenAnswer(new Answer() {
 public Object answer(InvocationOnMock invocation) {
     Object[] args = invocation.getArguments();
     Object mock = invocation.getMock();
     return document(fakeIndex((int)(Integer)args[0]));
     }
 });
Ed Staub
fonte
2
Acabei de notar o link do lado direito do Mockito: Como fazer um método retornar um argumento que foi passado para ele . Parece que estou perto, se não no local.
Ed Staub
forte reputação do usuário (666) para a correlação da pergunta original! Isso funcionou muito bem. A única alteração que fiz para obter a compilação do material foi colocada public na frente da resposta do objeto (invocação InvocationOnMock) ....
nflacco
54

Confira ArgumentCaptors:

https://site.mockito.org/javadoc/current/org/mockito/ArgumentCaptor.html

ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class);
Mockito.when(reader.document(argument.capture())).thenAnswer(
  new Answer() {
    Object answer(InvocationOnMock invocation) {
      return document(argument.getValue());
    }
  });
qualidafial
fonte
3
Uau, eu não sabia que você poderia usar ArgumentCaptors para fazer stub. No entanto, há um grande aviso sobre isso nesse link. Prossiga com cuidado.
einnocent
3
Sim, você está certo. Captores devem ser usados ​​apenas com verificações.
qualidafial de
41

Você pode querer usar verify () em combinação com o ArgumentCaptor para garantir a execução no teste e o ArgumentCaptor para avaliar os argumentos:

ArgumentCaptor<Document> argument = ArgumentCaptor.forClass(Document.class);
verify(reader).document(argument.capture());
assertEquals(*expected value here*, argument.getValue());

O valor do argumento é obviamente acessível através do argument.getValue () para posterior manipulação / verificação ou o que você desejar fazer.

fl0w
fonte
2
melhor resposta: direto, fácil de entender
Radu Cugut
Não responde à pergunta. A pergunta é sobre Mockito.when e não verificar.
seBaka28,
@ seBaka28 a melhor solução para obter argumentos é um capturador de argumentos. ArgumentCaptors é fortemente recomendado para ser usado com verificação pelos autores de Mockito, portanto, eu queria dar uma resposta em perspectiva completa. se você por si mesmo decidir não usá-los, a escolha é sua, mas não é aconselhável. EDIT: Não vejo como isso justifica um downvote, mas essa também é sua escolha.
fl0w
1
Porque não responde à pergunta. Sim, ArgumentCaptor é ótimo quando você deseja capturar o argumento, mas você não pode usá-lo em conjunto com when (...). ThenReturn () que é o que OP está tentando fazer: Com base em um certo parâmetro, um serviço simulado deve retornar um objeto específico.
seBaka28,
1
@YuraHoy essa é uma mensagem de erro padrão quando você usa verify e o objeto ou método é chamado com mais freqüência do que você disse para verify. Você pode alterar a contagem de expectativa adicionando o argumento times (n) como segue: verificar (leitor, tempos (5)) - isso esperaria 5 interações. Para referência, consulte: baeldung.com/mockito-verify
fl0w
13

Com o Java 8, isso poderia ser algo assim:

Mockito.when(reader.document(anyInt())).thenAnswer(
  (InvocationOnMock invocation) -> document(invocation.getArguments()[0]));

Presumo que documentseja um mapa.

Jean-François Beauchef
fonte