Mockito pode stub um método sem levar em consideração o argumento?

302

Estou tentando testar algum código legado, usando o Mockito.

Desejo stub a FooDaoque é usado na produção da seguinte maneira:

foo = fooDao.getBar(new Bazoo());

Eu consigo escrever:

when(fooDao.getBar(new Bazoo())).thenReturn(myFoo);

Mas o problema óbvio é que getBar()nunca é chamado com o mesmo Bazooobjeto para o qual eu apaguei o método. (Amaldiçoe esse newoperador!)

Eu adoraria se pudesse stub o método de uma maneira que ele retornasse myFooindependentemente do argumento. Caso contrário, ouvirei outras sugestões de solução alternativa, mas gostaria muito de evitar a alteração do código de produção até que haja uma cobertura de teste razoável.

Eric Wilson
fonte

Respostas:

456
when(
  fooDao.getBar(
    any(Bazoo.class)
  )
).thenReturn(myFoo);

ou (para evitar nulls):

when(
  fooDao.getBar(
    (Bazoo)notNull()
  )
).thenReturn(myFoo);

Não se esqueça de importar matchers (muitos outros estão disponíveis):

Para o Mockito 2.1.0 e mais recente:

import static org.mockito.ArgumentMatchers.*;

Para versões mais antigas:

import static org.mockito.Matchers.*;
Tomasz Nurkiewicz
fonte
2
Adoro quando a resposta precede o final do 'aceitar resposta congelar'.
Eric Wilson
10
Há um notNull(Bazoo.class)assim como o any(Bazoo.class)(talvez ele não existia no momento desta resposta)
Dandre Allison
2
eu tive uma situação um pouco especial em que eu poderia ter um dos dois argumentos possíveis - Bazooou Cazooquais são as duas subclasses de, digamos Azoo,. porque Bazooeu precisava voltar foo, mas Cazooeu precisava voltar bar. nessa situação, a Matchers.any()solução proposta não funciona, no entanto, Matchers.isA()funciona perfeitamente.
precisa
3
org.mockito.Matchersagora é preterido - utilização org.mockito.ArgumentMatchersem vez, ou seja, import static org.mockito.ArgumentMatchers.*(ver documentos )
DontDivideByZero
when(myFoo.knowsWhatsUp()).thenReturn(myMoney);
6rchid
18

Use assim:

when(
  fooDao.getBar(
    Matchers.<Bazoo>any()
  )
).thenReturn(myFoo);

Antes de precisar importar Mockito.Matchers

Hamad
fonte
1
Isso é privativo!
DrB 5/10
15

http://site.mockito.org/mockito/docs/1.10.19/org/mockito/Matchers.html

anyObject() deve atender às suas necessidades.

Além disso, você sempre pode considerar a implementação hashCode()e equals()a Bazooclasse. Isso faria seu exemplo de código funcionar da maneira que você deseja.

Buhb
fonte
Concordei com a segunda sugestão, mas continuo optando por não fazer isso por razões não técnicas.
Eric Wilson
1
A classe Matchers está obsoleta (consulte a documentação - "Esta classe provavelmente será removida na versão 3.0" )
Johannes Rabauer 10/10/1919
1

Outra opção é confiar no bom equalsmétodo à moda antiga . Enquanto o argumento no whenmock equalso argumento no código sendo testado, o Mockito corresponderá ao mock.

Aqui está um exemplo.

public class MyPojo {

    public MyPojo( String someField ) {
        this.someField = someField;
    }

    private String someField;

    @Override
    public boolean equals( Object o ) {
        if ( this == o ) return true;
        if ( o == null || getClass() != o.getClass() ) return false;
        MyPojo myPojo = ( MyPojo ) o;
        return someField.equals( myPojo.someField );
    }

}

supondo que você saiba qual será o valor someField, você pode zombar assim.

when(fooDao.getBar(new MyPojo(expectedSomeField))).thenReturn(myFoo);

prós: isso é mais explícito que os anycorrespondentes. Como revisor de código, fico de olho anyno código que os desenvolvedores juniores escrevem, pois olha a lógica do código para gerar o objeto apropriado que está sendo passado.

con: Às vezes, o campo que está sendo passado para o objeto é um ID aleatório. Nesse caso, você não pode construir facilmente o objeto de argumento esperado no seu código simulado.

Outra abordagem possível é usar o Answerobjeto de Mockito que pode ser usado com o whenmétodo Answerpermite interceptar a chamada real e inspecionar o argumento de entrada e retornar um objeto simulado. No exemplo abaixo, estou usando anypara capturar qualquer solicitação para o método que está sendo ridicularizado. Mas então, no Answerlambda, posso inspecionar ainda mais o argumento Bazo ... talvez para verificar se um ID adequado foi passado para ele. Prefiro isso anypor si só, para que pelo menos alguma inspeção seja feita no argumento.

    Bar mockBar = //generate mock Bar.

    when(fooDao.getBar(any(Bazo.class))
    .thenAnswer(  ( InvocationOnMock invocationOnMock) -> {
        Bazo actualBazo = invocationOnMock.getArgument( 0 );

        //inspect the actualBazo here and thrw exception if it does not meet your testing requirements.
        return mockBar;
    } );

Então, para resumir tudo, eu gosto de confiar equals(onde o argumento esperado e o argumento real devem ser iguais um ao outro) e se igual não for possível (devido à incapacidade de prever o estado real do argumento), vou recorrer para Answerinspecionar o argumento.

Jose Martinez
fonte