Crea tu primera API GraphQL con AWS AppSync

Avatar img
Alex Suquillo
Software Developer
agosto 17, 2020
Lectura de 2 minutos
Crea tu primera API GraphQL con AWS AppSync

Tenemos mucho que compartir sobre este tema. Así que si quieres aprender al respecto mantente atento.

Primero debes saber que divideremos la información en dos partes:

En este artículo abordaremos los conceptos básicos de GraphQL, además diseñaremos una API sencilla desde cero y la desplegaremos con el servicio AWS AppSync y serverless framework.

En la siguiente parte crearemos una aplicación del lado del cliente para consumir la API. Utilizaremos el framework Amplify+ReactJS y además daremos más funcionalidades a nuestra API mediante el uso de suscripciones para un caso de uso en tiempo real.

¿Qué es GraphQL?

Graphql es un lenguaje de consulta de datos para API, la idea fundamental es hacer que la API esté disponible a través de un único endpoint, en lugar de acceder a varios como se hace actualmente con REST.

Entre las ventajas de GraphQL podemos nombrar las siguientes:

  • El cliente puede especificar exactamente la estructura de los datos que serán servidos.

  • Carga de datos eficiente, mejorando en gran medida el uso del ancho de banda.

  • Estilo declarativo y auto-documentativo gracias a los esquemas fuertemente tipados.

Esquemas

Los esquemas determinan la capacidad de la API y definen tres operaciones fundamentales: query, mutation, subscription. También incluye los parámetros de entrada y las posibles respuestas.

Solucionadores

Los solucionadores implementan la API y describen el comportamiento del servidor. Básicamente son funciones que se encargan de obtener datos para cada campo definido en el esquema.

AWS Appsync+Serverless Framework

Cómo crear un API GrahpQL utilizando AWS

AppSync es un servicio serverless gestionado por AWS, es una capa de GraphQL que nos permitirá desarrollar nuestra API de manera ágil. Es importante conocer los componentes básicos de AppSync:

  • Esquema: como lo mencionamos anteriormente aquí definiremos los tipos y operaciones básicas para recuperar y guardar los datos.

  • Solucionadores: define las plantillas de mapeo de solicitud y respuesta para cada campo definido en el esquema. Están escritas en VTL y se encargan de interpretar las respuestas de las fuentes de datos y también analizan las solicitudes.

  • Fuentes de datos: integración con los servicios de AWS: DynamoDB, Lambda, RDS y ElasticSearch.

  • Autenticación: podemos autenticar la API con API_KEY, IAM o Cognito User Pools

Como podemos ver AppSync proporciona algunas herramientas para construir una API GraphQL de manera fluida, se puede seguir una serie de pasos desde la consola de AWS AppSync y en cuestión de minutos podemos tener algo funcional. Sin embargo preferimos hacerlo de una forma más cercana a la realidad de aplicaciones modernas en producción.

Aquí entra en juego serverless framework una tecnología robusta para definir la infraestructura como código orientado a aplicaciones serverless para diferentes proveedores cloud. En Kushki creemos que hoy en día es crucial tener una infraestructura serverless versionada y automatizada, veremos que es una forma práctica y eficiente de implementar nuestra API. Serverless tiene varias opciones para desplegar AppSync en la nube.

Sin más empecemos a poner en práctica todos estos conceptos mediante una sencilla aplicación.

La aplicación: To do List

En esta primera parte del artículo crearemos la API GraphQL de un CRUD To do List. Usaremos DynamoDB para la base de datos.

Requisitos:

  • NodeJS

  • Cuenta en AWS (credenciales configuradas)

  • Serverless Framework

Empecemos instalando serverless y un plugin para Appsync:

npm install -g serverless serverless-appsync-plugin

En el directorio principal del proyecto ejecutamos el siguiente comando para crear una plantilla con las configuraciones necesarias para crear una aplicación serverless en AWS.

serverless create --template aws-nodejs

Nota: agrega serverless-appsync-plugin en la sección plugins del archivo serverless.yml:

Primero definimos parte de nuestra infraestructura. Para esto, crearemos una tabla todos en DynamoDB en serverless.yml. Por el momento no nos preocuparemos sobre las diferentes configuraciones, con el siguiente fragmento de código bastará:

