ti-enxame.com

Qual é a diferença entre zombar e espionar ao usar Mockito?

O que seria um caso de uso para o uso de um espião Mockito?

Parece-me que cada caso de uso de espião pode ser tratado com uma simulação, usando callRealMethod.

Uma diferença que eu posso ver é se você quer que a maioria das chamadas de método seja real, ele salva algumas linhas de código para usar um simulado contra um espião. É isso ou estou perdendo a foto maior?

121
Victor Grazi

A resposta está em a documentação :

Mocks parciais reais (desde 1.8.0)

Finalmente, depois de muitos debates internos e discussões na lista de discussão, o suporte simulado parcial foi adicionado ao Mockito. Anteriormente, considerávamos zombarias parciais como cheiros de código. No entanto, encontramos um caso de uso legítimo para zombarias parciais - mais leitura: aqui

Antes do lançamento, o 1.8 spy () não estava produzindo simulações parciais reais e era confuso para alguns usuários.

callRealMethod() foi introduzido depois de spy(), mas o spy () foi deixado lá, é claro, para garantir a compatibilidade com versões anteriores.

Caso contrário, você está certo: todos os métodos de um espião são reais, a menos que sejam stubbed. Todos os métodos de um mock são stubbed a menos que callRealMethod() seja chamado. Em geral, eu preferiria usar callRealMethod(), porque isso não me força a usar o idioma doXxx().when() em vez do tradicional when().thenXxx()

87
JB Nizet

Diferença entre um Spy e um Mock

Quando Mockito cria uma simulação - ela faz isso da Classe de um Tipo, não de uma instância real. O simulado cria simplesmente uma instância shell da classe, completamente instrumentada para rastrear as interações com ele. Por outro lado, o espião envolverá uma instância existente. Ele ainda se comportará da mesma maneira que a instância normal - a única diferença é que ele também será instrumentado para rastrear todas as interações com ele.

No exemplo a seguir - criamos uma simulação da classe ArrayList:

@Test
public void whenCreateMock_thenCreated() {
    List mockedList = Mockito.mock(ArrayList.class);

    mockedList.add("one");
    Mockito.verify(mockedList).add("one");

    assertEquals(0, mockedList.size());
}

Como você pode ver, adicionar um elemento à lista de simulação não adiciona nada, apenas chama o método sem nenhum outro efeito colateral. Um espião, por outro lado, se comportará de maneira diferente - ele realmente chamará a implementação real do método add e adicionará o elemento à lista subjacente:

@Test
public void whenCreateSpy_thenCreate() {
    List spyList = Mockito.spy(new ArrayList());
    spyList.add("one");
    Mockito.verify(spyList).add("one");

    assertEquals(1, spyList.size());
}

Aqui podemos certamente dizer que o método interno real do objeto foi chamado porque quando você chama o método size () você obtém o tamanho como 1, mas esse método size () não foi ridicularizado! Então, de onde vem 1? O método do tamanho real interno () é chamado como size () não é ridicularizado (ou stubbed) e, portanto, podemos digamos que a entrada foi adicionada ao objeto real.

Fonte: http://www.baeldung.com/mockito-spy + notas auto.

71
Saurabh Patil

Se houver um objeto com 8 métodos e você tiver um teste em que deseja chamar 7 métodos reais e copiar um método, terá duas opções:

  1. Usando um mock você teria que configurá-lo invocando 7 callRealMethod e stub one method
  2. Usando um spy você tem que configurá-lo por um método stub

O documentação oficial on doCallRealMethod recomenda usar um espião para zombarias parciais.

Veja também javadoc spy (Object) para descobrir mais sobre mocks parciais. Mockito.spy () é uma maneira recomendada de criar simulações parciais. A razão é que os métodos reais são chamados contra objetos construídos corretamente porque você é responsável por construir o objeto passado ao método spy ().

32
user2412398

O espião pode ser útil quando você deseja criar testes de unidade para código legado .

Eu criei um exemplo executável aqui https://www.surasint.com/mockito-with-spy/ , eu copio alguns deles aqui.

Se você tem algo parecido com este código:

public void transfer(  DepositMoneyService depositMoneyService, WithdrawMoneyService withdrawMoneyService, 
             double amount, String fromAccount, String toAccount){
    withdrawMoneyService.withdraw(fromAccount,amount);
    depositMoneyService.deposit(toAccount,amount);
}

Você pode não precisar de espionagem, porque você pode simplesmente zombar de DepositMoneyService e WithdrawMoneyService.

Mas com algum código legado, a dependência está no código assim:

    public void transfer(String fromAccount, String toAccount, double amount){

        this.depositeMoneyService = new DepositMoneyService();
        this.withdrawMoneyService = new WithdrawMoneyService();

        withdrawMoneyService.withdraw(fromAccount,amount);
        depositeMoneyService.deposit(toAccount,amount);
    }

Sim, você pode mudar para o primeiro código, mas a API é alterada. Se esse método estiver sendo usado por muitos lugares, você precisará alterar todos eles.

Alternativa é que você pode extrair a dependência assim:

    public void transfer(String fromAccount, String toAccount, double amount){
        this.depositeMoneyService = proxyDepositMoneyServiceCreator();
        this.withdrawMoneyService = proxyWithdrawMoneyServiceCreator();

        withdrawMoneyService.withdraw(fromAccount,amount);
        depositeMoneyService.deposit(toAccount,amount);
    }
    DepositMoneyService proxyDepositMoneyServiceCreator() {
        return new DepositMoneyService();
    }

    WithdrawMoneyService proxyWithdrawMoneyServiceCreator() {
        return new WithdrawMoneyService();
    }

Então você pode usar o espião a injetar a dependência assim:

DepositMoneyService mockDepositMoneyService = mock(DepositMoneyService.class);
        WithdrawMoneyService mockWithdrawMoneyService = mock(WithdrawMoneyService.class);

    TransferMoneyService target = spy(new TransferMoneyService());

    doReturn(mockDepositMoneyService)
            .when(target).proxyDepositMoneyServiceCreator();

    doReturn(mockWithdrawMoneyService)
            .when(target).proxyWithdrawMoneyServiceCreator();

Mais detalhes no link acima.

4
Surasin Tancharoen