---
title: "Código Difícil de Testar"
type: "test-smell"
slug: "hard-to-test-code"
url: "http://localhost:3000/pt-br/test-smells/hard-to-test-code.md"
category: "Maus Cheiros de Acoplamento"
description: "Código de produção cujo design (acoplamento forte, dependências ocultas, estado global, IO não determinística ou interfaces apenas assíncronas) força os testes a contorções estranhas, ou torna impossível exercitar uma unidade isoladamente."
---
# Código Difícil de Testar

> Código de produção cujo design (acoplamento forte, dependências ocultas, estado global, IO não determinística ou interfaces apenas assíncronas) força os testes a contorções estranhas, ou torna impossível exercitar uma unidade isoladamente.

## Signs and Symptoms

Este smell vive no código de _produção_, mas você o descobre ao escrever testes. O sinal revelador é que um teste unitário não pode ser escrito de forma limpa — o teste precisa lutar contra o design para colocar o código sob teste em um estado conhecido e observar o resultado.

Fique atento a estes sintomas:

* **Blocos de arrange gigantes.** Você precisa construir uma teia de colaboradores só para instanciar a classe sob teste ("não consigo testar `OrderService` sem também montar um banco de dados, um gateway e um carregador de configuração").
* **Nenhuma costura para injetar um dublê.** O código chama `new ConcreteThing()`, acessa um singleton estático (`Database.getInstance()`) ou usa a rede/sistema de arquivos/relógio diretamente, então não há onde substituir por um stub ou fake.
* **Não determinismo embutido.** A lógica depende de `Date.now()`, `Math.random()`, `process.env` ou do tempo do relógio que o teste não pode controlar, produzindo resultados instáveis ou irrepetíveis.
* **Sem ponto de observação / sem ponto de controle.** O resultado que você quer verificar está enterrado em estado privado ou em um efeito colateral, então os testes acessam via reflexão, casts ou getters exclusivos de teste.
* **Interface apenas assíncrona.** A única forma de acionar o código é iniciar um timer/thread/fila e então usar `sleep()` ou polling, porque a conclusão nunca é diretamente observável.
* **Você altera o código de produção só para testá-lo.** Afrouxar `private` para `public`, adicionar hooks de teste `if (testMode) { ... }` ou criar subclasses apenas para alcançar os detalhes internos.

```js
// Difícil de testar: dependências ocultas + fixas, estado global, não determinismo
class OrderService {
  placeOrder(cart) {
    const db = Database.getInstance();            // singleton global (sem costura)
    const gateway = new StripeGateway(API_KEY);   // dependência concreta fixa -> rede real
    const id = Math.random().toString(36).slice(2); // não determinístico
    const charge = gateway.charge(cart.total);    // chamada HTTP real dentro da unidade
    db.save({ id, at: Date.now(), charge });       // relógio não injetado
    return id;
  }
}
// Para fazer "teste unitário" disso você precisa acessar o Stripe, fazer monkey-patch global de Date/Math,
// e inspecionar um singleton compartilhado — ou seja, Teste Indireto por meio de interfaces estranhas.

```

## Reasons for the Problem

**Por que isso acontece**

* **Test-Last em código legado.** Meszaros nomeia a causa raiz como _falta de Design para Testabilidade_. A testabilidade surge naturalmente do TDD, mas quando os testes são escritos "por último," nada pressionou o design a expor pontos de controle e observação, então ela precisa ser adaptada depois.
* **Acoplamento forte e dependências fixas no código.** Instanciar colaboradores concretos com `new` (ou obtê-los de singletons estáticos) significa que você não consegue substituí-los por dublês de teste — o teste exercita a unidade _e_ tudo o que ela toca.
* **Estado global/compartilhado e entradas ocultas.** Singletons, estáticos, tempo ambiente, aleatoriedade e leituras do ambiente são entradas que o teste nunca declarou e não pode definir, então o comportamento é implícito e incontrolável.
* **Assincronia e efeitos colaterais no núcleo.** Quando a lógica de negócio está emaranhada com threads, filas, IO ou a interface de usuário, não há um valor puro para verificar.

**Por que isso é prejudicial**

