Angular Testing Library - Fundamentos
Victor Sandoval Valladolid
Posted on January 23, 2023
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.
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', () => {
/* … */
});
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', () => {
/* … */
});
});
Especificaciones
Cada suit consta de una o más especificaciones llamadas it
(independent test)
describe('Suite description', () => {
it('Spec description', () => {
/* … */
});
/* … more specs … */
});
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', () => {
/* … */
});
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:
Organizar:
a. Crear una instancia de ExampleComponent.
b. Rederizar el Componente en el documento.Actuar:
a. Busque y enfoque el campo de entrada .
b. Introduzca el texto "5".
c. Busque y haga clic en el botón "Aceptar".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!')
})
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');
});
});
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...
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()
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()
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
November 30, 2024