ConstructiCat Logo
CodeBust.
Browse section ▾

Teste Verboso.

Um teste que usa muito mais código do que precisa para declarar seu cenário, soterrando a única relação de causa e efeito que verifica sob boilerplate de configuração, dados irrelevantes e asserções campo a campo.

##Signs and Symptoms

Você não consegue dizer o que um teste prova num relance, mesmo que nada de complicado esteja acontecendo — a intenção está afogada em volume. Sinais comuns:

  • O método de teste é longo (o catálogo de Test Smells usa um limite aproximado de >30 linhas) ou ultrapassa uma tela ao rolar.
  • Um grande bloco arrange constrói objetos campo a campo, boa parte irrelevante para o comportamento sob teste.
  • Muitos literais hard-coded sem ligação óbvia entre as entradas e as saídas asseridas.
  • O resultado é verificado com uma pilha de asserções de campo único em vez de uma comparação significativa.
  • Lendo as asserções, você não consegue mapear facilmente qual entrada causou qual valor esperado.
test('places an order for a returning customer', () => {
  const address = new Address();
  address.street = '123 Main St';
  address.city = 'Springfield';
  address.zip = '00000';                          // irrelevante para este teste
  address.country = 'US';

  const customer = new Customer();
  customer.id = 42;
  customer.firstName = 'Ada';
  customer.lastName = 'Lovelace';
  customer.email = 'ada@example.com';
  customer.address = address;
  customer.createdAt = new Date('2020-01-01');     // ruído

  const product = new Product();
  product.sku = 'SKU-1';
  product.name = 'Widget';
  product.price = 9.99;

  const cart = new Cart();
  cart.add(product, 2);

  const order = orderService.placeOrder(customer, cart);

  expect(order.status).toBe('CONFIRMED');          // as únicas linhas que importam
  expect(order.lineItems.length).toBe(1);
  expect(order.lineItems[0].sku).toBe('SKU-1');
  expect(order.lineItems[0].quantity).toBe(2);
  expect(order.total).toBe(19.98);
  expect(order.customerId).toBe(42);
});

Cerca de 25 linhas de construção protegem uma asserção de duas linhas sobre totais e status.

##Reasons for the Problem

Por que acontece

  • Construção inline com campos obrigatórios. Construir objetos reais por meio de construtores/setters obriga você a fornecer dados com os quais o teste não se importa, só para fazer o código compilar e rodar. Meszaros observa que o nível de detalhe necessário para tornar um teste executável pode deixá-lo "tão verboso a ponto de ser difícil de entender".
  • Crescimento por copiar e colar. Um teste começa pequeno e, então, cada novo cenário é criado copiando o anterior e ajustando um valor, de modo que o ruído se acumula.
  • Sem auxiliares de construção compartilhados. Sem Creation Methods, builders ou fixtures, todo teste reescreve o grafo de objetos completo.
  • Dados hard-coded e verificação campo a campo. Escrever literais por toda parte e asserir cada propriedade parece "minucioso", mas multiplica a contagem de linhas.

Por que isso prejudica

  • Legibilidade. O Teste Verboso é uma variação do Teste Obscuro — o catálogo até lista "Teste Obscuro" (Obscure Test) como seu alias. O leitor não consegue enxergar a relação de causa e efeito entre fixture e resultado, que é todo o propósito de um teste baseado em exemplo.
  • Manutenibilidade. Um teste com mais de 30 linhas tende a carregar "várias responsabilidades", então uma pequena mudança no código de produção repercute em muitas linhas em muitos testes. Configuração inline em massa também gera duplicação.
  • Falsa confiança. Testes longos e ruidosos escondem bugs à vista de todos: um literal incorreto ou uma asserção fora do lugar é fácil de passar despercebido, e a verbosidade faz os revisores lerem por cima. O volume parece rigoroso sem provar mais nada.
  • Confiabilidade. Quanto mais estado irrelevante um teste fixa, mais provável é que ele quebre por razões alheias à sua intenção (superespecificação), produzindo falhas frágeis e de baixo sinal.

##Treatment

Empurre o ruído para fora do corpo do teste, de modo que apenas as variáveis que conduzem o comportamento permaneçam visíveis. Movimentos concretos (todos do xUnit Test Patterns):

  1. Extraia Creation Methods / use um builder. Substitua a construção inline de objetos por um auxiliar de nome evocativo que forneça padrões sensatos; passe apenas os valores com os quais o teste realmente se importa (Parameterized Creation Method). Isso elimina tanto o boilerplate quanto a Informação Irrelevante.
  2. Atribua valores padrão aos dados irrelevantes dentro do auxiliar, sinalizando "esses valores não afetam o resultado".
  3. Substitua as verificações campo a campo por um Expected Object (toEqual/toMatchObject) ou por uma Custom Assertion / método de verificação que nomeie o conceito sendo verificado.
  4. Verifique um comportamento por teste. Se um único teste é longo porque exercita vários resultados (um Eager Test), divida-o.
  5. Parametrize testes verbosos quase idênticos com test.each / it.each em vez de copiar e colar.
// antes  — veja Sinais e Sintomas (≈30 linhas de setup + 6 asserções)

// depois
test('confirms the order and charges line-item total', () => {
  const customer = aCustomer();                          // Creation Method, com padrões
  const cart = aCartWith(aProduct({ price: 9.99 }), 2);  // apenas a entrada relevante

  const order = orderService.placeOrder(customer, cart);

  expect(order).toMatchObject({                          // Expected Object, não campo a campo
    status: 'CONFIRMED',
    total: 19.98,
  });
});

O cenário agora se lê em segundos: um cliente compra duas unidades de um produto de US$ 9,99 → o pedido é confirmado com um total de US$ 19,98. Os auxiliares (aCustomer, aProduct, aCartWith) são utilitários compartilhados e testados, então o ruído vive em um único lugar em vez de em cada teste.

Proteção: limite o tamanho da função de teste e a contagem de asserções no seu linter (veja os detectores) para que os testes não voltem silenciosamente a crescer e se tornar Testes Verbosos.

##Detected by

  • eslint max-lines-per-functionImpõe um número máximo de linhas por função — sinaliza corpos de teste que excedem o limite configurado, a medida central de um Teste Verboso.
  • eslint max-statementsImpõe um número máximo de instruções em um bloco de função — limita o quanto um único teste (uma callback de arrow/função) pode fazer.
  • sonar S138Funções não devem ter linhas de código demais — aplica-se a funções de teste; sinaliza métodos de teste grandes demais e com múltiplas responsabilidades (RSPEC-138).
  • eslint-jest jest/max-expectsImpõe um número máximo de chamadas expect() por teste (padrão 5) — captura a faceta de pilha de asserções dos testes verbosos/ansiosos.
  • eslint-vitest vitest/max-expectsImpõe um número máximo de chamadas expect() por teste (padrão 5) — equivalente do Vitest para jest/max-expects.