---
title: "Probar detalles de implementación"
type: "test-smell"
slug: "testing-implementation-details"
url: "http://localhost:3000/es/test-smells/testing-implementation-details.md"
category: "Olores de mocking"
description: "Una prueba hace aserciones sobre cómo funciona el código por dentro —campos privados, llamadas a métodos internos, estructura del DOM o clases CSS— en lugar del comportamiento observable del que depende un consumidor real."
---
# Probar detalles de implementación

> Una prueba hace aserciones sobre cómo funciona el código por dentro —campos privados, llamadas a métodos internos, estructura del DOM o clases CSS— en lugar del comportamiento observable del que depende un consumidor real.

## Signs and Symptoms

Reconoces este olor cuando una prueba va _más allá_ del contrato público y fija la maquinaria que hay detrás. Señales habituales:

* Aserciones sobre **estado o campos privados/internos**, o la invocación directa de métodos privados (a menudo mediante casts, reflexión o `// @ts-expect-error`).
* Espiar o afirmar que un **ayudante interno fue llamado** (`expect(internalCalc).toHaveBeenCalled()`) en lugar de comprobar el resultado.
* Pruebas de UI que consultan por **clase CSS, etiqueta, hooks `data-*` o posición en el DOM** en lugar de por rol/etiqueta/texto; por ejemplo, `container.querySelector('.btn-primary > span:nth-child(2)')`, `wrapper.state()`, `wrapper.find('SomeChildComponent').props()`.
* Pruebas de snapshot sobre **árboles de renderizado completos / objetos internos serializados**, de modo que cualquier retoque del marcado falla.
* Pruebas que **se rompen con cada refactorización** aunque la funcionalidad siga funcionando (falsos negativos) y, a la inversa, siguen pasando tras un error de lógica porque solo vuelven a comprobar el cableado (falsos positivos).

```js
// SMELL: acopla la prueba a los internos del componente y a la estructura del DOM
test('counter increments', () => {
  const wrapper = mount(<Counter />);
  wrapper.instance().handleClick();          // llama directamente a un método privado
  expect(wrapper.state('count')).toBe(1);    // afirma sobre el estado interno
  expect(wrapper.find('.count-display').text()).toBe('1'); // selector CSS frágil
});

```

La misma forma aparece en el lado del servidor: `expect(service._cache.size).toBe(1)` o afirmar la secuencia exacta de llamadas internas que hace un método.

## Reasons for the Problem

**Por qué ocurre**

* El estado interno es _fácil de alcanzar_: un campo público, un ayudante exportado o `container.querySelector` están ahí mismo, mientras que ejercitar el comportamiento real requiere más preparación.
* La persecución de métricas de cobertura: probar cada método privado uno a uno parece minucioso.
* El uso intensivo de mocks empuja a la gente a afirmar «¿se llamó a este colaborador?» en lugar de «¿ocurrió lo correcto?».
* Herramientas que lo fomentan: renderizado superficial (shallow rendering), APIs como `instance()` / `state()`, o la captura de nodos por nombre de clase.

**Por qué duele**

* **Mantenibilidad / fragilidad.** Esto es el _Test Frágil_ de Meszaros impulsado por _Software Sobreespecificado_: la prueba fija un comportamiento que el consumidor nunca requirió, de modo que refactorizaciones inofensivas (renombrar un método, reestructurar el marcado, cambiar un campo privado) rompen pruebas en verde sin motivo real. Las pruebas se convierten en un impuesto sobre la refactorización en lugar de una red de seguridad.
* **Falsa confianza (el peligro central, según Kent C. Dodds).** Las pruebas de detalles de implementación fallan en _ambas_ direcciones equivocadas: **falsos negativos** (la prueba se pone en rojo aunque la funcionalidad siga funcionando) y **falsos positivos** (la prueba sigue en verde aunque la funcionalidad esté rota: afirmaste el cableado, no el resultado). En cualquier caso, la suite deja de decirte la verdad.
* **Legibilidad.** La prueba documenta _cómo_ está construido el código, no _qué garantiza_. Quien la lee no puede saber qué comportamiento importa realmente, y la prueba ya no sirve como ejemplo de uso ni como especificación.
* **Acoplamiento.** Fija las decisiones de diseño actuales, desincentivando precisamente las refactorizaciones que las pruebas deberían hacer seguras.

## Treatment

Prueba a través del **contrato público** —la misma superficie que toca un llamador o usuario real— y haz aserciones sobre la **salida observable**: valores de retorno, errores lanzados, eventos emitidos, estado persistido o UI renderizada/visible.

1. **Identifica al consumidor.** Para un módulo, es su API exportada; para un componente de UI, es el usuario (clics, escritura) y lo que puede ver.
2. **Proporciona las entradas como lo haría un consumidor**, no llamando a métodos privados. Dispara un clic real en lugar de invocar el manejador; llama al método público en lugar al ayudante.
3. **Haz aserciones sobre resultados, no sobre internos.** Sustituye las comprobaciones de `state()`/campo-privado/`toHaveBeenCalled` por comprobaciones de lo que se produce.
4. **Consulta la UI por accesibilidad, no por estructura** —rol, etiqueta, texto— en lugar de clases CSS, etiquetas o `nth-child`.
5. **Deja de probar métodos privados directamente.** Cúbrelos a través del método público que los usa; si una unidad privada es lo bastante compleja como para necesitar sus propias pruebas, es señal de que conviene extraerla a su propio módulo con su propia API pública.
6. **Reserva las aserciones sobre mocks/espías para fronteras reales** (red, tiempo, pasarela de pago), donde la _llamada en sí_ es el comportamiento observable, no para colaboradores internos.

```js
// ANTES: prueba detalles de implementación
const wrapper = mount(<Counter />);
wrapper.instance().handleClick();
expect(wrapper.state('count')).toBe(1);
expect(wrapper.find('.count-display').text()).toBe('1');

// DESPUÉS: prueba el comportamiento observable a través del contrato público de cara al usuario
render(<Counter />);
await userEvent.click(screen.getByRole('button', { name: /increment/i }));
expect(screen.getByText('1')).toBeInTheDocument();

```

Regla práctica: si una refactorización que preserva el comportamiento rompe la prueba, la prueba estaba afirmando un detalle de implementación.

## Detected by

- **eslint-testing-library** `testing-library/no-node-access` — no-node-access (https://github.com/testing-library/eslint-plugin-testing-library/blob/main/docs/rules/no-node-access.md)
- **eslint-testing-library** `testing-library/no-container` — no-container (https://github.com/testing-library/eslint-plugin-testing-library/blob/main/docs/rules/no-container.md)
