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

Avatar img
Mauricio Morales
Software Developer
julio 28, 2020
Lectura de 2 minutos
¿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.

Flujo pruebas unitarias

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.

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

Covid-19: Un nuevo reto para fortalecer y crear estrategias en pro de la continuidad del servicio al cliente.

Covid-19: Un nuevo reto para fortalecer y crear estrategias en pro de la continuidad del servicio al cliente. La llegada de la epidemia del coronavirus (Covid-19) nos tomó a todos por sorpresa, ocasionando preocupación e inquietud, pues aunque constantemente como seres humanos nos adaptamos a los diferentes cambios que se presentan a partir de las dinámicas del mundo, este nuevo virus conflictuó un gran número de aspectos, principalmente relacionados con la salud, las relaciones sociales, el mercado laboral, y la economía; entre otros. Por esto, en Kushki tuvimos que buscar alternativas en nuestras formas de trabajar pensando en la seguridad de los trabajadores y la satisfacción de los clientes. De esto surgió la interrogante: ¿Cómo mejorar el servicio al cliente en tiempos de pandemia?, para lo que tuvimos que considerar cuáles son las necesidades y solicitudes de nuestros clientes y realizar cambios en el área de soporte, de acuerdo a éstas. A continuación exploraremos un poco los procedimientos que permitieron los respectivos cambios: En relación a la seguridad de los trabajadores se optó por la modalidad de home office, donde se redujo la exposición de las personas que conforman el cuerpo de la empresa al coronavirus. Incentivamos así medidas que disminuyen el riesgo y la propagación de contagio. Adicionalmente, implementamos reuniones virtuales de seguimiento diarias, donde evaluamos constantemente los indicadores de satisfacción al cliente. Esto nos permite desarrollar actividades de mejora continua, para incrementar la calidad del servicio. Por otro lado, potenciamos la calidad laboral de nuestros trabajadores, a partir de cursos y capacitaciones de servicio al cliente, con el fin de promover la escucha efectiva, lo que incrementa la eficiencia, al atender los requerimientos de los antiguos y nuevos clientes de una manera más ágil. De esta manera, también cuidamos a nuestros clientes, propiciando el equipo necesario para atender las diferentes solicitudes que éstos puedan tener. Razón por la cual fué fundamental trabajar en la sensibilización del personal, a través de las capacitaciones. Vale destacar que, la pandemia fomentó que los comercios que no tenían plataformas tecnológicas buscarán alternativas tales como nuestra pasarela de pago, para el desarrollo de sus negocios, y poder ofrecer sus servicios y productos a través de aplicaciones electrónicas. Esto acarreó un aumento en las solicitudes en línea, por lo que capacitar al personal adecuadamente fue fundamental para afrontar el incremento en la demanda. Hoy en día seguimos trabajando en tomar las medidas necesarias para garantizar un servicio de alta calidad para nuestros clientes, cumpliendo con todas las políticas de bioseguridad que han decretado los gobiernos en los países donde tenemos presencia y la misma compañía. Por supuesto, el COVID-19 plantea grandes retos, por lo que continuamos gestionando tareas claras con el fin de satisfacer las necesidades de los comercios afiliados a nuestra plataforma y tomando acciones para la protección de nuestros trabajadores. Por último, realizamos constantes actualizaciones de acuerdo a la evolución de la crisis causada por la pandemia, ejecutando cambios en los diferentes procesos de atención, revisando al detalle las solicitudes que recibimos, considerando las posibles soluciones a las necesidades que surgen y proyectando estrategias de soporte en el menor tiempo posible. Igualmente, y gracias a la escucha activa hacia nuestros clientes, nos encontramos realizando desarrollos técnicos en nuestros productos según los requerimientos y necesidades que éstos nos han expresado. Como empresa, es necesario pensar y preocuparse constantemente por el cliente, entendiendo que éste también está siendo afectado por los cambios que han surgido. Por lo que se hace necesario fortalecer y crear estrategias en pro de la continuidad del servicio al cliente, teniendo como prioridad el valor que podemos entregarles en estos momentos de dificultad, desarrollando los cambios mencionados, donde la habilitación y mejoramiento de los canales de servicio electrónico permiten continuar de forma precisa con la asesoría al cliente.
Avatar img
Daniel Castañeda
Analista de Soporte
julio 16, 2020

