Tipos de teste de caixa branca, Teste de caixa preta

Os testes de software são fundamentais para garantir a qualidade, segurança e desempenho das aplicações. Dentre os tipos mais conhecidos, destacam-se os testes de caixa branca e caixa preta, cada um com sua abordagem, objetivos e técnicas específicas. Neste artigo, vamos entender as diferenças entre eles e explorar exemplos práticos com a linguagem Java para facilitar a compreensão.


O que é Teste de Caixa Branca?

O teste de caixa branca (ou teste estrutural) é aquele em que o testador tem acesso ao código-fonte da aplicação. A ideia é verificar o funcionamento interno do sistema, testando estruturas lógicas, caminhos de decisão, laços e fluxos de dados.

🔍 Técnicas comuns:

  • Cobertura de instrução: verificar se todas as linhas de código foram executadas.

  • Cobertura de decisão: testar todas as estruturas de decisão (if, switch, while, etc).

  • Cobertura de caminho: explorar todos os fluxos possíveis de execução.

Exemplo prático em Java:

public class ClassificadorIdade {

    public static String classificar(int idade) {
        if (idade < 12) {
            return "Criança";
        } else if (idade < 18) {
            return "Adolescente";
        } else {
            return "Adulto";
        }
    }
}

No teste de caixa branca, você analisaria o código-fonte diretamente e criaria testes que percorrem todos os caminhos:

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

public class ClassificadorIdadeTest {

    @Test
    public void testCrianca() {
        assertEquals("Criança", ClassificadorIdade.classificar(10));
    }

    @Test
    public void testAdolescente() {
        assertEquals("Adolescente", ClassificadorIdade.classificar(15));
    }

    @Test
    public void testAdulto() {
        assertEquals("Adulto", ClassificadorIdade.classificar(20));
    }
}

Esse conjunto garante 100% de cobertura de decisão e instrução para esse método.


O que é Teste de Caixa Preta?

O teste de caixa preta ignora a implementação interna. O foco está nos requisitos funcionais: o sistema retorna as respostas corretas dadas certas entradas?

🔍 Técnicas comuns:

  • Partição de equivalência

  • Análise de valor-limite

  • Tabela de decisão

  • Casos de uso

✅ Exemplo prático em Java:

Sem conhecer o código, o testador sabe que:

  • Se a idade for menor que 12 → deve retornar "Criança".

  • Se a idade for entre 12 e 17 → "Adolescente".

  • A partir de 18 → "Adulto".

Cria-se testes com foco nas entradas e saídas esperadas:

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

public class CaixaPretaTest {

    @Test
    public void testLimiteInferior() {
        assertEquals("Criança", ClassificadorIdade.classificar(0));
    }

    @Test
    public void testLimiteTransicaoParaAdolescente() {
        assertEquals("Adolescente", ClassificadorIdade.classificar(12));
    }

    @Test
    public void testLimiteTransicaoParaAdulto() {
        assertEquals("Adulto", ClassificadorIdade.classificar(18));
    }

    @Test
    public void testEntradaInvalida() {
        assertThrows(IllegalArgumentException.class, () -> {
            ClassificadorIdade.classificar(-5);
        });
    }
}

Para esse último teste, seria necessário adaptar o método para lançar uma exceção caso a idade seja negativa.


Comparativo entre Caixa Branca e Caixa Preta

Característica
Caixa Branca
Caixa Preta

Visibilidade do código

Requer acesso ao código-fonte

Não requer acesso ao código

Foco

Lógica e estrutura interna

Funcionalidade e requisitos

Quem realiza?

Desenvolvedores ou testadores técnicos

Testadores, QA ou analistas

Exemplos de ferramenta

JUnit com cobertura de código

JUnit, Selenium, Postman, etc.

Gráficos de Cobertura de Testes

O que é a cobertura de testes?

A cobertura de testes indica quantas partes do código foram realmente testadas. Em Java, ferramentas como JaCoCo, Cobertura ou EclEmma (Eclipse) geram relatórios visuais após a execução dos testes unitários.

Exemplo de cobertura com JaCoCo:

Suponha que temos a seguinte classe:

public class Calculadora {

    public int somar(int a, int b) {
        return a + b;
    }

    public int dividir(int a, int b) {
        if (b == 0) {
            throw new IllegalArgumentException("Divisão por zero");
        }
        return a / b;
    }
}

E os testes:

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

public class CalculadoraTest {

    @Test
    public void testSoma() {
        Calculadora calc = new Calculadora();
        assertEquals(5, calc.somar(2, 3));
    }

    @Test
    public void testDivisao() {
        Calculadora calc = new Calculadora();
        assertEquals(2, calc.dividir(10, 5));
    }
}

Cobertura gerada (exemplo visual de um relatório JaCoCo):

EditarMétodo         | Cobertura de Instruções | Cobertura de Ramos
---------------------|-------------------------|---------------------
somar(int, int)      | 100%                    | n/a
dividir(int, int)    | 85%                     | 50%

Observação: O método dividir não teve a exceção testada! Logo, falta testar b == 0.


Exemplo de Código com Erros Intencionais

A seguir, um exemplo de código com problemas lógicos e estruturais para fins didáticos. A proposta é que os estudantes identifiquem os erros usando teste de caixa branca.

public class VerificadorParidade {

    public String verificar(int numero) {
        if (numero % 2 == 0) {
            return "Ímpar"; // ❌ ERRO: deve retornar "Par"
        } else if (numero % 2 == 1) {
            return "Ímpar";
        } else {
            return "Desconhecido"; // ❌ nunca será alcançado
        }
    }
}

Erros intencionais:

  1. Lógica invertida no if (retorna "Ímpar" quando é par).

  2. Condição else if (numero % 2 == 1) é redundante — o else já cobriria esse caso.

  3. O else final é morto, nunca será atingido (código morto).

Como testar:

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

public class VerificadorParidadeTest {

    @Test
    public void testNumeroPar() {
        assertEquals("Par", new VerificadorParidade().verificar(4)); // este teste irá falhar
    }

    @Test
    public void testNumeroImpar() {
        assertEquals("Ímpar", new VerificadorParidade().verificar(5));
    }
}

Esse exercício ajuda a diagnosticar erros de lógica que muitas vezes passam despercebidos em revisões manuais.

Last updated