# Testes unitários com JUnit

Testes com JUnit, mocks e stubs com Mockito, cobertura e mutation testing.

Página: https://resumos.rgo.pt/cadeiras/ldts/testes-unitarios/

Um **teste unitário** verifica uma unidade pequena de código, tipicamente um método, de forma automática e repetível. Em LDTS os testes protegem o projeto: quando refatoras na semana 5, são eles que dizem se partiste a semana 2. JUnit executa os testes, Mockito cria substitutos para as dependências, e a cobertura e o mutation testing medem a qualidade da tua bateria.

## O primeiro teste com JUnit

Cada teste prepara um cenário, executa e verifica o resultado. Por convenção, um teste por comportamento, com nome que diz o que verifica:

```
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;

class PosicaoTest {
    @Test
    void somarDeslocamentoDevolveNovaPosicao() {
        Posicao origem = new Posicao(3, 4);

        Posicao destino = origem.somar(1, -2);

        assertEquals(4, destino.getX());
        assertEquals(2, destino.getY());
        assertEquals(3, origem.getX());
    }
}
```

As três fases estão separadas por linhas em branco: preparar, agir, verificar. A última verificação confirma que a origem não mudou, porque `somar` devolve um objeto novo. Se já escreveste asserts simples em [exceções e testes](https://resumos.rgo.pt/cadeiras/p/excecoes-testes/), isto é a versão industrial da mesma ideia: cada teste corre sozinho, sem ordem garantida e sem depender de outros testes.

Quando vários testes partilham a mesma preparação, um método `@BeforeEach` corre antes de cada teste e evita a repetição:

```
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class ArenaTest {
    private Arena arena;

    @BeforeEach
    void prepararArenaVazia() {
        arena = new Arena();
    }

    @Test
    void arenaComecaComZeroPassos() {
        assertEquals(0, arena.getPassos());
    }
}
```

Para a mesma verificação com vários valores, um teste parametrizado corre o corpo uma vez por argumento:

```
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

@ParameterizedTest
@ValueSource(ints = {1, 2, 5})
void somarMantemOrigemIntacta(int deslocamento) {
    Posicao origem = new Posicao(3, 4);
    origem.somar(deslocamento, 0);
    assertEquals(3, origem.getX());
}
```

Cada valor de `deslocamento` é uma execução separada: se uma falhar, sabes exatamente qual.

O JUnit e o Mockito não correm no bloco executável

Os blocos executáveis desta página usam apenas Java puro, porque o motor do navegador não tem as bibliotecas de teste. O exemplo abaixo imita um teste à mão: em vez de `assertEquals`, uma função `verifica` compara e conta. No projeto, com JUnit no `build.gradle`, escreves os testes a sério.

```java
class Posicao {
    private final int x;
    private final int y;

    Posicao(int x, int y) {
        this.x = x;
        this.y = y;
    }

    int getX() { return x; }
    int getY() { return y; }

    Posicao somar(int dx, int dy) {
        return new Posicao(x + dx, y + dy);
    }
}

public class Main {
    static int passados = 0;
    static int falhados = 0;

    static void verifica(String nome, int esperado, int obtido) {
        passados++;
        if (esperado == obtido) {
            System.out.println("PASSOU: " + nome);
        } else {
            falhados++;
            System.out.println("FALHOU: " + nome + " (esperava " + esperado + ", deu " + obtido + ")");
        }
    }

    public static void main(String[] args) {
        Posicao origem = new Posicao(3, 4);
        Posicao destino = origem.somar(1, -2);
        verifica("soma x", 4, destino.getX());
        verifica("soma y", 2, destino.getY());
        verifica("origem intacta", 3, origem.getX());
        System.out.println(passados - falhados + " de " + passados + " verificacoes verdes");
    }
}
```

A saída mostra as três verificações a passar e `3 de 3 verificacoes verdes`. É a mesma disciplina do JUnit (preparar, agir, verificar, contar), sem a biblioteca.

[Vídeo: JUnit 5 Tutorial, curso rápido (Marco Codes)](https://www.youtube.com/watch?v=6uSnF6IuWIw)

A miniatura vem do YouTube. O vídeo só carrega quando clicas. [Abrir no YouTube](https://www.youtube.com/watch?v=6uSnF6IuWIw)

## Mocks e stubs com Mockito

O problema: testar o `ControladorArena` exige um ecrã e um teclado, que não existem nos testes. A solução é trocar as dependências reais por **doubles**:

Um **stub** devolve respostas fixas. Usas quando o teste precisa de uma dependência que responda sempre igual, como uma entrada presa na tecla “cima”. O stub não verifica nada: só alimenta o código.

```
when(entrada.lerTecla()).thenReturn("cima");
```

Um **mock** regista as chamadas para verificação posterior. Usas quando o que interessa é provar que o código chamou a dependência certa, como confirmar que o controlador pediu o desenho ao ecrã.

```
verify(ecra).desenhar("heroi:(3,3)");
```

Um **spy** embrulha o objeto real e regista as chamadas, deixando o comportamento original passar salvo indicação contrária. Usas quando precisas do objeto a sério mas queres espiar uma chamada específica, como contar quantas vezes a arena redesenhou.

```
Arena espiada = spy(new Arena());
```

Por defeito o spy chama os métodos reais; com `doReturn(...).when(espiada)` prendes só o que precisas.

```
import static org.mockito.Mockito.*;

interface Ecra { void desenhar(String conteudo); }

class ControladorArenaTest {
    @Test
    void passoDesenhaHeroiNaNovaPosicao() {
        Ecra ecra = mock(Ecra.class);
        ControladorArena arena = new ControladorArena(ecra);

        arena.passo("cima");

        verify(ecra).desenhar("heroi:(3,3)");
    }
}
```

O `mock` cria um ecrã falso que regista tudo. Depois de agir, o `verify` confirma que o controlador pediu exatamente o desenho esperado. Se o controlador desenhasse duas vezes, ou desenhasse outra coisa, o teste falhava. É por isto que a inversão de dependências importa: só consegues injetar o mock porque o controlador depende da interface `Ecra`, não de uma classe concreta. O [guia oficial do Mockito](https://site.mockito.org/) documenta cada verificação disponível, e o [guia do JUnit 5](https://docs.junit.org/5.13.0/user-guide/) explica a escrita e a organização dos testes.

[Vídeo: Mocks, Stubs and Spies com Mockito (Aneesh Mistry)](https://www.youtube.com/watch?v=xXO8ft-tsrY)

A miniatura vem do YouTube. O vídeo só carrega quando clicas. [Abrir no YouTube](https://www.youtube.com/watch?v=xXO8ft-tsrY)

## Cobertura e mutation testing

A **cobertura** diz que percentagem do código os testes executam. 100 por cento de cobertura com testes fracos vale pouco: um teste que chama o método e não verifica nada conta para a cobertura. O **mutation testing** (Pitest no projeto) resolve isto ao introduzir mutantes, pequenas alterações como trocar `+` por `-` ou apagar uma chamada. Cada mutante que a bateria apanha é um ponto; cada mutante que sobrevive denuncia um teste fraco ou código morto.

Para medir ambas no Gradle, junta os plugins de cobertura e de mutação ao `build.gradle`:[1](https://resumos.rgo.pt/cadeiras/ldts/testes-unitarios/#user-content-fn-pitest)

```
plugins {
    id 'java'
    id 'jacoco'
    id 'info.solidsoft.pitest' version '1.19.0'
}

tasks.named('test') {
    useJUnitPlatform()
    finalizedBy 'jacocoTestReport'
}

tasks.named('jacocoTestReport') {
    reports {
        xml.required = true
        html.required = true
    }
}
```

`gradle test` corre os testes e gera o relatório de cobertura em `build/reports/jacoco`. `gradle pitest` corre os mutantes e abre o relatório em `build/reports/pitest`: cada ficheiro mostra os mutantes sobreviventes por linha, e é por aí que começas a escrever os testes em falta.

Como ler o relatório do Pitest

Um mutante sobrevivente numa condição de fronteira (`<` contra `<=`) quase sempre significa que falta um teste para o caso limite. Escreve esse teste antes de mexer no código.

## Exemplo completo: controlador com stub e mock

Testar o movimento do herói junta as duas técnicas. Um stub de entrada finge que o utilizador premiu “cima”, e um mock de ecrã verifica o desenho:

```
@Test
void heroiSobeQuandoTeclaECima() {
    Entrada entrada = mock(Entrada.class);
    when(entrada.lerTecla()).thenReturn("cima");
    Ecra ecra = mock(Ecra.class);
    ControladorArena arena = new ControladorArena(entrada, ecra);

    arena.passo();

    verify(ecra).desenhar("heroi:(3,3)");
}
```

O teste não precisa de teclado nem de terminal: a entrada está presa na resposta fixa e o ecrã regista a chamada. Corre com `gradle test` em menos de um segundo, e vai continuar a correr em cada build até ao fim do projeto.

## Notas de rodapé

1.  O plugin de Gradle do Pitest é mantido por Marcin Zajączkowski; a página de ligações do Pitest ([https://pitest.org/links/](https://pitest.org/links/)) aponta para o plugin e para a documentação. [Voltar](https://resumos.rgo.pt/cadeiras/ldts/testes-unitarios/#user-content-fnref-pitest)
