Prueba el comportamiento en lugar de la implementación, aprovecha las dobles invocaciones de StrictMode y mide los renderizados con Profiler.
Abrir esta lección en KodokonLa filosofía de Testing Library cabe en una frase: prueba lo que el usuario percibe, nunca los detalles de implementación. Concretamente, consultas el DOM a través del árbol de accesibilidad: getByRole primero (la reina de las consultas, que valida de paso tus roles ARIA), luego getByLabelText, getByText, y getByTestId solo como último recurso. Para las interacciones, prefiere user-event antes que fireEvent: donde fireEvent.change emite un evento aislado, user.type reproduce la secuencia completa (focus, keydown, keypress, input, keyup), exactamente como un teclado real. Las variantes findBy* combinan una consulta con una espera asíncrona: imprescindibles tras un paso de carga.
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
test('adds a task to the list', async () => {
const user = userEvent.setup();
render(<TodoApp />);
await user.type(
screen.getByRole('textbox'),
'Review the report'
);
await user.click(
screen.getByRole('button', { name: /add/i })
);
expect(
await screen.findByText('Review the report')
).toBeInTheDocument();
});StrictMode es una herramienta de desarrollo sin efecto alguno en producción. En desarrollo, llama a tus funciones de componente, a los inicializadores de estado y a las funciones pasadas a setState dos veces, para sacar a la luz los renderizados impuros: si dos ejecuciones dan resultados diferentes, tienes un efecto secundario oculto. Desde React 18, también hace pasar cada efecto por el ciclo montaje, desmontaje, remontaje: un efecto cuya limpieza no sea perfectamente simétrica (una suscripción nunca cancelada, una petición nunca abortada) se delata de inmediato. Es una preparación directa para funcionalidades en las que React desmonta y luego remonta una pantalla conservando su estado.
useEffect(() => {
const controller = new AbortController();
fetch('/api/user', { signal: controller.signal })
.then((res) => res.json())
.then(setUser)
.catch(() => {});
return () => controller.abort();
}, []);Para medir, React proporciona el componente <Profiler> y su equivalente gráfico en las DevTools. Su callback onRender recibe, entre otras cosas, phase (mount, update, o nested-update), actualDuration (el tiempo realmente dedicado a renderizar el subárbol para este commit), y baseDuration (una estimación del tiempo para renderizar todo el subárbol sin ninguna memoización). La diferencia entre ambos mide la eficacia de tus memo y useMemo: un actualDuration muy por debajo de baseDuration significa que la memoización está trabajando a tu favor.
import { Profiler } from 'react';
function App() {
return (
<Profiler
id="Sidebar"
onRender={(id, phase, actualDuration) => {
console.log(id, phase, actualDuration);
}}
>
<Sidebar />
</Profiler>
);
}baseDuration en el callback del Profiler?