resources:  
  Resources:  
    todos:  
      Type: "AWS::DynamoDB::Table"  
  Properties:  
        TableName: todos  
        AttributeDefinitions:  
          - AttributeName: id  
            AttributeType: S  
        KeySchema:  
          - AttributeName: id  
            KeyType: HASH  
        BillingMode: PAY_PER_REQUEST

Define un esquema GraphQL

En la raíz crea un archivo schema.graphql. Aquí es donde definimos nuestro esquema y los tipos de datos. Por el momento definiremos las operaciones query y mutation. GraphQL maneja su propia sintaxis llamada SDL, aquí puedes revisar mas sobre este tema.

schema {  
    query: Query  
    mutation: Mutation  
}  

type Query {  
    listToDos: [ToDo]  
    getToDoById(id: ID!): ToDo  
}  

type Mutation {  
    createToDo(input: CreateToDoInput!): ToDo  
    updateToDo(input: UpdateToDoInput!): ToDo  
    deleteToDo(id: ID!): Boolean  
}  

type ToDo {  
  id: ID!  
  name: String  
  title: String!  
  description: String  
  completed: Boolean!  
}  

input CreateToDoInput {  
  id: ID!  
  name: String!  
  title: String!  
  description: String!  
  completed: Boolean!  
}  

input UpdateToDoInput {  
  name: String!  
  title: String!  
  description: String!  
  completed: Boolean!  
}

Recordemos que para cada campo definido se requiere implementar un solucionador. En este caso los solucionadores son consultas a DynamoDB escritas en VTL. Existen algunas funciones de utilidad que hacen más fácil su implementación, para más información de plantillas de mapeo de solucionadores puedes visitar este link.

Solucionador createToDo:

Creamos un directorio mapping-templates en la raíz del proyecto donde alojamos los solucionadores de petición y respuesta por cada campo creado en el esquema.

Petición: crear archivo createToDo-request.vtl e insertar el siguiente código:

{  
    "version" : "2017-02-28",  
    "operation" : "PutItem",  
    "key" : {  
        "id" : $util.dynamodb.toDynamoDBJson($context.arguments.input.id)  
    },  
    "attributeValues" : {  
        "name" : $util.dynamodb.toDynamoDBJson($context.arguments.input.name),  
        "title" : $util.dynamodb.toDynamoDBJson($context.arguments.input.title),  
        "description" : $util.dynamodb.toDynamoDBJson($context.arguments.input.description),  
        "completed" : $util.dynamodb.toDynamoDBJson($context.arguments.input.completed)  
    }  
}

Con la variable $context.argument podemos acceder a los parámetros de entrada que hemos definido en nuestro esquema.

Respuesta: crear archivo createToDo-response.vtl e insertar el siguiente código:

$utils.toJson($context.result)

No te preocupes en como se obtienen los datos desde DynamoDB, AppSync realiza la conexión con las fuentes de datos, los devuelve en la variable $context.result y $utils.toJson lo presenta en un formato entendible para GraphQL. Si deseas procesar estos datos en el solucionador lo puedes hacer con VTL.

Solucionador updateToDo:

Petición: crear archivo updateToDo-request.vtl e insertar el siguiente código, en este caso usaremos una operación de UpdateItem de DynamoDB:

{  
    "version" : "2017-02-28",  
    "operation" : "UpdateItem",  
    "key" : {  
        "id" : $util.dynamodb.toDynamoDBJson($context.arguments.input.id)  
    },  
    "update" : {  
        "expression" : "SET name = :name, title = :title, description = :description, completed = :completed",  
        "expressionValues": {  
            ":author" : $util.dynamodb.toDynamoDBJson($context.arguments.input.name),  
            ":title" : $util.dynamodb.toDynamoDBJson($context.arguments.input.title),  
            ":content" : $util.dynamodb.toDynamoDBJson($context.arguments.input.description),  
            ":url" : $util.dynamodb.toDynamoDBJson($context.arguments.input.completed)  
        }  
    }  
}

Respuesta: crear archivo createToDo-response.vtl e insertar el siguiente código:

