*** titre : "Écrire des tests unitaires" description: "Présentation des tests unitaires" balises : \['Ingénierie'] ------------------------- ## Tests unitaires * Tous les tests unitaires individuels sont indépendants les uns des autres. Nous n'utilisons aucun état partagé entre les tests. * L’environnement de test comprend`sakila`exemple de base de données et toute modification apportée à celle-ci par un test est annulée avant l’exécution d’autres tests. * Lors de l’exécution de tests unitaires, il essaie de se connecter au serveur MySQL exécuté sur`localhost:3306`avec nom d’utilisateur`root`et mot de passe`password`(qui peut être configuré) et s’il n’est pas trouvé, il utilisera`sqlite`comme solution de secours, donc aucune exigence d’un serveur SQL pour exécuter des tests. ### Conditions préalables * MySQL est préférable, mais les tests peuvent également s’appuyer sur SQLite ### Installation ```bash pnpm --filter=-nocodb install # add a .env file cp tests/unit/.env.sample tests/unit/.env # open .env file open tests/unit/.env ``` Configurez les variables suivantes > DB\_HOST : hôte > DB\_PORT : port > DB\_USER : nom d'utilisateur > DB\_PASSWORD : mot de passe ### Exécuter des tests ```bash pnpm run test:unit ``` ### Structure des dossiers Le dossier racine des tests unitaires est`packages/nocodb/tests/unit` * `rest`Le dossier contient toutes les suites de tests pour les API restantes. * Le dossier `rest` contient toutes les suites de tests pour les API restantes. * Le dossier `model` contient toutes les suites de tests pour les modèles. * Le dossier `factory` contient toutes les fonctions d’aide pour créer des données de test. * `init`Le dossier contient des fonctions d'assistance pour configurer l'environnement de test. * `index.test.ts` est le fichier racine de la suite de tests et importe toutes les suites de tests. * `TestDbMngr.ts` est une classe d'assistance pour gérer les bases de données de test (c'est-à-dire la création, la suppression, etc.). ### Modèle d'usine * Utilisez des usines pour créer/mettre à jour/supprimer des données. Aucune donnée ne doit être directement créée/mise à jour/supprimée dans le test. * Lors de l'écriture d'une usine, assurez-vous qu'elle peut être utilisée avec le moins de paramètres possible et utilisez les valeurs par défaut pour les autres paramètres. * Utilisez des paramètres nommés pour les usines. ```ts createUser({ email, password}) ``` * Utilisez un fichier par usine. ### Procédure pas à pas pour l'écriture d'un test unitaire Nous allons créer un`Table` suite de tests à titre d’exemple. #### Configurer le test Nous allons configurer`beforeEach`qui est appelé avant l’exécution de chaque test. Nous utiliserons`init`fonction de`nocodb/packages/nocodb/tests/unit/init/index.ts`, qui est une fonction d'assistance qui configure l'environnement de test (c'est-à-dire la réinitialisation de l'état, etc.). `init`fait les choses suivantes - * Il initialise un`Noco`instance (réutilisée dans tous les tests). * Restaure`meta`et`sakila`base de données à son état initial. * Crée l'utilisateur root. * Retour`context`qui a`auth token`pour l'utilisateur créé, instance de serveur de nœud (`app`), et`dbConfig`. Nous utiliserons`createProject`et`createProject`usines pour créer un projet et une table. ```typescript let context; beforeEach(async function () { context = await init(); project = await createProject(context); table = await createTable(context, project); }); ``` #### Cas de test Nous utiliserons`it`fonction pour créer un scénario de test. Nous utiliserons`supertest`pour faire une requête au serveur. Nous utilisons`expect`(`chai`) pour affirmer la réponse. ```typescript it('Get table list', async function () { const response = await request(context.app) .get(`/api/v1/db/meta/projects/${project.id}/tables`) .set('xc-auth', context.token) .send({}) .expect(200); expect(response.body.list).to.be.an('array').not.empty; }); ``` :::Info We can also run individual test by using `.only`dans`describe`ou`it`fonction et l’exécution de la commande test. ::: ```typescript it.only('Get table list', async () => { ``` #### Intégration de la nouvelle suite de tests Nous créons un nouveau fichier`table.test.ts`dans`packages/nocodb/tests/unit/rest/tests`annuaire. ```typescript import 'mocha'; import request from 'supertest'; import init from '../../init'; import { createTable, getAllTables } from '../../factory/table'; import { createProject } from '../../factory/project'; import { defaultColumns } from '../../factory/column'; import Model from '../../../../src/lib/models/Model'; import { expect } from 'chai'; function tableTest() { let context; let project; let table; beforeEach(async function () { context = await init(); project = await createProject(context); table = await createTable(context, project); }); it('Get table list', async function () { const response = await request(context.app) .get(`/api/v1/db/meta/projects/${project.id}/tables`) .set('xc-auth', context.token) .send({}) .expect(200); expect(response.body.list).to.be.an('array').not.empty; }); } export default function () { describe('Table', tableTests); } ``` Nous pouvons alors importer le`Table`suite de tests pour`Rest`suite de tests dans`packages/nocodb/tests/unit/rest/index.test.ts`déposer(`Rest`la suite de tests est importée dans le fichier racine de la suite de tests qui est`packages/nocodb/tests/unit/index.test.ts`). ### Base de données d'échantillons d'ensemencement (Sakila) ```typescript function tableTest() { let context; let sakilaProject: Project; let customerTable: Model; beforeEach(async function () { context = await init(); /******* Start : Seeding sample database **********/ sakilaProject = await createSakilaProject(context); /******* End : Seeding sample database **********/ customerTable = await getTable({project: sakilaProject, name: 'customer'}) }); it('Get table data list', async function () { const response = await request(context.app) .get(`/api/v1/db/data/noco/${sakilaProject.id}/${customerTable.id}`) .set('xc-auth', context.token) .send({}) .expect(200); expect(response.body.list[0]['FirstName']).to.equal('MARY'); }); } ```