Mocks - ne mordez pas!
Ils sont conçus pour vous aider à créer des tests plus simples et plus fiables. Dans cette série d'articles, je vais vous montrer les modèles sur lesquels je me fie lorsque je moque (ou «stubbing») des composants React.
Voici un bon exemple de stub de composant. J'utilise
jest.mock
, que nous examinerons plus en détail ci-dessous.
jest.mock("../src/PostContent", () => ({
PostContent: jest.fn(() => (
<div data-testid="PostContent" />
))
}))
Un stub de composant React typique ne devrait pas paraître plus compliqué. Notez la valeur de stub très simple (
div
) et un attribut data-testid
qui nous permet de trouver très facilement une instance rendue dans le DOM. Par convention, l'identifiant de test utilisé doit correspondre au nom du composant. Dans ce cas, c'est le cas PostContent
.
Avant de regarder comment ils sont utilisés, rappelons-nous d'abord ce que sont les simulacres et pourquoi vous pourriez même vouloir les utiliser.
Qu'est-ce qu'une simulation?
Dans le monde JavaScript, le terme mock est très largement utilisé pour désigner toute implémentation simulée ou test double . Les implémentations simulées sont simplement des valeurs qui remplacent d'autres dans votre code de production pendant l'exécution de vos tests. Ils essaient l'interface de l'objet à remplacer, de sorte que le reste de votre code fonctionne comme s'il n'y avait pas de remplacement.
Il y a plusieurs raisons différentes pour lesquelles vous pourriez vouloir faire cela; nous les examinerons avec des exemples.
Si vous souhaitez en savoir plus sur les implémentations simulées , lisez le livre de Martin Fowler Mocks Are Not Stubs .
Blague et moquerie
Jest a une fonctionnalité
jest.mock
qui vous permet de simuler des modules entiers que vous remplacez. Dans ce didacticiel, je me suis concentré sur cette fonctionnalité, bien qu'il existe d'autres moyens de remplacer des objets en JavaScript.
Dans le livre Mastering React Test-Driven Development, j'utilise les importations de modules nommés ES6 pour créer des doubles de test. Cette approche donne un peu plus de flexibilité, mais semble un peu plus hackish.
Jest jest.mock
il dit que les moqueries garantissent que vos tests sont rapides et non fragiles .
Bien que cela soit vrai, ce n'est pas la principale raison pour laquelle j'utilise des simulations.
J'utilise des simulations parce qu'elles m'aident à garder mes tests indépendants les uns des autres.
Pour comprendre pourquoi c'est le cas, regardons un exemple.
Pourquoi moki?
Vous trouverez ci-dessous une liste d'un composant
BlogPage
qui fait deux choses: il récupère id
une propriété url
puis affiche un PostContent
composant avec cela id
.
const getPostIdFromUrl = url =>
url.substr(url.lastIndexOf("/") + 1)
export const BlogPage = ({ url }) => {
const id = getPostIdFromUrl(url)
return (
<PostContent id={id} />
)
}
Imaginez que vous écrivez des tests pour ce composant et que tous vos tests sont inclus dans
BlogPage.test.js
, qui est une suite de tests unique qui couvre les composants BlogPage
et PostContent
.
À ce stade, vous n'avez pas encore besoin des simulacres: nous ne l'avons pas encore vu
PostContent
, mais étant donné la taille BlogPage
, il n'est vraiment pas nécessaire d'avoir deux suites de tests distinctes, car BlogPage
c'est généralement simple PostContent
.
Imaginez maintenant ajouter des fonctionnalités à
BlogPage
y et y PostContent
. En tant que développeur doué, vous ajoutez de plus en plus de fonctionnalités chaque jour.
Il devient plus difficile de maintenir les tests en état de marche. Chaque nouveau test a une configuration plus complexe et la suite de tests commence à consommer plus de temps - une charge qui doit maintenant être prise en charge.
C'est un problème courant et je le vois tout le temps dans les bases de code React. Des suites de tests dans lesquelles même le changement le plus simple entraînera l'échec de nombreux tests.
Une solution consiste à diviser les suites de tests. Nous partirons
BlogPage.test.js
et en créerons un nouveau PostContent.test.js
qui devrait contenir des tests exclusivement pour PostContent
. L'idée de base est que toutes les fonctions placées dans PostContent
doivent l'être PostContent.test.js
, et toutes les fonctions placées BlogPage
(telles que l'analyse d'URL) doivent l'être BlogPage.test.js
.
D'accord.
Mais que faire si le rendu
PostContent
a des effets secondaires?
export const PostContent = ({ id }) => {
const [ text, setText ] = useState("")
useEffect(() => {
fetchPostContent(id)
}, [id])
const fetchPostContent = async () => {
const result = await fetch(`/post?id=${id}`)
if (result.ok) {
setText(await result.text())
}
}
return <p>{text}</p>
};
La suite de tests
BlogPage.test.js
doit être consciente des effets secondaires et être prête à les gérer. Par exemple, il devra garder une fetch
réponse prête .
La dépendance que nous avons essayé d'éviter en fractionnant nos suites de tests existe toujours.
L'organisation de nos tests s'est certes améliorée, mais au final, rien n'est arrivé pour rendre nos tests moins fragiles.
Pour cela, nous avons besoin d'un stub (ou d'une maquette)
PostContent
.
Et dans la partie suivante, nous verrons comment procéder.
Est-ce vraiment nécessaire?
Au fait, quelques mots sur le passage des tests de bout en bout aux tests unitaires.
Avoir des doubles de test est un indicateur clé que vous écrivez des tests unitaires.
De nombreux testeurs expérimentés lancent immédiatement de nouveaux projets avec des tests unitaires (et des simulations) car ils savent que, à mesure que leur base de code se développe, ils seront confrontés au problème de l'instabilité des tests.
Les tests unitaires sont généralement beaucoup plus petits que les tests de bout en bout. Ils peuvent être si petits qu'ils ne prennent souvent pas plus de trois ou quatre lignes de code. Cela en fait de bons candidats pour les pratiques de codage social telles que l'appariement et la programmation d'ensemble.
Même lorsque nous faisons des tests unitaires, les doubles tests ne sont pas toujours nécessaires - ils ne sont qu'un autre outil de votre suite dont vous avez besoin pour savoir quand et comment appliquer.
Dans la partie suivante, nous aborderons les techniques de base du moqueur .