DEV Community

Cover image for Automatización de pruebas en Angular con GitHub Actions
Angel Hurtado
Angel Hurtado

Posted on

Automatización de pruebas en Angular con GitHub Actions

Conforme un producto de software crece, también suele incrementar el número de pruebas unitarias que se deben ejecutar. Existen varias soluciones para automatizar la ejecución de pruebas y una de ellas es GitHub Actions. A continuación explicaré como configurarlo en un proyecto de Angular.

Creando un nuevo proyecto

Primero crearemos un proyecto Angular en blanco. Es necesario instalar la ultima versión de Angular CLI, la cual podemos instalar o actualizar con el siguiente comando.

npm i @angular/cli -g # Instalación global
Enter fullscreen mode Exit fullscreen mode

Ahora podemos crear nuestro proyecto con el siguiente comando

ng new ghactions-angular # Cambia ghactions... por el nombre de tu proyecto
Enter fullscreen mode Exit fullscreen mode

Escoge las opciones de Angular Router y de formato de hojas de estilo que más te convengan. En mi caso si utilizaré Angular Router y SCSS como formato de hojas de estilo. La instalación de paquetes podrá demorar varios minutos. Obtendremos un resultado como este:

asciicast

No olvides subir tu nuevo proyecto a un repositorio de GitHub público o privado. Si deseas hacerlo desde la terminal te recomiendo GitHub Actions.

Implementando pruebas

Ahora creemos unas cuantas pruebas. En el archivo app.component.html eliminamos todo el contenido por defecto, y dejamos solo un título y un párrafo:

<h1 id="app-title">Hola mundo!</h1>
<p id="app-descr">Esta es una aplicación Angular</p>

<router-outlet></router-outlet>
Enter fullscreen mode Exit fullscreen mode

Como puedes ver, este título y párrafo tienen un id. Este nos servirá para buscar los elementos en el DOM durante las pruebas unitarias. Ahora modifiquemos el archivo app.component.spec.ts, y dejemos solo dos pruebas:

it('should create the app', () => {
  const fixture = TestBed.createComponent(AppComponent);
  const app = fixture.componentInstance;
  expect(app).toBeTruthy();
});

it('should render title & description', () => {
  const fixture = TestBed.createComponent(AppComponent);
  fixture.detectChanges();
  const compiled = fixture.nativeElement;
  const title = compiled.querySelector('#app-title');
  const descr = compiled.querySelector('#app-descr');
  expect(title.textContent).toContain('Hola mundo!');
  expect(descr.textContent).toContain('Esta es una aplicación Angular');
});
Enter fullscreen mode Exit fullscreen mode

Ahora comprobemos que las pruebas se ejecutan correctamente con el comando ng test. Por defecto estas pruebas las ejecuta exitosamente en el navegador, como podemos observar en esta imagen:

Resultado de ejecución en Chrome

Ejecutar pruebas sin interfaz gráfica

Ya que las pruebas se ejecutan en un navegador (y que este se debe mostrar en pantalla) la ejecución de pruebas suele fallar en GitHub Actions, ya que solo está equipado con las herramientas mínimas y sin capacidades gráficas. Necesitamos una solución que nos permita usar un navegador por medio de la Terminal, sin interfaz gráfica. Para esto instalaremos Puppeter:

npm i puppeteer --dev
Enter fullscreen mode Exit fullscreen mode

Esta instalación demorará algunos minutos, ya que puppeter incluye su propio binario del navegador Chromium. Al finalizar la instalación, cambiaremos la configuración de Karma en el archivo karma.conf.js:

// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html

process.env.CHROME_BIN = require("puppeteer").executablePath();

module.exports = function (config) {
  config.set({
        ...
    browsers: ['Chrome', 'ChromeHeadlessCI'],
    customLaunchers: {
      ChromeHeadlessCI: {
        base: 'ChromeHeadless',
        flags: [
          '--no-sandbox',
          '--disable-gpu',
          '--enable-features=NetworkService',
        ],
      },
    }
        ...
  });
};
Enter fullscreen mode Exit fullscreen mode

Principalmente, estamos obteniendo el directorio del ejecutable de Chromium por medio de Puppeteer, y lo añadimos como ChromeHeadlessCI.

Si tu proyecto contiene pruebas E2E con Protractor, también puedes configurarlo creando un nuevo archivo protractor-ci.conf.js en el directorio e2e, con la siguiente configuración:

const config = require('./protractor.conf').config;

config.capabilities = {
  browserName: 'chrome',
  chromeOptions: {
    args: ['--headless', '--no-sandbox', '--disable-gpu'],
    binary: require('puppeteer').executablePath(),
  },
};

exports.config = config;
Enter fullscreen mode Exit fullscreen mode

Como ves, este archivo extiende la configuración del archivo protractor.conf.js. También modificaremos ese archivo para usar Puppeteer:

config.capabilities = {
    ...
  browserName: 'chrome',
  chromeOptions: {
    binary: require('puppeteer').executablePath(),
  },
    ...
};
Enter fullscreen mode Exit fullscreen mode

