Conteúdos da cadeira

Testes unitários com JUnit

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

Markdown

Perguntar sobre esta página

ChatGPTClaudePerplexityGeminiCopiar e abrir

Envia o link e pede à IA para ler a página. No Gemini, cola a pergunta copiada.

Ver pergunta para copiar
Nesta página

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, isto é a versão industrial da mesma ideia: cada teste corre sozinho, sem ordem garantida e sem depender de outros testes.

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 e um mock regista as chamadas para verificação posterior.

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.

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.

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.

Ver o ficheiro no GitHub

À tua maneira

Escolhe como preferes ler.

Aparência
Ajustar cores e largura
Cor de destaque do tema FEUP
Tipo de letra

Álgebra, lógica e uma ideia de cada vez.

As tuas escolhas ficam guardadas neste navegador.

Pesquisar

Escreve para pesquisar em todo o site.

para escolher · Enter para abrir · Esc para fechar

Atalhos de teclado

Clica numa tecla para a mudar. Esc cancela. Backspace desativa.

PesquisarCtrl / Cmd K

Os atalhos não interferem enquanto escreves. Tab e Enter funcionam sempre.