Logotipo do ConstructiCat
CodeBust.
Explorar a seção ▾

Teste Desconhecido.

Um método de teste que exercita o código mas não contém nenhuma asserção, de modo que passa desde que nada lance uma exceção — deixando desconhecido seu real propósito e o que ele verifica.

##Sinais e sintomas

Um teste que configura objetos e chama o sistema sob teste e então para sem uma única asserção. Ele está verde simplesmente porque nenhuma exceção foi lançada — não porque algum comportamento esperado foi confirmado. Nada no corpo declara como é o "correto".

Sinais reveladores:

  • Nenhum expect/assert/verify em lugar algum do corpo do teste.
  • "Verificação" feita por console.log/print do resultado que um humano supostamente deveria conferir a olho.
  • Um nome de teste vago (testChainDependencies) e um corpo que não dá nenhuma pista do que ele garante.
  • O teste continuaria verde mesmo que o método de produção retornasse um valor completamente errado.
  • Testes que se apoiam inteiramente em "não lançou exceção" sem dizê-lo explicitamente.
// Smell: roda código, imprime, não faz asserção alguma — passa não importa o que calculate() retorne
test('chain dependencies', () => {
  const game = Game.newGame(0, '');
  game.setOtherGoods(Building.TOOLMAKERS, 1);
  const logic = new Logic(game);

  const res = logic.calculateChainWithDependencies(Goods.TOOLS);
  console.log(res.toString()); // sem expect(...) — o que isso está verificando?
});

Uma variante comum se esconde atrás de mocking pesado: o teste configura mocks e chama o SUT, mas nunca faz asserção sobre um valor de retorno ou sobre as interações com os mocks.

##Causas do problema

Por que acontece

  • Um teste de espaço reservado/TODO foi gerado como esqueleto ("faça compilar e rodar") e a asserção nunca foi preenchida.
  • Andaime de depuração — console.log, uma verificação manual — acabou commitado como se fosse um teste.
  • Uma refatoração excluiu ou comentou a asserção, mas deixou a configuração (setup) para trás.
  • "Roda sem lançar exceção" é confundido com "funciona." Testes de fumaça (smoke tests) são legítimos, mas aqui a intenção de fazer um smoke test nunca é tornada explícita.
  • Stubs de teste gerados automaticamente ou por IA que exercitam um método sem verificar o resultado.

Por que prejudica

  • Falsa confiança. O teste contribui para a barra verde e para a cobertura de linhas/ramos, mas não verifica nada. As métricas de cobertura mentem ativamente sobre o quão protegido o código está.
  • Nenhuma proteção contra regressões. O comportamento pode quebrar silenciosamente — valor de retorno errado, estado errado — e a suíte continua verde. Este é o pior tipo de teste: custa manutenção mas não pega nada.
  • Intenção desconhecida. Um leitor (ou futuro mantenedor) não consegue dizer qual comportamento é garantido, então não pode alterar com segurança o código ou o teste. Ele não documenta nada.
  • Corrói a confiança na suíte. Quando as pessoas percebem testes que não testam de verdade, elas param de acreditar que verde significa bom — minando todos os outros testes também.

É a imagem espelhada da Roleta de Asserções: aquele code smell tem asserções demais e sem documentação; o Teste Desconhecido não tem nenhuma.

##Tratamento

Faça cada teste declarar o que ele espera e torne "apenas não deve lançar exceção" uma escolha explícita e deliberada.

  1. Adicione pelo menos uma asserção sobre um resultado observável — o valor de retorno, o estado resultante ou um erro lançado. Substitua a depuração com console.log/print por um expect sobre esse valor.
  2. Se a intenção real é "isto não deve lançar exceção," diga isso explicitamente com expect(() => fn()).not.toThrow() (para uma função assíncrona, await expect(fn()).resolves.not.toThrow(); .resolves.toBeDefined() falharia se a função resolvesse com undefined). Agora o smoke test está documentado em vez de acidental.
  3. Para testes apenas de interação, faça asserção sobre o colaborador: expect(mock).toHaveBeenCalledWith(...).
  4. Exclua espaços reservados mortos ou transforme-os em um todo com ticket. it.todo('handles chained deps — PROJ-123') aparece no relatório como todo, nunca como aprovado, então a lacuna continua visível sem fingir cobertura. Não deixe para trás um teste vazio que passa, nem estacione o teste com um skip sem motivo — isso só troca este smell por um teste ignorado.
  5. Ative um detector (jest/expect-expect, vitest/expect-expect ou SonarSource S2699) na CI. Se você encapsular asserções em helpers personalizados, registre-os pela opção assertFunctionNames da regra para que asserções genuínas não sejam sinalizadas.
// Antes — Teste Desconhecido
test('chain dependencies', () => {
  const logic = new Logic(Game.newGame(0, ''));
  const res = logic.calculateChainWithDependencies(Goods.TOOLS);
  console.log(res.toString());
});

// Depois — intenção e garantia são explícitas
test('resolves tools to the toolmakers workshop chain', () => {
  const logic = new Logic(Game.newGame(0, ''));
  const res = logic.calculateChainWithDependencies(Goods.TOOLS);
  expect(res).toHaveLength(1);
  expect(res[0].building).toBe(Building.TOOLMAKERS);
});

##Detectado por

  • eslint-jest jest/expect-expect — Exige pelo menos uma asserção no corpo de cada teste
  • eslint-vitest vitest/expect-expect — Exige pelo menos uma asserção no corpo de cada teste
  • sonar javascript:S2699 — Os testes devem incluir asserções (JavaScript)
  • sonar java:S2699 — Os testes devem incluir asserções (Java)