Uso de stubs para pruebas en JavaScript con Sinon.js

    Introducción

    Las pruebas son una parte fundamental del proceso de desarrollo de software. Al crear aplicaciones web, realizamos llamadas a API, bases de datos u otros servicios de terceros en nuestro entorno. Por lo tanto, nuestras pruebas deben validar que las solicitudes se envíen y las respuestas se manejen correctamente. Sin embargo, es posible que no siempre podamos comunicarnos con esos servicios externos cuando realizamos pruebas.

    En nuestra computadora de desarrollo local, es posible que no tengamos las claves API de la compañía o las credenciales de la base de datos para ejecutar una prueba con éxito. Es por eso que a veces “falsificamos” las respuestas HTTP o de la base de datos con un talón, engañando a nuestro código para que se comporte como si se hubiera realizado una solicitud real.

    En este artículo, comenzaremos analizando qué son los códigos auxiliares y por qué querríamos usarlos. Entonces aprovecharemos Sinon.js, una biblioteca de pruebas de JavaScript popular, para crear pruebas unitarias para JavaScript que aprueban una solicitud HTTP.

    Luego seguiremos esto con artículos sobre espías y simulacros:

    • Uso de stubs para realizar pruebas en JavaScript con Sinon.js (está aquí)
    • Uso de espías para realizar pruebas en JavaScript con Sinon.js
    • Uso de Mocks para pruebas en JavaScript con Sinon.js

    ¿Qué son los talones?

    Un stub de prueba es una función u objeto que reemplaza el comportamiento real de un módulo con una respuesta fija. El stub solo puede devolver la respuesta fija para la que fue programado.

    Un stub puede verse como una suposición para nuestra prueba: si asumimos que un servicio externo devuelve esta respuesta, así es como se comportará la función.

    Imagine que tiene una función que acepta una solicitud HTTP y obtiene datos de un punto final GraphQL. Si no podemos conectarnos al punto final de GraphQL en nuestras pruebas, bloquearemos su respuesta para que nuestro código se ejecute como si GraphQL fuera realmente afectado. Nuestro código de función no conocería la diferencia entre una respuesta GraphQL real y nuestra respuesta stubped.

    Veamos los escenarios en los que resulta útil utilizar stubbing.

    ¿Por qué utilizar stubs?

    Al realizar solicitudes a servicios externos en una prueba, puede encontrarse con estos problemas:

    • Pruebas fallidas debido a errores de conectividad de red en lugar de errores de código
    • Tiempos de ejecución prolongados, ya que la latencia de la red aumenta el tiempo de prueba
    • Afectar por error los datos de producción con pruebas si se produce un error de configuración

    Podemos solucionar estos problemas aislando nuestras pruebas y eliminando estas llamadas de servicio externo. No habría dependencia de la red, lo que haría que nuestras pruebas fueran más predecibles y menos propensas a fallar. Sin latencia de red, se espera que nuestras pruebas también sean más rápidas.

    Hay escenarios en los que las solicitudes externas no funcionarían. Por ejemplo, es común en los procesos de construcción de CI / CD bloquear solicitudes externas mientras se realizan pruebas por razones de seguridad. También es probable que en algún momento escribamos código que dependa de un servicio que todavía está en desarrollo y no en un estado para ser utilizado.

    En estos casos, los stubs son muy útiles ya que nos permiten probar nuestro código incluso cuando el servicio no está disponible.

    Ahora que sabemos qué son los stubs y por qué son útiles, usemos Sinon.js para obtener experiencia práctica con stubs.

    Uso de Sinon.js para crear un código auxiliar

    Usaremos Sinon.js para copiar una respuesta de una API JSON que recupera una lista de fotos en un álbum. Nuestras pruebas se crearán con el Moca y Chai pruebas de bibliotecas. Si desea obtener más información sobre las pruebas con Mocha y Chai antes de continuar, puede seguir nuestra guía.

    Preparar

    Primero, en tu terminal crea una nueva carpeta y muévete a ella:

    $ mkdir PhotoAlbum
    $ cd PhotoAlbum
    

    Inicialice NPM para que pueda realizar un seguimiento de los paquetes que instala:

    $ npm init -y
    

    Una vez que esté completo, podemos comenzar a instalar nuestras dependencias. Primero, instalemos la biblioteca de solicitudes, que será utilizada por nuestro código para crear una solicitud HTTP a la API. En su terminal, ingrese:

    $ npm i request --save
    

    Ahora, instalemos todas las bibliotecas de prueba como dependencias de desarrollo. El código de prueba no se usa en producción, por lo que no instalamos bibliotecas de prueba como dependencias de código regular con el --save opción. En su lugar, usaremos el --save-dev opción para decirle a NPM que estas dependencias solo deben usarse en nuestro entorno de desarrollo / prueba. Ingrese el comando en su terminal:

    $ npm i mocha chai sinon --save-dev
    

    Con todas nuestras bibliotecas importadas, crearemos una nueva index.js archivo y agregue el código para realizar la solicitud de API allí. Puede utilizar el terminal para crear el index.js archivo:

    $ touch index.js
    

    En su editor de texto o IDE, escriba el siguiente código:

    const request = require('request');
    
    const getPhotosByAlbumId = (id) => {
        const requestUrl = `https://jsonplaceholder.typicode.com/albums/${id}/photos?_limit=3`;
        return new Promise((resolve, reject) => {
            request.get(requestUrl, (err, res, body) => {
                if (err) {
                    return reject(err);
                }
                resolve(JSON.parse(body));
            });
        });
    };
    
    module.exports = getPhotosByAlbumId;
    

    Esta función realiza una llamada a una API que devuelve una lista de fotos de un álbum cuyo ID se pasa como parámetro a la función. Limitamos la respuesta a devolver solo tres fotos.

    Ahora escribiremos pruebas para nuestra función para confirmar que funciona como se esperaba. Nuestra primera prueba no utilizará códigos auxiliares, sino que realizará la solicitud real.

    Prueba sin stubs

    Primero, creemos un archivo para escribir nuestras pruebas. En la terminal o de otra manera, haga un index.test.js archivo en el directorio actual:

    $ touch index.test.js
    

    Nuestro código probará que obtenemos tres fotos y que cada foto tiene el esperado id, titley url propiedades.

    En el index.test.js archivo, agregue el siguiente código:

    const expect = require('chai').expect;
    const getPhotosByAlbumId = require('./index');
    
    describe('withoutStub: getPhotosByAlbumId', () => {
        it('should getPhotosByAlbumId', (done) => {
            getPhotosByAlbumId(1).then((photos) => {
                expect(photos.length).to.equal(3);
                photos.forEach(photo => {
                    expect(photo).to.have.property('id');
                    expect(photo).to.have.property('title');
                    expect(photo).to.have.property('url');
                });
                done();
            });
        });
    });
    

    En esta prueba, primero requerimos el expect() función de Chai, y luego requieren la getPhotosByAlbumId() función de nuestra index.js archivo.

    Usamos Mocha’s describe() y it() funciones para que podamos usar el mocha comando para ejecutar el código como prueba.

    Antes de ejecutar nuestra prueba, debemos agregar un script a nuestro package.json para ejecutar nuestras pruebas. En el package.json archivo, agregue lo siguiente:

    "scripts": {
        "test": "mocha index.test.js"
    }
    

    Ahora ejecuta tu prueba con el siguiente comando:

    $ npm test
    

    Debería ver este resultado:

    $ mocha index.test.js
    
      withoutStub: getPhotosByAlbumId
        ✓ should getPhotosByAlbumId (311ms)
    
      1 passing (326ms)
    

    En este caso, la prueba tardó 326 ms en ejecutarse, sin embargo, eso puede variar según la velocidad y ubicación de Internet.

    Esta prueba no se aprobaría si no tiene una conexión a Internet activa, ya que la solicitud HTTP fallaría. Aunque eso no significa que la función no se comporte como se esperaba. Usemos un código auxiliar para que podamos probar el comportamiento de nuestra función sin una dependencia de red.

    Prueba con stubs

    Reescribamos nuestra función para que podamos enviar la solicitud a la API, devolviendo una lista predefinida de fotos:

    const expect = require('chai').expect;
    const request = require('request');
    const sinon = require('sinon');
    const getPhotosByAlbumId = require('./index');
    
    describe('with Stub: getPhotosByAlbumId', () => {
        before(() => {
            sinon.stub(request, 'get')
                .yields(null, null, JSON.stringify([
                    {
                        "albumId": 1,
                        "id": 1,
                        "title": "accusamus beatae ad facilis cum similique qui sunt",
                        "url": "https://via.placeholder.com/600/92c952",
                        "thumbnailUrl": "https://via.placeholder.com/150/92c952"
                    },
                    {
                        "albumId": 1,
                        "id": 2,
                        "title": "reprehenderit est deserunt velit ipsam",
                        "url": "https://via.placeholder.com/600/771796",
                        "thumbnailUrl": "https://via.placeholder.com/150/771796"
                    },
                    {
                        "albumId": 1,
                        "id": 3,
                        "title": "officia porro iure quia iusto qui ipsa ut modi",
                        "url": "https://via.placeholder.com/600/24f355",
                        "thumbnailUrl": "https://via.placeholder.com/150/24f355"
                    }
                ]));
        });
    
        after(() => {
            request.get.restore();
        });
    
        it('should getPhotosByAlbumId', (done) => {
            getPhotosByAlbumId(1).then((photos) => {
                expect(photos.length).to.equal(3);
                photos.forEach(photo => {
                    expect(photo).to.have.property('id');
                    expect(photo).to.have.property('title');
                    expect(photo).to.have.property('url');
                });
                done();
            });
        });
    });
    

    Antes de que se ejecute la prueba, le decimos a Sinon.js que elimine el get() función de la request objeto que se usa en getPhotosByAlbumId ().

    Los argumentos pasados ​​al yields() La función del stub son los argumentos que se pasarán a la devolución de llamada de la solicitud de obtención. Pasamos null Para el err y res parámetros y una serie de datos de álbumes de fotos falsos para el body parámetro.

    Nota: Los after() La función se ejecuta después de completar una prueba. En este caso restauramos el comportamiento del request de la biblioteca get() función. Las mejores prácticas alientan a nuestros estados de prueba a ser independientes para cada prueba. Al restaurar la función, los cambios que hicimos para esta prueba no afectarían cómo se usa en otras pruebas.

    Como antes, ejecutamos esta prueba con npm test. Debería ver el siguiente resultado:

    $ mocha index.test.js
    
      with Stub: getPhotosByAlbumId
        ✓ should getPhotosByAlbumId
    
      1 passing (37ms)
    

    ¡Excelente! Ahora, sin una conexión a Internet, todavía estamos seguros de que nuestra función funciona bien con los datos esperados. ¡La prueba también fue más rápida! Sin una solicitud de red, simplemente necesitamos obtener los datos de la memoria.

    Conclusión

    Un stub es un reemplazo de una función que devuelve datos fijos cuando se llama. Por lo general, enviamos solicitudes a sistemas externos para hacer que las pruebas sean más predecibles y eliminar la necesidad de conexiones de red.

    Sinon.js se puede utilizar junto con otros marcos de prueba para funciones stub. En este artículo, detectamos una solicitud HTTP GET para que nuestra prueba se pueda ejecutar sin una conexión a Internet. También redujo el tiempo de prueba.

    Si desea ver el código de este tutorial, puede encontrarlo aquí.

    En nuestro próximo artículo, continuamos con Sinon.js y cubrimos cómo usar espías para probar JavaScript.

    Etiquetas:

    Deja una respuesta

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