$utils.toJson($context.result)

Solucionador getToDoById:

Petición: crear archivo getToDoById-request.vtl e insertar el siguiente código:

{  
    "version" : "2017-02-28",  
    "operation" : "GetItem",  
    "key" : {  
        "id" : $util.dynamodb.toDynamoDBJson($context.args.id)  
    }  
}

Respuesta: crear archivo getToDoById-response.vtl e insertar el siguiente código:

$utils.toJson($context.result)

Definimos nuestra infraestructura de AppSync y el archivo servereless.yml en donde definimos los solucionadores (mappingTemplates), esquema (schema), fuente de datos(dataSources) y el tipo de autenticación(authenticationType), debería tener la siguiente estructura, :

service:  
  name: appsync-todo-app-backend  

plugins:  
  - serverless-appsync-plugin  

custom:  
  appSync:  
    name: todo-app  
    authenticationType: API_KEY  
    mappingTemplates:  
      - dataSource: todos  
        type: Mutation  
        field: createToDo  
        request: "createToDo-request.vtl"  
		response: "createToDo-response.vtl"  
	  - dataSource: todos  
        type: Mutation  
        field: updateToDo  
        request: "updateToDo-request.vtl"  
		response: "updateToDo-response.vtl"  
	  - dataSource: todos  
        type: Query  
        field: getToDoById  
        request: "getToDoById-request.vtl"  
		response: "getToDoById-response.vtl"  
  schema: # defaults schema.graphql  
  dataSources:  
      - type: AMAZON_DYNAMODB  
        name: todos  
        description: 'Todos table'  
  config:  
      tableName: todos  

provider:  
  name: aws  
  runtime: nodejs12.x  

resources:  
  Resources:  
    todos:  
      Type: "AWS::DynamoDB::Table"  
  Properties:  
        TableName: todos  
        AttributeDefinitions:  
          - AttributeName: id  
            AttributeType: S  
        KeySchema:  
          - AttributeName: id  
            KeyType: HASH  
        BillingMode: PAY_PER_REQUEST

Finalmente ejecutamos el siguiente comando para desplegar nuestra API en la nube de AWS:

serverless deploy

Probando nuestra API GrapQL:

Si abrimos la consola de AWS AppSync veremos el esquema, los solucionadores, la autenticación y la fuente de datos que hemos definido.

1

Por otro lado también tenemos un cliente GraphQL con el cual podemos realizar pruebas de nuestra API. En la siguiente figura podemos observar la documentación de la API de acuerdo a los esquemas definidos.

2

Al ejecutar la operación de mutación createToDo se crea un registro en DynamoDB y devuelve el objeto creado según los campos definidos.

3

4

Para recuperar los datos de un ToDo ejecutamos la operación query de getToDoById, podemos solicitar solo el campo name o los que sean necesarios.

5

6


Ahí lo tuviste:

En este artículo abordamos los conceptos básicos de GraphQL, además diseñamos una API sencilla desde cero y la desplegamos con el servicio AWS AppSync y serverless framework.

Recuerda que aún queda una siguiente parte, en la que crearemos una aplicación del lado del cliente para consumir la API. Utilizaremos el framework Amplify+ReactJS y además daremos más funcionalidades a nuestra API mediante el uso de suscripciones para un caso de uso en tiempo real.

Te esperamos en la segunda parte de este artículo.

Sé el alma de la fiesta con la información más fresca sobre pagos digitales.

Suscríbete a nuestro Kushki Hub para recibir alertas de nuestro nuevo contenido.

Suscribe illustration
¿No sabes qué producto es el indicado para tu comercio?
¿El mundo de los pagos llama tu atención?

Más de nuestro Kushki Hub

La llegada de Kushki a México