Finalmente cambiamos el archivo de configuración de Angular angular.json:

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "ghactions-angular": {
      ...
      "architect": {
        ...
        "test": {
          ...
                    "configurations": { // Añadir configuración opcional 'ci'
            "ci": {
              "progress": false,
              "watch": false,
              "browsers": "ChromeHeadlessCI" // Usar Chromium sin GUI al usar configuración 'ci'
            }
          }
        }
                ...
                "e2e": { // Si usas e2e
          ...
          "configurations": {
                        ...
            "ci": {
              "devServerTarget": "app:serve:ci",
              "protractorConfig": "e2e/protractor-ci.conf.js" // Usar protractor-ci al usar la configuración 'ci'
            }
          }
        },
      }
    }
  },
  ...
}
Enter fullscreen mode Exit fullscreen mode

Ahora podemos ejecutar los tests sin abrir el navegador, pasando el flag --configuration=ci

ng test --configuration=ci
ng e2e --configuration=ci
Enter fullscreen mode Exit fullscreen mode

Este es el resultado al ejecutar el comando con el flag --configuration=ci

asciicast

Asi mismo, si ejecutas los tests sin el flag, notaras que esta vez se abre el navegador Chromium y no el navegador Chrome que usas normalmente. Es importante que sigas ejecutando los tests en tu equipo local, y usar un entorno lo más parecido al que usaremos en GitHub Actions, usando el mismo binario y versión de Puppeteer.

Asegurate de subir estos últimos cambios a GitHub.

Pruebas automáticas en GitHub Actions

Para utilizar GitHub Actions es necesario que tengamos un directorio .github, y dentro de este un directorio workflows. Dentro del directorio workflows podremos crear varios archivos .yml con distintos workflows para GitHub Actions. Por ahora solo crearemos un archivo ci.yml con el siguiente contenido:


Enter fullscreen mode Exit fullscreen mode

Ahora crearemos el primer Job del Workflow, que será el de la instalación de paquetes de NodeJS:

jobs:
  install:
    name: Installation # Nombre del Job
    runs-on: ubuntu-latest # Ejecutar en Ubuntu
    steps:
      - uses: actions/checkout@v2 # Clonar repositorio actual
      - uses: actions/setup-node@v1 # Usar Node
        with:
          node-version: 12 # Versión de Node
      # Cargar cache de node_modules, para reducir tiempo de instalación en próximas ejecuciones
      - name: Cache node modules
        id: node-cache # Id del cache
        uses: actions/cache@v1
        with:
          path: node_modules
          # Se usará el mismo cache siempre y cuando sea el mismo sistema operativo y no existan cambios en el archivo package-lock
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
      # Instalar dependencias
      - name: Install dependencies
        # No ejecutar si se obtuvo node_modules de cache
        if: steps.node-cache.outputs.cache-hit != 'true' 
        run: npm install
Enter fullscreen mode Exit fullscreen mode

Guardamos los cambios y los subimos a GitHub con el comando git push. Ahora vamos al repositorio y podemos ver que se ha iniciado GitHub Actions:

Ejecución en GitHub Actions

Asi mismo podemos ver los detalles del workflow:

Detalles del Workflow en progreso

Al finalizar nos mostrará un check-mark en color verde, indicando que todos los Jobs fueron ejecutados exitosamente:

Detalles del Workflow completado

Terminemos de configurar el Workflow. Este es el código completo del archivo ci.yml:

name: Continuous Integration # Nombre del workflow

on: # ¿Cuando ejecutar?
  push: # Al hacer push a las siguientes ramas
    branches: [main] # o master
  pull_request: # Al crear un pull request a las siguientes ramas
    branches: [main] # o master

jobs:
  ci:
    name: Continuous Integration # Nombre del Job
    runs-on: ubuntu-latest # Ejecutar en Ubuntu
    steps:
      - uses: actions/checkout@v2 # Clonar repositorio actual
      - uses: actions/setup-node@v1 # Usar Node
        with:
          node-version: 12 # Versión de Node
      # Cargar cache de node_modules, para reducir tiempo de instalación en próximas ejecuciones
      - name: Cache node modules
        id: node-cache # Id del cache
        uses: actions/cache@v1
        with:
          path: node_modules
          # Se usará el mismo cache siempre y cuando sea el mismo sistema operativo y no existan cambios en el archivo package-lock
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
      # Instalar dependencias
      - name: Install dependencies
        # No ejecutar si se obtuvo node_modules de cache
        if: steps.node-cache.outputs.cache-hit != 'true' 
        run: npm install
      # Generar compilación de producción
      - name: Run Build command
        run: npm run build -- --prod
      # Ejecutar pruebas unitarias
      - name: Run Unit tests
        run: npm run test -- --configuration=ci
Enter fullscreen mode Exit fullscreen mode

Subimos los cambios a GitHub con el comando git push, y volvemos a la página del repositorio en GitHub. Ahora vemos que esta ejecutando los nuevos pasos que especificamos:

Ejecutando el Workflow Final

Finalmente obtendremos este resultado:

Resultado final

Ahora cada vez que hagamos push a la rama principal main, o creemos un pull request a esa rama, se ejecutará este Workflow. Puedes ver todo el código fuente aquí.

Top comments (1)

Collapse
 
douglasfugazi profile image
Douglas Fugazi

Muchas gracias por esta genial explicación, voy a probarlo con Cypress 🌟😁