* **Confiabilidade.** Testes forçados a usar IO real, sleeps, singletons compartilhados ou relógios globais ficam lentos, instáveis e dependentes de ordem — o caminho clássico para um Teste Errático/Frágil.
* **Manutenibilidade.** Configurações enormes e testes que alcançam os detalhes internos acoplam a suíte de testes aos detalhes de implementação, então refatorações inofensivas quebram testes (Teste Frágil / Fixture Frágil).
* **Falsa confiança.** Os caminhos de código mais difíceis e importantes são testados apenas por interfaces grosseiras e indiretas — ou são totalmente ignorados. Os números de cobertura parecem bons enquanto a lógica arriscada mal é exercitada.
* **Legibilidade.** Um teste dominado por andaimes obscurece o único comportamento que ele deveria especificar, então não documenta nada.

No Open Catalog of Test Smells, o _Código Difícil de Testar_ é a contraparte, do lado da produção, de smells de teste como _Teste Indireto_ e _Teste Frágil_: o design ruim é a causa, os testes estranhos são o sintoma.

## Treatment

Corrija o _design_, não o teste. O objetivo é dar a cada unidade um **ponto de controle** (uma forma de colocá-la em um estado conhecido) e um **ponto de observação** (uma forma de ler o resultado).

1. **Introduza costuras via Injeção de Dependência.** Passe os colaboradores (injeção via construtor) em vez de construí-los ou procurá-los. Injete também as entradas ambientes — relógio, gerador de id/uuid, fonte de aleatoriedade — para que se tornem parâmetros controláveis.
2. **Dependa de abstrações.** Programe para uma interface e substitua por um stub/fake/mock nos testes. Isso remove diretamente a _Hard-wired Dependency_ do Designite e reduz a _Excessive Dependency_.
3. **Aplique o padrão Humble Object.** Empurre as partes genuinamente difíceis de testar (UI, cola assíncrona, IO bruta) para um adaptador fino e sem lógica, e mova a lógica de decisão para um objeto simples e síncrono que você possa testar diretamente.
4. **Extraia funções puras.** Separe a computação dos efeitos colaterais; verifique os valores retornados, faça IO apenas na fronteira.
5. **Torne o assíncrono observável.** Retorne uma promise/future ou exponha um sinal de conclusão, e injete o escalonador para que os testes usem fake timers em vez de `sleep`.
6. **Para código legado que você ainda não pode reprojetar,** use uma _Subclasse Específica de Teste_ ou uma costura de escopo restrito (técnicas de Feathers) para colocar o código sob teste, e então refatore em direção à injeção — em vez de deixar hooks `if (testMode)` permanentes em produção.
7. **Evite estado global/estático.** Substitua singletons por dependências passadas explicitamente para que os testes não compartilhem nem precisem redefinir estado oculto.

```js
// DEPOIS: dependências, relógio e id são injetados -> testável isoladamente
class OrderService {
  constructor(db, gateway, clock = () => Date.now(), newId = uuid) {
    this.db = db; this.gateway = gateway; this.clock = clock; this.newId = newId;
  }
  placeOrder(cart) {
    const id = this.newId();
    const charge = this.gateway.charge(cart.total); // uma abstração PaymentGateway
    this.db.save({ id, at: this.clock(), charge });
    return id;
  }
}

// Teste: determinístico, sem rede, sem patch global, rápido.
const svc = new OrderService(fakeDb, fakeGateway, () => 1000, () => 'id-1');
expect(svc.placeOrder({ total: 50 })).toBe('id-1');
expect(fakeGateway.charge).toHaveBeenCalledWith(50);
expect(fakeDb.saved[0]).toEqual({ id: 'id-1', at: 1000, charge: fakeGateway.result });

```

## Detected by

- **designite** `Hard-wired Dependency (testability smell)` — Hard-wired Dependency (https://www.designite-tools.com/blog/understanding-testability-test-smells)
- **designite** `Excessive Dependency (testability smell)` — Excessive Dependency (https://www.designite-tools.com/blog/understanding-testability-test-smells)
- **designite** `Global State (testability smell)` — Global State (https://www.designite-tools.com/blog/understanding-testability-test-smells)
- **designite** `Law of Demeter Violation (testability smell)` — Law of Demeter Violation (https://www.designite-tools.com/blog/understanding-testability-test-smells)