¡Kushki llega a México! “Desde hace ya un tiempo veíamos en México una oportunidad muy grande de desarrollo”, señaló nuestro fundador Sebastian Castro, durante una entrevista con Forbes México. Por lo que estamos felices de anunciar que abrimos nuestras puertas en este país. A pesar de la pandemia del Covid-19, este año ha traído muchas oportunidades para Kushki, pues el aislamiento llevó a muchos negocios a digitalizar sus pagos o ampliar sus canales de cobro para incluir transferencias bancarias, tarjetas de crédito o débito e incluso efectivo. Sebastian comentó que “compañías con las que platicamos a principios de año y que no tenían en mente el entrar en esta fase, nos buscaron en su momento para hacerlo de forma inmediata, todo esto significó un empuje muy fuerte para nosotros que no creemos que se vaya a detener en un futuro”. De esta manera, seguimos creciendo, logrando llegar a México y abrir nuestra oficina en el país, con ayuda de nuestro inversionista Dila Capital , el cual ha sido un gran aliado en este proceso. México tiene un gran potencial de desarrollo en torno a los medios de pago Alrededor del 96% de los negocios aún ocurren offline, como señaló Sebastian en su entrevista con Forbes. Este país y sus habitantes, así como toda América Latina, se beneficiaría en gran medida por la digitalización y queremos ser parte de este proceso. Después de todo, buscamos unir a Latam con pagos. Si deseas conocer más al respecto te invitamos a leer el artículo de Forbes, La fintech Kushki quiere hacer de México su principal fortaleza en Latam.
Avatar img
Stella Vargas
Líder de contenido @Kushki
agosto 11, 2020

A/B testing utilizando Google optimize

