Volver al inicio

React Testing Library: Deshaciéndose de Snapshots

El artículo describe la transición de Enzyme a React Testing Library en el proyecto. Análisis de problemas de snapshot y pruebas de implementación. Listas de verificación para escribir y revisar pruebas, recomendaciones para meetups y migración.

RTL en lugar de Enzyme: pruebas de comportamiento React
Advertisement 728x90

De instantáneas a comportamiento: migrar a React Testing Library

En proyectos típicos, las pruebas suelen centrarse en la estructura del código en lugar de en el funcionamiento real. Las pruebas de instantánea capturan el estado del DOM, pero fallan con cualquier refactorización: cambiar clases CSS, reordenar atributos o optimizar la renderización. Los errores lógicos reales permanecen sin detectar.

Un ejemplo problemático con Enzyme

describe("UserProfileCards", () => {
    it("should render", () => {
        const wrapper = shallow(
            <Provider store={store}>
                <UserProfileCards items={mockedUserProfiles} />
            </Provider>
        )
        expect(wrapper).toMatchSnapshot()
    })
})

Esta prueba no ofrece confianza real sobre el comportamiento del componente. Verifica la estructura, no lo que el usuario realmente experimenta.

Acoplamiento en implementación: falsos positivos

Las pruebas ligadas a detalles internos generan un problema doble: fallan durante refactorizaciones y pasan por alto errores reales.

Google AdInline article slot

Considera un componente CollapseIcon con un estado opened:

import CollapseIcon from "./CollapseIcon"
import { shallow } from "enzyme"

describe("CollapseIcon", () => {
    it("should render", () => {
        const wrapper = shallow(<CollapseIcon opened={false} />)
        expect(wrapper).toMatchSnapshot()
    })

    it("should react to closed", () => {
        const wrapper = shallow(<CollapseIcon opened={false} />)
        expect(wrapper.find(".collapse__icon").hasClass("collapseIcon_closed")).toEqual(true)
    })

    it("should react to opened", () => {
        const wrapper = shallow(<CollapseIcon opened={true} />)
        expect(wrapper.find(".collapse__icon").hasClass("collapseIcon_opened")).toEqual(true)
    })
})

Problemas:

  • Las instantáneas no detectan errores en la lógica de animaciones.
  • Las comprobaciones de clases fallan al refactorizar estilos.
  • Las pruebas pasan incluso si las animaciones están rotas.

Otro caso: acoplamiento fuerte con constantes de localización:

Google AdInline article slot
it("should render no-answers button", () => {
    const button = getButton(mockCommentsList[0])
    const noAnswersText = `${mockedTranslation.t(`${tNamespace}no-answers`)}
    expect(button.text()).toBe(noAnswersText)
})

Cambiar la clave en tNamespace rompe la prueba, aunque el texto del botón sea correcto. Solución: mover las constantes a un módulo compartido importado tanto por el componente como por las pruebas.

Descripciones de pruebas ambiguas empeoran la situación:

  • "should show tooltip" — sin condiciones.
  • "should render without tooltipLabel" — sin resultado esperado.

React Testing Library: probando el comportamiento del usuario

RTL cambia el paradigma: las pruebas simulan acciones reales del usuario y ignoran los detalles internos. No se tiene acceso al estado, props ni métodos — solo a la interfaz visible.

Google AdInline article slot

Beneficios de RTL:

  • Enfoque centrado en el usuario: clics, escritura, lectura de pantalla.
  • Seguro frente a refactorizaciones: independiente de nombres de clases, IDs o estado interno.
  • API: render(), screen.getByRole(), fireEvent.click(), waitFor().
  • Soporte asíncrono: findByRole() para carga de datos.
  • Accesibilidad: getByLabelText() asegura etiquetado adecuado.

RTL está diseñado para resiliencia: las pruebas reflejan el uso real, no la estructura del DOM.

Checklist para pruebas de alta calidad

Escribir pruebas

  • Propósito: define claramente lo que se está probando (por ejemplo, "muestra tooltip cuando isTooltipShow=true").
  • Perspectiva del usuario: acciones como lo haría un usuario real — getByRole('button'), fireEvent.
  • Aislamiento: mocks para APIs; ninguna dependencia entre pruebas.
  • Descripciones: frases completas legibles sin código.
  • Cobertura: camino feliz + casos extremos + estados de error.

Revisar pruebas

  • ¿El objetivo es claro?
  • ¿Se evitan detalles de implementación (clases, estado)?
  • ¿La prueba está aislada?
  • ¿Las descripciones son informativas?
  • ¿Los comentarios son constructivos?

Organizar la migración del equipo

Organiza un taller de migración: reúne pruebas problemáticas, analiza ejemplos y transita a RTL. Recomendaciones:

  • Divide las sesiones en bloques de 45 minutos con práctica hands-on.
  • Refactoriza pruebas juntos mediante programación en pareja.
  • Prioriza la práctica sobre la teoría.

Migración de legacy: escribe nuevas pruebas en RTL y reescribe gradualmente las antiguas. Caso Airbnb: 3.500 archivos reescritos en 6 semanas usando una pipeline impulsada por LLM (75% automatizada).

Conclusiones clave

  • Cambio de mentalidad: de markup a comportamiento del usuario.
  • RTL vs Enzyme: RTL bloquea el acceso interno; Enzyme lo fomenta.
  • Checklists: obligatorios para escribir y revisar.
  • Talleres: diálogo supera monólogo.
  • Migración: la automatización acelera el progreso, pero entender la filosofía es esencial.

— Editorial Team

Advertisement 728x90

Leer después