A ferramenta JUnit

Prof. Marcelo Cohen

03/2016

A ferramenta JUnit

Material baseado nos slides do prof. Júlio Machado

Versão para impressão


Introdução


Teste unitário


Exemplo de classe driver

public class ContaCorrenteTest {

    public void testaDeposito() {
        ContaCorrente target = new ContaCorrente(100,"Fulano");
        target.depositar(1000.0);
        if (target.getSaldo() == 1000.0)
            System.out.println("TestaDeposito: Pass");
        else System.out.println("TestaDeposito: Fail");
    }

    public void testaRetirada() {
        ContaCorrente target = new ContaCorrente(100,"Fulano");
        target.depositar(6000.0);
        target.retirar(1000.0);
        if (target.getSaldo() == 5000.0)
            System.out.println("TestaRetirada: Pass");
        else System.out.println("TestaRetirada: Fail");
    }

    public void testaDepositoNegativo {
        ...
    }
}

Vantagens do uso de classes "drivers"


Desvantagens do uso de classes "drivers"


O framework XUnit


Recursos do JUnit

import ContaCorrente.*;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;

public class ContaCorrenteTest {

    @Test // Anotações
    public void retirarTest() {
        ContaCorrente target = new ContaCorrente(100, "Fulano");
        target.depositar(5000.0);
        target.retirar(1000);
        assertEquals(4000.0, target.getSaldo(), 0.1); // Asserções
    }

    @Test
    public void depositarTest() {
        ContaCorrente target = new ContaCorrente(100, "Fulano");
        target.depositar(5000.0);
        target.depositar(1000.0);
        assertEquals(6000.0, target.getSaldo(), 0.1);
    }
}

Anotações


Asserções


Exemplo: classe Funcionario


Exemplo: casos de teste para Funcionario

public class FuncionarioTest {
  private Funcionario func;

  @Before
  public void setUp() {
    func = new Funcionario(200,"Ze",1900.0);
  }

  @Test
  public void testGetSalLiquidoMenosQue2000() {
    double expected = 1710;
    double actual = func.getSalarioLiquido();
    assertEquals(expected, actual, 0.1);
  }

  @Test
  public void testGetSalLiquidoIgual2000() {
    func.setSalarioBase(2000.0);
    double expected = 1900;
    double actual = func.getSalarioLiquido();
    assertEquals(expected, actual, 0.1);
  }

  @Test
  public void testGetSalLiquidoMaior2000() {
    func.setSalarioBase(3000.0);
    double expected = 2580;
    double actual = func.getSalarioLiquido();
    assertEquals(expected, actual, 0.1);
  }
}

JUnit annotations


JUnit asserts


O processo de teste unitário


O processo de teste unitário

Processo de desenvolvimento com teste unitário (versão 1)

  1. Definir a interface (esqueleto) da classe alvo
  2. Definir o conjunto de casos de teste
  3. Implementar a classe driver
  4. Completar a codificação da classe alvo
  5. Executar os testes
  6. Corrigir os bugs, se houver
  7. Complementar os testes

Exemplo: parquímetro


Etapa 1: definir interface da classe

public class Parquimetro {
    // Permite inserir moedas no parquimetro (soma no saldo)
    // Valores possíveis: 1, 5, 10, 25, 50 e 100 (1 real)
    // Gera InvalidValueException no caso de valor inválido
    public void insereMoeda(int valor) throws InvalidValueException {}

    // Retorna o saldo acumulado no parquimetro public double getSaldo() { }

    // Emite um ticket de 2 reais se houver saldo disponível
    // Retorna true se a operação foi possível
    public bool emiteTicket() { }

    // Devolve o saldo existente
    // Retorna o valor devolvido
    public int devolve() { }
}

Etapa 2: definir os casos de teste


Etapa 3: implementar o driver

    public class ParquimetroTest {

        private Parquimetro parq;

        @Before
        public void setUp() throws Exception {
            parq = new Parquimetro();
            parq.insereMoeda(100);
        }

        @Test public void testInsereMoeda() {
            parq.insereMoeda(1);
            parq.insereMoeda(5);
            parq.insereMoeda(10);
            parq.insereMoeda(25);
            parq.insereMoeda(50);
            parq.insereMoeda(100);
            assertEquals(291, parq.getSaldo());
        }

        @Test 
        public void testGetSaldo() {
            int actual = parq.getSaldo();
            assertEquals(100, actual);
        }

        @Test
        public void testEmiteTicket() {
            parq.insereMoeda(50);
            parq.insereMoeda(50);
            parq.insereMoeda(100);
            boolean actual = parq.emiteTicket();
            assertEquals(true, actual);
        }

        @Test public void testDevolve() {
            parq.insereMoeda(50);
            parq.insereMoeda(50);
            parq.insereMoeda(100);
            parq.emiteTicket();
            int actual = parq.devolve();
            assertEquals(100, actual);
        }
    }

Etapa 4: completar a classe alvo

public class Parquimetro {
    private int saldo;
    public Parquimetro() {
        saldo = 0;
    }

    public void insereMoeda(int valor) {
        switch (valor) {
            case 1:
            case 5:
            case 10:
            case 25:
            case 50:
            case 100:
                saldo += valor;
                break;
            default:
                System.out.println("Erro!");
        }
    }

    public int getSaldo() { return saldo; }

    public boolean emiteTicket() {
        if (getSaldo() < 2) { return false; }
        saldo -= 2;
        return true;
    }

    public int devolve() {
        int tmp = saldo;
        saldo = 0;
        return tmp;
    }
}

Etapa 5: executar os testes

{@class=center}junit
{@class=center}junit

Etapa 6: corrigir os bugs


Etapa 7: completar os testes


Recursos avançados do JUnit 4


Recursos avançados


Verificação do tempo de execução


Comparação de coleções


Testes parametrizados (1)

...
@Test
public void testSubtrai1() {
    calc.subtrai(3);
    assertEquals(6,calc.get());
}

@Test
public void testSubtrai2() {
    calc.subtrai(-3);
    assertEquals(12,calc.get());
}

@Test
public void testSubtrai3() {
    calc.subtrai(0);
    assertEquals(9,calc.get());
}
...

Testes parametrizados (2)


Testes parametrizados (3)


Testes parametrizados (4): exemplo

@RunWith(Parameterized.class) // especificação do runner
public class TesteParametrizado {
  private static Calculadora calc;
  private int param;
  private int result;

  // Método que retorna coleção com pares valor/resultado esperado
  @Parameters
  public static Collection data() {
    return Arrays.asList(new Object[][]{ {10, 0}, {9, 1}, {11, -1}, {4, -5} });
  }
  ...

Testes parametrizados (4): exemplo

  ...
  // Relaciona cada par da coleção com as variáveis param e result
  public TesteParametrizado(int param, int result) {
    this.param = param;
    this.result = result;
  }

  @Before
  public void inicializaTeste() {
    calc = new Calculadora(10);
  }

  // Este método será ativado 4 vezes, uma para cada posição da coleção
  @Test
  public void subtracoes() {
    calc.subtrai(param);
    assertEquals(result, calc.get());
  }
}

Casos de teste incompletos


Recomendações


Limites


Bibliografia recomendada