Cuando estás realizando pruebas unitarias con React Testing Library, es muy frecuente que necesites simular el comportamiento de una función para poder aislar correctamente el componente que estás testeando y que este no tenga dependencias externas. En este post, te contaré cómo mockear funciones síncronas y asíncronas, gracias a Jest, para que puedas controlar la respuesta que te devuelven.

React Testing Library y Jest son herramientas que suelen utilizarse juntas en el ecosistema de pruebas de aplicaciones React. React Testing Library se beneficia de las funciones y utilidades que facilitan la escritura y ejecución de pruebas que proporciona Jest.

Entre estas utilidades tenemos las Mock Functions, encargadas de simular el comportamiento de las funciones durante las pruebas. Aquí tienes una lista completa de todas las Mock Functions que proporciona Jest, pero hoy nos vamos a centrar en las encargadas de mockear el valor que devuelven las funciones, tanto síncronas como asíncronas, de nuestro código React.

Comprendiendo .mockReturnValue() en funciones síncronas

Imagina que tenemos un componente React que utiliza una función externa para calcular la suma de dos números.

// sum.js
export const sum = (a, b) => a + b;

// MyComponent.jsx
import React from 'react';
import { sum } from './sum';

const MyComponent = () => {
  const result = sum(2, 3);
  return Result: {result};
};

export default MyComponent;

En nuestro archivo de test MyComponent.test.js queremos simular la función sum() y la respuesta de esta. Lo haremos de la siguiente manera:

//  MyComponent.test.js
import React from 'react';
import { render } from '@testing-library/react';
import MyComponent from './MyComponent';
import { sum } from './sum';

jest.mock('./sum'); // Mockeamos la función sum

test('renderiza correctamente con la función sum simulada', () => {
   sum.mockReturnValue(10); // Configuramos el valor de retorno simulado
   const { getByText } = render(<MyComponent />);
   expect(getByText('Result: 10')).toBeInTheDocument();
});

En este ejemplo utilizamos jest.mock() para mockear la función sum y .mockReturnValue() para simular el resultado de la función, haciendo que siempre devuelva 10.

Trabajando con Promesas: .mockResolvedValue()

Supongamos ahora que estamos manejando una llamada a una API en nuestro componente React. Vamos a ver cómo usar .mockResolvedValue() para simular una función asíncrona que devuelve una Promesa.

Partiendo del siguiente código React:

//api.js
export const getUser = async () => {
  const response = await fetch ('https://api.example.com/user');
  const user = await response.json();
  return user;
};

//MyAsyncComponent.jsx
import React, { useEffect, useState } from "react";
import { getUser } from "./api";

const MyAsyncComponent = () => {
  const [user, setUser] = useState(null);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchUserAsync = async () => {
      try {
        const result = await getUser();
        setUser(result);
      } catch (err) {
        setError(err.message);
      }
    };
    fetchUserAsync();
  }, []);

  return (
    <div>
      {user && `Hola ${user.name}`}
      {error && `Error al cargar los detalles del usuario: ${error}`}
      {!user && !error &&  Cargando usuario...}
    </div>
  );
};

export default MyAsyncComponent;

En el archivo de test MyAsyncComponent.test.js podemos utilizar .mockResolvedValue() para simular la función getUser y resolver la Promesa con un valor específico:

//MyAsyncComponent.test.js
import React from "react";
import { render, waitFor } from "@testing-library/react";
import MyAsyncComponent from "./MyAsyncComponent";
import { getUser } from "./api";

jest.mock("./api"); // Mockeamos la función getUser

test("renderiza correctamente con el usuario simulado", async () => {
  getUser.mockResolvedValue({ name: "MockedUser" }); // Mockeamos el valor de retorno de la Promesa

  const { getByText } = render(<MyAsyncComponent />);

  // En lugar de hacer una llamada real a la API, obtendremos el usuario simulado
  await waitFor(() => {
    expect(getByText("Hola, MockedUser")).toBeInTheDocument();
  });
});

En este caso, .mockResolvedValue() se utiliza para simular la función getUser y hacer que devuelva una Promesa resuelta con el objeto especificado ({ name: “MockedUser”' }). Esto permite probar el componente sin depender de la lógica real de la función getUser.

Caso de uso avanzado: simulando errores con .mockRejectedValue()

En el caso de que la llamada asíncrona que hemos realizado falle y nos devuelva un error, ¿cómo simulamos esa respuesta en nuestros test de React Testing Library? Para ello, Jest nos ofrece .mockRejectedValue, que simula un escenario de error durante la llamada a la API.

Partiendo del ejemplo anterior, tenemos el mismo componente MyAsyncComponent.jsx, la forma de implementar el control del error en nuestros test sería la siguiente:

//MyAsyncComponent.test.js
import React from "react";
import { render, waitFor } from "@testing-library/react";
import MyAsyncComponent from "./MyAsyncComponent";
import { getUser } from "./api";

jest.mock("./api"); // Mockeamos la función getUser

test("maneja correctamente el error de la llamada a la API", async () => {
  getUser.mockRejectedValue(new Error("Error en la llamada a la API")); //Configuramos el valor de retorno simulado para errores

  const { getByText } = render(<MyAsyncComponent />);
  await waitFor(() => {
    expect(getByText("Error al cargar los detalles del usuario: Error en la llamada a la API")).toBeInTheDocument();
  });
});

Con .mockRejectedValue() forzamos que la llamada a la API falle, y verificamos que nuestro componente ‘MyAsyncComponent’ maneja correctamente el error mostrando el mensaje correspondiente en pantalla.

Conclusiones

En este post, hemos aprendido a utilizar .mockReturnValue() y .mockResolvedValue() de Jest para simular funciones síncronas y asíncronas en pruebas realizadas con React Testing Library. Además, hemos descubierto .mockRejectedValue() para manejar errores en llamadas asíncronas.

Recordemos que existen otras Mock Functions en Jest que ofrecen funcionalidades específicas. Te invito a explorar y experimentar con ellas, elevando así la cobertura y eficacia de tus pruebas, asegurando un código más sólido y confiable.

¡Feliz testing!

Referencias

Cuéntanos qué te parece.

Los comentarios serán moderados. Serán visibles si aportan un argumento constructivo. Si no estás de acuerdo con algún punto, por favor, muestra tus opiniones de manera educada.

Suscríbete

Estamos comprometidos.

Tecnología, personas e impacto positivo.