Mockito. Verificar argumentos do método

220

Eu pesquisei sobre isso, mas não encontrei nada relevante. Eu tenho algo parecido com isto:

Object obj = getObject();
Mockeable mock= Mockito.mock(Mockeable.class);
Mockito.when(mock.mymethod(obj )).thenReturn(null);

Testeable testableObj = new Testeable();
testableObj.setMockeable(mock);
command.runtestmethod();

Agora, quero verificar se mymethod(Object o), que é chamado por dentro runtestmethod(), foi chamado com o Objeto o, não com nenhum outro. Mas sempre passo no teste, seja lá o que colocar na verificação, por exemplo, com:

Mockito.verify(mock.mymethod(Mockito.eq(obj)));

ou

Mockito.verify(mock.mymethod(Mockito.eq(null)));

ou

Mockito.verify(mock.mymethod(Mockito.eq("something_else")));

Eu sempre passo no teste. Como posso realizar essa verificação (se possível)?

Obrigado.

manolowar
fonte

Respostas:

334

Uma alternativa para ArgumentMatcheré ArgumentCaptor.

Exemplo oficial:

ArgumentCaptor<Person> argument = ArgumentCaptor.forClass(Person.class);
verify(mock).doSomething(argument.capture());
assertEquals("John", argument.getValue().getName());

Um captador também pode ser definido usando a anotação @Captor :

@Captor ArgumentCaptor<Person> captor;
//... MockitoAnnotations.initMocks(this);
@Test public void test() {
    //...
    verify(mock).doSomething(captor.capture());
    assertEquals("John", captor.getValue().getName());
}
eugene82
fonte
1
Obrigado pela amostra! Nunca usei. Parece um pouco estranho ter coisas como captor no código, mas isso ajudou.
Artemis
1
Haha, eu não entendi a pergunta, mas a resposta me ajudou muito. Thanks :-)
Marcus K.
13
Importante: Ligue para verificar () / capture () depois de usar a simulação. Eu estava pensando que tem que ser "instalado" antes ...
Daniel Alder
1
Obrigado por esta resposta!
Jose Flavio Quispe Irrazábal
Esta é uma ótima resposta !! Muito obrigado!
Ulky Igor
61

Você está tentando fazer a igualdade lógica utilizando o método .equals do objeto? Você pode fazer isso utilizando o correspondente argThat incluído no Mockito

import static org.mockito.Matchers.argThat

Em seguida, você pode implementar seu próprio comparador de argumentos que será diferido para cada objeto .equals method

private class ObjectEqualityArgumentMatcher<T> extends ArgumentMatcher<T> {
    T thisObject;

    public ObjectEqualityArgumentMatcher(T thisObject) {
        this.thisObject = thisObject;
    }

    @Override
    public boolean matches(Object argument) {
        return thisObject.equals(argument);
    }
}

Agora, usando seu código, você pode atualizá-lo para ler ...

Object obj = getObject();
Mockeable mock= Mockito.mock(Mockeable.class);
Mockito.when(mock.mymethod(obj)).thenReturn(null);

Testeable obj = new Testeable();
obj.setMockeable(mock);
command.runtestmethod();

verify(mock).mymethod(argThat(new ObjectEqualityArgumentMatcher<Object>(obj)));

Se você está apenas buscando igualdade EXATA (mesmo objeto na memória), basta

verify(mock).mymethod(obj);

Isso verificará que foi chamado uma vez.

Matthew Kirkley
fonte
1
Você pode usar a construção em ReflectionEqualsclasse para esse fim.
precisa saber é o seguinte
2
+1 para sua resposta. Mas eu gostaria de acrescentar que verify(mock).mymethod(obj);não verifica a igualdade EXATA (mesmo objeto na memória). Em vez disso, usa os objetos equals-method que poderiam ter sido substituídos.
efux 20/05/2015
Você também pode criar uma implementação anônima ArgumentMatcherpara ser menos detalhado.
Botchniaque # 1/15
1
Mais detalhes: por padrão, verify()chama o equals()método / do argumento de entrada / , em vez do equals()método / do objeto / gravado . isso é irrelevante, a menos que você esteja tentando confirmar que o sujeito do teste retorna uma instância específica do objeto, e o sujeito retorna o que deveria ser um decorador transparente dessa instância. Os verifyargumentos equals()não sabiam do decorador; enquanto o decorador equals()seria reescrito para tolerar o original. Nesse caso, seu teste falhará falsamente.
Mark McKenna
54
  • Você não precisa do eqmatcher se não usar outros matchers.
  • Você não está usando a sintaxe correta - sua chamada de método deve estar fora do .verify(mock). Agora você está iniciando a verificação do resultado da chamada de método, sem verificar nada (não fazendo uma chamada de método). Portanto, todos os testes estão passando.

Seu código deve se parecer com:

Mockito.verify(mock).mymethod(obj);
Mockito.verify(mock).mymethod(null);
Mockito.verify(mock).mymethod("something_else");
Bozho
fonte
Eu tinha tentado isso antes, e novamente agora para ter certeza. Eu ainda tenho o mesmo problema, o teste sempre passa.
precisa saber é o seguinte
2
É verifeis por referência
cnexans
17

