Resumão Mockito 3

utilizando Java 11 e JUnit 5

Código fonte único com as principais funcionalidades do Mockito 3.

Conteúdo

  1. pom.xml
  2. Mockito 3
  3. Referências

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>dev.caiosantesso</groupId>
    <artifactId>resumao-mockito</artifactId>
    <version>2021.4.0</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.7.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>3.9.0</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-junit-jupiter</artifactId>
            <version>3.9.0</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
      <!-- Necessário para executar 'mvn test' com JUnit 5 -->
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.2</version>
            </plugin>
        </plugins>
    </build>
</project>

Mockito 3

package dev.caiosantesso;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.*;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.IntSupplier;
import java.util.stream.IntStream;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.startsWith;
import static org.mockito.Mockito.*;

// Classe que servirá de mock. A implementação será ignorada.
class Inacabada {

    static String estatico() {
        return "método estático";
    }

    int quinze() {
        return 15;
    }

    double pi() {
        return Math.PI;
    }

    boolean verdadeiro() {
        return true;
    }

    String frase() {
        return "Mockito";
    }

    List<Number> colecao() {
        return List.of(quinze(), pi(), 23);
    }

    void receba(String string) {
    }

    void receba(Number numero) {
    }

    void receba2(String string, List<Integer> lista) {
    }

    int tamanho(Collection<String> colecao) {
        return colecao.size();
    }

    long tamanho(IntSupplier gerador) {
        return IntStream.generate(gerador).limit(1).count();
    }
}

//Classe que utiliza o mock.
class Regular {
    private Inacabada classe;

    void quinzeSeVerdadeiroSeNaoFrase() {
        if (classe.verdadeiro())
            classe.quinze();
        else
            classe.frase();
    }

    void definaArgs() {
        classe.receba("ABC");
        classe.receba2("Qualquer", List.of(7, 8, 9));
    }

    void repitaXVezes(int x) {
        for (int i = 0; i < x && classe.pi() == 0.0; i++) {
            classe.receba(i);
        }
        definaArgs();
    }

    double obtenhaPi() {
        return classe.pi();
    }

    int dobroDoTamanho(Collection<String> colecao) {
        return classe.tamanho(colecao) * 2;
    }

    long triploDoTamanhoDeUmStreamDeUmElemento() {
        long tamanho = classe.tamanho(() -> 7);
        return tamanho * 3;
    }
}

// Necessário para utilizar anotações como @Mock e @InjectMocks.
@ExtendWith(MockitoExtension.class)
public class ResumaoMockitoTest {

    // As chamadas aos métodos da classe Inacabada devolverão
    // 0/false/null na falta de stubbings. A implementação não é
    // executada.
    @Mock
    Inacabada meuMock;

    // Injeta a classe Inacabada no campo de Regular.
    // Inacabada poderia ser injetada também por construtor ou setter.
    @InjectMocks
    Regular regular;

    @Test
    void toStringDeMocksDevolvemONomeDaVariavel() {
        assertEquals("meuMock", meuMock.toString());
    }

    @Test
    void metodosDeMockDevolvemZeroOuFalsoOuNullOuColecaoVazia() {
        assertEquals(0, meuMock.quinze());
        assertEquals(0.0, meuMock.pi());
        assertFalse(meuMock.verdadeiro());
        assertNull(meuMock.frase());
        assertIterableEquals(Collections.emptyList(),
                meuMock.colecao());
    }

    @Test
    void verifyVerificaSeMetodoFoiChamadoUmaVez() {
        regular.quinzeSeVerdadeiroSeNaoFrase();
        // Verifica se verdadeiro() e frase() foram chamados uma vez.
        verify(meuMock).frase();
        verify(meuMock).verdadeiro();
    }