¿Crees que tu página web podría tener una mejor conversión o más visitas si le realizas algunos cambios? ¿Tu desarrollador o equipo de marketing te sugirió modificar algunas cosas, pero no estas totalmente seguro o convencido de que sea lo mejor? Aquí te mostramos cómo realizar pruebas con dos o más versiones de una página web utilizando una herramienta muy útil de Google, llamada Optimize. Así podrás decidir qué funciona mejor en tu sitio, basado en datos reales, en lugar de suposiciones. Primero, ¿Qué es A/B testing? En primer lugar debes saber que, una prueba A/B es un experimento aleatorio en el que se usan dos o más variantes de la misma página web (A y B). Donde la versión A es la original, y la variante B (C,D, F, o tantas variantes como se necesite) contiene al menos un elemento modificado de la página. La idea es comprobar cuál de todas las versiones es la más eficiente o tiene un mejor rendimiento, dependiendo de cuál sea tu objetivo. El desarrollo de estas pruebas puede ser confuso al inicio, por lo que es aconsejable iniciar con experimentos pequeños, como: cambiar el color de un botón o eliminar un campo superfluo de un formulario. Cuando te hayas acostumbrado a crear variantes y experimentos, puedes ampliar el alcance de las pruebas. Realizar pruebas con tu página web puede ayudarte a reducir la tasa de rebote (personas que abandonan tu página), mejorar tu contenido y aumentar tus niveles de conversión, entre otras cosas. Sin embargo, no siempre es la opción más recomendable. No deberías usar pruebas A/B cuando: Tienes pocas visitas, pues sería más recomendable enfocar tus esfuerzos en aumentar tu tráfico antes de realizar alguna prueba. Tu página tiene problemas de programación o algo no funciona de forma adecuada. Concéntrate en resolver esto primero. El costo de realizar estos experimentos es mayor que el retorno que puede proporcionarte. Segundo, ¿Qué es Google Optimize? Es una plataforma de Google que permite a los usuarios realizar diferentes campañas de A/B Testing. Lo interesante de Google Optimize es que lleva el A/B Testing a otro nivel, permitiendo mostrar dos o más variantes a los usuarios de una misma página web (hasta 5 con la nueva actualización de la plataforma). Otra funcionalidad interesante, son los redirect tests (o pruebas de redirección). Estos permiten realizar pruebas entre páginas web independientes, enviando un porcentaje de tráfico a una y un porcentaje a la otra. ¿Por qué utilizar Google Optimize? Aparte de pruebas multivariantes o de redirección, de sus numerosas ventajas, podemos destacar las siguientes: Conexión e integración con la suite de Google Analytics 360. Potencial de realización de análisis más profundos desde la propia interfaz de Google Analytics. Facilidad del editor visual para cualquier persona que no tenga conocimiento sobre código, para la creación de experimentos sencillos. Ya que sabes de qué trata el A/B testing y porqué aplicarlo en Google Optimize es una gran alternativa, veamos cuáles son los pasos para implementarlo. A continuación, vamos a crear un pequeño proyecto en Angular en el cual, vamos a cambiar el estilo de un botón. Haciéndolo más llamativo para verificar la cantidad de veces que los usuarios han dado clic en este. Plantea una Hipótesis Antes de crear un experimento por primera vez, es de gran ayuda identificar el problema a solucionar, y luego crear una hipótesis sobre lo que puedes cambiar para mejorarlo. Una vez que tengas una hipótesis lista, compárala con varios parámetros, como su nivel de confianza, su impacto en los objetivos macro y cuán fácil es configurarla, etc. Por ejemplo, digamos que un sitio web no está "convirtiendo" tanto como esperabas (considerando “convertir” a acciones como concretar una venta o que se genere un registro). Luego de haber analizado las posibles causas para tus resultados, determinaste que cambiando el botón de llamado a la acción o CTA podrías influir en el resultado. Una hipótesis posible podría ser, que si cambias el color del botón de acción, el número de conversiones mejoraría en un 20%. Crea un experimento con Google Optimize Luego de que tengas una hipótesis definida, te queda instalar la herramienta de Google Optimize en tu sitio para poder realizar las pruebas. Antes deberás tener una cuenta de Optimize creada. Si no tienes una, ingresa aquí y crea tu cuenta gratis. Crea una nueva Experiencia Una vez creada tu cuenta, entra a la plataforma para empezar con las pruebas A/B. En primer lugar, deberás crear una nueva experiencia, donde configuraras los experimentos. Luego selecciona “A/B test“ Agrega una nueva variante Puedes agregar hasta cinco variantes por experimento, estas las podrás editar luego. Luego de crear la variante debes editar su contenido para lo cual tendrás que instalar la extensión para Chrome disponible aquí. Luego haz clic en el Botón Editar de la variante creada y si ya tienes instalada la extensión de Google Optimize aparecerá un editor web. Utilizaremos este editor para realizar los cambios planteados en la hipótesis. Para el ejemplo actual, cambia el color del botón blanco a uno azul y agrega un icono a este botón. De igual forma, modifica el título de la Página a “Versión B”. El resultado obtenido es el siguiente: También asigna el valor de redirección del tráfico de la página web al 50% para que puedas evidenciar los resultados rápidamente. Vincula Google Analytics con Google Optimize Para vincular estos dos servicios procede a insertar el siguiente script en el archivo index.html de tu proyecto; el cual permite enviar los datos obtenidos hacia Google Analytics. Es importante insertar este script ya que te permite obtener información de los objetivos planteados para el experimento y evidenciar los resultados en Google Analytics. <script> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-XXXXXXX-X', 'auto'); ga('require', 'GTM-XXXXXX'); ga('send', 'pageview'); </script> También es importante que instales el snippet de Google Optimize para que el experimento renderice las dos versiones de tu página Web. 1. Define los objetivos del experimento Ahora, define los objetivos del experimento, estos pueden ser escogidos de una lista predeterminada o puedes crear un objetivo personalizado. En esta ocasión, vamos a crear un objetivo personalizado para registrar el número de clics que realizan los usuarios en las dos versiones de la página. Para este experimento el Label del evento fue “Click boton azul” Prueba la configuración de tu experimento Es importante verificar que la configuración realizada es correcta, de lo contrario el experimento no renderizará la versión B de tu página Web. Para esto haz clic en la opción Verificar Instalación, en la sección de configuración del snippet de Google Optimize y te debe aparecer un mensaje como este: Envia los eventos Finalmente, envía el evento de clic hacia google Analytics. Para lograr esto, crea un servicio en tu proyecto de Angular, el cual se encargará de disparar el evento cuando hagas clic en el botón. El servicio es el siguiente: Luego de definir los criterios del experimento podrás ejecutarlo Si accedes desde el navegador a la página web verás la versión B. El código del proyecto de angular lo puedes encontrar aquí. Conclusión del experimento Para observar los resultados de nuestro experimento podemos ingresar a la consola de Google Analytics. En la sección de eventos podemos verificar el número de llamadas al evento creado “Click boton azul”. Este número supera al número de veces que se hizo clic en el botón blanco en más de un 20% pudiendo probar la hipótesis que nos planteamos para nuestro experimento. ¿Cuánto debe durar el experimento? Segun la documentacion oficial de Google se recomienda continuar con el experimento hasta que se haya cumplido por lo menos una de las condiciones siguientes: Que hayan pasado dos semanas, para poder ver representadas las variaciones cíclicas en el tráfico web de toda una semana. Al menos una variante debe tener un 95 % de probabilidad de superar el valor de referencia (página web actual antes de hacer modificaciones). Esperamos que este artículo te haya servido para realizar pruebas en tu sitio y basarte en conocimiento validado para tomar decisiones en el diseño o flujo de tu página o aplicación.
Avatar img
Francisco Limaico
Software Developer
agosto 03, 2020