Self-Service: Clave en la obtención de una experiencia de cliente exitosa

Conoce más sobre el Self-service ¿Te has visto agobiado con miles de solicitudes de clientes y crees que contratar personal es la única solución?, ¿Te asusta tener múltiples canales de atención abiertos al público?, ¿Crees que el servicio sólo puede ser brindado por tus agentes?, ¿No sabes como mejorar los índices de satisfacción de los clientes?, ¿Qué hacer si los tiempos de respuesta y resolución de solicitudes suben y suben sin control? Estas son solo algunas de las interrogantes más frecuentes que los directores de servicio enfrentan a menudo, y en algunas ocasiones, las soluciones a estos temas, más que complejas, son el resultado de un arduo proceso de planeación y análisis de información. Y te preguntarás: “¿Análisis de información? ¿Cómo?, todo lo manejo en Excel y llevar control del proceso consume todo mi tiempo. Apenas puedo generar algunos reportes para medir el servicio y, ahora, implementar una herramienta de servicio que me ayude, tendrá un costo muy elevado; imposible en estos momentos donde el ahorro de costos es fundamental”. La realidad es que, lo más importante que puedes tener para tu área de servicio es la información, puesto que con base en ella es posible tomar cualquier medida que permita dar un giro radical a las fallas que se hayan detectado dentro de la mesa de servicio. Esta será tu ladrillo principal para la construcción de conocimiento, que le permitará al usuario poder autogestionar sus inquietudes, preguntas frecuentes, e incluso fallas técnicas conocidas en tu proceso o en el de terceros que intervengan en tu servicio. Para iniciar esto, es recomendable que cuentes con un sistema que permita no sólo recibir la información por los canales de servicio que tengas disponibles para tus usuarios (mail, Chat, Redes sociales, portal servicio, etc.), sino que también permita realizar una análisis adecuado de la información recopilada. Con base en esta información y su análisis, es que harás la construcción de tu base de conocimientos, sobre la cual tus clientes podrán: Apoyarse para autogestionar sus requerimientos Aportar información importante que permita mejorar el contenido publicado. Calificar tus publicaciones. Lo más importante es que, le permitirás a tus agentes, dedicarse a resolver temas más complejos y que generan un mayor valor para la organización (esto ocurrirá en la medida en que la información publicada sea la correcta y esperada por tus usuarios). ¿Sabías que el 60% de los clientes buscan resolver sus incidencias o consultas por sí solos? Esto aumenta exponencialmente sus niveles de satisfacción, y esta explicación radica en un concepto muy básico pero poderoso, el poder resolver cualquier problema sin requerir apoyo de algún tercero, hace que el ser humano se sienta mejor e incrementa su nivel de felicidad. Este tipo de soluciones son idealmente aplicables en conjunto con un sistema de Chat-Bot que le dé a los usuarios una experiencia en línea sobre la atención de sus consultas la cual también se nutre de la información que has recopilado para construir tu base de conocimientos y reduce la carga de trabajo de los equipos de servicio. En conclusión, si tu estrategia se basa en tener la mejor experiencia de servicio posible a disposición de tus clientes, debes tener siempre en cuenta la implementación de buenas herramientas que te permitan recopilar la mayor cantidad de información posible sobre las solicitudes que ellos poseen, y con base en esta información, crear contenido de alto nivel, que ellos puedan utilizar para resolver sus consultas. Lo ideal, es acompañar esto con un portal que no solo preste este servicio, sino que también te permita interactuar y tener un flujo de comunicación dinámico y en doble vía.
Avatar img
Juan Pablo Herrera
VP Service @ Kushki
junio 17, 2020

Micro-Frontends utilizando Single-SPA Framework

