Dobles de prueba en Javascript
Contenidos
Ejemplos de implementación de dobles de prueba con Jest.
Vamos a partir conociendo la función básica para crear dobles de prueba jest.fn()
Agrega las siguientes lineas de código:
Podemos configurar esta función para que retorne los valores que necesitamos. A continuación intenta lo siguiente:
Corroboración de llamadas a funciones.
Con esto ahora podemos revisar si fue llamada, cuantas veces fue llamada y con que parámetros se llamó:
// Expect pendiente
Podemos ir un paso más allá y configurar diferentes valores para cada llamada:
jestFunction
.mockReturnValueOnce(1)
.mockReturnValueOnce(2)
console.log(jestFunction())
console.log(jestFunction())
console.log(jestFunction())
Incluso tenemos más opciones. Podemos configurar una opción por defecto:
jestFunction
.mockReturnValue('default')
.mockReturnValueOnce(1)
.mockReturnValueOnce(2)
console.log(jestFunction())
console.log(jestFunction())
console.log(jestFunction())
console.log(jestFunction())
Reject y resolve: Resolver y rechazar promesas con Jest
jestFunction.mockResolvedValue({ id: 1, username: 'miles1986' })
let resolvedValue
DESAFÍO: Transformar en async/await
jestFunction()
.then((value) => {
resolvedValue = {
...value
}
console.log('value', value)
})
expect(resolvedValue).toEqual({ id: 1, username: 'miles1986' })
const expectedError = new Error('User is not found')
try {
const jestFunction = jest.fn()
jestFunction.mockRejectedValue(expectedError)
jestFunction()
} catch(error) {
console.log('Esto no pasará en un caso real de pruebas porque es el código fuente el que debe implementar el bloque try/catch')
console.log(error)
}
Fluent interface
Este estilo de programación permite encadenar llamados devolviendo el objeto original. Esto lo podemos utilizar en jest para que los mock retornen una función que simplemente deja continuar la prueba. La ventaja es que podemos corroborar si estas funciones fueron llamadas y validar los parámetros que recibieron.
logger: {
log: jest.fn().mockImplementation(function(){
return this
})
}
expect(logger.log).toHaveBeenCalledWith('Some string')
Remplazo de módulos Nodejs con Jest.mock().
Esta funciones nos serán muy útiles cuando tengamos que enfrentarnos a código productivo. Imagina que tenemos el siguiente código:
const axios = require('axios')
class CountriesService{
getCountries() {
const url = 'https://restcountries.eu/rest/v2/all'
return axios.get(url)
.then(response => response.data)
}
async getSouthAmericanCountries() {
try {
const result = await this.getCountries()
return result
.filter(country => country.subregion === 'South America')
.map(country => country.name)
} catch(error) {
console.log('Error', error.message)
return []
}
}
}
module.exports = new CountriesService()
Necesitamos intervenir el método axios.get
para poder controlar como sería un caso de uso de este método. Hasta el momento solo sabemos crear funciones que retornen valores previamente configurados pero no tenemos una manera de intervenir códigos ya existentes, en este caso, Axios.
Acá aparece una utilidad llamada jest.mock
. Veamos con un ejemplo su uso:
const CountriesService = require('./countriesService');
const axios = require('axios');
jest.mock('axios');
describe('CountriesService', () => {
it('respuesta OK', async () => {
const mockedResponse = [
{ name: 'Argentina', subregion: 'South America' },
{ name: 'Brazil', subregion: 'South America' },
{ name: 'Chile', subregion: 'South America' }
]
axios.get.mockResolvedValue({ data: mockedResponse })
const result = await CountriesService.getSouthAmericanCountries()
expect(result).toEqual(['Argentina', 'Brazil', 'Chile'])
expect(axios.get).toHaveBeenCalledTimes(1)
expect(axios.get).toHaveBeenCalledWith('https://restcountries.eu/rest/v2/all')
});
it('respuesta error', async () => {
const errorMessage = 'Error al obtener países'
axios.get.mockRejectedValue(new Error(errorMessage))
const result = await CountriesService.getSouthAmericanCountries()
expect(result).toEqual([])
expect(axios.get).toHaveBeenCalledTimes(1)
expect(axios.get).toHaveBeenCalledWith('https://restcountries.eu/rest/v2/all')
})
})
En Jest, la función jest.mock()
utiliza hoisting para reemplazar el módulo original con una versión reemplazada antes de que se cargue en la prueba. Esto significa que podemos definir nuestras expectativas de comportamiento para el módulo mockeado después de importar el módulo, pero al ejecutarse la prueba jest.mock
se ejecutará antes y al obtener el módulo este ya vendrá reemplazado por un doble de pruebas.
Notarás como para reemplazar el método axios.get
utilizamos jest.fn()
para configurar un comportamiento a la función que se comportara tal cual lo hace el método original, pero con una respuesta predefinida funcional a la prueba que estamos haciendo.