argThat mais lambda

é assim que você pode falhar na verificação do argumento:

    verify(mock).mymethod(argThat(
      (x)->false
    ));

Onde

import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.verify;

argThat mais afirma

o teste acima "dirá" Expected: lambda$... Was: YourClass.toSting.... Você pode obter uma causa mais específica da falha se usar declarações no lambda:

    verify(mock).mymethod(argThat( x -> {
      assertThat(x).isNotNull();
      assertThat(x.description).contains("KEY");
      return true;
    }));

MAS: APENAS FUNCIONA COM 1 CHAMADA DE MÉTODO. Se o método verificado chamar mais de 2 vezes, o mockito passa todas as combinações chamadas para cada verificador. Portanto, o mockito espera que seu verificador retorne silenciosamente truepara um dos argumentos definidos e false(sem nenhuma exceção de asserção) para outras chamadas válidas. Essa expectativa não é um problema para uma chamada de método - ela deve retornar apenas uma vez verdadeira.

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.verify;

Agora o teste diz: Expected: Obj.description to contain 'KEY'. Was: 'Actual description'. NOTA: Eu usei assertJafirmações, mas cabe a você qual estrutura de afirmação usar.


argThat com vários argumentos.

Se você usar argThat, todos os argumentos deverão ser fornecidos com correspondências. Por exemplo:

    verify(mock).mymethod(eq("VALUE_1"), argThat((x)->false));
    // above is correct as eq() is also an argument matcher.

verify(mock).mymethod("VALUE_1", argThat((x)->false));

// above is incorrect; an exceptoin will be thrown, as the fist arg. is given without an argument matcher.

Onde:

import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;

eq matcher

a maneira mais fácil de verificar se o argumento é igual:

verify(mock).mymethod(eq(expectedValue));
// NOTE:   ^ where the parentheses must be closed.

argumento direto

se a comparação por ref for aceitável, continue com:

verify(mock).mymethod(expectedArg);
// NOTE:   ^ where the parentheses must be closed.

A causa da falha pergunta original era o lugar errado das paranthes: verify(mock.mymethod.... Isso estava errado. O direito seria:verify(mock).*

epóxi
fonte
1
Esta é a minha resposta favorita, funciona e muito mais elegante que as outras.
Airwavezx
11

Eu usei o Mockito.verify dessa maneira

@UnitTest
public class JUnitServiceTest
{
    @Mock
    private MyCustomService myCustomService;


    @Test
    public void testVerifyMethod()
    {
       Mockito.verify(myCustomService, Mockito.never()).mymethod(parameters); // method will never call (an alternative can be pick to use times(0))
       Mockito.verify(myCustomService, Mockito.times(2)).mymethod(parameters); // method will call for 2 times
       Mockito.verify(myCustomService, Mockito.atLeastOnce()).mymethod(parameters); // method will call atleast 1 time
       Mockito.verify(myCustomService, Mockito.atLeast(2)).mymethod(parameters); // method will call atleast 2 times
       Mockito.verify(myCustomService, Mockito.atMost(3)).mymethod(parameters); // method will call at most 3 times
       Mockito.verify(myCustomService, Mockito.only()).mymethod(parameters); //   no other method called except this
    }
}
Mente livre
fonte
5

Você verificou o método equals para a classe ridicularizável? Se este retornar sempre verdadeiro ou você testar a mesma instância na mesma instância e o método equal não for sobrescrito (e, portanto, apenas verificar as referências), ele retornará verdadeiro.

rit
fonte
4

O outro método é usar o método org.mockito.internal.matchers.Equals.Equals em vez de redefinir um:

verify(myMock).myMethod((inputObject)Mockito.argThat(new Equals(inputObjectWanted)));
Nils Renaud
fonte
3

Você já tentou com o mesmo matcher ()? Como em:

verify(mockObj).someMethod(same(specificInstance));

Eu tive o mesmo problema. Eu tentei com o eq () e o refEq (), mas sempre tive falsos positivos. Quando usei o mesmo combinador (), o teste falhou quando os argumentos eram instâncias diferentes e passou quando os argumentos eram a mesma instância.

cbbcloud
fonte
-1

Você também pode usar TypeSafeDiagnosingMatcher

    private Matcher<GetPackagesRequest> expectedPackageRequest(final AvailabilityRequest request) {
    return new TypeSafeDiagnosingMatcher<GetPackagesRequest>() {

        StringBuilder text = new StringBuilder(500);

        @Override
        protected boolean matchesSafely(GetPackagesRequest req, Description desc) {
            String productCode = req.getPackageIds().iterator().next().getValue();
            if (productCode.equals(request.getSupplierProductCode())) {
                text.append("ProductCode not equal! " + productCode + " , " + request.getSupplierProductCode());
                return true;
            }

            text.append(req.toString());
            return false;
        }

        @Override
        public void describeTo(Description d) {
            d.appendText(text.toString());
        }
    };
}

Em seguida, verifique essa chamada:

Mockito.verify(client).getPackages(Mockito.argThat(expectedPackageRequest(request)));
sendon1982
fonte