Tests
Nous utilisons une combinaison de tests unitaires, de tests d’intégration et de tests e2e pour assurer la fiabilité et la maintenabilité d’O3. Nous utilisons les outils suivants :
- Vitest (par défaut dans la majorité des modules) ou Jest (dans le petit nombre de modules encore sur l’ancien runner), avec React Testing Library , pour les tests unitaires et d’intégration. Voir le guide de migration si vous faites passer un module de Jest à Vitest.
- Playwright pour les tests e2e.
Voici quelques lignes directrices générales pour les tests :
Tests unitaires et d’intégration
-
Évitez ces pièges de test courants :
- N’utilisez pas la mauvaise assertion. Installez et utilisez @testing-library/jest-dom pour étendre
expectavec des matchers DOM pour tester les composants React. La bibliothèque fonctionne avec Vitest (via l’import sous-chemin@testing-library/jest-dom/vitest) et avec Jest — le nom est historique. - N’utilisez pas la mauvaise requête. Utilisez cette ressource pour choisir la bonne requête pour l’élément que vous voulez tester.
- N’utilisez pas les variantes
query*pour quoi que ce soit sauf pour vérifier si un élément existe. Utilisez uniquement les variantesquery*pour affirmer qu’un élément n’existe pas. - N’utilisez pas
waitForpour attendre des éléments qui peuvent être interrogés avecscreen.find*.
// ❌ const submitButton = await waitFor(() => screen.getByRole("button", { name: /submit/i }) ); // ✅ const submitButton = await screen.findByRole("button", { name: /submit/i });- Ne passez pas un callback vide à
waitFor.
// ❌ await waitFor(() => {}); expect(window.fetch).toHaveBeenCalledWith("foo"); expect(window.fetch).toHaveBeenCalledTimes(1); // ✅ await waitFor(() => expect(window.fetch).toHaveBeenCalledWith("foo")); expect(window.fetch).toHaveBeenCalledTimes(1);- N’ajoutez pas
aria-*,roleet d’autres attributs d’accessibilité aux éléments sauf si l’application les utilise réellement. - Utilisez les plugins ESLint de Testing Library : eslint-plugin-testing-library et eslint-plugin-jest-dom .
- N’utilisez pas la fonction
cleanup. RTL s’occupe du nettoyage après chaque test. - Utilisez
screenpour interroger et déboguer les éléments. C’est la façon recommandée de trouver des éléments dans vos tests car cela garantit que vous travaillez avec le document actuel et fournit de meilleurs messages d’erreur.
// ❌ const { getByText } = render(<MyComponent />); const element = getByText(/hello/i); // ✅ render(<MyComponent />); const element = screen.getByText(/hello/i);- Ne pas envelopper inutilement les choses dans
act. Suivez ce guide à la place pour corriger les avertissementsNot wrapped in act(...). - Ne créez pas de composants wrapper ponctuels pour
renderourenderHooksauf si le test a besoin de providers. Lorsque des providers sont nécessaires, préférez un helper partagé commerenderWithSwrafin que chaque test obtienne un cache isolé.
- N’utilisez pas la mauvaise assertion. Installez et utilisez @testing-library/jest-dom pour étendre
-
Évitez de tester les détails d’implémentation. Testez plutôt l’API publique du composant. Cela facilite le refactoring du composant sans avoir à réécrire les tests.
-
Lors du stub de fonctionnalités de
@openmrs/esm-framework, suivez les modèles de mock décrits ici. -
Écrivez moins de tests, plus complets qui couvrent des scénarios complets, plutôt que de nombreux petits tests isolés. Lisez ce billet de blog de l’auteur de Testing Library, Kent C. Dodds pour plus d’informations.
- Groupez les scénarios de test connexes ensemble.
- Construisez progressivement des scénarios complexes.
- Utilisez les hooks de test (
beforeEach,afterEach,beforeAll,afterAll) pour réduire la duplication.
-
Ne nettoyez pas les mocks dans les hooks
beforeEach. La plupart des mocks devraient être nettoyés automatiquement par le framework de test si vous avezclearMocks: truedans votre configuration de test de niveau racine :vitest.config.tsexport default defineConfig({ test: { clearMocks: true, }, });jest.config.jsmodule.exports = { clearMocks: true, }; -
Ne mockez pas les composants. Au lieu de cela, rendez-les avec la vraie implémentation. Cela facilite la détection des régressions lors du refactoring des composants.
// ❌ vi.mock("./my-component", () => ({ default: () => <div>My Component</div>, })); // (ou `jest.mock(...)` si le module est encore sur Jest)// ✅ render(<MyComponent />);
Tests e2e
-
Suivez les meilleures pratiques de test e2e décrites dans la documentation Playwright.
-
Structurez les grandes suites de tests en utilisant des modèles d’objets de page :
-
Créez des classes séparées pour chaque page/composant majeur
-
Encapsulez les sélecteurs et les actions communes
e2e/pages/conditions-page.tsimport { type Page } from "@playwright/test"; export class ConditionsPage { constructor(readonly page: Page) {} readonly conditionsTable = () => this.page.getByRole("table", { name: /conditions summary/i }); async goTo(uuid: string) { await this.page.goto(`/openmrs/spa/patient/${uuid}/chart/Conditions`); } }
-
-
Suivez les meilleures pratiques de test e2e de Playwright :
-
Utilisez des assertions web-first plutôt que des attentes manuelles
-
Implémentez une gestion d’erreur appropriée et des mécanismes de nouvelle tentative
-
Utilisez des fixtures de test pour la configuration/nettoyage communs
test.beforeEach(async ({ api }) => { patient = await generateRandomPatient(api); }); test("Record, edit and delete a condition", async ({ page }) => { const conditionsPage = new ConditionsPage(page); const headerRow = conditionsPage.conditionsTable().locator("thead > tr"); const dataRow = conditionsPage.conditionsTable().locator("tbody > tr"); await test.step("When I go to the Conditions page", async () => { await conditionsPage.goTo(patient.uuid); }); await test.step("And I click on the `Record conditions` button", async () => { await conditionsPage.page.getByText(/record conditions/i).click(); }); await test.step("Then I should see the conditions form launch in the workspace", async () => { await expect( conditionsPage.page.getByText(/record a condition/i) ).toBeVisible(); }); });
-