    @Test
    void verifyTambemVerificaSeMetodoFoiChamadoNenhumaOuVariasVezes() {
        // Iterará todas as 10 vezes pois pi() sempre retornará 0.
        regular.repitaXVezes(10);

        // verify() verifica que...
        // ...pi() foi chamado exatamente 10 vezes.
        verify(meuMock, times(10)).pi();
        // ...frase() nunca foi executado.
        verify(meuMock, never()).frase();
        // ...receba() com o argumento 5 foi chamado no máximo uma
        // vez.
        verify(meuMock, atMostOnce()).receba(5);
        // ...receba() com o argumento 6 foi chamado exatamente uma
        // vez.
        verify(meuMock).receba(6);
        verify(meuMock).receba("ABC");
    }

    @Test
    void verifyNoMoreInteractionsVerificaSeTodosOsMetodosDoMockChamadosForamVerificados() {
        regular.repitaXVezes(1);

        verify(meuMock).pi();
        verify(meuMock).receba(0);
        verify(meuMock).receba("ABC");
        verify(meuMock).receba2("Qualquer", List.of(7, 8, 9));
        verifyNoMoreInteractions(meuMock);
    }

    @Test
    void argumentMatchersSubstituemArgumentos() {
        int x = 5;
        regular.repitaXVezes(x);

        // Verifica que receba() foi chamado 5 vezes com qualquer
        // inteiro como argumento.
        verify(meuMock, times(x)).receba(anyInt());

        // Ambos verificam se o argumento de receba() é do tipo
        // Number ou um de seus subtipos.
        verify(meuMock, times(x)).receba(any(Number.class));
        verify(meuMock, times(x)).receba(isA(Number.class));

        // Verifica que receba() foi chamado 5 vezes com um
        // argumento 0 ou maior.
        verify(meuMock, times(x)).receba(intThat(i -> i > -1));
        // Métodos xxxThat() aceitam lambdas que retornam booleano,
        // denotando alguma condição.

        // Verifica que receba() foi chamado 1 vez com um argumento
        // contendo 'B'.
        verify(meuMock).receba(contains("B"));

        // Nenhum ou todos os argumentos devem ser ArgumentMatchers
        // por isso eq() é necessário abaixo.
        verify(meuMock).receba2(eq("Qualquer"), isNotNull());
        // 1. eq() verifica que o primeiro arg de receba2() foi
        // 'Qualquer'.
        // 2. isNotNull() certifica que o segundo arg recebido por
        // receba2() não é nulo.

        verify(meuMock).receba2(startsWith("Qu"),
                argThat(list -> list.size() == 3));
    }

    @Test
    void argumentCaptorVerificaOsArgumentosUtilizadosNaChamadaDeUmMetodo() {
        regular.repitaXVezes(3);

        // Especifica o tipo de argumento a ser capturado.
        ArgumentCaptor<Integer> argumento =
                ArgumentCaptor.forClass(Integer.class);

        // Verifica que receba() foi chamado 3 vezes e captura os 3
        // args utilizados anteriormente.
        verify(meuMock, times(3)).receba(argumento.capture());

        // getAllValues() traz uma lista com todos os argumentos
        // capturados.
        assertIterableEquals(List.of(0, 1, 2),
                argumento.getAllValues());

        // getValue() traz o último argumento capturado.
        assertEquals(2, argumento.getValue());
        assertEquals(2, argumento.getValue());
    }

    @Test
    void inOrderVerificaChamadasNaOrdemCorreta() {
        regular.repitaXVezes(3);

        InOrder emOrdem = inOrder(meuMock);
        emOrdem.verify(meuMock).receba(0);
        emOrdem.verify(meuMock).receba(1);
        emOrdem.verify(meuMock).receba(2);
    }

    @Test
    public void whenCriaStubsQueSaoMetodosComOValorDevolvidoPreDefinido() {
        // Quando pi() for chamado, 9.99 será devolvido. A
        // implementação no mock é substituída.
        when(meuMock.pi()).thenReturn(9.99D);

        double naoPi = regular.obtenhaPi();
        assertEquals(9.99D, naoPi, 0.1);
        assertEquals(9.99D, meuMock.pi(), 0.1);
    }