Micro-Frontends + Single-SPA Framework La arquitectura de micro-frontends es un enfoque de diseño en el que una aplicación web se descompone en "micro partes" individuales, semi-independientes, que trabajan en conjunto. Esta arquitectura puede ser tan liberal como sea posible, donde cada aplicación puede ser implementada con diferentes frameworks. El concepto de micro-frontend está inspirado ampliamente en los microservicios. En cuanto a los micro-frontends, es importante destacar que a pesar de que la aplicación se divide en varios proyectos, cada uno de ellos se integrará en una sola. Así, para el usuario final, todo parecería ser una única aplicación, en lugar de varias. Aunque los micro-frontends han comenzado a recibir mucha atención últimamente, no existe una sola manera de implementarlos. De hecho, hay una variedad de enfoques en función de los objetivos y requisitos que se tenga en cada organización. Entre las implementaciones más conocidas en la actualidad tenemos: Single-SPA Framework: Es un framework que permite la implementación de micro-frontends combinando otros frameworks (React, Angular, Vue, etc). Esta implementación es la que utilizamos en Kushki y la explicaremos a más detalle a continuación. Iframes: Aisla las micro aplicaciones en IFrames usando las APIs de Window.postMessage para coordinar los diferentes frames. Los IFrames comparten APIs expuestas por la ventana padre. Web Components: Las aplicaciones de front-end solo tratan con el enrutamiento y toman decisiones sobre el conjunto de componentes a mostrar y la orquestación de eventos entre los diferentes web components. Monolito SPA versus Micro-Frontends En el caso de Kushki, vamos a analizar el ejemplo de una aplicación construida con el propósito de que nuestros clientes puedan editar y ver diferentes configuraciones que mantienen con nuestro sistema de pagos, entre otras acciones. Esta aplicación en un principio fue construida como un monolito SPA hecho en Angular 5, la aplicación creció tanto que llegó a tener más de 200 mil líneas de código y alrededor de 13 componentes bastante complejos. Todo esto llevó a que la aplicación fuese cada vez más difícil de mantener y de trabajar en diferentes características al mismo tiempo. Por lo cual, decidimos cambiarnos a micro-Frontends para continuar con el desarrollo de esta aplicación. Se analizaron varias alternativas, pero la alternativa más madura y con mejor documentación en ese momento, y en la actualidad, es el framework Single-SPA. En un principio se decidió realizar la migración de la aplicación manteniendo el framework con el cual estaba hecho el monolito SPA (Angular). En una primera versión del nuevo micro-frontend se decidió que los componentes de la aplicación original se conviertan en uno micro-frontend cada uno. Pero, una de las dificultades que se tuvieron en esta primera versión fue la barra de navegación, que tenía que ser compartida por todas las aplicaciones. Por lo cual, se decidió crear una librería de Angular, la cual se instaló en cada micro-frontend y, de esta manera, se lanzó la primera versión de nuestra primera aplicación con micro-frontends. La cual tenía la siguiente estructura: Con el tiempo nos dimos cuenta, que nuestra primera idea de que la barra de navegación sea una librería de Angular y tenga que instalarse en cada uno de nuestros micro-frontends, y por lo tanto actualizarse en cada aplicación cada vez que se realizaba un cambio en la navegación, no era nada práctico y nos restringía a utilizar sólo Angular para el desarrollo de nuestros micro-frontends. El framework Single-SPA sugiere que se utilice un archivo index.html en donde se registren todos los micro-frontends que van a ser utilizados por la aplicación. El framework le llama a este archivo root config y por lo general consiste en un solo archivo HTML en donde se utilizan algunos métodos del framework para que todo funcione de manera correcta. Entonces, para solucionar el problema de nuestra barra de navegación, en Kushki decidimos que nuestro root config del framework no sea un solo archivo HTML sino más bien un SPA hecho en Angular. A este migramos toda la lógica que teníamos en la librería y configuramos el index.html que genera Angular, para que funcione de manera correcta con las configuraciones que necesita el framework (todo esto será explicado con más detalle en un ejemplo más adelante). Con este cambio se logró comenzar a desarrollar micro-frontends no solo en Angular sino también en React. Por lo cual la estructura original cambió un poco y quedo de la siguiente manera: Al dejar de lado nuestro monolito SPA y comenzar a utilizar micro-frontends en nuestra aplicación, se logró separar las diferentes funcionalidades que se tenían en un solo lugar y facilitar el ingreso de nuevas funcionalidades. Sin embargo, esto no quiere decir que los micro-frontends son para todos los proyectos. Pues, para la gran mayoría de estos, un monolito SPA es probablemente más que suficiente. La arquitectura de micro-frontends es más adecuada para grandes aplicaciones que agrupan muchas funcionalidades. En Kushki la gran mayoría de nuestros SPAs siguen siendo monolitos debido a que no son tan grandes. Por lo cual, se debe considerar esta arquitectura solo si se cree que el proyecto va a tener problemas de escalabilidad a mediano o largo plazo. Ejemplo de Micro-Frontends con Single-SPA Framework Para este ejemplo, vamos a crear una aplicación root en React en donde se registrarán 2 aplicaciones creadas con Single-SPA framework, una con Angular y una con React. Primera parte: Crear la aplicacion Root Config Para la aplicación root solo se necesita una aplicación web creada con create-react-app. Para este ejemplo vamos utilizar npm como administrador de paquetes de javascript y Typescript, por sus múltiples beneficios y porque ahora es soportado por React. npx create-react-app root-config-app --template typescript --use-npm Una vez creada nuestra aplicación tenemos que agregar single-spa a nuestro proyecto para poder registrar nuestros micro-frontends y navegar entre ellos. npm i single-spa Lo siguiente que tenemos que hacer, es editar el archivo index.html del proyecto para agregar los scripts e imports necesarios para que nuestra aplicación funcione. Nuestro archivo deberá quedar de la siguiente manera: <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="theme-color" content="#000000" /> <meta name="description" content="Web site created using create-react-app" /> <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> <!-- manifest.json provides metadata used when your web app is installed on a user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/ --> <link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> <!-- Notice the use of %PUBLIC_URL% in the tags above. It will be replaced with the URL of the `public` folder during the build. Only files inside the `public` folder can be referenced from the HTML. Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. --> <title>React App</title> <meta name="importmap-type" content="systemjs-importmap"> <script type="systemjs-importmap"> { "imports": { "@single-spa-test/app-angular": "http://localhost:4200/main.js", "@single-spa-test/app-react": "http://localhost:8080/app-react.js" } } </script> <script type="systemjs-importmap" src="https://storage.googleapis.com/react.microfrontends.app/importmap.json"></script> <script src="https://cdn.jsdelivr.net/npm/systemjs@6.2.5/dist/system.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/systemjs@6.2.5/dist/extras/amd.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/systemjs@6.2.5/dist/extras/named-exports.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/systemjs@6.2.5/dist/extras/named-register.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/import-map-overrides/dist/import-map-overrides.js"></script> <script src="https://unpkg.com/zone.js"></script> </head> <body> <noscript>You need to enable JavaScript to run this app.</noscript> <div id="root"></div> <!-- This HTML file is a template. If you open it directly in the browser, you will see an empty page. You can add webfonts, meta tags, or analytics to this file. The build step will place the bundled scripts into the <body> tag. To begin the development, run `npm start` or `yarn start`. To create a production bundle, use `npm run build` or `yarn build`. --> </body> </html> Para no tener que agregar CSS innecesarios a nuestro breve ejemplo vamos a agregar Material UI a nuestro proyecto y agregar un ejemplo de navegación rápida. npm i @material-ui/core Agregamos un nuevo componente Nav a nuestro proyecto en donde copiamos el ejemplo de navegación. import React from 'react'; import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'; import Drawer from '@material-ui/core/Drawer'; import AppBar from '@material-ui/core/AppBar'; import CssBaseline from '@material-ui/core/CssBaseline'; import Toolbar from '@material-ui/core/Toolbar'; import List from '@material-ui/core/List'; import Typography from '@material-ui/core/Typography'; import ListItem from '@material-ui/core/ListItem'; import ListItemText from '@material-ui/core/ListItemText'; const drawerWidth = 240; const useStyles = makeStyles((theme: Theme) => createStyles({ root: { display: 'flex', }, appBar: { zIndex: theme.zIndex.drawer + 1, }, drawer: { width: drawerWidth, flexShrink: 0, }, drawerPaper: { width: drawerWidth, }, drawerContainer: { overflow: 'auto', }, content: { flexGrow: 1, padding: theme.spacing(3), }, }), ); export default function ClippedDrawer() { const classes = useStyles(); return ( <div className={classes.root}> <CssBaseline /> <AppBar position="fixed" className={classes.appBar}> <Toolbar> <Typography variant="h6" noWrap> Single-SPA Root Config </Typography> </Toolbar> </AppBar> <Drawer className={classes.drawer} variant="permanent" classes={{ paper: classes.drawerPaper, }} > <Toolbar /> <div className={classes.drawerContainer}> <List> <ListItem button> <ListItemText primary="App Angular" /> </ListItem> <ListItem button> <ListItemText primary="App React" /> </ListItem> </List> </div> </Drawer> <main className={classes.content}> <Toolbar /> <div id="single-spa-application:app-angular"></div> <div id="single-spa-application:app-react"></div> </main> </div> ); } Editamos el archivo App.tsx para incluir nuestro nuevo componente. El cual nos queda de la siguiente manera: import React from 'react'; import Nav from './Nav'; function App() { return ( <React.Fragment> <Nav /> </React.Fragment> ); } export default App; Una vez hecho todo esto tenemos una aplicación como la siguiente: Segunda parte: Crear micro-frontends Single-SPA Angular Para crear una aplicación Angular con single-spa, basta con crear un SPA con el CLI que proporciona el framework. Es importante crear la aplicación con Angular Router para que el micro-frontend funcione correctamente. ng new app-angular Una vez creada la aplicación tenemos que instalar el framework Single-SPA en nuestro proyecto. Para lo cual debemos seguir los pasos que nos encontramos en la documentación del framework. ng add single-spa-angular Lo que hace este comando es configurar automáticamente el framework en nuestra aplicación. También existe la posibilidad de instalar de forma manual (los detalles se pueden encontrar en la documentación antes mencionada). Por último, crearemos un componente simple el cual vamos a mostrar en esta aplicación y configuraremos las rutas que va a usar este SPA. ng g c angular En el archivo de rutas se debe configurar una ruta principal, la cual debe coincidir con la ruta que se registre en single-spa del root config, también es importante configurar los providers del Routing Module. El archivo quedará configurado de la siguiente manera: import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { APP_BASE_HREF } from "@angular/common"; import { AngularComponent } from './angular/angular.component'; const routes: Routes = [ { path: "angular", component: AngularComponent, } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule], providers: [{ provide: APP_BASE_HREF, useValue: "/" }] }) export class AppRoutingModule { } Eso es todo lo que debemos hacer al momento de crear un micro-frontend con este framework. Una vez realizado estos simples pasos, puedes editar tu aplicación Angular como desees. React Para crear un micro-frontend con React es necesario utilizar la herramienta con la que nos provee el framework Single-SPA (create-single-spa) en la cual deberemos contestar React cuando nos pregunte qué framework utilizar. npx create-single-spa Y con React es todo lo que debemos configurar. Tercera parte: Registrar Micro-Frontends con Single-SPA Una vez creados los micro-frontends tenemos que registrarlos en la aplicación root config que creamos en la primera parte. Para lo cual debemos editar el archivo index.tsx de la siguiente manera: import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; import { LifeCycles, registerApplication, start } from "single-spa"; ReactDOM.render( <React.StrictMode> <App /> </React.StrictMode>, document.getElementById('root') ); registerApplication({ name: "app-angular", app: (): Promise<LifeCycles> => (window as any).System.import("http://localhost:4200/main.js"), activeWhen: "/angular" }); registerApplication({ name: "app-react", app: (): Promise<LifeCycles> => (window as any).System.import("@single-spa-test/app-react"), activeWhen: "/react" }); start(); Para poder ejecutar este ejemplo se deben correr los siguientes comandos: npm start # root config app npm run serve:single-spa # angular app npm start # react app Con todas las aplicaciones corriendo tendremos nuestro ejemplo de micro-frontends desarrollado en diferentes frameworks completamente funcional y en muy pocos pasos. El código completo del ejemplo explicado en este artículo lo puedes encontrar en el siguiente repositorio: Micro frontends single spa example
Avatar img
Francisco Izurieta
Tech Lead de Payments @Kushki
junio 08, 2020