Angular Testing Library - Fundamentos

victorjsv

Victor Sandoval Valladolid

Posted on January 23, 2023

Angular Testing Library - Fundamentos

Para realizar pruebas en Angular es necesario saber lo siguiente:

Jest
Como framework
Obtenemos las funciones necesarias para ejecutar nuestras pruebas.

Testing Library
Como utilidades para las pruebas
Envuelve al componente en nodos DOM para consultar e interactuar con él.

Gherkin Reference - Cucumber Documentation
Como referencia para el nombramiento de las pruebas
Nos da una estructura de nombramiento basado en BDD (Desarrollo orientado al comportamiento)

Testing Library

El principio detrás de Testing Library es:

"Cuanto más se parezcan sus pruebas a la forma en que se usa su software, más confianza le pueden brindar"

Testing Library también lo empuja a escribir más pruebas de integración y menos pruebas unitarias. Esto es importante porque una aplicación Angular puede estar compuesta por muchos componentes. Probar un componente individual es importante, pero podría decirse que probar cómo funcionan todos estos componentes juntos es más importante.

A sus usuarios no les importa cómo está estructurada su aplicación o qué tecnología está utilizando. Les importa que puedan interactuar con su aplicación y que funcione sin problemas.

Image description

Creación de un conjunto de pruebas

Una prueba consta de una o más suites. Una suite se declara con un bloque describe:

describe('Suite description', () => {
  /* … */
});
Enter fullscreen mode Exit fullscreen mode

Los bloques describe se pueden anidar para estructurar grandes conjuntos y dividirlos en secciones lógicas:

describe('Suite description', () => {
  describe('One aspect', () => {
    /* … */
  });
  describe('Another aspect', () => {
    /* … */
  });
});
Enter fullscreen mode Exit fullscreen mode

Especificaciones

Cada suit consta de una o más especificaciones llamadas it (independent test)

describe('Suite description', () => {
  it('Spec description', () => {
    /* … */
  });
  /* … more specs …  */
});
Enter fullscreen mode Exit fullscreen mode

El nombre que se le debe dar al it debe afirmar el comportamiento del código bajo prueba en lenguaje humano.

Se recomienda que el nombre de la prueba esté bajo esta estructura: When the orders are loaded, then it should display the data correctly

it('When the orders are loaded, then it should display the data correctly', () => {
  /* … */
});
Enter fullscreen mode Exit fullscreen mode

Donde:

1.When: Se trata de las condiciones de las acciones que se van a ejecutar
2.Then: Se trata del resultado esperado de las acciones ejecutadas

Este formato se inspira en el lenguaje Gherkin. Ver más sobre lenguaje Gherkin

Estructura de un test

Dentro del bloque it se encuentra el código de prueba real. El código de prueba generalmente consta de tres fases: Organizar (Arrange), Actuar (Act) y Afirmar (Asserts).

Ejemplo:

  1. Organizar:
    a. Crear una instancia de ExampleComponent.
    b. Rederizar el Componente en el documento.

  2. Actuar:
    a. Busque y enfoque el campo de entrada .
    b. Introduzca el texto "5".
    c. Busque y haga clic en el botón "Aceptar".

  3. Afirmar:
    a. Espere que el numero mostrado ahora lea "5".

import '@testing-library/jest-dom'
import userEvent from '@testing-library/user-event'
import { render } from '@testing-library/angular'
import { MatInputModule } from '@angular/material/input'
import { MatButtonModule } from '@angular/material/button'
import { MatFormFieldModule } from '@angular/material/form-field'

it('Example title', async () => {
  // Arrange
  const user = userEvent.setup()
  const { fixture } = await render(ExampleComponent, { 
    declarations: [ReactiveFormsModule, MatFormFieldModule, MatButtonModule, MatInputModule],
    imports: [],
    providers: []
  })

  //Act
  const priceControl = screen.getByLabelText(/Price/i)
  await user.type(priceControl, '5')
  await user.click(screen.getByText(/Save/i))

  //Assert
  const content = screen.getByTestId('example_message_label'))
  expect(content).toHaveTextContent('Hi 5!')
})
Enter fullscreen mode Exit fullscreen mode

Conjunto de pruebas

Al escribir una suit podrá haber casos donde hay código que se repite por cada it. Para ello existen otras funciones que se ejecutan antes o despues de cada it

describe('Suite description', () => {
  beforeAll(() => {
    console.log('Called before all specs are run');
  });
  afterAll(() => {
    console.log('Called after all specs are run');
  });

  beforeEach(() => {
    console.log('Called before each spec is run');
  });
  afterEach(() => {
    console.log('Called after each spec is run');
  });

  it('Spec 1', () => {
    console.log('Spec 1');
  });
  it('Spec 2', () => {
    console.log('Spec 2');
  });
});
Enter fullscreen mode Exit fullscreen mode

Para más información, ver la documenación de Jest

Mocking

La idea detrás del mock es la siguiente: Cuando queremos probar una función, muchas veces su resultado dependen de la respuesta de otras funciones, entonces podemos hacer mock a esas otras funciones para simular su respuesta.

En el siguiente ejemplo tenemos la función thumbWar() que simula un juego al azar donde una de las 2 personas gana después de 2 intentos y la función getWinner() que aleatoriamente trae 1 ganador.

En este caso se usa jest.spyOn() para hacer “mock" a la función getWinner() y que siempre traiga la primera persona.

Lo bueno de usar jest.spyOn() es que podemos recuperar la implementación anterior, usando [nom-var].mockRestore() que servirá para no entrar en conflicto con otros tests en un mismo archivo.

También se puede usar jest.fn(), pero no se podrá recuperar la implementación original

Adicionalmente con la implementación de jest.spyOn() o jest.fn() podemos hacer rastreo de llamadas del método. Ejm:

const spy = jest.spyOn(...)

// Saber si el metodo ha sido llamado con determinados parametros de entrada
expect(spy).toHaveBeenCalledWith(arg1, arg2)

// Saber si ha sido llamado
expect(spy).toHaveBeenCalled()

// Saber si ha sido llamado determinadas veces
expect(spy).toHaveBeenCalledTimes(number)

// Saber si ha retornado alguna variable
expect(spy).toHaveReturnedWith(value)

// Etc...
Enter fullscreen mode Exit fullscreen mode

Para mas información ver el siguiente LINK

Métodos más usados para las aserciones

// Saber si valor es igual a otro valor
expect(form.valid).toEqual(false)

// Saber si un elemento tiene un atributo
expect(toggleEnabled).toHaveAttribute('ng-reflect-checked', true)

// Saber si un objecto tiene una determinada propiedad
expect(form.clientCode?.errors).toHaveProperty('clientCodeExist', true)

// Saber si un elemento tiene un determinado texto como contenido
expect(screen.getByTestId('orderTable_status_label')).toHaveTextContent('CANCELATION REQUESTED')

// Saber si un texto o elemento está en el documento
expect(screen.getByText('Distributor reason:')).toBeInTheDocument()
Enter fullscreen mode Exit fullscreen mode

Si queremos negar las aserciones le antepodemos .not

// Saber si un texto o elemento NO está en el documento
expect(screen.getByText('Distributor reason:')).not.toBeInTheDocument()
Enter fullscreen mode Exit fullscreen mode
💖 💪 🙅 🚩
victorjsv
Victor Sandoval Valladolid

Posted on January 23, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related