    @Test
    public void argumentoDoStubDeveSerIgualAoDaChamadaNaImplementacao() {
        var lista = List.of("A");
        var outraLista = Collections.singletonList("A");
        assertEquals(lista, outraLista);

        when(meuMock.tamanho(lista)).thenReturn(22);
        int dobroDoTamanho = regular.dobroDoTamanho(outraLista);

        // Como tamanho() foi configurado no when(), quando tamanho
        // () é chamado dentro de
        // dobroDoTamanho() e os argumentos são iguais então
        // thenReturn() devolve o valor (22).
        assertEquals(44, dobroDoTamanho);

        var conjunto = Set.of("A");
        assertNotEquals(lista, conjunto);

        // Como tamanho() em dobroDoTamanho() abaixo não tem stub 0
        // será devolvido.
        int zeroDeTamanho = regular.dobroDoTamanho(conjunto);
        assertEquals(0, zeroDeTamanho);
    }

    @Test
    public void stubbingsPodemUtilizarArgumentMatchers() {
        when(meuMock.tamanho(anyCollection())).thenReturn(5);

        int tamanho1 = regular.dobroDoTamanho(Set.of("Z", "T", "R"));
        int tamanho2 =
                regular.dobroDoTamanho(Collections.singleton("X"));
        assertEquals(10, tamanho1);
        assertEquals(10, tamanho2);
    }

    @Test
    public void thenReturnTambemDevolveListaDeValores() {
        // O valor é devolvido conforme a vez que pi() é chamado.
        when(meuMock.pi()).thenReturn(9.99D, 7.23D, 0.15D);

        assertEquals(9.99D, regular.obtenhaPi(), 0.1);
        assertEquals(7.23D, regular.obtenhaPi(), 0.1);
        assertEquals(0.15D, regular.obtenhaPi(), 0.1);
        // Quando a lista de retornos é exaurida, o último sempre
        // será devolvido.
        assertEquals(0.15D, regular.obtenhaPi(), 0.1);
    }

    @Test
    public void thenThrowLancaExcecoesNaoChecadas() {
        Class<NullPointerException> npe = NullPointerException.class;

        when(meuMock.pi()).thenThrow(npe);
        assertThrows(npe, () -> regular.obtenhaPi());

        // doThrow() é a versão para método que não tem tipo de
        // retorno (void).
        doThrow(npe).when(meuMock).receba(anyString());
        assertThrows(npe, () -> regular.definaArgs());
    }

    @Test
    public void thenThrowEThenReturnPodemSerEncadeados() {
        Class<IllegalStateException> illegalState =
                IllegalStateException.class;
        Class<NullPointerException> npe = NullPointerException.class;

        when(meuMock.pi()).thenThrow(npe).thenReturn(9.99D)
                .thenThrow(illegalState);

        assertThrows(npe, () -> regular.obtenhaPi());
        assertEquals(9.99D, regular.obtenhaPi(), 0.1);
        assertThrows(illegalState, () -> regular.obtenhaPi());
    }

    @Test
    public void lambdasPrecisamDeArgumentMatchersNosStubs() {
        // Exemplificando que lambdas são apenas iguais a si.
        IntSupplier a = () -> 7;
        IntSupplier b = () -> 7;
        assertNotEquals(a, b);

        // ArgumentMatcher necessário já que arg só seria igual a
        // si próprio.
        when(meuMock.tamanho(any(IntSupplier.class))).thenReturn(5L);
        long tamanho =
                regular.triploDoTamanhoDeUmStreamDeUmElemento();
        assertEquals(15, tamanho);
    }

    @Test
    public void verificaoNoEstiloBDD() {
        // given() é apelido para o when() utilizado para criar
        // stubbings.
        BDDMockito.given(meuMock.pi()).willReturn(4.2D);
        // when
        regular.obtenhaPi();
        // then() é similar a verify().
        BDDMockito.then(meuMock).should().pi();
    }
}

Referências

Escrito por Caio Santesso.

Comentários

  • Conteúdo dos posts, exceto onde indicado contrário, licenciado sob a licença CC BY-SA 4.0 .