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 *