O teste exploratório realizado sobre o sistema de Cálculo de IMC demonstrou funcionamento correto para entradas válidas, como (70, 1.75) e (90, 1.80). Entretanto, o uso de vírgula, comum no Brasil, como em 1,75, gera NumberFormatException, causando encerramento abrupto do programa. A aplicação aceita pesos e alturas negativos ou iguais a zero, produzindo IMCs matematicamente calculáveis, porém fisicamente impossíveis. Altura zero causa divisão por zero e resulta em Infinity.
Trecho de código evidenciando ausência de tratamento de exceções:
peso = Double.parseDouble(scannerPeso.nextLine());
altura = Double.parseDouble(scannerAltura.nextLine());E trecho que permite divisão por zero:
public static double calcularPeso(double peso, double altura) {
return peso / (altura * altura);
}Foram identificadas falhas graves: ausência de validação das entradas; falta de mensagens de erro amigáveis; encerramento abrupto em caso de falhas; classificação do IMC com intervalos sobrepostos; aceitação de pesos e alturas irreais; e falta de resiliência no fluxo de interação.
A lógica de classificação contém problemas:
if (imc < 16.0) {
return "Magreza grave";
}
else if (imc == 16.0 || imc < 17.0) {
return "Magreza moderada";
}
else if (imc == 17.0 || imc < 18.5) {
return "Magreza leve";
}As expressões imc == 16.0 || imc < 17.0 e imc == 17.0 || imc < 18.5 tornam impossível separar limites corretamente.
O sistema deve:
-
Aceitar entrada de peso e altura usando ponto ou vírgula.
-
Validar peso > 0 e ≤ 500 kg.
-
Validar altura ≥ 0,50 m e ≤ 3,00 m.
-
Calcular o IMC por: IMC = peso / (altura × altura).
-
Classificar IMC com faixas não sobrepostas:
- < 16,0: Magreza grave
- 16,0–16,99: Magreza moderada
- 17,0–18,49: Magreza leve
- 18,5–24,99: Saudável
- 25,0–29,99: Sobrepeso
- 30,0–34,99: Obesidade I
- 35,0–39,99: Obesidade II
- ≥ 40: Obesidade III
-
Exibir IMC com duas casas decimais.
-
Tratar erros sem encerrar a aplicação abruptamente.
Para peso:
- 0 (inválido)
- 0.1 (limite inferior técnico)
- 500 (válido limite)
- 500.1 (inválido)
Para altura:
- 0 (inválido)
- 0.49 (inválido)
- 0.50 (mínimo válido)
- 3.00 (máximo válido)
- 3.01 (inválido)
Para IMC (limites de classificação):
- 15.99 / 16.0 / 16.01
- 16.99 / 17.0 / 17.01
- 18.49 / 18.5 / 18.51
- 24.99 / 25.0 / 25.01
- 29.99 / 30.0 / 30.01
- 34.99 / 35.0 / 35.01
- 39.99 / 40.0 / 40.01
Trecho de cálculo utilizado nos testes:
double imc = IMCService.calcular(peso, altura);
String classificacao = IMCService.classificar(imc);Os cenários foram definidos com base em:
- Risco clínico direto: classificações erradas podem induzir decisões incorretas sobre saúde.
- Tendência natural a erros humanos (vírgulas, valores fora do domínio plausível).
- Limites matemáticos sensíveis (divisão por zero, negativos, extremos).
- Aplicação de Boundary Value Analysis, pois erros frequentes ocorrem nos limites.
- Partições de equivalência para garantir que cada comportamento possível seja exercitado.
- Garantia de cobertura completa dos intervalos da função
classificarIMC.
Ideia da propriedade
Para qualquer número inteiro de entrada, o resultado de MultiplyByTwo deve ser sempre par, pois é o produto de um inteiro por 2. Mesmo em cenários de overflow, a aritmética de inteiros em duas complementares preserva a paridade.
Trecho de código de teste com jqwik
Caminho sugerido:
src/test/java/br/com/at/math/MathFunctionsPropertyTests.java
package br.com.at.math;
import net.jqwik.api.*;
import org.junit.jupiter.api.Assertions;
class MathFunctionsPropertyTests {
private final MathFunctions mathFunctions =
new MathFunctions(operation -> {
// Logger fake para os testes; em cenários reais, usar mock (Mockito, etc.).
});
@Property
void multiplyByTwo_deveRetornarSempreNumeroPar(@ForAll int numero) {
int resultado = mathFunctions.multiplyByTwo(numero);
Assertions.assertEquals(0, resultado % 2,
"O resultado de multiplyByTwo deve ser sempre par.");
}
}Ideia da propriedade
Para qualquer número inteiro e um limite positivo, todos os elementos da tabela gerada devem obedecer à relação:
resultado[i] == numero * (i + 1) para i de 0 até limit - 1.
Isso cobre casos normais, negativos, zero e limites maiores, garantindo consistência da tabela.
Trecho de código de teste com jqwik
package br.com.at.math;
import net.jqwik.api.*;
import net.jqwik.api.constraints.*;
import org.junit.jupiter.api.Assertions;
class MathFunctionsPropertyTests {
private final MathFunctions mathFunctions =
new MathFunctions(operation -> {});
@Property
void generateMultiplicationTable_todosElementosDevemSerMultiplosDoNumero(
@ForAll int numero,
@ForAll @IntRange(min = 1, max = 100) int limite) {
int[] tabela = mathFunctions.generateMultiplicationTable(numero, limite);
Assertions.assertEquals(limite, tabela.length,
"O tamanho da tabela deve ser igual ao limite.");
for (int i = 0; i < limite; i++) {
int esperado = numero * (i + 1);
Assertions.assertEquals(esperado, tabela[i],
"Elemento da posição " + i + " não corresponde ao múltiplo esperado.");
}
}
}Esse teste cobre cenários realistas, negativos, positivos e zero (tabela de zeros), e limita o tamanho do array para evitar explosões de memória.
Ideia da propriedade
Para qualquer número inteiro inteiro n ≥ 2, se IsPrime(n) retornar true, então não pode existir nenhum divisor inteiro d com 2 ≤ d ≤ sqrt(n) tal que n % d == 0. Em outras palavras, se a função afirmar que um número é primo, essa afirmação deve ser consistente com a definição matemática de primalidade.
Trecho de código de teste com jqwik
package br.com.at.math;
import net.jqwik.api.*;
import net.jqwik.api.constraints.*;
import org.junit.jupiter.api.Assertions;
class MathFunctionsPropertyTests {
private final MathFunctions mathFunctions =
new MathFunctions(operation -> {});
@Property
void isPrime_quandoRetornaTrue_naoHaDivisoresEntre2ESqrt(
@ForAll @IntRange(min = 2, max = Integer.MAX_VALUE) int numero) {
// Só nos interessa investigar os casos em que o método afirma que é primo.
Assume.that(mathFunctions.isPrime(numero));
int limite = (int) Math.sqrt(numero);
for (int divisor = 2; divisor <= limite; divisor++) {
Assertions.assertNotEquals(0, numero % divisor,
"Numero " + numero + " não deveria ter divisor " + divisor + ".");
}
}
}Esse teste garante que, sempre que a função declara um número como primo, essa declaração é consistente com a ausência de divisores não triviais.
Ideia da propriedade
Para qualquer array de inteiros não vazio:
- A média aritmética deve estar sempre entre o menor e o maior valor do array.
- O método deve lançar exceção quando o array é nulo ou vazio (já coberto por testes tradicionais; aqui focamos na propriedade da média).
Gerador customizado para arrays não vazios
package br.com.at.math;
import net.jqwik.api.*;
import org.junit.jupiter.api.Assertions;
import java.util.Arrays;
class MathFunctionsPropertyTests {
private final MathFunctions mathFunctions =
new MathFunctions(operation -> {});
@Provide
Arbitrary<int[]> arraysInteirosNaoVazios() {
return Arbitraries.integers()
.between(-1_000_000, 1_000_000)
.array(int[].class)
.ofMinSize(1)
.ofMaxSize(1000);
}
@Property
void calculateAverage_mediaDeveEstarEntreMinimoEMaximo(
@ForAll("arraysInteirosNaoVazios") int[] numeros) {
double media = mathFunctions.calculateAverage(numeros);
int minimo = Arrays.stream(numeros).min().orElseThrow();
int maximo = Arrays.stream(numeros).max().orElseThrow();
Assertions.assertTrue(media >= minimo && media <= maximo,
"A média deve estar entre o mínimo e o máximo do array.");
}
}O gerador cobre arrays com valores grandes (positivos e negativos), tamanhos variados e casos potencialmente problemáticos para overflow de soma, enquanto a propriedade se baseia apenas na relação entre média, mínimo e máximo.
A versão original da classe MathFunctions utiliza apenas métodos estáticos, o que dificulta o uso de mocks e stubs em testes de unidades que dependem dessa biblioteca. Para melhorar a testabilidade e alinhar-se a princípios de design (como SOLID), a classe foi refatorada para:
- Aceitar uma dependência via construtor de uma interface
MathLogger. - Armazenar essa dependência internamente.
- Permitir que, em testes, seja injetado um mock (por exemplo, com Mockito) ou um logger fake.
Caminho sugerido:
src/main/java/br/com/at/math/MathLogger.java
package br.com.at.math;
public interface MathLogger {
void log(String operation, int[] inputs);
}Caminho sugerido:
src/main/java/br/com/at/math/MathFunctions.java
package br.com.at.math;
import java.util.Arrays;
public class MathFunctions {
private final MathLogger mathLogger;
public MathFunctions(MathLogger mathLogger) {
this.mathLogger = mathLogger;
}
public int multiplyByTwo(int number) {
log("multiplyByTwo", new int[]{number});
return number * 2;
}
public int[] generateMultiplicationTable(int number, int limit) {
int[] result = new int[limit];
for (int i = 0; i < limit; i++) {
result[i] = number * (i + 1);
}
log("generateMultiplicationTable", new int[]{number, limit});
return result;
}
public boolean isPrime(int number) {
if (number <= 1) {
log("isPrime", new int[]{number});
return false;
}
int limite = (int) Math.sqrt(number);
for (int divisor = 2; divisor <= limite; divisor++) {
if (number % divisor == 0) {
log("isPrime", new int[]{number, divisor});
return false;
}
}
log("isPrime", new int[]{number});
return true;
}
public double calculateAverage(int[] numbers) {
if (numbers == null || numbers.length == 0) {
log("calculateAverage", new int[0]);
throw new IllegalArgumentException("Array não pode ser nulo ou vazio.");
}
double media = Arrays.stream(numbers)
.average()
.orElseThrow(() -> new IllegalArgumentException("Não foi possível calcular a média."));
log("calculateAverage", numbers);
return media;
}
private void log(String operation, int[] inputs) {
if (mathLogger != null) {
mathLogger.log(operation, inputs);
}
}
}Com essa estrutura, qualquer classe que dependa de MathFunctions pode receber uma instância configurada com um MathLogger específico. Em testes, podemos injetar facilmente:
MathLogger loggerMock = Mockito.mock(MathLogger.class);
MathFunctions mathFunctions = new MathFunctions(loggerMock);Isso permite:
- Verificar se determinadas operações foram logadas.
- Simular comportamentos específicos do logger.
- Isolar testes de alto nível das implementações concretas de logging.
Dessa forma, o código fica mais flexível, testável e alinhado ao objetivo de ser uma biblioteca matemática de alta confiabilidade, preparada para ser integrada em aplicações críticas.
A API ViaCEP utiliza a rota /ws/{cep}/json/ para consultas. Os testes devem validar adequadamente:
- CEP vazio ou nulo: deve ser bloqueado antes da requisição.
- CEP com letras ou caracteres especiais: deve ser rejeitado por validação local.
- CEP com menos ou mais de 8 dígitos: inválido por formato.
- CEP válido mas inexistente: ViaCEP retorna
{"erro": true}, e a aplicação deve tratar como “CEP não encontrado”. - CEP válido e existente: deve retornar campos completos corretamente.
Trecho ilustrativo da resposta de CEP inexistente:
{
"erro": true
}Rota utilizada:
/ws/{UF}/{cidade}/{logradouro}/json/
Cenários testáveis:
- UF válida + cidade válida + logradouro válido: retorno esperado com lista de resultados.
- UF válida + cidade válida + logradouro inexistente: lista vazia ou retorno indicando ausência.
- Cidade com e sem acento: garantir tratamento correto de UTF-8 e URL encoding.
- UF inválida (tamanho ≠ 2 ou inexistente): deve ser bloqueada antes da chamada.
- Cidade inválida (números, símbolos) ou vazia: deve ser validada localmente.
- Logradouro vazio ou inválido: evitar requisições desnecessárias.
Exemplo de requisição válida:
https://viacep.com.br/ws/SP/Sao%20Paulo/Avenida%20Paulista/json/
| Caso | UF | Cidade | Logradouro | Situação | Resultado esperado | Comportamento esperado |
|---|---|---|---|---|---|---|
| 1 | SP | Sao Paulo | Avenida Paulista | Tudo válido | Lista com itens | Exibir dados |
| 2 | SP | São Paulo | Avenida Paulista | Acento na cidade | Lista com itens | Tratar UTF-8 corretamente |
| 3 | SP | Sao Paulo | Logradouro inexistente | Não encontrado | Lista vazia | Indicar ausência |
| 4 | SP | vazio | Avenida Paulista | Cidade ausente | Erro | Bloquear requisição |
| 5 | SP | 12345 | Avenida Paulista | Cidade inválida | Erro ou vazio | Tratar adequadamente |
| 6 | S | Sao Paulo | Avenida Paulista | UF com tamanho inválido | Erro | Bloquear requisição |
| 7 | ZZ | Sao Paulo | Avenida Paulista | UF inexistente | Vazio | Informar “UF inválida” |
| 8 | SP | Sao Paulo | vazio | Logradouro ausente | Erro | Bloquear requisição |
Partições:
- Válido (8 dígitos)
- Válido mas inexistente
- Inválido por formato
- Vazio/nulo
Valores limite:
- 7 dígitos (inválido)
- 8 dígitos (válido)
- 9 dígitos (inválido)
Partições:
- Válida (2 letras e existente)
- Inexistente (2 letras não pertencentes a estados reais)
- Inválida por tamanho
Valores limite:
- Tamanho 1 → inválido
- Tamanho 2 → válido
- Tamanho 3 → inválido
Partições:
- Válida (com ou sem acento)
- Inválida (números, caracteres especiais)
- Vazia
Valores limite:
- Tamanho 0 (inválido)
- Mínimo (1–2 caracteres)
- Muito longa (stress test)
Partições:
- Válido existente
- Válido porém inexistente
- Vazio
- Inválido (caracteres aleatórios)
Valores limite:
- 0 caracteres → inválido
- 1 caractere → mínimo
- Muito longo → teste de robustez
Utilizar partição de equivalência reduz a quantidade de testes mantendo ampla cobertura, enquanto a análise de valor limite explora pontos propensos a falhas, como tamanho de CEP e UF. A API ViaCEP é tolerante, mas a responsabilidade de interpretar corretamente erros, inexistência de dados, encoding e inputs inválidos recai sobre a aplicação cliente. A estratégia garante robustez, segurança e previsibilidade, essenciais em sistemas que consomem APIs públicas em produção
O objetivo deste exercício foi automatizar fluxos essenciais de um e-commerce utilizando o site https://automationexercise.com como ambiente de testes. A automação inclui:
- Cadastro de novo usuário
- Login com credenciais válidas
- Login com credenciais inválidas
- Estruturação seguindo Page Object Model (POM)
- Execução com JUnit 5
- Setup via WebDriverManager
- Captura de screenshots em falhas
Devido a restrições de rede e ao fato de o host msedgedriver.azureedge.net estar fora do ar, foi necessário implementar fallback para driver local, garantindo que os testes pudessem rodar independentemente do ambiente.
src/
└── test/
└── java/
└── br/com/infnet/at/atseleniumex4/
├── base/
│ └── BaseTest.java
├── extensions/
│ └── ScreenshotOnFailureExtension.java
├── pages/
│ ├── HomePage.java
│ ├── SignupLoginPage.java
│ ├── AccountCreationPage.java
│ └── LoggedInHomePage.java
└── tests/
└── FluxoCadastroLoginTests.java
A estrutura adota o padrão POM, mantendo páginas, testes, base de driver e extensões separados.
O Selenium normalmente utiliza WebDriverManager para baixar automaticamente os drivers.
Entretanto, no ambiente de execução:
- O domínio https://msedgedriver.azureedge.net estava inacessível.
- O WebDriverManager não conseguiu baixar o driver do Edge.
- Uma exceção
UnknownHostExceptionera lançada.
Para contornar isso, foi implementado:
- Tentativa de uso do WebDriverManager
- Fallback automático para driver local (
msedgedriver.exe)
Trecho principal:
try {
WebDriverManager.edgedriver().setup();
driver = new EdgeDriver();
} catch (Exception e) {
System.setProperty("webdriver.edge.driver", "C:\webdrivers\msedgedriver.exe");
driver = new EdgeDriver();
}Cada classe representa uma página real da aplicação:
- Acessa a área de login/cadastro.
- Inicia login ou criação de usuário.
- Preenche o formulário obrigatório do cadastro.
- Valida que o usuário aparece como logado após autenticação.
O uso de POM:
- Aumenta reusabilidade
- Facilita manutenção
- Separa claramente responsabilidades
Foi criada uma extensão JUnit:
ScreenshotOnFailureExtension
Ela captura screenshots automaticamente no diretório:
target/screenshots/
A captura ocorre somente quando o teste falha, seguindo boas práticas de QA.
Valida:
- Abertura da Home
- Acesso ao formulário de cadastro
- Preenchimento obrigatório
- Login automático após criação da conta
- Verificação de que o usuário está logado
Verifica:
- Mensagem de erro exibida
- Comportamento esperado em credenciais incorretas
Ambos os cenários são essenciais em regressão de plataforma e‑commerce.
Durante a automação, foram encontrados:
- Motivo: domínio da Microsoft indisponível.
- Solução: fallback para driver local.
Avisos como:
Unable to find CDP implementation matching 143
Eles não impedem a automação e são comuns quando versões do navegador são mais novas que as do Selenium.
SLF4J(W): No SLF4J providers were found.
Apenas indica falta de implementação de logging; não afeta os testes.
No final, os testes executaram com sucesso:
✔ O Edge abriu
✔ A automação navegou corretamente
✔ Login válido passou
✔ Login inválido foi validado
✔ Screenshots configurados
✔ Todos os testes passaram
A funcionalidade analisada do repositório TheAlgorithms/Java foi o algoritmo BinarySearch, localizado em com.thealgorithms.searches.BinarySearch.
Essa escolha foi motivada porque possui lógica clara e ramificações explícitas, inclui versões iterativa e recursiva, faz validações importantes (null, array não ordenado) e possibilita ampla cobertura estrutural.
Foram desenvolvidos testes cobrindo casos de sucesso, falha e validações de erro. A meta foi exercitar todas as decisões (if/else), ramificações, e o fluxo completo da recursão.
// Conteúdo reduzido para demonstrar a estrutura; versão completa utilizada nos testes
assertEquals(2, BinarySearch.search(new int[]{1,3,5,7}, 5));
assertThrows(IllegalArgumentException.class, () -> BinarySearch.search(null, 3));Gerado com:
mvn clean test jacoco:report
Relatório disponível em:
target/site/jacoco/index.html
A cobertura esperada é próxima de 100% (linhas e ramos), exceto trechos auxiliares internos.
A auditoria evidencia que a funcionalidade BinarySearch é adequadamente testada e robusta frente a entradas inválidas, limites e casos recursivos.