Mocker les requêtes
Table des matières
Lors de l'écriture des tests, comment faire pour couper la dépendance à un service web ? En créant un mock ! Il existe plusieurs librairies ou framework en JavaScript qui permettent de mocker les requêtes faites à un serveur. L'un des plus utilisés est Msw.
Msw intercepte toutes les requêtes et les redirige vers des handlers. Un handler contient ce qui doit être retourné lorsqu'une requête est appelée.
Installation
Si n'est pas déjà fait, l'installation dans le projet de Msw se avec la commande suivante :
npm install msw --save-dev
Exemple
Code à tester
Soit le service suivant qui communique avec un API REST afin de récupérer une liste de films :
moviesService.js
import axios from 'axios'
export default class MoviesService {
constructor () {
this.API_URL = 'http://localhost:3000'
}
async getMovies () {
const { data } = await axios.get(this.API_URL + '/movies')
return data
}
}
Pour mocker les requêtes faites à l'API REST, on doit créer des handlers et configurer le serveur msw.
Handlers
Un handler permet de configurer ce qui sera retourné lorsque le code testé fera une requête.
Par exemple, pour indiquer ce que doit retourner la route http://localhost:3000/movies
, il faut créer le handler suivant :
handlers.js
import { rest } from 'msw'
import { movies } from './movies'
const API_URL = 'http://localhost:3000'
export const success = [
rest.get(API_URL + '/movies', (req, res, ctx) => {
return res(ctx.status(200), ctx.json(movies))
}),
// Autres handlers à ajouter ici...
]
===========
Dans l'exemple ci-dessus, la requête à l'api sera interceptée seulement si elle correspond à l'url exact http://localhost:3000/movies
. Mais comment faire si la route contient des paramètres, comme, par exemple un id ?
Route avec paramètres
Utiliser l'*
comme caractère de substitution dans l'url. Exemple :
rest.get(API_URL + '/movies/*', (request, response, context) => {
return response(context.status(200), context.json(movies[0]))
})
Utiliser le :nomDuParamètre
pour y faire référence. Par exemple :
rest.get(API_URL + '/movies/:id', (request, response, context) => {
return response(
context.status(200),
context.json(dogs.find(dog => dog.id == request.params.id))
)
})
Exemple de tests
moviesService.test.js
import { setupServer } from 'msw/node'
import { success } from './handlers'
import { movies } from './movies'
import MoviesService from './MoviesService'
const server = setupServer(...success)
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }))
afterAll(() => server.close())
afterEach(() => server.resetHandlers())
describe('MoviesService.js', () => {
test("getMovies doit retourner l'ensemble des films", async () => {
const MoviesService = new MoviesService()
const response = await MoviesService.getMovies()
expect(movies).toStrictEqual(response)
})
})
Simuler des erreurs
Si on veut simuler le fait qu'un API REST n'est pas joignable, on pourrait créer un handler comme celui-ci :
handlers.js
...
export const networkError = [
rest.get(API_URL + '/movies', (request, response, context) => {
return response.networkError('Failed to connect')
})
]
Si on veut retourner un code d'erreur, comme 400, on pourrait avoir le handler suivant :
handlers.js
...
export const badRequestMovies = [
rest.get(API_URL + '/movies', (request, response, context) => {
return response(context.status(400))
})
]
Remplacer un handler
Parfois, lors de l'écriture d'un test, on veut remplacer le contenu d'un handler par un autre. Par exemple, pour simuler une erreur de réseau pour un test en particulier, il faut :
- Créer un nouveau handler (comme
networkError
dans l'exemple ci-dessus). - Utiliser dans le test l'instruction
server.use
pour remplacer temporairement un handler.
moviesComponent.test.js
...
const server = setupServer(...success)
...
...
test("mon test ")
{
// Arrange
server.use(...networkError)
...
}
Attention
À noter que les handlers dans server.use
son effectif seulement pour un seul usage ("one-time override"). Après, ce sont les handlers initiaux qui sont utilisées.