Uso de simulacros para pruebas en JavaScript con Jest

    Introducción

    Jest es un popular marco de prueba de código abierto para JavaScript. Podemos usar Jest para crear simulaciones en nuestra prueba: objetos que reemplazan objetos reales en nuestro código mientras se prueba.

    En nuestra serie anterior sobre técnicas de pruebas unitarias que usan Sinon.js, cubrimos cómo podemos usar Sinon.js para apuntar, espiar y simular aplicaciones Node.js, particularmente llamadas HTTP.

    En esta serie, cubriremos las técnicas de prueba unitaria en Node.js usando Broma. Jest fue creado por Facebook y se integra bien con muchas bibliotecas y marcos de JavaScript como React, Angular y Vue, por nombrar algunos. Tiene un enfoque particular en la simplicidad y el rendimiento.

    En este artículo, revisaremos qué son las simulaciones y luego nos centraremos en cómo podemos configurar Jest para una aplicación Node.js para simular una llamada HTTP en nuestra prueba. Luego compararemos cómo usamos Jest y Sinon para crear simulaciones para nuestros programas.

    ¿Qué son los simulacros?

    En las pruebas unitarias, los simulacros nos brindan la capacidad de eliminar la funcionalidad proporcionada por una dependencia y un medio para observar cómo nuestro código interactúa con la dependencia. Los simulacros son especialmente útiles cuando es caro o poco práctico incluir una dependencia directamente en nuestras pruebas, por ejemplo, en los casos en que su código realiza llamadas HTTP a una API o interactúa con la capa de la base de datos.

    Es preferible eliminar las respuestas para estas dependencias, mientras se asegura de que se llamen según sea necesario. Aquí es donde las burlas son útiles.

    Veamos ahora cómo podemos usar Jest para crear simulacros en Node.js.

    Configuración de Jest en una aplicación Node.js

    En este tutorial, configuraremos una aplicación Node.js que realizará llamadas HTTP a una API JSON que contiene fotos en un álbum. Jest se utilizará para simular las llamadas a la API en nuestras pruebas.

    Primero, creemos el directorio bajo el cual residirán nuestros archivos y nos moveremos a él:

    $ mkdir PhotoAlbumJest && cd PhotoAlbumJest
    

    Luego, inicialicemos el proyecto Node con la configuración predeterminada:

    $ npm init -y
    

    Una vez inicializado el proyecto, procederemos a crear un index.js archivo en la raíz del directorio:

    $ touch index.js
    

    Para ayudarnos con las solicitudes HTTP, usaremos Axios.

    Configuración de Axios

    Nosotros usaremos axios como nuestro cliente HTTP. Axios es un cliente HTTP ligero y basado en promesas para Node.js que también puede ser utilizado por navegadores web. Esto lo convierte en una buena opción para nuestro caso de uso.

    Primero instalemoslo:

    $ npm i axios --save
    

    Antes de usar axios, crearemos un archivo llamado axiosConfig.js a través del cual configuraremos el cliente Axios. La configuración del cliente nos permite usar configuraciones comunes en un conjunto de solicitudes HTTP.

    Por ejemplo, podemos establecer encabezados de autorización para un conjunto de solicitudes HTTP o, más comúnmente, una URL base que se utilizará para todas las solicitudes HTTP.

    Creemos el archivo de configuración:

    touch axiosConfig.js
    

    Ahora accedamos axios y configurar la URL base:

    const axios = require('axios');
    
    const axiosInstance = axios.default.create({
        baseURL: 'https://jsonplaceholder.typicode.com/albums'
    });
    
    module.exports = axiosInstance;
    

    Después de configurar el baseURL, hemos exportado el axios instancia para que podamos usarlo en nuestra aplicación. Estaremos usando www.jsonplaceholder.typicode.com que es una API REST en línea falsa para pruebas y prototipos.

    En el index.js archivo que creamos anteriormente, definamos una función que devuelva una lista de URL de fotos dada la ID de un álbum:

    const axios = require('./axiosConfig');
    
    const getPhotosByAlbumId = async (id) => {
        const result = await axios.request({
            method: 'get',
            url: `/${id}/photos?_limit=3`
        });
        const { data } = result;
        return data;
    };
    
    module.exports = getPhotosByAlbumId;
    

    Para acceder a nuestra API, simplemente usamos el axios.request() método de nuestro axios ejemplo. Pasamos el nombre del método, que en nuestro caso es un get y el url que invocaremos.

    La String que pasamos al url el campo se concatenará al baseURL desde axiosConfig.js.

    Ahora, configuremos una prueba de Jest para esta función.

    Configurando Jest

    Para configurar Jest, primero debemos instalar Jest como una dependencia de desarrollo usando npm:

    $ npm i jest -D
    

    los -D bandera es un atajo para --save-dev, que le dice a NPM que lo guarde como una dependencia de desarrollo.

    Luego procederemos a crear un archivo de configuración para Jest llamado jest.config.js:

    touch jest.config.js
    

    Ahora, en el jest.config.js file, configuraremos los directorios en los que residirán nuestras pruebas:

    module.exports = {
        testMatch: [
            '<rootDir>/**/__tests__/**/?(*.)(spec|test).js',
            '<rootDir>/**/?(*.)(spec|test).js'
        ],
        testEnvironment: 'node',
    };
    

    los testMatch value es una matriz de patrones globales que Jest usará para detectar los archivos de prueba. En nuestro caso, estamos especificando que cualquier archivo dentro del __tests__ directorio o en cualquier lugar de nuestro proyecto que tenga un .spec.js o.test.js La extensión debe tratarse como un archivo de prueba.

    Nota: En JavaScript, es común ver que los archivos de prueba terminan con .spec.js. Los desarrolladores usan “spec” como abreviatura de “especificación”. La implicación es que las pruebas contienen los requisitos funcionales o la especificación para las funciones que se implementan.

    los testEnvironment value representa el entorno en el que se ejecuta Jest, es decir, ya sea en Node.js o en el navegador. Puede leer más sobre otras opciones de configuración permitidas aquí.

    Ahora modifiquemos nuestro package.json script de prueba para que use Jest como nuestro ejecutor de prueba:

    "scripts": {
      "test": "jest"
    },
    

    Nuestra configuración está lista. Para probar que nuestra configuración funciona, cree un archivo de prueba en la raíz del directorio llamado index.spec.js:

    touch index.spec.js
    

    Ahora, dentro del archivo, escribamos una prueba:

    describe('sum of 2 numbers', () => {
        it(' 2 + 2 equal 4', () => {
            expect(2 + 2).toEqual(4)
        });
    });
    

    Ejecute este código con el siguiente comando:

    $ npm test
    

    Debería obtener este resultado:

     PASS  ./index.spec.js
      sum of 2 numbers
        ✓ 2 + 2 equal 4 (3ms)
    
    Test Suites: 1 passed, 1 total
    Tests:       1 passed, 1 total
    Snapshots:   0 total
    Time:        0.897s, estimated 1s
    Ran all test suites.
    

    Con Jest configurado correctamente, ahora podemos proceder a escribir el código para simular nuestra llamada HTTP.

    Burlarse de una llamada HTTP con Jest

    En el index.spec.js file, comenzaremos de nuevo, eliminaremos el código anterior y escribiremos un nuevo script que simulará una llamada HTTP:

    const axios = require('./axiosConfig');
    const getPhotosByAlbumId = require('./index');
    
    jest.mock('./axiosConfig', () => {
        return {
            baseURL: 'https://jsonplaceholder.typicode.com/albums',
            request: jest.fn().mockResolvedValue({
                data: [
                    {
                        albumId: 3,
                        id: 101,
                        title: 'incidunt alias vel enim',
                        url: 'https://via.placeholder.com/600/e743b',
                        thumbnailUrl: 'https://via.placeholder.com/150/e743b'
                    },
                    {
                        albumId: 3,
                        id: 102,
                        title: 'eaque iste corporis tempora vero distinctio consequuntur nisi nesciunt',
                        url: 'https://via.placeholder.com/600/a393af',
                        thumbnailUrl: 'https://via.placeholder.com/150/a393af'
                    },
                    {
                        albumId: 3,
                        id: 103,
                        title: 'et eius nisi in ut reprehenderit labore eum',
                        url: 'https://via.placeholder.com/600/35cedf',
                        thumbnailUrl: 'https://via.placeholder.com/150/35cedf'
                    }
                ]
            }),
        }
    });
    
    

    Aquí, primero importamos nuestras dependencias usando el require sintaxis. Dado que no queremos hacer ninguna llamada de red real, creamos una simulación manual de nuestro axiosConfig utilizando el jest.mock() método. los jest.mock() El método toma la ruta del módulo como argumento y una implementación opcional del módulo como parámetro de fábrica.

    Para el parámetro de fábrica, especificamos que nuestro simulacro, axiosConfig, debe devolver un objeto que consta de baseURL y request(). los baseUrl se establece en la URL base de la API. los request() es una función simulada que devuelve una serie de fotos.

    los request() función que hemos definido aquí reemplaza a la real axios.request() función. Cuando llamamos al request() en su lugar, se llamará a nuestro método simulado.

    Lo que es importante tener en cuenta es el jest.fn() función. Devuelve una nueva función simulada y su implementación se define entre paréntesis. Lo que hemos hecho a través del mockResolvedValue() función es proporcionar una nueva implementación para el request() función.

    Normalmente, esto se hace a través del mockImplementation() función, aunque como en realidad solo estamos devolviendo el data que contiene nuestros resultados, podemos usar la función de azúcar en su lugar.

    mockResolvedValue() es lo mismo que mockImplementation(() => Promise.resolve(value)).

    Con una simulación en su lugar, sigamos adelante y escribamos una prueba:

    describe('test getPhotosByAlbumId', () => {
        afterEach(() => jest.resetAllMocks());
    
        it('fetches photos by album id', async () => {
            const photos = await getPhotosByAlbumId(3);
            expect(axios.request).toHaveBeenCalled();
            expect(axios.request).toHaveBeenCalledWith({ method: 'get', url: '/3/photos?_limit=3' });
            expect(photos.length).toEqual(3);
            expect(photos[0].albumId).toEqual(3)
        });
    });
    

    Después de cada caso de prueba, nos aseguramos de que jest.resetAllMocks() Se llama a la función para restablecer el estado de todos los simulacros.

    En nuestro caso de prueba, llamamos al getPhotosByAlbumId() función con un ID de 3 como argumento. Luego hacemos nuestras afirmaciones.

    La primera afirmación espera que el axios.request() se llamó al método, mientras que la segunda aserción comprueba que el método se llamó con los parámetros correctos. También verificamos que la longitud de la matriz devuelta sea 3 y que el primer objeto de la matriz tiene un albumId de 3.

    Ejecutemos nuestras nuevas pruebas con:

    npm test
    

    Deberíamos obtener el siguiente resultado:

    PASS  ./index.spec.js
      test getPhotosByAlbumId
        ✓ fetches photos by album id (7ms)
    
    Test Suites: 1 passed, 1 total
    Tests:       1 passed, 1 total
    Snapshots:   0 total
    Time:        0.853s, estimated 1s
    Ran all test suites.
    

    Con esta nueva familiaridad y experiencia, hagamos una comparación rápida de las experiencias de prueba con Jest y Sinon, que también se usa comúnmente para burlarse.

    Sinon Mocks vs Jest Mocks

    Sinon.js y Jest tienen diferentes formas de abordar el concepto de burla. Las siguientes son algunas de las diferencias clave a tener en cuenta:

    • En Jest, los módulos de Node.js se simulan automáticamente en sus pruebas cuando coloca los archivos simulados en un __mocks__ carpeta que está al lado de la node_modules carpeta. Por ejemplo, si tiene un archivo llamado __mock__/fs.js, entonces cada vez que el fs se llama al módulo en su prueba, Jest usará automáticamente los simulacros. Por otro lado, con Sinon.js debes simular cada dependencia manualmente usando el sinon.mock() método en cada prueba que lo necesite.
    • En Jest, usamos el jest.fn().mockImplementation() método para reemplazar la implementación de una función simulada con una respuesta stubped. Un buen ejemplo de esto se puede encontrar en la documentación de Jest. aquí. En Sinon.js, usamos el mock.expects() método para manejar eso.
    • Jest proporciona una gran cantidad de métodos para trabajar con su API simulada y, en particular, con módulos. Puedes verlos todos aquí. Sinon.js, por otro lado, tiene menos métodos para trabajar con simulacros y expone una API generalmente más simple.
    • Sinon.js simula el envío como parte de la biblioteca Sinon.js que se puede conectar y usar en combinación con otros marcos de prueba como Mocha y bibliotecas de afirmaciones como Chai. Jest simula, por otro lado, se envía como parte del marco de Jest, que también se envía con su propia API de afirmaciones.

    Las simulaciones de Sinon.js suelen ser más beneficiosas cuando estás probando una pequeña aplicación que puede no requerir todo el poder de un marco como Jest. También es útil cuando ya tiene una configuración de prueba y necesita agregar simulaciones a algunos componentes en su aplicación.

    Sin embargo, cuando se trabaja con aplicaciones grandes que tienen muchas dependencias, aprovechar el poder de la API simulada de Jest junto con su marco puede ser muy beneficioso para garantizar una experiencia de prueba consistente.

    Conclusión

    En este artículo, hemos visto cómo podemos usar Jest para simular una llamada HTTP realizada con axios. Primero configuramos la aplicación para usar axios como nuestra biblioteca de solicitudes HTTP y luego configurar Jest para ayudarnos con las pruebas unitarias. Finalmente, hemos revisado algunas diferencias entre Sinon.js y los simulacros de Jest y cuándo podemos emplearlos mejor.

    Para leer más sobre los simulacros de Jest y cómo puede aprovecharlos para casos de uso más avanzados, consulte su documentación. aquí.

    Como siempre, el código de este tutorial se puede encontrar en GitHub.

    Etiquetas:

    Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *