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 鈥嬧媋l 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 *