¿Cómo el uso de pruebas unitarias puede prevenir desastres en sistemas informáticos?

El uso de pruebas unitarias puede prevenir desastres en sistemas informáticos, conoce cómo Cuando se empieza el camino profesional como desarrollador de software, es común pensar en lo divertido que resultará escribir código para solventar cualquier tipo de problema o necesidad que nuestros clientes requieran, en este afán nuestras prioridades suelen ser aprender los diferentes lenguajes de programación, frameworks, patrones de diseño, buenas prácticas y demás conceptos, técnicas y herramientas que ayudan a agilizar y optimizar la escritura de código y su funcionamiento. Sin embargo, un área que es bastante olvidada, sobre todo en startups y empresas pequeñas dedicadas al desarrollo de software, es precisamente la adopción de herramientas que permitan escribir, ejecutar y medir pruebas unitarias y de integración, muchas personas piensan que esto resulta en una pérdida de tiempo. En este artículo explicaré por qué ejecutar este tipo de pruebas debe ser parte de la rutina y la disciplina de un desarrollador de software, para ello es importante primero entender ciertos conceptos y por qué no, escribir un par de pruebas. TDD Cuando hablamos de pruebas unitarias es preciso hablar de TDD, por sus siglas en inglés "Test Driven Development". Este es un enfoque que permite a los desarrolladores escribir código basándose en las pruebas que son previamente escritas, con la finalidad de que la escritura del código (que satisfaga la prueba) se desarrolle de forma natural y ágil. TDD tiene el siguiente flujo de trabajo. Cómo puedes apreciar en el esquema, TDD es bastante estricto en cuanto al orden de lo que debemos hacer para la escritura de una prueba. En lo personal, no sigo este orden de manera rigurosa, pues considero que cada desarrollador puede adaptar este proceso a su flujo de trabajo. Por ejemplo en mi día a día escribo mi código de la siguiente manera: Defino el método que voy a implementar en su respectiva interfaz. Escribo la implementación del método de tal forma que devuelva la respuesta que espero, obviando cualquier lógica interna. Escribo la prueba con aserciones super básicas, como que el resultado no sea indefinido. Integro la lógica de lo que hará mi método paso a paso y a la vez voy añadiendo a mi prueba lo que necesito; por ejemplo si mi primer paso es ir a buscar un registro en la base datos, en la prueba haré el respectivo cambio para simular esa respuesta, y a su vez voy ajustando mis aserciones con relación al problema que intento resolver. El paso anterior será un bucle hasta obtener la lógica completa, de igual manera si hubiese condicionales o errores que debo probar, al final tendré que poner pruebas adicionales para satisfacer estos casos. Finalmente, ajusto las aserciones de mis pruebas y realizó un úlitmo análisis en caso de que tenga que hacer pequeñas refactorizaciones en mis pruebas o en el código mismo. Se pueden escribir pruebas de alta calidad mientras tengamos en mente qué queremos probar y por qué lo hacemos; en un inicio quizás sean conceptos turbios, pero estos se irán esclareciendo a medida que vayas ganando práctica y experiencia. Pruebas unitarias Consisten en la validación del fragmento mínimo de código, que puede ser desde un método o función hasta una pequeña clase, esto dependerá mucho del contexto en el cual nos encontremos. A continuación detallo cómo escribir una prueba unitaria usando el framework Mocha y la librería de aserciones Chai en el entorno de ejecución de Javascript para backend, Node.js. Inicializamos nuestro proyecto en Node usando el siguiente comando desde la terminal ubicados en el directorio que hayamos seleccionado. npm init Este comando nos preguntará varias cosas respecto a nuestro proyecto, para este ejemplo dejaremos los valores por defecto. Lo importante es que como resultado de la ejecución de este comando obtengamos el archivo package.json dentro de la raíz de nuestro directorio. { "name": "unittesting", "version": "1.0.0", "description": "simple examples about unit testing with mocha on node.js", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" } Instalamos Mocha y Chai como dependencias de desarrollo. $ npm install --save-dev mocha chai Creamos en archivo service.spec.js dentro del directorio src en la raíz del proyecto. Escribimos una prueba para que dada una función que recibe el parámetro greetType, si el valor es 1, el resultado de su ejecución sea un saludo amable con el texto en inglés Hello, how are you sir?” y otra prueba para cualquier otro valor, el resultado será Hi, What's up bro. const expect = require("chai").expect; const service = require("./service"); describe("Unit Test Examples", function() { it("getMessage should greet kindly", function() { const greetType = 1; const response = service.greet(greetType); expect(response).to.be.eqls(`Hello, how are you sir?`); }); it("getMessage should greet", function() { const greetType = 1; const response = service.greet(greetType); expect(response).to.be.eqls(`Hi, What's up bro`); }); }); En este fragmento, importamos nuestro archivo service.js que contendrá nuestro método getMessage, usamos describe e it, provistos por Mocha, para encapsular nuestras aserciones y para escribir nuestra prueba como tal, ambos reciben un primer parámetro que es una descripción de lo que contienen, es decir lo que vamos a probar, y una función que se encargará de ejecutar nuestras pruebas. Ejecutamos nuestras pruebas ejecutando el comando en la terminal. $ npx mocha ./service.spec.js Como podíamos esperar el resultado es un error, dado que aún no hemos escrito el código de la función y exportado la misma desde el archivo service.js. Unit Test Examples 1) getMessage should greet kindly 2) getMessage should greet 0 passing (20ms) 2 failing 1) Unit Test Examples getMessage should greet kindly: TypeError: service.greet is not a function at Context.<anonymous> (src\service.spec.js:7:34) 2) Unit Test Examples getMessage should greet: TypeError: service.greet is not a function at Context.<anonymous> (src\service.spec.js:14:34) Escribe el código de la función que satisfaga la prueba en el archivo service.js dentro del directorio src. module.exports = { greet: function (greetType) { if(greetType === 1) return `Hello, how are you sir?`; else return `Hi, What's up bro`; } }; En este fragmento simplemente exportamos la función greet, que recibe como argumento un número y devuelve un mensaje a partir de dicho número. Volvemos a ejecutar las pruebas. $ npx mocha ./service.spec.js Ahora sí obtendremos resultados satisfactorios. Unit Test Examples √ getMessage should greet kindly √ getMessage should greet 2 passing (26ms) Supongamos que hicimos una refactorización del código, y esta vez el número que saluda amablemente será el 3. module.exports = { greet: function (greetType) { if(greetType === 3) return `Hello, how are you sir?`; else return `Hi, What's up bro`; } }; Ejecutamos nuevamente las pruebas. Unit Test Examples 1) getMessage should greet kindly √ getMessage should greet 1 passing (23ms) 1 failing 1) Unit Test Examples getMessage should greet kindly: AssertionError: expected 'Hi, What\'s up bro' to deeply equal 'Hello, how are you sir?' + expected - actual + -Hi, What's up bro + +Hello, how are you sir? Y como podíamos imaginar, esta vez una de las pruebas ha fallado debido a los cambios que hemos hecho en nuestra función. Con ese pequeño ejercicio podemos empezar a comprender lo que ganamos al escribir pruebas unitarias para nuestro código. Beneficios de realizar pruebas unitarias Agilidad. Hoy en día los sistemas informáticos son cada vez más cambiantes en el tiempo, por lo que estos deben ir adaptándose a los requerimientos que traten de satisfacer. Por ejemplo, un sistema sencillo de procesamiento de imágenes probablemente requiera más formatos procesables. Las pruebas unitarias nos ayudarán a que cualquier cambio que hagamos en nuestro código no afecte a los casos de uso que están funcionando sin errores o bugs. Calidad. Para toda empresa seria que se dedica al desarrollo de software o depende de software desarrollado puertas adentro, es de vital importancia su preocupación en la calidad del código de sus aplicaciones. Las pruebas unitarias sirven como un cedazo que va a permitir filtrar la mayor cantidad de bugs posibles dentro de sus módulos y corregirlos a tiempo, evidentemente no podremos reducir la totalidad de estos incidentes, pero los mitigará en un gran porcentaje. Facilita el análisis y depuración del código. Las pruebas unitarias nos pueden ayudar a hacer debug de nuestro código de forma mucho más sencilla, si tenemos errores en ambiente de producción y no sabemos su causa podemos empezar a ejecutar pruebas en ambiente local y jugar con los parámetros que podrían generar el error, esto nos ahorra tiempo al no tener que lanzar un parche sin saber siquiera si va o no a funcionar. Diseño. Cuando pensamos en las cosas que debemos probar, es un efecto secundario también planear el diseño que va a tener nuestro código. En este caso surgirán preguntas como: ¿en realidad este método debe ir dentro de esta clase? y ¿Puedo desacoplar este método usando una clase estática?. Las respuestas a estas preguntas nos dan un feedback para mejorar la arquitectura y el diseño de nuestro código, lo cual a su vez resulta en una mayor legibilidad y mantenibilidad del mismo. Ahorro de recursos. En toda industria el tiempo y el dinero son factores importantísimos; los anteriores beneficios se resumen en una disminución en el tiempo que los desarrolladores usan para resolver bugs, realizar cambios, adaptaciones o mejoras. Esto, a su vez, les permite ser más productivos y generar mayor valor para la empresa. Consejo de desarrollador a desarrollador Cuando empecé mi carrera profesional jamás le di importancia a nada más que a escribir el código que necesitaba para resolver los problemas que las aplicaciones que mis clientes requerían. Muchas veces pasaba horas probando manualmente el código para resolver bugs o para aplicar cambios a mi código. Por ello uno de los retos más grandes que tuve al empezar a trabajar en Kushki fue adaptarme al esquema tan estricto que la empresa mantiene en referencia a las pruebas unitarias, pues cada mínima línea de código debe ser probada. Así aprendí que, como desarrolladores debemos invertir un gran porcentaje de nuestro tiempo en aprender a escribir pruebas que generen valor para nuestros equipos de trabajo, nuestros proyectos, empleadores y clientes. Investigar nuevas herramientas de testing que nos provean mayor agilidad, calidad, eficiencia, seguridad y mantenibilidad es fundamental. No importa el lenguaje en el cual aprendemos a escribir estas pruebas, pues al momento de cambiar de tecnología, ya tendremos los cimientos suficientes para poder migrar sin mayores dolores de cabeza. Conclusión Existen infinidad de tecnologías y herramientas que nos permiten escribir pruebas unitarias, está en cada equipo de trabajo investigar y escoger las que más se acoplen al flujo de trabajo y a los objetivos que se quieren cumplir, así como a los diferentes lenguajes de programación que existen en el mercado de esta industria. A pesar de que el concepto de prueba unitaria es pequeño y conciso, es un recurso extremadamente poderoso. Como hemos visto no hay ninguna excusa para no escribir pruebas unitarias en nuestros proyectos. Los beneficios son apreciables desde el primer momento en que decidimos aplicarlas, nos ayudan a entender muchísimo mejor las tecnologías que estamos usando y los problemas que estamos resolviendo. En un caso extremo nuestras pruebas unitarias pueden llegar a salvarnos de un desastre total, solo imagina el aumentar o quitar un par de ceros en transacciones bancarias, y que este error se reproduzca millones de veces antes de ser detectado.
Avatar img
Mauricio Morales
Software Developer
julio 28, 2020