diff --git a/.all-contributorsrc b/.all-contributorsrc index 19225d22..db0972e7 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -341,10 +341,147 @@ "contributions": [ "content" ] + }, + { + "login": "YeeJone", + "name": "Yiqiao Xu", + "avatar_url": "https://avatars.githubusercontent.com/u/20400822?v=4", + "profile": "https://github.com/YeeJone", + "contributions": [ + "content" + ] + }, + { + "login": "yubinTW", + "name": "YuBin, Hsu", + "avatar_url": "https://avatars.githubusercontent.com/u/31545456?v=4", + "profile": "https://github.com/yubinTW", + "contributions": [ + "translation", + "code" + ] + }, + { + "login": "TREER00T", + "name": "Ali Azmoodeh", + "avatar_url": "https://avatars.githubusercontent.com/u/76606342?v=4", + "profile": "https://github.com/TREER00T", + "contributions": [ + "content" + ] + }, + { + "login": "Saimon398", + "name": "Alex Popov", + "avatar_url": "https://avatars.githubusercontent.com/u/71539667?v=4", + "profile": "https://github.com/Saimon398", + "contributions": [ + "content" + ] + }, + { + "login": "Shramkoweb", + "name": "Serhii Shramko", + "avatar_url": "https://avatars.githubusercontent.com/u/42001531?v=4", + "profile": "http://shramko.dev", + "contributions": [ + "content" + ] + }, + { + "login": "yugoccp", + "name": "Yugo Sakamoto", + "avatar_url": "https://avatars.githubusercontent.com/u/1724114?v=4", + "profile": "https://github.com/yugoccp", + "contributions": [ + "content" + ] + }, + { + "login": "Fdawgs", + "name": "Frazer Smith", + "avatar_url": "https://avatars.githubusercontent.com/u/43814140?v=4", + "profile": "https://yeovilhospital.co.uk/", + "contributions": [ + "content" + ] + }, + { + "login": "wralith", + "name": "Wralith", + "avatar_url": "https://avatars.githubusercontent.com/u/75392169?v=4", + "profile": "https://github.com/wralith", + "contributions": [ + "content" + ] + }, + { + "login": "saseungmin", + "name": "Harang", + "avatar_url": "https://avatars.githubusercontent.com/u/60910665?v=4", + "profile": "https://haranglog.tistory.com", + "contributions": [ + "content" + ] + }, + { + "login": "rcanelav", + "name": "rcanelav", + "avatar_url": "https://avatars.githubusercontent.com/u/64812826?v=4", + "profile": "https://github.com/rcanelav", + "contributions": [ + "content" + ] + }, + { + "login": "drewrwilson", + "name": "Drew Wilson", + "avatar_url": "https://avatars.githubusercontent.com/u/4324656?v=4", + "profile": "https://github.com/drewrwilson", + "contributions": [ + "content" + ] + }, + { + "login": "XtLee", + "name": "XtLee", + "avatar_url": "https://avatars.githubusercontent.com/u/30145777?v=4", + "profile": "https://github.com/XtLee", + "contributions": [ + "content" + ] + }, + { + "login": "smonn", + "name": "Simon Ingeson", + "avatar_url": "https://avatars.githubusercontent.com/u/44818?v=4", + "profile": "https://www.smonn.se", + "contributions": [ + "content" + ] + }, + { + "login": "elfacu0", + "name": "elfacu0", + "avatar_url": "https://avatars.githubusercontent.com/u/30785449?v=4", + "profile": "https://github.com/elfacu0", + "contributions": [ + "content" + ] + }, + { + "login": "jorbelca", + "name": "jorbelca", + "avatar_url": "https://avatars.githubusercontent.com/u/76847923?v=4", + "profile": "https://github.com/jorbelca", + "contributions": [ + "content" + ] } ], "projectName": "javascript-testing-best-practices", "projectOwner": "goldbergyoni", "repoType": "github", - "repoHost": "https://github.com" + "repoHost": "https://github.com", + "commitConvention": "angular" } diff --git a/.operations/questions-answers.md b/.operations/questions-answers.md index 47fcd2f4..2f71c638 100644 --- a/.operations/questions-answers.md +++ b/.operations/questions-answers.md @@ -4,7 +4,7 @@ **Answer:** -Welcome aboard! Having a Brazilian Portuguese translation would be awesome đŸ”„đŸŒˆđŸ‘Œ . I'll be glad to collaborate with you on this and help wherever I can +Welcome aboard! Having a Brazilian Portuguese translation would be awesome đŸ”„đŸŒˆđŸ‘ŒđŸ’š . I'll be glad to collaborate with you on this and help wherever I can Before you start with this, I've prepared some basic workflow guidelines: diff --git a/assets/header.pptx b/assets/header.pptx index 925889c9..57b1eb07 100644 Binary files a/assets/header.pptx and b/assets/header.pptx differ diff --git a/assets/zh-CN/bp-1-3-parts.jpg b/assets/zh-CN/bp-1-3-parts.jpg new file mode 100644 index 00000000..7a907611 Binary files /dev/null and b/assets/zh-CN/bp-1-3-parts.jpg differ diff --git a/assets/zh-CN/bp-12-rich-testing.jpg b/assets/zh-CN/bp-12-rich-testing.jpg new file mode 100644 index 00000000..d7eb34fe Binary files /dev/null and b/assets/zh-CN/bp-12-rich-testing.jpg differ diff --git a/assets/zh-CN/bp-13-component-test-yoni-goldberg.png b/assets/zh-CN/bp-13-component-test-yoni-goldberg.png new file mode 100644 index 00000000..e943ddc2 Binary files /dev/null and b/assets/zh-CN/bp-13-component-test-yoni-goldberg.png differ diff --git a/assets/zh-CN/bp-14-testing-best-practices-contract-flow.png b/assets/zh-CN/bp-14-testing-best-practices-contract-flow.png new file mode 100644 index 00000000..74a4ebaa Binary files /dev/null and b/assets/zh-CN/bp-14-testing-best-practices-contract-flow.png differ diff --git a/assets/zh-CN/bp-20-yoni-goldberg-mutation-testing.jpg b/assets/zh-CN/bp-20-yoni-goldberg-mutation-testing.jpg new file mode 100644 index 00000000..bd2c6589 Binary files /dev/null and b/assets/zh-CN/bp-20-yoni-goldberg-mutation-testing.jpg differ diff --git a/assets/zh-CN/headspace.png b/assets/zh-CN/headspace.png new file mode 100644 index 00000000..25225b18 Binary files /dev/null and b/assets/zh-CN/headspace.png differ diff --git a/index.js b/index.js index a068a34d..e3c1c215 100644 --- a/index.js +++ b/index.js @@ -1 +1,2 @@ // This is a book for now, but code examples are coming soon +// See https://github.com/testjavascript/nodejs-integration-tests-best-practices to see an example app diff --git a/package.json b/package.json index dde3fe4b..daf57f48 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "javascript-testing-best-practices", - "version": "1.0.0", + "version": "2.0.0", "description": "📗🌐 🚱 Comprehensive and exhaustive JavaScript & Node.js testing best practices (April 2020) https://testjavascript.com/", "main": "index.js", "scripts": { diff --git a/readme-es.md b/readme-es.md index 3325f51b..a2b4ebda 100644 --- a/readme-es.md +++ b/readme-es.md @@ -35,6 +35,7 @@ Empieza por comprender las tĂ©cnicas de testing ubicuas que son la base de cualq - đŸ‡”đŸ‡±[Polaco](readme-pl.md) - cortesĂ­a de [Michal Biesiada](https://github.com/mbiesiad) - đŸ‡Ș🇾[Español](readme-es.md) - cortesĂ­a de [Miguel G. Sanguino](https://github.com/sanguino) - đŸ‡§đŸ‡·[PortuguĂ©s-BR](readme-pt-br.md) - cortesĂ­a de [Iago Angelim Costa Cavalcante](https://github.com/iagocavalcante), [Douglas Mariano Valero](https://github.com/DouglasMV) y [koooge](https://github.com/koooge) +- đŸ‡ș🇩[Ukrainian](readme-ua.md) - cortesĂ­a de [Serhii Shramko](https://github.com/Shramkoweb) - ÂżQuieres traducir a tu propio lenguaje? por favor abre una issue 💜

@@ -893,7 +894,7 @@ Crédito: + +
+ +# 👇 Comment ce guide peut faire passer vos compĂ©tences de test au niveau supĂ©rieur + +
+ +## 📗 46+ bonnes pratiques : complet et exhaustif + +Ceci est un guide complet pour Javascript & Node.js de A Ă  Z. Il rĂ©sume et organise pour vous les meilleurs articles de blogs, livres et outils du marchĂ© + +## 🚱 AvancĂ© : va bien au-delĂ  des bases + +Embarque pour un voyage qui va bien au-delĂ  des bases et aborde des sujets avancĂ©s tels que les tests en production, les tests de mutations, les tests basĂ©s sur les propriĂ©tĂ©s et de nombreux autres outils stratĂ©giques et professionnels. Si vous lisez chaque mot de ce guide, vos compĂ©tences de tests seront probablement bien au-dessus la moyenne. + +## 🌐 Full-stack: front, backend, CI ... + +Commence par comprendre les pratiques de tests omniprĂ©sentes qui sont Ă  la base de tout niveau d'application. Ensuite, plonge dans ton domaine de prĂ©dilection : frontend/UI, backend, CI ou peut-ĂȘtre tous ça Ă  la fois ? + +
+ +### Écrit par Yoni Goldberg + +- Un consultant JavaScript & Node.js +- 📗 [Les tests Node.js & JavaScript de A à Z](https://www.testjavascript.com) - Mon cours en ligne complet avec plus de [10 heures de video](https://www.testjavascript.com), 14 types de tests et plus de 40 bonnes pratiques +- [Suis-moi sur Twitter ](https://twitter.com/goldbergyoni/) + +
+ +### Traductions - Lis dans la langue de ton choix +- 🇬🇧[Anglais](readme.md) +- 🇹🇳[Chinois](readme-zh-CN.md) - Traduit par [Yves yao](https://github.com/yvesyao) +- đŸ‡°đŸ‡·[CorĂ©en](readme.kr.md) - Traduit par [Rain Byun](https://github.com/ragubyun) +- đŸ‡”đŸ‡±[Polonais](readme-pl.md) - Traduit par [Michal Biesiada](https://github.com/mbiesiad) +- đŸ‡Ș🇾[Espagnol](readme-es.md) - Traduit par [Miguel G. Sanguino](https://github.com/sanguino) +- đŸ‡§đŸ‡·[Portugais brĂ©silien](readme-pt-br.md) - Traduit par [Iago Angelim Costa Cavalcante](https://github.com/iagocavalcante) , [Douglas Mariano Valero](https://github.com/DouglasMV) et [koooge](https://github.com/koooge) +- đŸ‡ș🇩[Ukrainian](readme-ua.md) - Traduit par [Serhii Shramko](https://github.com/Shramkoweb) +- Envie de traduire dans ta propre langue ? Ouvres une issue 💜 + +

+ +## `Table des matiĂšres` + +#### [`Section 0: La rĂšgle d'or`](#section-0ïžâƒŁ-the-golden-rule) + +Un seul conseil qui inspire tout les autres (1 point spĂ©cial) + +#### [`Section 1: Anatomie d'un test`](#section-1-the-test-anatomy-1) + +La base - structurer des tests propre (12 points) + +#### [`Section 2: Backend`](#section-2ïžâƒŁ-backend-testing) + +Écrire efficacement des tests backend et de microservices (8 points) + +#### [`Section 3: Frontend`](#section-3ïžâƒŁ-frontend-testing) + +Écrire des tests pour l'interface utilisateur, y compris des tests de composants et des tests E2E (11 points) + +#### [`Section 4: Mesurer l'efficacitĂ© des tests`](#section-4ïžâƒŁ-measuring-test-effectiveness) + +Surveiller le surveillant - mesurer la qualitĂ© des tests (4 points) + +#### [`Section 5: IntĂ©gration continue`](#section-5ïžâƒŁ-ci-and-other-quality-measures) + +Lignes directrices pour l'intĂ©gration continue dans le monde du JS (9 points) + +

+ +# Section 0ïžâƒŁ: La rĂšgle d'or + +
+ +## âšȘ 0 La rĂšgle d'or: Concevoir des tests minimalistes + +:white_check_mark: **À faire:** +Le code des tests n'est pas comme le code de production - conçoit le pour ĂȘtre simple, court, sans abstraction, agrĂ©able Ă  utiliser et minimaliste. En regardant le code d'un test, on doit pouvoir comprendre son but instantanĂ©ment. + +Nos esprits sont dĂ©jĂ  occupĂ©s avec le code de production, on n'a pas "d'espace" pour de la complexitĂ© additionnelle. Si on essaye d'insĂ©rer un autre code compliquĂ© dans nos pauvres cerveaux, l'Ă©quipe va ĂȘtre ralentie ce qui est en contradiction avec la raison pour laquelle on fait des tests. +En pratique, c'est lĂ  que de nombreuses Ă©quipes abandonnent tout simplement les tests. + +Les tests sont une opportunitĂ© pour autre chose - un assistant amical et souriant, un avec qui il est agrĂ©able de travailler et qui nous apporte beaucoup pour peu d'investissement. La science nous dit que l'on a deux systĂšmes cĂ©rĂ©braux : le premier est utilisĂ© pour les activitĂ©s qui ne demandent pas d'effort comme conduire une voiture sur une route vide ; le deuxiĂšme sert aux opĂ©rations complexes et conscientes comme rĂ©soudre une Ă©quation mathĂ©matique. Conçois tes tests pour le premier systĂšme, lire un test doit _sembler_ aussi simple que de modifier un fichier HTML, et pas comme rĂ©soudre 2X(17 x 24). + +On peut y arriver en sĂ©lectionnant des techniques, des outils et des cibles de tests qui sont rentables et offrent un bon retour sur investissement. Test seulement ce qui doit ĂȘtre testĂ©, essaye de conserver de la souplesse, et parfois, il vaut mĂȘme mieux supprimer quelques tests et Ă©changer la fiabilitĂ© contre de l'agilitĂ© et de la simplicitĂ©. + +![alt text](/assets/headspace.png "On a pas de place disponible pour une complexitĂ© supplĂ©mentaire") + +La plupart des conseils ci-dessous sont des dĂ©rivĂ©s de ce principe. + +### PrĂȘt Ă  commencer ? + +

+ +# Section 1: Anatomie d'un test + +
+ +## âšȘ  1.1 Chaque nom devrait contenir 3 parties + +:white_check_mark: **À faire:** Un rapport de test devrait indiquer si la version actuelle de l'application correspond aux attentes pour des personnes qui ne sont pas forcĂ©ment familiĂšres avec la base de code : le testeur, le dev ops qui dĂ©ploie et toi dans 2 ans. Dans ce but, les noms des tests doivent expliciter les attentes et inclure 3 parties : + +(1) Qu'est-ce qui est testĂ© ? Par exemple, la mĂ©thode ProductService.addNewProduct + +(2) Dans quelle circonstance et scĂ©nario ? Par exemple, aucun prix n'est passĂ© Ă  la mĂ©thode + +(3) Quel est le rĂ©sultat attendu ? Par exemple, le produit n'est pas approuvĂ© + +
+ +❌ **Autrement:** Un dĂ©ploiement a Ă©chouĂ©, un test appelĂ© "Add product" Ă  Ă©chouĂ©. Est-ce que cela indique exactement ce qui ne fonctionne plus correctement ? + +
+ +**👇 Note:** Chaque point contient des exemples de codes et parfois une image d'illustration. Cliques pour agrandir. +
+ +
✏ Exemple de code + +
+ +### :clap: Bien faire les choses, exemple: Un nom de test constituĂ© de 3 parties + +![](https://img.shields.io/badge/🔹%20Example%20using%20Mocha-blue.svg "Using Mocha to illustrate the idea") + +```javascript +//1. unit under test +describe('Products Service', function() { + describe('Add new product', function() { + //2. scenario and 3. expectation + it('When no price is specified, then the product status is pending approval', ()=> { + const newProduct = new ProductService().add(...); + expect(newProduct.status).to.equal('pendingApproval'); + }); + }); +}); + +``` + +
+ +### :clap: Bien faire les choses, exemple: Un nom de test constitué de 3 parties + +![alt text](/assets/bp-1-3-parts.jpeg "A test name that constitutes 3 parts") + +
+ +
+
© Credits & read-more + 1. Roy Osherove - Naming standards for unit tests +
+ +

+ +## âšȘ  1.2 Structurer les tests avec le pattern AAA + +:white_check_mark: **À faire:** Structure tes tests avec 3 sections sĂ©parĂ©es: Organiser, Agir & VĂ©rifier (Arrange, Act & Assert: AAA). Suivre cette structure garantit que le lecteur n'utilise pas de "CPU" de cerveau pour comprendre le plan du test : + +1er A - Organiser (Arrange): Tout le code permettant de configurer le systĂšme selon le scĂ©nario qui doit ĂȘtre simulĂ©. Cela peut inclure d'instancier le constructeur de l'Ă©lĂ©ment testĂ©, ajouter des entrĂ©es en DB, mocking/stubbing des objets et autres codes de prĂ©paration + +2Ăšme A - Agir (Act): ExĂ©cute l'Ă©lĂ©ment testĂ©. En gĂ©nĂ©ral 1 seule ligne de code + +3Ă©me A - VĂ©rifier (Assert): VĂ©rifier que les valeurs reçues correspondent aux attentes. En gĂ©nĂ©ral 1 seule ligne de code + +
+ +❌ **Autrement:** Non seulement, vous avez passĂ© des heures Ă  comprendre le code principal, mais en plus ce qui devait ĂȘtre la partie la plus simple de la journĂ©e (tester) vous tord le cerveau. + + +
+ +
✏ Exemple de code + +
+ +### :clap: Bien faire les choses, exemple: Un test structurĂ© avec le pattern AAA + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Jest") ![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "Examples with Mocha") + +```javascript +describe("Customer classifier", () => { + test("When customer spent more than 500$, should be classified as premium", () => { + //Arrange + const customerToClassify = { spent: 505, joined: new Date(), id: 1 }; + const DBStub = sinon.stub(dataAccess, "getCustomer").reply({ id: 1, classification: "regular" }); + + //Act + const receivedClassification = customerClassifier.classifyCustomer(customerToClassify); + + //Assert + expect(receivedClassification).toMatch("premium"); + }); +}); +``` + +
+ +### :thumbsdown: Exemple d'anti pattern: Pas de séparation, un bloc, plus dur à interpréter + +```javascript +test("Should be classified as premium", () => { + const customerToClassify = { spent: 505, joined: new Date(), id: 1 }; + const DBStub = sinon.stub(dataAccess, "getCustomer").reply({ id: 1, classification: "regular" }); + const receivedClassification = customerClassifier.classifyCustomer(customerToClassify); + expect(receivedClassification).toMatch("premium"); +}); +``` + +
+ +

+ +## âšȘ 1.3 DĂ©crire les attentes dans un language produit: Utiliser des assertions de type BDD + +:white_check_mark: **À faire:** Coder tes tests dans un langage dĂ©claratif permet au lecteur de comprendre immĂ©diatement sans effectuer un seul cycle de "CPU" de cerveau. Lorsque tu Ă©cris du code impĂ©ratif remplis de logique conditionnelles, le lecteur est forcĂ© d'utiliser plus de cycles de "CPU" de cerveau. Dans ce cas, code les attentes dans un langage similaire au langage humain, dans un style dĂ©claratif de type BDD avec `expect` ou `should` et sans utiliser de code custom. Si Chai et Jest n'incluent pas les assertions nĂ©cessaires et qu'elles reviennent rĂ©guliĂšrement, considĂšre [d'Ă©tendre Jest matcher (Jest)](https://jestjs.io/docs/en/expect#expectextendmatchers) ou d'Ă©crire un [plugin Chai custom](https://www.chaijs.com/guide/plugins/) + +
+ +❌ **Autrement:** L'Ă©quipe Ă©crira moins de tests et dĂ©corera ceux qui sont ennuyeux avec .skip() + +
+ +
✏ Exemple de code
+ +![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "Examples with Mocha & Chai") ![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Jest") + +### :thumbsdown: Exemple d'anti pattern: Le lecteur doit parcourir un long code impĂ©ratif juste pour comprendre l'histoire du test + +```javascript +test("When asking for an admin, ensure only ordered admins in results", () => { + //assuming we've added here two admins "admin1", "admin2" and "user1" + const allAdmins = getUsers({ adminOnly: true }); + + let admin1Found, + adming2Found = false; + + allAdmins.forEach(aSingleUser => { + if (aSingleUser === "user1") { + assert.notEqual(aSingleUser, "user1", "A user was found and not admin"); + } + if (aSingleUser === "admin1") { + admin1Found = true; + } + if (aSingleUser === "admin2") { + admin2Found = true; + } + }); + + if (!admin1Found || !admin2Found) { + throw new Error("Not all admins were returned"); + } +}); +``` + +
+ +### :clap: Bien faire les choses, exemple: Parcourir le test déclaratif suivant est un jeu d'enfant + +```javascript +it("When asking for an admin, ensure only ordered admins in results", () => { + //assuming we've added here two admins + const allAdmins = getUsers({ adminOnly: true }); + + expect(allAdmins) + .to.include.ordered.members(["admin1", "admin2"]) + .but.not.include.ordered.members(["user1"]); +}); +``` + +
+ +

+ +## âšȘ  1.4 S'en tenir aux tests des boites noires : Ne tester que les mĂ©thodes publiques + +:white_check_mark: **À faire:** Tester les composants internes apporte beaucoup de complexitĂ© pour presque rien. Si ton code/API dĂ©livre les bon rĂ©sultats, est-ce que tu dois vraiment passer les 3 prochaines heures Ă  tester COMMENT il fonctionne et maintenir ces tests ? À chaque fois qu'un comportement publique est testĂ©, l'implĂ©mentation privĂ©e est aussi testĂ© implicitement, et test tests n'Ă©choueront que si il y a un certain problĂšme (par exemple: mauvais retour). Cette approche est aussi appelĂ©e `behavioral testing` (test de comportement). De l'autre cĂŽtĂ©, si tu dois tester les Ă©lĂ©ments internes (approche de la boĂźte blanche) - l'objectif passe de planifier le rĂ©sultat du composant Ă  des dĂ©tails de bases, et votre test peut Ă©chouer Ă  cause de refactoring mineurs alors que le rĂ©sultat est toujours bon - cela augmente la charge de maintenance. +
+ +❌ **Autrement:** Tes tests se comportent comme [l'enfant qui criait au loup](https://fr.wikipedia.org/wiki/L%27Enfant_qui_criait_au_loup): crier des faux positifs (par exemple, un test Ă©choue parce qu'un nom de variable privĂ© a Ă©tĂ© changĂ©). Sans surprise, les gens vont rapidement ignorer les notifications, jusqu'Ă  ce qu'un jour, un vrai bug soit ignorĂ© + +
+
✏ Exemple de code + +
+ +### :thumbsdown: Exemple d'anti pattern: Un cas qui test une mĂ©thode interne sans raison valable + +![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "Examples with Mocha & Chai") + +```javascript +class ProductService { + //this method is only used internally + //Change this name will make the tests fail + calculateVATAdd(priceWithoutVAT) { + return { finalPrice: priceWithoutVAT * 1.2 }; + //Change the result format or key name above will make the tests fail + } + //public method + getPrice(productId) { + const desiredProduct = DB.getProduct(productId); + finalPrice = this.calculateVATAdd(desiredProduct.price).finalPrice; + return finalPrice; + } +} + +it("White-box test: When the internal methods get 0 vat, it return 0 response", async () => { + //There's no requirement to allow users to calculate the VAT, only show the final price. Nevertheless we falsely insist here to test the class internals + expect(new ProductService().calculateVATAdd(0).finalPrice).to.equal(0); +}); +``` + +
+ +

+ +## âšȘ  1.5 Choisir les bons "test doubles": Éviter les mocks en faveur des stubs et spies + +:white_check_mark: **À faire:** Les "test doubles" sont un mal nĂ©cessaire parce qu'ils sont couplĂ©s aux composants internes mais apportent nĂ©anmoins beaucoup de valeur ([Retrouve ici un rappel Ă  propos des "test doubles": mocks vs stubs vs spies](https://martinfowler.com/articles/mocksArentStubs.html)). + +Avant d'utiliser des "test doubles", pose toi une question trĂšs simple: Est-ce que je l'utilise pour tester une fonctionnalitĂ© qui apparaĂźt, ou peut apparaĂźtre, dans le document de spĂ©cification ? Si non, ça sent le test de boite blanche. + +Par exemple, si tu veux tester que ton application se comporte correctement quand le service de paiement est coupĂ©, tu peux faire un stub du service de paiement et dĂ©clencher une rĂ©ponse de type 'No Response' pour vĂ©rifier que l'unitĂ© testĂ©e retourne la bonne valeur. Cela vĂ©rifie le comportement/rĂ©ponse de notre application suivant un certain scĂ©nario. Tu peux aussi utiliser un spy pour vĂ©rifier qu'un email a bien Ă©tĂ© envoyĂ© quand ce service Ă©tait coupĂ© - il s'agit encore une fois d'un test de comportement qui pourrait apparaĂźtre dans les spĂ©cifications ("Envoyer un email si le paiement n'as pas pu ĂȘtre enregistrĂ©"). +D'un autre cĂŽtĂ©, si tu mock le service de paiement pour vĂ©rifier qu'il a bien Ă©tĂ© appelĂ© avec le bon type Javascript, alors ton test est orientĂ© sur des comportements internes qui n'ont rien Ă  voir avec les fonctionnalitĂ©s de l'application et changeront probablement frĂ©quemment. +
+ +❌ **Autrement:** Chaque refactoring du code implique de chercher l'ensemble des mock dans le code afin de les mettre Ă  jour. Les tests deviennent une corvĂ©e plutĂŽt qu'un ami aidant. +
+ +
✏ Exemple de code + +
+ +### :thumbsdown: Exemple d'anti pattern: Les mocks se concentrent sur des composants internes + +![](https://img.shields.io/badge/🔧%20Example%20using%20Sinon-blue.svg "Examples with Sinon") + +```javascript +it("When a valid product is about to be deleted, ensure data access DAL was called once, with the right product and right config", async () => { + //Assume we already added a product + const dataAccessMock = sinon.mock(DAL); + //hmmm BAD: testing the internals is actually our main goal here, not just a side-effect + dataAccessMock + .expects("deleteProduct") + .once() + .withArgs(DBConfig, theProductWeJustAdded, true, false); + new ProductService().deletePrice(theProductWeJustAdded); + dataAccessMock.verify(); +}); +``` + +
+ +### :clap: Bien faire les choses, exemple : Les spies se concentrent sur les fonctionnalités requises mais touchent les composants internes par effet de bord + +```javascript +it("When a valid product is about to be deleted, ensure an email is sent", async () => { + //Assume we already added here a product + const spy = sinon.spy(Emailer.prototype, "sendEmail"); + new ProductService().deletePrice(theProductWeJustAdded); + //hmmm OK: we deal with internals? Yes, but as a side effect of testing the requirements (sending an email) + expect(spy.calledOnce).to.be.true; +}); +``` + +
+ +

+ +## 📗 Envie d'apprendre ces bonnes pratiques en vidĂ©o ? + +### Va voir mon cours en ligne [Testing Node.js & JavaScript From A To Z](https://www.testjavascript.com) + +

+ +## âšȘ 1.6 Utiliser des donnĂ©es rĂ©alistes + +:white_check_mark: **À faire:** Souvent les bugs de production sont rĂ©vĂ©lĂ©s par des entrĂ©es trĂšs spĂ©cifiques et surprenantes. Plus les entrĂ©es de tests seront rĂ©alistes, plus il y a de chance de dĂ©tecter les bugs tĂŽt. Utilise une librairie dĂ©diĂ©e comme [Faker](https://www.npmjs.com/package/faker) pour gĂ©nĂ©rer des pseudo-vrais donnĂ©es qui ressemble aux donnĂ©es de production. Par exemple, ce type de librairie peut gĂ©nĂ©rer de façon rĂ©aliste des numĂ©ros de tĂ©lĂ©phone, noms d'utilisateur, cartes de crĂ©dit, nom de sociĂ©tĂ© et mĂȘme du 'Lorem ipsum'. Tu peux aussi crĂ©er des tests (en plus des tests unitaires, par Ă  leur place) qui utilise des fausses donnĂ©es randomisĂ©es pour pousser test tests, ou mĂȘme importer de vraies donnĂ©es depuis ton environnement de production. Envie de passer au niveau supĂ©rieur ? Regarde le prochain point (property-based testing). +
+ +❌ **Autrement:** Tout vos tests de dĂ©veloppement vont montrer du vert Ă  tort avec des entrĂ©es tels que "Foo", mais en production ils passeront au rouge lorsqu'un hacker passera une chaine de caractĂšre tel que “@3e2ddsf . ##’ 1 fdsfds . fds432 AAAA” +
+ +
✏ Exemple de code + +
+ +### :thumbsdown: Exemple d'anti pattern: Une suite de test qui passe Ă  cause de donnĂ©es non rĂ©alistes +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Jest") + +```javascript +const addProduct = (name, price) => { + const productNameRegexNoSpace = /^\S*$/; //no white-space allowed + + if (!productNameRegexNoSpace.test(name)) return false; //this path never reached due to dull input + + //some logic here + return true; +}; + +test("Wrong: When adding new product with valid properties, get successful confirmation", async () => { + //The string "Foo" which is used in all tests never triggers a false result + const addProductResult = addProduct("Foo", 5); + expect(addProductResult).toBe(true); + //Positive-false: the operation succeeded because we never tried with long + //product name including spaces +}); +``` + +
+ +### :clap: Bien faire les choses, exemple : Données réalistes randomisés + +```javascript +it("Better: When adding new valid product, get successful confirmation", async () => { + const addProductResult = addProduct(faker.commerce.productName(), faker.random.number()); + //Generated random input: {'Sleek Cotton Computer', 85481} + expect(addProductResult).to.be.true; + //Test failed, the random input triggered some path we never planned for. + //We discovered a bug early! +}); +``` + +
+ +

+ +## âšȘ  1.7 Tester plusieurs combinaisons d'input avec le Property-based testing + +:white_check_mark: **À faire:** En rĂšgle gĂ©nĂ©ral, on choisit quelques valeurs d'entrĂ©es pour chaque test. MĂȘme lorsque le format des inputs est rĂ©aliste (voir le point 'Utiliser des donnĂ©es rĂ©alistes'), on couvre seulement quelques combinaisons d'entrĂ©es. En revanche, en production, une API appelĂ©e avec 5 paramĂštres peut ĂȘtre invoquĂ©e avec des milliers de permutations diffĂ©rentes, l'une d'entre elle peut faire Ă©chouer notre processus ([voir le Fuzz testing](https://fr.wikipedia.org/wiki/Fuzzing)). Et si tu pouvais Ă©crire un seul test qui envoie 1000 permutations d'entrĂ©es automatiquement et dĂ©tecte pour lequel d'entre eux notre processus ne retourne pas la bonne valeur ? Le Property-based testing c'est une mĂ©thode qui fait exactement ça : En testant toutes les combinaisons d'entrĂ©es possible on augmente les chance de dĂ©tecter un bug. Par exemple, prenons une mĂ©thode : addNewProduct(id, name, isDiscount), la librairie appellera cette mĂ©thode avec plusieurs combinaisons de (number, string, boolean) tel que (1, “iPhone”, false), (2, “Galaxy”, true). Tu peux utiliser le property-based testing avec ta librairie de test prĂ©fĂ©rĂ© (Mocha, Jest ...etc) Ă  l'aide de librairie tel que [js-verify](https://github.com/jsverify/jsverify) ou [testcheck](https://github.com/leebyron/testcheck-js) (meilleure documentation). MAJ: Nicolas Dubien Ă  suggĂ©rĂ© dans les commentaire de [regarder fast-check](https://github.com/dubzzz/fast-check#readme) qui semble offrir des fonctionnalitĂ©es supplĂ©mentaire et ĂȘtre activement maintenue. +
+ +❌ **Autrement:** Inconsciemment, tu choisis des entrĂ©es de test qui ne couvrent que les cas qui fonctionnent correctement. Malheureusement, cela rĂ©duit l'efficacitĂ© tests et leur capacitĂ© a dĂ©tecter des bugs. +
+ +
✏ Exemple de code + +
+ +### :clap: Bien faire les choses, exemple: Tester plusieurs permutations d'entrĂ©es avec "fast-check" + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Jest") + +```javascript +import fc from "fast-check"; + +describe("Product service", () => { + describe("Adding new", () => { + //this will run 100 times with different random properties + it("Add new product with random yet valid properties, always successful", () => + fc.assert( + fc.property(fc.integer(), fc.string(), (id, name) => { + expect(addNewProduct(id, name).status).toEqual("approved"); + }) + )); + }); +}); +``` + +
+ +

+ +## âšȘ  1.8 Si besoin, n'utiliser que des snapshots courts et inline + +:white_check_mark: **Do:** Quand il y a un besoin pour du [snapshot testing](https://jestjs.io/docs/en/snapshot-testing), utilise seulement des snapshots courts (3-7 lignes) qui sont inclut dans le test ([Inline Snapshot](https://jestjs.io/docs/en/snapshot-testing#inline-snapshots)) et pas dans des fichiers externes. Respecter cette rĂšgle permettra Ă  vos tests de rester auto-explicatif et moins fragile. + +D'un autre cĂŽtĂ©, les tutoriels et outils 'classique' encouragent Ă  stocker de gros fichiers (rĂ©sultats d'API JSON, markup d'un composant) sur un emplacement externe et de s'assurer Ă  chaque fois que le test est lancĂ©, de comparer le rĂ©sultat reçu avec la version sauvegardĂ©e. Cela peut, par exemple, implicitement coupler notre test Ă  1000 lignes avec 3000 valeurs que le lecteur du test ne verra jamais et auquel il ne pensera pas. Pourquoi est-ce que c'est mauvais ? En faisant ça, il y a 1000 raisons pour votre test d'Ă©chouer - Il suffit qu'une seule ligne change pour que le snapshot soit invalide, et cela arrivera probablement souvent. À quelle frĂ©quence ? Pour chaque espace, commentaire ou changement mineur dans le HTML/CSS. De plus, le nom du test ne donnera pas la moindre indication Ă  propos de l'erreur vu qu'il vĂ©rifie simplement que les 1000 lignes n'ont pas changĂ©. Cela encourage aussi celui qui Ă©crit les tests Ă  accepter comme valeur de succĂšs un long document qu'il ne pourra pas inspecter et vĂ©rifier. Tous ces points sont des symptĂŽmes d'un test obscure qui n'est pas ciblĂ© et cherche Ă  en faire trop. + +Il faut noter qu'il y a quelques cas ou de long snapshots externes sont acceptable - Pour valider un schĂ©ma et pas des donnĂ©es ou concernant des documents qui ne changent presque jamais +
+ +❌ **Sinon:** Un test UI Ă©choue. Le code semble bon, l'Ă©cran rend parfaitement les pixels, que s'est-il passĂ© ? Ton test de snapshot a trouvĂ© une diffĂ©rence entre le document original et le document actuel - un simple espace a Ă©tĂ© ajoutĂ© dans le markdown... + +
+ +
✏ Exemple de code + +
+ +### :thumbsdown: Exemple d'anti pattern: Coupler nos tests à 2000 lignes de code qu'on ne voit pas + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Jest") + +```javascript +it("TestJavaScript.com is renderd correctly", () => { + //Arrange + + //Act + const receivedPage = renderer + .create( Test JavaScript ) + .toJSON(); + + //Assert + expect(receivedPage).toMatchSnapshot(); + //We now implicitly maintain a 2000 lines long document + //every additional line break or comment - will break this test +}); +``` + +
+ +### :clap: Bien faire les choses, exemple: Les attentes sont claires et spécifiques + +```javascript +it("When visiting TestJavaScript.com home page, a menu is displayed", () => { + //Arrange + + //Act + const receivedPage = renderer + .create( Test JavaScript ) + .toJSON(); + + //Assert + + const menu = receivedPage.content.menu; + expect(menu).toMatchInlineSnapshot(` + +`); +}); +``` + +
+ +

+ +## âšȘ 1.9 Éviter les fixtures et seeds globals, ajouter les donnĂ©es par test + +:white_check_mark: **À faire:** En suivant la rĂšgle d'or (point 0), chaque test doit ajouter et agir sur son propre jeu d'entrĂ©e en base de donnĂ©es pour Ă©viter d'ĂȘtre couplĂ©s et faciliter le raisonnement Ă  propos de la logique du test. En rĂ©alitĂ©, cette rĂšgle est souvent violĂ©e par les testeurs qui remplissent la base de donnĂ©es avant de lancer les tests ([aussi connu sous le nom ‘test fixture’](https://en.wikipedia.org/wiki/Test_fixture)) afin d'amĂ©liorer les performances. MĂȘme si la performance est effectivement une inquiĂ©tude valide, elle peut ĂȘtre attĂ©nuĂ©e (voir "Component testing"), en revanche, la complexitĂ© des tests est une peine bien plus douloureuse qui devrait rĂ©gir les autres considĂ©rations la plupart du temps. +En pratique, chaque cas testĂ© doit explicitement ajouter les entrĂ©es en base de donnĂ©es dont il a besoin et n'agir que sur ces entrĂ©es. Si la performance devient une inquiĂ©tude critique - un compromis peut se trouver sous la forme de seeds pour les jeux de tests qui ne modifient pas les donnĂ©es (queries). + +
+ +❌ **Autrement:** Certains tests Ă©choue, le dĂ©ploiement est annulĂ©, l'Ă©quipe va dĂ©penser un temps prĂ©cieux maintenant, est-ce qu'on a un bug ? Investiguons, oh non - il semble que deux tests modifiaient les mĂȘme donnĂ©es + +
+ +
✏ Exemple de code + +
+ +### :thumbsdown: Exemple d'anti pattern: les tests ne sont pas indĂ©pendants et reposent sur un hook global pour des donnĂ©es globales en DB + +![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "Examples with Mocha") + +```javascript +before(async () => { + //adding sites and admins data to our DB. Where is the data? outside. At some external json or migration framework + await DB.AddSeedDataFromJson('seed.json'); +}); +it("When updating site name, get successful confirmation", async () => { + //I know that site name "portal" exists - I saw it in the seed files + const siteToUpdate = await SiteService.getSiteByName("Portal"); + const updateNameResult = await SiteService.changeName(siteToUpdate, "newName"); + expect(updateNameResult).to.be(true); +}); +it("When querying by site name, get the right site", async () => { + //I know that site name "portal" exists - I saw it in the seed files + const siteToCheck = await SiteService.getSiteByName("Portal"); + expect(siteToCheck.name).to.be.equal("Portal"); //Failure! The previous test change the name :[ +}); + +``` + +
+ +### :clap: Bien faire les choses, exemple: On peut rester dans le test, chaque test agis sur ses propres données + +```javascript +it("When updating site name, get successful confirmation", async () => { + //test is adding a fresh new records and acting on the records only + const siteUnderTest = await SiteService.addSite({ + name: "siteForUpdateTest" + }); + + const updateNameResult = await SiteService.changeName(siteUnderTest, "newName"); + + expect(updateNameResult).to.be(true); +}); +``` + +
+ +
+ +## âšȘ  1.10 Ne pas catcher les erreurs, les prĂ©voir + +:white_check_mark: **À faire:** Lorsqu'on essaye de dĂ©tecter que certaines entrĂ©es dĂ©clenchent une erreur, il peut sembler ĂȘtre une bonne idĂ©e d'utiliser try-catch-finally et de vĂ©rifier qu'on est passĂ© dans le catch. Le rĂ©sultat est un test Ă©trange et verbeux (exemple plus bas) qui cache l'intention simple du test et le rĂ©sultat attendu. + +Une alternative plus Ă©lĂ©gante est d'utiliser l'assertion Chai dĂ©diĂ©e : expect(method).to.throw (ou en Jest: expect(method).toThrow()). Il est Ă©galement obligatoire de vĂ©rifier que l'exception contient une propriĂ©tĂ© qui indique le type d'erreur, sinon, en recevant un message d'erreur gĂ©nĂ©rique, l'application ne sera pas capable de faire beaucoup plus que de montrer un message dĂ©cevant Ă  l'utilisateur. +
+ +❌ **Autrement:** Il sera compliquĂ© de dĂ©duire du rapport de test ce qui s'est mal passĂ© + +
+ +
✏ Exemple de code + +
+ +### :thumbsdown: Exemple d'anti pattern: Un long test qui essaye de vĂ©rifier la prĂ©sence d'une erreur avec try-catch + +![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "Examples with Mocha") + +```javascript +it("When no product name, it throws error 400", async () => { + let errorWeExceptFor = null; + try { + const result = await addNewProduct({}); + } catch (error) { + expect(error.code).to.equal("InvalidInput"); + errorWeExceptFor = error; + } + expect(errorWeExceptFor).not.to.be.null; + //if this assertion fails, the tests results/reports will only show + //that some value is null, there won't be a word about a missing Exception +}); +``` + +
+ +### :clap: Bien faire les choses, exemple: Un attente lisible qui peut ĂȘtre comprise simplement, peut ĂȘtre mĂȘme par un QA ou PM technique + +```javascript +it("When no product name, it throws error 400", async () => { + await expect(addNewProduct({})) + .to.eventually.throw(AppError) + .with.property("code", "InvalidInput"); +}); +``` + +
+ +

+ +## âšȘ  1.11 Taguer tes tests + +:white_check_mark: **À faire:** Des tests diffĂ©rents doivent ĂȘtre lancĂ©s dans diffĂ©rents scĂ©narios. Les tests de fumĂ©e (quick smoke), IO-less, doivent tourner Ă  chaque fois qu'un dĂ©veloppeur sauvegarde ou commit un fichier, les tests complets end-to-end sont en gĂ©nĂ©ral lancĂ©s quand une nouvelle pull-request est soumise, etc. +On peut rĂ©aliser ça en taggant les tests avec des mots clefs comme #cold #api #sanity pour pouvoir utiliser grep et sĂ©lectionner les tests qui nous interesse. Par exemple, voilĂ  comment invoquer uniquement le groupe de test 'sanity' avec Mocha : mocha - grep 'sanity' +
+ +❌ **Autrement:** Lancer tous les tests, y compris ceux qui exĂ©cutent des dizaines de requĂȘtes DB, Ă  chaque fois qu'un dĂ©veloppeur fait un petit changement peut ĂȘtre extrĂȘmement lent et pousser les dĂ©veloppeurs a ne pas utiliser les tests. +
+ +
✏ Exemple de code + +
+ +### :clap: Bien faire les choses, exemple: Taguer des tests avec '#cold-test' permet Ă  celui qui les lance de n'executer que les tests rapide (cold = tests rapides qui ne font pas d'opĂ©rations IO et peuvent ĂȘtre executĂ©s souvent, meme pendant que le dĂ©veloppeur code) + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Jest") + +```javascript +//this test is fast (no DB) and we're tagging it correspondigly +//now the user/CI can run it frequently +describe("Order service", function() { + describe("Add new order #cold-test #sanity", function() { + test("Scenario - no currency was supplied. Expectation - Use the default currency #sanity", function() { + //code logic here + }); + }); +}); +``` + +
+ +

+ +## âšȘ  1.12 CatĂ©goriser tes tests sous au moins 2 niveaux + +:white_check_mark: **À faire:** Applique une structure Ă  ta suite de tests pour qu'un visiteur occasionnel puisse facilement comprendre les attentes (Les tests sont la meilleure documentation) et les diffĂ©rents scĂ©narios testĂ©s. Une mĂ©thode frĂ©quence pour ça est de placer au moins 2 blocs 'describe' au-dessus de vos tests : Le premier est pour le nom de l'unitĂ© testĂ© et le deuxiĂšme pour un niveau supplĂ©mentaire de catĂ©gorisation comme le scĂ©nario ou une catĂ©gorie (voir l'exemple de code plus bas). Cette organisation amĂ©liorera grandement vos rapports de tests: Le lecteur comprendra simplement les catĂ©gories de tests, examinera la section voulue et verra les corrĂ©lations entre les tests qui Ă©chouent. De plus, ce sera bien plus simple pour un dĂ©veloppeur de naviguer dans le code d'une suite avec de nombreux tests. Il y a plusieurs structures alternatives pour les suites de tests que tu peux envisager comme [given-when-then](https://github.com/searls/jasmine-given) et [RITE](https://github.com/ericelliott/riteway) + +
+ +❌ **Autrement:** En regardant un rapport avec une longue liste de tests a plat, le lecteur devra lire un long texte pour comprendre les scĂ©narios majeurs et comprendre les liens entre les tests qui Ă©chouent. Imagine le cas suivant : Quand 7/100 tests Ă©chouent, regarder une liste Ă  plat nĂ©cessitera d'aller lire le contenu des tests qui Ă©chouent pour comprendre le lien entre eux. En revanche, dans un rapport hiĂ©rarchique, ils pourraient tous ĂȘtre au sein du mĂȘme scĂ©nario ou d'une catĂ©gorie et le lecteur pourra rapidement dĂ©duire ce qui est, ou du moins oĂč est, la cause de l'erreur. + +
+ +
✏ Exemple de code + +
+ +### :clap: Bien faire les choses, exemple: Structurer une suite avec le nom de l'unitĂ© testĂ© et les scĂ©narios mĂšnera au rapport pratique montrĂ© ci-dessous +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Jest") + +```javascript +// Unit under test +describe("Transfer service", () => { + //Scenario + describe("When no credit", () => { + //Expectation + test("Then the response status should decline", () => {}); + + //Expectation + test("Then it should send email to admin", () => {}); + }); +}); +``` + +![alt text](assets/hierarchical-report.png) + +
+ +### :thumbsdown: Exemple d'anti-pattern: Une liste de tests à plat qui rend l'identification du problùme difficile pour le lecteur + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Mocha") + +```javascript +test("Then the response status should decline", () => {}); + +test("Then it should send email", () => {}); + +test("Then there should not be a new transfer record", () => {}); +``` + +![alt text](assets/flat-report.png) + +
+ +
+ +

+ +## âšȘ 1.13 Autre bonnes pratiques gĂ©nĂ©riques + +:white_check_mark: **À faire:** Ce post se concentre sur des conseils de tests qui sont en rapport, ou au moins peuvent ĂȘtre prĂ©sentĂ©s, avec Node JS. Ce point, cependant, regroupe quelques conseils sans rapport avec Node qui sont bien connus. + +Apprends et pratique [les principes TDD](https://www.sm-cloud.com/book-review-test-driven-development-by-example-a-tldr/) - ils ont beaucoup de valeurs pour la plupart, mais ne soit pas intimidĂ©s s'ils ne correspondent pas Ă  ton style, tu n'es pas le seul. Envisage d'Ă©crire les tests avec le code dans un [red-green-refactor style](https://blog.cleancoder.com/uncle-bob/2014/12/17/TheCyclesOfTDD.html), vĂ©rifie que chaque test vĂ©rifie exactement une chose, quand tu trouves un bug - avant de le fixer, Ă©crit un test qui dĂ©tectera le bug Ă  l'avenir, laisse chaque test Ă©chouer au moins une fois avant de devenir vert, commence un module en Ă©crivant du code simple et rapide qui valide le test - puis refactor graduellement et passe le code a un niveau de production, Ă©vite toute dĂ©pendance Ă  l'environnement (paths, OS, etc) +
+ +❌ **Autrement:** Tu manqueras les perles de sagesses recueillies pendant des dĂ©cennies. + +

+ +# Section 2ïžâƒŁ: Tests Backend + +## âšȘ 2.1 Enrichis ton portefeuille de test: Vois plus loin que les tests unitaire et la pyramide + +:white_check_mark: **À faire:** La [pyramide de tests](https://martinfowler.com/bliki/TestPyramid.html), bien que vielle de plus de 10 ans, est un bon modĂšle qui suggĂšre trois types de tests et influence la plupart des stratĂ©gies de tests des dĂ©veloppeurs. Dans un mĂȘme temps, une poignĂ©e de nouvelles techniques de tests brillantes ont Ă©mergĂ© et sont dans l'ombre de la pyramide de tests. Étant donnĂ© l'Ă©tendu des changements que l'on a vu ces 10 derniĂšres annĂ©es (micro-services, cloud, serverless), est-il seulement possible qu'un vieux modĂšle soit adaptĂ© Ă  *tout* les types d'applications ? Le monde du test ne devrait-il pas accueillir de nouvelles techniques ? + +Ne vous mĂ©prenez pas, en 2019, la pyramide de tests, le TDD et les tests unitaires sont toujours une technique puissante et sont probablement le meilleur choix pour beaucoup d'applications. Seulement, comme les autres modĂšles, malgrĂ© qu'il soit utile, [il doit ĂȘtre faux parfois](https://en.wikipedia.org/wiki/All_models_are_wrong). Par exemple, imagine une application IoT qui traite de nombreux Ă©vĂ©nements dans une queue (message-bus) comme Kafka/RabbitMQ, qui vont ensuite dans un entrepot de donnĂ©e puis sont lus par une UI d'analyse. Est-ce qu'on devrait vraiment dĂ©penser 50% de notre budget de test pour Ă©crire des tests unitaires sur une application qui est centrĂ©e sur l'intĂ©gration et n'a presque aucune logique ? Plus la diversitĂ© des applications augmente (bots, crypto, Alexa-skills) plus les chances sont grandes de trouver un scĂ©nario ou la pyramide de test n'est pas le meilleur choix. + +Il est temps d'enrichir ton portefeuille de test et de devenir familier avec plus de types de tests (les prochains points suggĂšrent quelques idĂ©es), des modĂšles tels que celui de la pyramide de tests mais aussi d'associer les types de tests aux problĂšmes que tu rencontres dans le monde rĂ©el ('Hey, notre API est cassĂ©, Ă©crivons des consumer-driven contract testing!'), diversifie tes tests comme un investisseur qui construit son portefeuille en se basant sur l'analyse des risques - estime oĂč les problĂšmes risquent de se poser et applique des mesures de prĂ©vention pour rĂ©duire ces risques. + +Un mot d'avertissement: l'argument du TDD dans le monde du dĂ©veloppement Ă  un visage typique de fausse dichotomie, certains disent de l'utiliser de partout, d'autres pensent que c'est le diable. Tous ceux qui parlent en absolu ont tord :] + +
+ +❌ **Autrement:** Tu va rater des outils avec un retour sur investissement incroyable, certains comme Fuzz, lint, ou mutation peuvent apporter de la valeur en 10 minutes + +
+ +
✏ Exemple de code + +
+ +### :clap: Bien faire les choses, exemple: Cindy Sridharan propose un portefeuille de tests riche dans son excellent post 'Testing Microservices - the same way' + +![alt text](assets/bp-12-rich-testing.jpeg "Cindy Sridharan suggests a rich testing portfolio in her amazing post ‘Testing Microservices — the sane way’") + +â˜șExample: [YouTube: “Beyond Unit Tests: 5 Shiny Node.JS Test Types (2018)” (Yoni Goldberg)](https://www.youtube.com/watch?v=-2zP494wdUY&feature=youtu.be) + +
+ +![alt text](assets/bp-12-Yoni-Goldberg-Testing.jpeg "A test name that constitutes 3 parts") + +
+ +

+ +## âšȘ 2.2 Les tests de composant pourrait ĂȘtre ton meilleur arrangement + +:white_check_mark: **À faire:** Chaque test unitaire couvre une petite portion de l'application et il est coĂ»teux de couvrir l'ensemble, alors que les tests end-to-end couvrent facilement une grande partie mais sont lent, pourquoi ne pas appliquer une approche intermĂ©diaire et Ă©crire des tests qui sont plus gros que les tests unitaires mais plus petits que les tests end-to-end ? Les tests de composant (Component testing) sont mĂ©connus du monde de test mais ils offrent le meilleur des deux mondes: des performances raisonnable et la possibilitĂ© d'appliquer le pattern TDD + une couverture correcte et rĂ©aliste + +Les tests de composant se concentrent sur "l'unitĂ©" du microservice, ils fonctionnent sur l'API, ne mock rien qui appartient au microservice lui-mĂȘme (une vrai DB, ou au moins une version in-memory de cette DB) mais stub tout ce qui est externe, comme les appels Ă  d'autres microservices. En faisant ça, on test ce que l'on dĂ©ploie, on approche l'application de l'extĂ©rieur vers l'intĂ©rieur et on gagne en confiance dans un laps de temps raisonnable. + +
+ +❌ **Autrement:** Tu risque de passer de longues journĂ©e Ă  Ă©crire des tests unitaire pour te rendre compte que tu n'as que 20% de couverture + +
+ +
✏ Exemple de code + +
+ +### :clap: Bien faire les choses, exemple: Supertest permet d'approcher l'API Express (rapide et couvre plusieurs niveaux) + +![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "Examples with Mocha") + +![alt text](assets/bp-13-component-test-yoni-goldberg.png " [Supertest](https://www.npmjs.com/package/supertest) allows approaching Express API in-process (fast and cover many layers)") + +
+ +

+ +## âšȘ 2.3 VĂ©rifier que les nouvelles versions ne cassent pas l'API avec les tests de contrat + +:white_check_mark: **À faire:** Ton microservice a plusieurs clients, et tu exĂ©cutes plusieurs versions du service pour des raisons de compatibilitĂ© (pour que tout le monde soit content). Puis tu changes un champ et 'boom!', un client important qui compte sur ce champ est en colĂšre. C'est le Catch-22 du monde de l'intĂ©gration : Il est trĂšs difficile pour le cotĂ© serveur de considĂ©rer toutes les attentes des clients. D'un autre cotĂ©, les clients ne peuvent pas rĂ©aliser de tests puisque le serveur contrĂŽles les dates de sorties. [Les "consumer-driven contracts" et le framework PACT](https://docs.pact.io) sont nĂ©s pour formaliser ce processus avec une approche disruptive - ce n'est pas le serveur qui dĂ©finit ses propres plans de test, mais le client qui dĂ©finit les tests du ...serveur! PACT peut enregistrer les attentes du client et les placer dans un emplacement partagĂ©, "broker", afin que le serveur puisse extraire les attentes et s'exĂ©cuter sur chaque version en utilisant la librairie PACT pour dĂ©tecter les contrats rompus - une attente du client qui n'est pas satisfaite. En faisant ça, toutes les incompatibilitĂ©s d'API server-client sont repĂ©rĂ©s tĂŽt pendant le build/CI et peuvent vous Ă©viter beaucoup de frustration. +
+ +❌ **Autrement:** L'alternative sont les tests manuels Ă©puisants ou la peur du dĂ©ploiement +
+ +
✏ Exemple de code + +
+ +### :clap: Bien faire les choses, exemple: + +![](https://img.shields.io/badge/🔧%20Example%20using%20PACT-blue.svg "Examples with PACT") + +![alt text](assets/bp-14-testing-best-practices-contract-flow.png) + +
+ +

+ +## âšȘ  2.4 Tester tes middlewares de maniĂšre isolĂ©e + +:white_check_mark: **À faire:** Beaucoup Ă©vitent les tests de Middleware parce qu'ils reprĂ©sentent une petite portion du systĂšme et requiĂšrent un serveur express live. Ce sont deux mauvaises raisons - les Middlewares sont petits, mais affectent toute ou la plupart des requĂȘtes et peuvent ĂȘtre testĂ©s simplement en tant que fonction qui reçoit un objet JS {req,res}. Pour tester un middleware, il suffit de l'invoquer et d'espionner ([avec Sinon par exemple](https://www.npmjs.com/package/sinon) l'interaction avec l'objet {req,res} pour s'assurer que la fonction a effectuĂ©e la bonne action. La librairie [node-mock-http](https://www.npmjs.com/package/node-mocks-http) va encore plus loin et prend en compte l'objet {req,res} tout en surveillant son comportement. Par exemple, il peut vĂ©rifier que le status http qui Ă  Ă©tĂ© dĂ©fini sur l'objet res correspond aux attentes (voir l'exemple ci-dessous) +
+ +❌ **Autrement:** Un bug dans un middleware Express === un bug dans toutes ou la plupart des requĂȘtes +
+ +
✏ Exemple de code + +
+ +### :clap: Bien faire les choses, exemple: Tester le middleware en isolation sans effectuer d'appel rĂ©seau et sans rĂ©veiller l'ensemble de la machine Express + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Jest") + +```javascript +//the middleware we want to test +const unitUnderTest = require("./middleware"); +const httpMocks = require("node-mocks-http"); +//Jest syntax, equivelant to describe() & it() in Mocha +test("A request without authentication header, should return http status 403", () => { + const request = httpMocks.createRequest({ + method: "GET", + url: "/user/42", + headers: { + authentication: "" + } + }); + const response = httpMocks.createResponse(); + unitUnderTest(request, response); + expect(response.statusCode).toBe(403); +}); +``` + +
+ +

+ +## âšȘ 2.5 Mesurer et refactoriser en utilisant des outils d'analyse statique + +:white_check_mark: **À faire:** Utiliser des outils d'analyse statique donne des moyens objectif d'amĂ©liorer la qualitĂ© et de garder le code maintenable. Tu peux ajouter un outil d'analyse statique Ă  ton build CI pour l'annuler si il dĂ©tecte un "code smell". Ses arguments de vente par rapport au linting simple sont la capacitĂ© d'inspecter la qualitĂ© dans le contexte de plusieurs fichiers (e.g. dĂ©tecter des duplications), effectuer des analyses avancĂ©es (e.g. complexitĂ© du code) et suivre l'histoire et le progrĂ©s d'un problĂšme de code. Deux exemples d'outils que tu peux utiliser sont [SonarQube](https://www.sonarqube.org/) (4,900+ [stars](https://github.com/SonarSource/sonarqube)) et [Code Climate](https://codeclimate.com/) (2,000+ [stars](https://github.com/codeclimate/codeclimate)) + +Credit: [Keith Holliday](https://github.com/TheHollidayInn) + +
+ +❌ **Autrement:** Avec du code de mauvaise qualitĂ©, les bugs et la performance seront toujours un problĂšme qu'aucune nouvelle librairie ou fonctionnalitĂ© de pointe ne peux corriger + +
+ +
✏ Exemple de code + +
+ +### :clap: Bien faire les choses, exemple: CodeClimate, un outil commercial qui peux identifier des mĂ©thodes complexes: + +![](https://img.shields.io/badge/🔧%20Example%20using%20Code%20Climate-blue.svg "Examples with CodeClimate") + +![alt text](assets/bp-16-yoni-goldberg-quality.png "CodeClimate, a commercial tool that can identify complex methods:") + +
+ +

+ +## âšȘ  2.6 VĂ©rifier ta prĂ©paration pour le chaos liĂ©s a Node + +:white_check_mark: **À faire:** Bizarrement, la plupart des tests software concernent uniquement la logique et les donnĂ©es, mais certaines des pires choses qui peuvent arriver ( et qui sont vraiment difficile Ă  attĂ©nuer ) sont les problĂšmes d'infrastructures. Par exemple, est-ce que tu as dĂ©jĂ  testĂ© ce qui arrivera quand la mĂ©moire du processus est surchargĂ©e, ou quand le serveur/process tombe, est-ce que ton systĂšme de monitoring dĂ©tecte lorsque l'API devient 50% plus lente ? Pour tester et attĂ©nuer ce type de choses - [l'ingĂ©nierie du Chaos](https://principlesofchaos.org/) est nĂ© de Netflix. +Il vise Ă  fournir une sensibilisation, des frameworks et des outils pour tester la rĂ©silience de notre application aux problĂšmes chaotiques. Par exemple, l'un de ces fameux outils, [le chaos monkey](https://github.com/Netflix/chaosmonkey), tue alĂ©atoirement des serveurs pour vĂ©rifier que notre service peut toujours servir les utilisateurs et ne repose pas sur un unique serveur ( Il y a aussi une version Kubernetes, [kube-monkey](https://github.com/asobti/kube-monkey), qui tue des pods). Tous ces outils fonctionnent au niveau de l'hĂ©bergeur/la plateforme, mais que se passe-t-il si tu veux gĂ©nĂ©rer un chaos Node pour vĂ©rifier comment ton process gĂšre les erreurs non prĂ©vus, les rejets de promesse, la surcharge de mĂ©moire v8 avec l'allocation maximum de 1.7Gb ou est-ce que ton UX reste satisfaisante si l'event loop est bloquĂ© rĂ©guliĂšrement ? Pour rĂ©pondre Ă  ça, j'ai Ă©crit, [node-chaos](https://github.com/i0natan/node-chaos-monkey) (alpha) qui fournit toute sorte d'actions chaotiques liĂ©es a Node. +
+ +❌ **Autrement:** Pas d'Ă©chappatoire ici, la loi de Murphy heurtera votre production sans merci +
+ +
✏ Exemple de code + +
+ +### :clap: Bien faire les choses, exemple: Le chaos-Node peut générer toute sortes de d'erreurs Node.js afin que tu puisses tester la résilience de ton application au chaos + +![alt text](assets/bp-17-yoni-goldberg-chaos-monkey-nodejs.png "Node-chaos can generate all sort of Node.js pranks so you can test how resilience is your app to chaos") + +
+ +
+ +## âšȘ 2.7 Éviter les fixtures et seeds globals, ajouter les donnĂ©es par test + +:white_check_mark: **À faire:** En suivant la rĂšgle d'or (point 0), chaque test doit ajouter et agir sur son propre jeu d'entrĂ©e en base de donnĂ©es pour Ă©viter d'ĂȘtre couplĂ©s et faciliter le raisonnement Ă  propos de la logique du test. En rĂ©alitĂ©, cette rĂšgle est souvent violĂ©e par les testeurs qui remplissent la base de donnĂ©es avant de lancer les tests (aussi connu sous le nom ‘test fixture’) afin d'amĂ©liorer les performances. MĂȘme si la performance est effectivement une inquiĂ©tude valide, elle peut ĂȘtre attĂ©nuĂ©e (voir "Component testing"), en revanche, la complexitĂ© des tests est un chagrin bien plus douloureux qui devrait rĂ©gir les autres considĂ©rations la plupart du temps. En pratique, chaque cas testĂ© doit explicitement ajouter les entrĂ©es en base de donnĂ©es dont il a besoin et n'agir que sur ces entrĂ©es. Si la performance devient une inquiĂ©tude critique - un compromis peut se trouver sous la forme de seeds pour les jeux de tests qui ne modifient pas les donnĂ©es (queries). +
+ +❌ **Autrement:** Certains tests Ă©choue, le dĂ©ploiement est annulĂ©, l'Ă©quipe va dĂ©penser un temps prĂ©cieux maintenant, est-ce qu'on a un bug ? Investiguons, oh non - il semble que deux tests modifiaient les mĂȘme donnĂ©es + +
+ +
✏ Exemple de code + +
+ +### :thumbsdown: Exemple d'anti pattern: les tests ne sont pas indĂ©pendants et reposent sur un hook global pour des donnĂ©es globales en DB + +![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "Examples with Mocha") + +```javascript +before(async () => { + //adding sites and admins data to our DB. Where is the data? outside. At some external json or migration framework + await DB.AddSeedDataFromJson('seed.json'); +}); +it("When updating site name, get successful confirmation", async () => { + //I know that site name "portal" exists - I saw it in the seed files + const siteToUpdate = await SiteService.getSiteByName("Portal"); + const updateNameResult = await SiteService.changeName(siteToUpdate, "newName"); + expect(updateNameResult).to.be(true); +}); +it("When querying by site name, get the right site", async () => { + //I know that site name "portal" exists - I saw it in the seed files + const siteToCheck = await SiteService.getSiteByName("Portal"); + expect(siteToCheck.name).to.be.equal("Portal"); //Failure! The previous test change the name :[ +}); + +``` + +
+ +### :clap: Bien faire les choses, exemple: On peut rester dans le test, chaque test agis sur ses propres données + +```javascript +it("When updating site name, get successful confirmation", async () => { + //test is adding a fresh new records and acting on the records only + const siteUnderTest = await SiteService.addSite({ + name: "siteForUpdateTest" + }); + const updateNameResult = await SiteService.changeName(siteUnderTest, "newName"); + expect(updateNameResult).to.be(true); +}); +``` + +
+ +

+ +# Section 3ïžâƒŁ: Tests Frontend + +## âšȘ  3.1 Separer l'UI des fonctionnalitĂ©s + +:white_check_mark: **À faire:** Lorsqu'on veut tester la logique d'un composant, les dĂ©tails UI deviennent du bruit qui devrait ĂȘtre extrait, afin que les tests se concentrent purement sur les donnĂ©es. En pratique, extrait les donnĂ©es dĂ©sirĂ©es du markup d'une façon abstraite qui n'est pas trop couplĂ©e avec l'implĂ©mentation graphique, assert seulement les donnĂ©es (vs des dĂ©tails graphiques HTML/CSS) et dĂ©sactive les animations qui ralentissent. Tu peux ĂȘtre tentĂ© d'Ă©viter le rendu et ne tester que les parties derriĂšre l'UI (e.g. services, actions, store) mais il s'agira de tests fictionnels qui ne ressemblent pas Ă  la rĂ©alitĂ© et ne rĂ©vĂ©leront pas les cas ou la bonne donnĂ©e n'arrive pas Ă  l'UI. +
+ +❌ **Autrement:** Les donnĂ©es calculĂ©es de ton test peuvent ĂȘtre prĂȘtes en 10ms, mais l'ensemble du test durera 500ms (100 tests = 1 min) Ă  cause d'animation qui ne nous concerne pas dans le cadre du test. +
+ +
✏ Exemple de code + +
+ +### :clap: Bien faire les choses, exemple: SĂ©parer les dĂ©tails UI + +![](https://img.shields.io/badge/🔧%20Example%20using%20React-blue.svg "Examples with React") ![](https://img.shields.io/badge/🔧%20Example%20using%20React%20Testing%20Library-blue.svg "Examples with react-testing-library") + +```javascript +test("When users-list is flagged to show only VIP, should display only VIP members", () => { + // Arrange + const allUsers = [{ id: 1, name: "Yoni Goldberg", vip: false }, { id: 2, name: "John Doe", vip: true }]; + + // Act + const { getAllByTestId } = render(); + + // Assert - Extract the data from the UI first + const allRenderedUsers = getAllByTestId("user").map(uiElement => uiElement.textContent); + const allRealVIPUsers = allUsers.filter(user => user.vip).map(user => user.name); + expect(allRenderedUsers).toEqual(allRealVIPUsers); //compare data with data, no UI here +}); +``` + +
+ +### :thumbsdown: Exemple d'anti pattern: L'assertion mélange des détails UX et les données + +```javascript +test("When flagging to show only VIP, should display only VIP members", () => { + // Arrange + const allUsers = [{ id: 1, name: "Yoni Goldberg", vip: false }, { id: 2, name: "John Doe", vip: true }]; + + // Act + const { getAllByTestId } = render(); + + // Assert - Mix UI & data in assertion + expect(getAllByTestId("user")).toEqual('[
  • John Doe
  • ]'); +}); +``` + +
    + +

    + +## âšȘ  3.2 Query les Ă©lĂ©ments HTML en te basant sur des attributs qui ont peu de chance de changer + +:white_check_mark: **À faire:** Query les Ă©lĂ©ments HTML en te basant sur des attributs qui ont de grandes chances de survivre Ă  un changement graphique, contrairement aux sĂ©lecteurs CSS ou aux labels des forms. Si l'Ă©lĂ©ment n'as pas d'attribut de ce type, crĂ©e un attribut dĂ©diĂ© au test comme 'test-id-submit-sutton'. Utiliser cette mĂ©thode permet non seulement d'ĂȘtre sĂ»r que vos tests fonctionnels/logique ne cassent pas Ă  cause d'un changement visuel mais il devient Ă©galement plus clair pour toute votre Ă©quipe que cet Ă©lĂ©ment et son attribut sont utilisĂ©s par les tests et ne devraient pas ĂȘtre supprimĂ©s. +
    + +❌ **Autrement:** Tu veux tester la fonctionnalitĂ© de connexion qui couvre de nombreux composants, logiques et services, tout est configurĂ© parfaitement - subs, spies, les appels Ajax sont isolĂ©s. Tout semble parfait. Puis le test Ă©choue car le designer Ă  changĂ© la class CSS du div de 'thick-border' Ă  'thin-border' + +
    + +
    ✏ Exemple de code + +
    + +### :clap: Bien faire les choses, exemple: Query un Ă©lĂ©ment en utilisant un attribut dĂ©diĂ© aux tests + +![](https://img.shields.io/badge/🔧%20Example%20using%20React-blue.svg "Examples with React") + +```html +// the markup code (part of React component) +

    + + {value} + + +

    +``` + +```javascript +// this example is using react-testing-library +test("Whenever no data is passed to metric, show 0 as default", () => { + // Arrange + const metricValue = undefined; + + // Act + const { getByTestId } = render(); + + expect(getByTestId("errorsLabel").text()).toBe("0"); +}); +``` + +
    + +### :thumbsdown: Exemple d'anti pattern: Compter sur les attributs CSS + +```html + +{value} + +``` + +```javascript +// this exammple is using enzyme +test("Whenever no data is passed, error metric shows zero", () => { + // ... + + expect(wrapper.find("[className='d-flex-column']").text()).toBe("0"); +}); +``` + +
    + +
    + +## âšȘ  3.3 Lorsque c'est possible, tester avec un composant rĂ©aliste et totalement rendu + +:white_check_mark: **Do:** Lorsqu'ils sont de taille raisonnable, tests tes composants de l'extĂ©rieur comme le font tes utilisateurs, rend complĂštement l'UI, agit dessus, et vĂ©rifie que l'UI rendu se comporte comme on l'attend. +Évite toute sorte de mocking, de rendu partiels ou superficiel - cette approche peut rĂ©sulter en bugs non dĂ©tectĂ©s Ă  cause du manque de dĂ©tails et rendre plus difficile la maintenance des tests puisque les tests modifient les propriĂ©tĂ©s interne (voir le point 'PrivilĂ©gier les tests blackbox'). Si l'un des composants enfants ralentis significativement (e.g animations) ou complique la configuration - considĂšre de le remplacer explicitement avec un faux. + +Maintenant que c'est dit, une mise en garde s'impose: cette technique fonctionne pour des petit/moyens composants qui ont un nombre raisonnable de composants enfants. Rendre complĂštement un composant avec trop d'enfants compliquera le raisonnement Ă  propos des Ă©checs de tests (analyse de la cause originelle) et peut ĂȘtre trop lent. Dans ces cas, Ă©crit seulement quelques tests pour ce parent, et plus de tests pour ses enfants. + +
    + +❌ **Autrement:** Lorsque tu fouilles dans les dĂ©tails internes du composant en invoquant ses mĂ©thodes privĂ©es, et en vĂ©rifiant l'Ă©tat interne - tu devras refactoriser tous les tests lorsque tu refactorisera l'implĂ©mentation du composant. Est-ce que tu as vraiment la capacitĂ© de tenir ce niveau de maintenance ? + +
    + +
    ✏ Exemple de code + +
    + +### :clap: Bien faire les choses, exemple: Travailler de façon rĂ©aliste sur un composant complĂštement rendu + +![](https://img.shields.io/badge/🔧%20Example%20using%20React-blue.svg "Examples with React") ![](https://img.shields.io/badge/🔧%20Example%20using%20Enzyme-blue.svg "Examples with Enzyme") + +```javascript +class Calendar extends React.Component { + static defaultProps = { showFilters: false }; + + render() { + return ( +
    + A filters panel with a button to hide/show filters + +
    + ); + } +} + +//Examples use React & Enzyme +test("Realistic approach: When clicked to show filters, filters are displayed", () => { + // Arrange + const wrapper = mount(); + + // Act + wrapper.find("button").simulate("click"); + + // Assert + expect(wrapper.text().includes("Choose Filter")); + // This is how the user will approach this element: by text +}); +``` + +### :thumbsdown: Exemple d'anti pattern: Mocker la réalité avec un rendu superficiel + +```javascript +test("Shallow/mocked approach: When clicked to show filters, filters are displayed", () => { + // Arrange + const wrapper = shallow(); + + // Act + wrapper + .find("filtersPanel") + .instance() + .showFilters(); + // Tap into the internals, bypass the UI and invoke a method. White-box approach + + // Assert + expect(wrapper.find("Filter").props()).toEqual({ title: "Choose Filter" }); + // what if we change the prop name or don't pass anything relevant? +}); +``` + +
    + +
    + +## âšȘ  3.4 Ne pas attendre, utiliser la gestion des Ă©vĂšnements asynchrone implĂ©mentĂ© dans les frameworks. Essayer aussi d'accĂ©lĂ©rer les choses + +:white_check_mark: **À faire:** Souvent, le temps de complĂ©tion de l'unitĂ© qu'on test est inconnu (e.g animations qui suspendent l'apparition d'Ă©lĂ©ments) - Dans ce cas, Ă©vite d'attendre (e.g. setTimeOut ) et prĂ©fĂšre des mĂ©thodes dĂ©terministe que la plupart des frameworks fournissent. Certaines librairies permettent d'attendre certaines opĂ©rations (e.g. [Cypress cy.request('url')](https://docs.cypress.io/guides/references/best-practices.html#Unnecessary-Waiting)), d'autres fournissent une API pour attendre comme [@testing-library/dom method wait(expect(element))](https://testing-library.com/docs/guide-disappearance). +Parfois il est plus Ă©lĂ©gant de stub la ressource lente, comme une API, une fois que le moment de rĂ©ponse devient dĂ©terminĂ©, le composant peut ĂȘtre re-rendu explicitement. Lorsque l'on dĂ©pend d'un composant externe qui attend, il peut ĂȘtre utile d'[accĂ©lĂ©rer l'horloge](https://jestjs.io/docs/en/timer-mocks). +Attendre est un pattern Ă  Ă©viter puisqu'il force tes tests Ă  ĂȘtre lent ou risquĂ© ( s'ils n'attendent pas assez longtemps ). À chaque fois qu'attendre ou requĂȘter sont inĂ©vitable et qu'il n'y a pas de support de la part du framework de test, des librairies comme [wait-for-expect](https://www.npmjs.com/package/wait-for-expect) peuvent aider avec une solution demi-dĂ©terministe. +
    + +❌ **Autrement:** En attendant pour un long moment, les tests seront plus lent. En attendant trop peu, les tests Ă©choueront si l'unitĂ© testĂ©e n'a pas rĂ©pondu dans les temps. Cela se rĂ©sume donc Ă  un compromis entre l'instabilitĂ© et les mauvaises performances. + +
    + +
    ✏ Exemple de code + +
    + +### :clap: Bien faire les choses, exemple: E2E API qui se rĂ©soud uniquement lorsque l'opĂ©ration asynchrone est terminĂ©e (Cypress) + +![](https://img.shields.io/badge/🔹%20Example%20using%20Cypress-blue.svg "Using Cypress to illustrate the idea") +![](https://img.shields.io/badge/🔧%20Example%20using%20React%20Testing%20Library-blue.svg "Examples with react-testing-library") + +```javascript +// using Cypress +cy.get("#show-products").click(); // navigate +cy.wait("@products"); // wait for route to appear +// this line will get executed only when the route is ready +``` + +### :clap: Bien faire les choses, exemple: Librairie de tests qui attend les Ă©lĂ©ments du DOM + +```javascript +// @testing-library/dom +test("movie title appears", async () => { + // element is initially not present... + + // wait for appearance + await wait(() => { + expect(getByText("the lion king")).toBeInTheDocument(); + }); + + // wait for appearance and return the element + const movie = await waitForElement(() => getByText("the lion king")); +}); +``` + +### :thumbsdown: Exemple d'anti pattern: Code custom qui attend + +```javascript +test("movie title appears", async () => { + // element is initially not present... + + // custom wait logic (caution: simplistic, no timeout) + const interval = setInterval(() => { + const found = getByText("the lion king"); + if (found) { + clearInterval(interval); + expect(getByText("the lion king")).toBeInTheDocument(); + } + }, 100); + + // wait for appearance and return the element + const movie = await waitForElement(() => getByText("the lion king")); +}); +``` + +
    + +
    + +## âšȘ  3.5 Regarder comment le contenu est servi sur le rĂ©seau + +![](https://img.shields.io/badge/🔧%20Example%20using%20Google%20LightHouse-blue.svg "Examples with Lighthouse") + +✅ **À faire:** Applique un monitoring active qui s'assure que le chargement de la page sur un vrai rĂ©seau est optimisĂ© - ça inclue les questions UX comme un chargement lent ou un bundle non minifiĂ©. Le marchĂ© des outils d'inspection n'est pas petit: des outils basiques comme pingdom](https://www.pingdom.com/), AWS CloudWatch, [gcp StackDriver](https://cloud.google.com/monitoring/uptime-checks/) peuvent ĂȘtre configurĂ© rapidement pour vĂ©rifier sur le server est disponible et rĂ©pond sous un dĂ©lai raisonnable. Cela ne fait qu'effleurer la surface de ce qui pourrait aller mal, il est donc prĂ©fĂ©rable de choisir des outils spĂ©cialisĂ©s pour le frontend (e.g [lighthouse](https://developers.google.com/web/tools/lighthouse/), [pagespeed](https://developers.google.com/speed/pagespeed/insights/)) et d'effectuer une analyse plus complĂšte. L'attention doit ĂȘtre portĂ©e sur les symptĂŽmes, les mĂ©triques qui affectent directement l'expĂ©rience utilisateur, comme le temps de chargement d'une page, [meaningful paint](https://scotch.io/courses/10-web-performance-audit-tips-for-your-next-billion-users-in-2018/fmp-first-meaningful-paint), [le temps jusqu'Ă  ce que la page devienne intĂ©ractive (TTI)](https://calibreapp.com/blog/time-to-interactive/). En plus de ça, on peut Ă©galement surveiller les causes techniques, comme s'assurer que le contenu est complet, le temps jusqu'au premier byte, l'optimisation des images, s'assurer d'une taille de DOM raisonnable, SSL et autres. Il est recommandable d'avoir ces monitorings complets Ă  la fois pendant le dĂ©veloppement, dans le processus CI et surtout - 24h/24 7j/7 sur les serveurs/CDN de production +
    + +❌ **Autrement:** Il doit ĂȘtre dĂ©cevant de se rendre compte qu'aprĂšs tout le soin apportĂ© Ă  la crĂ©ation d'une interface utilisateur, des tests 100% fonctionnels rĂ©ussis et des bundles sophistiquĂ© - l'expĂ©rience utilisateur est horrible et lente Ă  cause d'une mauvaise configuration du CDN. + +
    + +
    ✏ Exemple de code + +### :clap: Bien faire les choses, exemple: Rapport d'inspection du temps de chargement avec Lighthouse + +![](/assets/lighthouse2.png "Lighthouse page load inspection report") + +
    + +
    + +## âšȘ  3.6 Stub les ressources lente ou incertaine comme l'API backend + +:white_check_mark: **À faire:** Lorsque tu codes tes tests mainstream ( pas les tests E2E ), Ă©vite d'impliquer toute ressource qui n'est pas sous ta responsabilitĂ© et sous ton contrĂŽle comme l'API et utilise des stubs Ă  la place (i.e. test double). En pratique, Ă  la place de vrais appels Ă  une API, utilise une librairie de tests double ( comme [Sinon](https://sinonjs.org/), [Test doubles](https://www.npmjs.com/package/testdouble), etc) pour simuler la rĂ©ponse. L'avantage principal est d'Ă©viter les comportements incertains - les APIs de tests ou de staging par dĂ©finition ne sont pas toujours stable et de temps en temps peuvent faire Ă©chouer tes tests mĂȘme si ton composant se comporte bien (l'environnement de production n'a pas Ă©tĂ© fait pour les tests et limite gĂ©nĂ©ralement les requĂȘtes). Faire ça permettra de simuler plusieurs comportements d'API qui devrait diriger le comportement de ton composant, comme lorsqu'aucune donnĂ©e n'est trouvĂ©e ou que l'API Ă©met une erreur. Enfin et surtout, les appels rĂ©seau vont Ă©normĂ©ment ralentir les tests. +
    + +❌ **Autrement:** Le test moyen ne tourne pas plus de quelques ms, un API call moyen dure environ 100ms. Cela rend les tests ~20x plus lent. +
    + +
    ✏ Exemple de code + +
    + +### :clap: Bien faire les choses, exemple: Stub ou intercepter les appels API +![](https://img.shields.io/badge/🔧%20Example%20using%20React-blue.svg "Examples with React") ![](https://img.shields.io/badge/🔧%20Example%20using%20React%20Testing%20Library-blue.svg "Examples with react-testing-library") + +```javascript +// unit under test +export default function ProductsList() { + const [products, setProducts] = useState(false); + + const fetchProducts = async () => { + const products = await axios.get("api/products"); + setProducts(products); + }; + + useEffect(() => { + fetchProducts(); + }, []); + + return products ?
    {products}
    :
    No products
    ; +} + +// test +test("When no products exist, show the appropriate message", () => { + // Arrange + nock("api") + .get(`/products`) + .reply(404); + + // Act + const { getByTestId } = render(); + + // Assert + expect(getByTestId("no-products-message")).toBeTruthy(); +}); +``` + +
    + +
    + +## âšȘ  3.7 Avoir quelques tests end-to-end qui lancent le systĂšme entier + +:white_check_mark: **À faire:** MĂȘme si E2E (end-to-end) veut gĂ©nĂ©ralement dire test UI avec un vrai navigateur (voir point 3.6), pour d'autre ils signifient des tests qui englobent le systĂšme entier, en incluant le vrai backend. Ce type de tests a beaucoup de valeurs puisqu'ils couvrent les erreurs d'intĂ©grations entre le frontend et le backend Ă  cause d'une mauvaise comprĂ©hension des schĂ©mas d'Ă©changes. Ils sont aussi un moyen efficace de dĂ©couvrir des erreurs d'intĂ©grations entre backends (e.g. le microservice A qui envoie le mauvais message au microservice B) et mĂȘme de dĂ©tecter des erreurs de dĂ©ploiement - Il n'y a pas de framework backend pour les tests E2E qui soit aussi simple et mature que les frameworks UI comme [Cypress](https://www.cypress.io/) and [Puppeteer](https://github.com/GoogleChrome/puppeteer). Le point nĂ©gatif de ces tests, c'est le haut cout de configuration pour un environnement avec autant de composants, et surtout leur fragilitĂ© - avec 50 microservices, mĂȘme si un seul Ă©choue l'ensemble du test E2E Ă©choue. Pour cette raison, cette technique doit ĂȘtre utilisĂ©e avec parcimonie, il ne faut pas avoir plus de 1-10 tests de ce type. Ceci dit, mĂȘme un petit nombre de tests E2E sont susceptibles de dĂ©tecter les problĂšmes pour lesquels ils sont en place - les dĂ©fauts de dĂ©ploiement et d'intĂ©gration. Il est conseillĂ© de les exĂ©cuter sur un environnement de prĂ©-production. + +
    + +❌ **Autrement:** L'UI peut investir beaucoup en testant ces fonctionnalitĂ©s seulement pour rĂ©aliser que les donnĂ©es retournĂ©es par le backend sont diffĂ©rentes de ce qui Ă©tait attendu +
    + +## âšȘ  3.8 AccĂ©lĂ©rer les tests E2E en rĂ©utilisant les informations d'authentification + +:white_check_mark: **Ă  faire:** Dans des tests E2E qui incluent un vrai backend et utilisent un token utilisateur valide pour les appels API, ce n'est pas rentable d'isoler les tests Ă  un niveau ou l'utilisateur est créé et authentifiĂ© Ă  chaque requete. À la place, authentifie l'utilisateur une seule fois avant que l'exĂ©cution des tests commence (i.e before-all hook), enregistre le token en local et rĂ©utilise le dans les requetes. Ça semble violer un des principes de test principal - garder les tests autonomes sans associer les ressources. MĂȘme si c'est une inquiĂ©tude valide, dans les tests E2E la performance est une inquiĂ©tude clĂ© et crĂ©er 1-3 requĂȘtes API avant chaque test peut mener a un temps d'execution horrible. RĂ©utiliser les informations d'authentification ne veut pas dire que les tests doivent agir sur la mĂȘme entrĂ©e utilisateur - si le test compte sur les entrĂ©es utilisateur (e.g. test l'historique de paiement d'un utilisateur) alors assure toi de gĂ©nĂ©rer ces entrĂ©es dans le test et Ă©vite de les partager avec d'autres tests. Rappelle-toi aussi que le backend peut ĂȘtre simulĂ© - Si les tests se concentrent sur le frontend, il vaut mieux les isoler et simuler l'API backend (voir point 3.6). +
    + +❌ **Autrement:** Si on prend 200 cas de tests et qu'on estime l'authentification à 100ms = 20 secondes simplement pour s'authentifier encore et encore + +
    + +
    ✏ Exemple de code + +
    + +### :clap: Bien faire les choses, exemple: Se connecter dans le before-all pas dans le before-each +![](https://img.shields.io/badge/🔹%20Example%20using%20Cypress-blue.svg "Using Cypress to illustrate the idea") + +```javascript +let authenticationToken; + +// happens before ALL tests run +before(() => { + cy.request('POST', 'http://localhost:3000/login', { + username: Cypress.env('username'), + password: Cypress.env('password'), + }) + .its('body') + .then((responseFromLogin) => { + authenticationToken = responseFromLogin.token; + }) +}) + +// happens before EACH test +beforeEach(setUser => () { + cy.visit('/home', { + onBeforeLoad (win) { + win.localStorage.setItem('token', JSON.stringify(authenticationToken)) + }, + }) +}) + +``` + +
    + +
    + +## âšȘ  3.9 Avoir un test E2E qui parcours juste les pages du site + +:white_check_mark: **À faire:** Pour le suivi de production et la vĂ©rification pendant le dĂ©veloppement, lance un seul test E2E qui visite toute ou la plupart des pages du site et vĂ©rifie qu'aucune n'Ă©choue. Ce type de test apporte un bon retour sur investissement puisqu'il est trĂšs simple Ă  Ă©crire et maintenir, mais peut dĂ©tecter tout type d'erreur en incluant les problĂšmes fonctionnels, de rĂ©seau ou de dĂ©ploiement. Les autres types de smoke test et sanity check ne sont pas aussi fiable et exhaustifs - certaines Ă©quipes opĂ©rationnelles ne font que ping la page d'accueil (production) ou les dĂ©veloppeurs qui lancent plusieurs tests d'intĂ©grations qui ne rĂ©vĂšlent pas les problĂšmes de packaging ou liĂ©s au navigateur. Il est Ă©vident que ce smoke test ne remplace pas les tests fonctionnels, mais il sert Ă  dĂ©tecter rapidement les problĂšmes. + +
    + +❌ **Autrement:** Tout peut sembler parfait, tous les tests passent, le health-check de production est Ă©galement positif, mais le composant de paiement a eu des erreurs de packaging et seul la route /payment ne s'affiche pas + +
    + +
    ✏ Exemple de code + +
    + +### :clap: Bien faire les choses, exemple: Smoke test qui parcours toute les pages + +![](https://img.shields.io/badge/🔹%20Example%20using%20Cypress-blue.svg "Using Cypress to illustrate the idea") + +```javascript +it("When doing smoke testing over all page, should load them all successfully", () => { + // exemplified using Cypress but can be implemented easily + // using any E2E suite + cy.visit("https://mysite.com/home"); + cy.contains("Home"); + cy.contains("https://mysite.com/Login"); + cy.contains("Login"); + cy.contains("https://mysite.com/About"); + cy.contains("About"); +}); +``` + +
    + +
    + +## âšȘ  3.10 Exposer les tests comme un document collaboratif + +:white_check_mark: **À faire:** En plus d'amĂ©liorer la stabilitĂ© de l'application, les tests apportent une autre opportunitĂ© intĂ©ressante - ils servent comment une documentation de l'app. +Puisque les tests parlent naturellement Ă  un niveau moins technique avec un langage plus produit/UX, en utilisant les bons outils, ils peuvent ĂȘtre utilisĂ©s comme un outil de communication qui aligne toute l'Ă©quipe - les dĂ©veloppeurs et les clients. +Par exemple, certains frameworks permettent d'exprimer les parcours et les attentes (i.e les plans de tests) en utilisant un langage lisible par l'humain, donc chaque personne impliquĂ©e, y compris les product manager, peuvent lire, approuver et communiquer sur les tests qui sont juste devenu le document de spĂ©cification. Cette technique s'appelle aussi 'test d'acceptance' puisqu'il permet au client de dĂ©finir ses critĂšres de validitĂ© en langage simple. Il s'agit de [BDD (behavior-driven testing)](https://en.wikipedia.org/wiki/Behavior-driven_development) dans sa forme la plus pure. L'un des frameworks populaire qui permet ça est [Cucumber qui a un goĂ»t de Javascript](https://github.com/cucumber/cucumber-js), voir l'exemple ci-dessous. Une autre opportunitĂ© similaire, [StoryBook](https://storybook.js.org/) permet d'exposer les composants UI comme un catalogue graphique dans lequel on peut se promener Ă  travers les diffĂ©rents Ă©tats de chaque composant (e.g afficher une grille avec ou sans filtre, l'afficher avec plusieurs lignes ou aucune, etc), voir Ă  quoi il ressemble, et comment dĂ©clencher cet Ă©tat - cela peut servir aux Ă©quipe produit mais sert surtout de documentation aux dĂ©veloppeurs qui utilisent ces composants + +❌ **Autrement:** AprĂšs avoir investi des ressources dans les tests, ce serait juste dommage de ne pas se servir de cet investissement pour apporter encore plus de valeur + +
    + +
    ✏ Exemple de code + +
    + +### :clap: Bien faire les choses, exemple: DĂ©crire les tests dans un language humain avec cucumber-js +![](https://img.shields.io/badge/🔹%20Example%20using%20Cucumber-blue.svg "Examples using Cucumber") + +```javascript +// this is how one can describe tests using cucumber: plain language that allows anyone to understand and collaborate + +Feature: Twitter new tweet + + I want to tweet something in Twitter + + @focus + Scenario: Tweeting from the home page + Given I open Twitter home + Given I click on "New tweet" button + Given I type "Hello followers!" in the textbox + Given I click on "Submit" button + Then I see message "Tweet saved" + +``` + +### :clap: Bien faire les choses, exemple: Visualiser nos composants, leurs Ă©tats et entrĂ©es en utilisant Storybook +![](https://img.shields.io/badge/🔹%20Example%20using%20StoryBook-blue.svg "Using StoryBook") + +![alt text](assets/story-book.jpg "Storybook") + +
    + +

    + +## âšȘ  3.11 DĂ©tecter les problĂšmes visuels avec des outils automatisĂ©s + +:white_check_mark: **À faire:** Configure des outils automatisĂ©s pour capturer des screenshots de l'UI quand des changements sont prĂ©sentĂ©s et dĂ©tecter les problĂšmes visuels comme du contenu qui se superpose ou qui est cassĂ©. Cela permet de vĂ©rifier que non seulement les bonnes donnĂ©es sont prĂ©sente mais Ă©galement que l'utilisateur peut les voir correctement. Cette technique n'est pas trĂšs courante, notre Ă©tat d'esprit quand on pense aux tests est tournĂ© sur les tests fonctionnels mais c'est le visuel que l'utilisateur expĂ©rimente et avec le nombre d'appareils diffĂ©rents disponible il est simple de rater un bug UI important. Certains outils gratuits procurent les bases - gĂ©nĂ©rer et enregistrer les screenshots pour qu'ils soient inspectĂ©s par un humain. MĂȘme si cette approche peut ĂȘtre suffisante pour de petites apps, son dĂ©faut comme tout test manuel est qu'il demande une intervention humaine Ă  chaque fois que quelque chose change. D'un autre cĂŽtĂ©, il est assez difficile de dĂ©tecter des problĂšmes UI automatiquement Ă  cause du manque de dĂ©finition claire - C'est ici que le domaine de 'Visual Regression' entre en jeu et rĂ©sout ce problĂšme en comparant d'ancienne UI avec les changements les plus rĂ©cent pour dĂ©tecter les diffĂ©rences. Certains outils gratuits peuvent fournir certaines de ces fonctionnalitĂ©s (e.g [wraith](https://github.com/BBC-News/wraith), [PhantomCSS](<[https://github.com/HuddleEng/PhantomCSS](https://github.com/HuddleEng/PhantomCSS)>)) mais peuvent demander un temps de configuration considĂ©rable. Les outils commerciaux (e.g. [Applitools](https://applitools.com/), [Percy.io](https://percy.io/)) vont un peu plus loin en simplifiant l'installation et en apportant des fonctionnalitĂ©s avancĂ©s comme la gestion de l'UI, des alertes, de la capture automatique en Ă©liminant le "bruit visuel" (e.g. pubs, animations) et mĂȘme l'analyse du changement DOM/CSS qui a provoquĂ© ce problĂšme. + +
    + +❌ **Autrement:** Quelle est la qualitĂ© d'une page qui affiche un bon contenu (100% des tests passent), charge instantanĂ©ment mais dont la moitiĂ© du contenu est cachĂ©e ? + +
    + +
    ✏ Exemple de code + +
    + +### :thumbsdown: Exemple d'anti pattern: Une régression visuelle typique - le bon contenu qui est mal servi + +![alt text](assets/amazon-visual-regression.jpeg "Amazon page breaks") + +
    + +### :clap: Bien faire les choses, exemple: Configurer wraith pour capturer et comparer les snapshots de l'UI + +![](https://img.shields.io/badge/🔹%20Example%20using%20Wraith-blue.svg "Using Wraith") + +``` +​# Add as many domains as necessary. Key will act as a label​ + +domains: + english: "http://www.mysite.com"​ + +​# Type screen widths below, here are a couple of examples​ + +screen_widths: + + - 600​ + - 768​ + - 1024​ + - 1280​ + +​# Type page URL paths below, here are a couple of examples​ +paths: + about: + path: /about + selector: '.about'​ + subscribe: + selector: '.subscribe'​ + path: /subscribe +``` + +### :clap: Bien faire les choses, exemple: Utiliser Applitools pour obtenir des comparaisons de snapshots et d'autres fonctionnalitĂ©s avancĂ©es + +![](https://img.shields.io/badge/🔹%20Example%20using%20AppliTools-blue.svg "Using Applitools") ![](https://img.shields.io/badge/🔹%20Example%20using%20Cypress-blue.svg "Using Cypress to illustrate the idea") + +```javascript +import * as todoPage from "../page-objects/todo-page"; + +describe("visual validation", () => { + before(() => todoPage.navigate()); + beforeEach(() => cy.eyesOpen({ appName: "TAU TodoMVC" })); + afterEach(() => cy.eyesClose()); + + it("should look good", () => { + cy.eyesCheckWindow("empty todo list"); + todoPage.addTodo("Clean room"); + todoPage.addTodo("Learn javascript"); + cy.eyesCheckWindow("two todos"); + todoPage.toggleTodo(0); + cy.eyesCheckWindow("mark as completed"); + }); +}); +``` + +
    + +

    + +# Section 4ïžâƒŁ: Mesurer l'efficacitĂ© des tests + +

    + +## âšȘ  4.1 Avoir assez de couverture pour ĂȘtre confiant, ~80% semble ĂȘtre le nombre magique + +:white_check_mark: **À faire:** Le but des tests est d'ĂȘtre assez confiant pour avancer rapidement, Ă©videmment, plus le code est testĂ© plus l'Ă©quipe peut ĂȘtre confiante. La couverture (coverage) est une mesure du nombre de lignes de code (et branches, statements, etc) sont atteint par les tests. À partir de quand est-ce suffisant ? 10-30% est Ă©videmment trop bas pour avoir la moindre idĂ©e de la validitĂ© du build, d'un autre cĂŽtĂ© 100% est vraiment coĂ»teux et peut dĂ©vier votre intĂ©rĂȘt des parties importantes pour des coins exotiques du code. La rĂ©ponse longue est que ça dĂ©pend de plusieurs facteurs comme le type de l'application - si tu construis la prochaine gĂ©nĂ©ration d'Airbus A380 alors 100% est obligatoire, pour un site d'image de dessin animĂ© 50% peut ĂȘtre dĂ©jĂ  trop. MĂȘme si la plupart des amateurs de tests assurent que le bon seuil dĂ©pend du contexte, la plupart d'entre eux mentionnent Ă©galement le nombre 80% est une bonne rĂšgle gĂ©nĂ©rale ([Fowler: “in the upper 80s or 90s”](https://martinfowler.com/bliki/TestCoverage.html)) qui devrait rĂ©pondre au besoin de la plupart des applications. + +Conseil d'implĂ©mentation: Tu peux vouloir configurer ton intĂ©gration continue pour qu'elle ait un seuil de couverture ([Jest link](https://jestjs.io/docs/en/configuration.html#collectcoverage-boolean)) et arrĂȘter les builds qui ne rĂ©pondent pas Ă  ce standard (il est Ă©galement possible de configurer un seuil par composant, voir l'exemple ci-dessous). En plus de ça, envisage de dĂ©tecter les baisses de couverture (quand un nouveau commit Ă  une couverture infĂ©rieure) - cela poussera les dĂ©veloppeurs Ă  augmenter ou au moins Ă  conserver la mĂȘme quantitĂ© de code testĂ©. Maintenant que c'est dit, la couverture n'est qu'une mesure, une quantitative, ce n'est pas assez pour dire si vos tests sont robustes. Et il peut aussi ĂȘtre biaisĂ© comme on le verra dans le point suivant + +
    + +❌ **Autrement:** La confiance et les nombres vont ensemble, sans vraiment savoir si tu testes la majoritĂ© du systĂšme, il y aura de la peur, et la peur va te ralentir + +
    + +
    ✏ Exemple de code + +
    + +### :clap: Exemple: Un rapport de couverture classique + +![alt text](assets/bp-18-yoni-goldberg-code-coverage.png "A typical coverage report") + +
    + +### :clap: Bien faire les choses, exemple: Configurer la couverture par composant (avec Jest) + +![](https://img.shields.io/badge/🔹%20Example%20using%20Jest-blue.svg "Using Jest") + +![alt text](assets/bp-18-code-coverage2.jpeg "Setting up coverage per component (using Jest)") + +
    + +

    + +## âšȘ  4.2 Inspecter les rapports de couverture pour dĂ©tecter les sections qui ne sont pas testĂ©es et autres bizarreries + +:white_check_mark: **À faire:** Certains problĂšmes passent juste sous le radar et sont difficiles Ă  dĂ©tecter en utilisant des outils traditionnels. Ce ne sont pas vraiment des bugs mais plutĂŽt des comportement surprenants de l'application qui peuvent avoir un impact important. Par exemple, souvent certaines parties du code sont rarement voir jamais invoquĂ©es - tu penses que la classe 'PricingCalculator' s'occupe toujours de dĂ©terminer le prix du produit mais il se trouve qu'elle n'est jamais invoquĂ©e alors qu'on a plus de 10000 produits en base de donnĂ©es et de nombreuses ventes ... Les rapports de couvertures t'aident Ă  dĂ©terminer si l'application se comporte comme tu penses qu'elle le fait. En plus de ça, ils peuvent aussi montrer le type de code qui n'est pas testĂ© - Être informĂ© que 80% du code est testĂ© n'indique pas si les parties critiques sont couvertes. GĂ©nĂ©rer des rapports est simple - lance juste ton application en production ou pendant les tests avec le tracking de couverture activĂ© et rĂ©cupĂšre des rapports qui montrent Ă  quelle frĂ©quence chaque partie du code est invoquĂ©e. Si tu prends ton temps pour regarder ces donnĂ©es, tu pourras trouver des piĂšges +
    + +❌ **Autrement:** Si tu ne sais pas quelles parties du code ne sont pas couvertes par les tests, tu ne sais pas d'oĂč peuvent venir les problĂšmes + +
    + +
    ✏ Exemple de code + +
    + +### :thumbsdown: Exemple d'anti pattern: Qu'est-ce qui ne va pas dans ce rapport de couverture ? + +BasĂ© sur un scĂ©nario rĂ©el, oĂč nous avont trackĂ© l'usage de notre application en QA et detectĂ© un pattern intĂ©ressant sur l'authentification (indice: la quantitĂ© d'erreur de connexion n'est pas proportionnelle, quelque chose ne va pas. Finalement il s'est avĂ©rĂ© qu'un bug front-end n'arrĂ©tait pas d'appeler l'API d'authentification) + +![alt text](assets/bp-19-coverage-yoni-goldberg-nodejs-consultant.png "What’s wrong with this coverage report?") + +
    + +

    + +## âšȘ  4.3 Mesurer la couverture logique en utilisant les tests de mutations + +:white_check_mark: **À faire:** Les donnĂ©es de couverture traditionnelles mentent souvent: elles peuvent montrer 100% de couverture, mais aucune de tes fonctions, pas mĂȘme une seule, ne retourne la bonne rĂ©ponse. Pourquoi ? Il mesure simplement le nombre de lignes de code que les tests ont visitĂ©es, mais ils ne vĂ©rifient pas si les tests ont effectivement testĂ© quelque chose et vĂ©rifiĂ© la rĂ©ponse. Comme quelqu'un qui effectuerai un voyage d'affaires et qui montre les tampons sur son passeport - Cela ne prouve pas qu'il a travaillĂ©, seulement qu'il a visitĂ© quelques aĂ©roports et hĂŽtels. + +Les tests de mutations sont lĂ  pour aider Ă  mesurer la quantitĂ© de code qui a effectivement Ă©tĂ© TESTÉ et pas juste VISITÉ. [Stryker](https://stryker-mutator.io/) est une librairie Javascript pour les tests de mutation et son implĂ©mentation est trĂšs soignĂ©e : + +(1) Il change volontairement le code et "implante des bugs". Par exemple, le code newOrder.price === 0 devient newOrder.price != 0. Ces "bugs" sont appelĂ©s des mutations + +(2) Il lance les tests, si tous rĂ©ussissent, alors on a un problĂšme - Les tests n'ont pas remplis leur rĂŽle en dĂ©couvrant les bugs, les mutations sont dites survivantes. Si les tests Ă©chouent, c'est bon, les mutations ont Ă©tĂ© tuĂ©es. + +Savoir que toute ou la plupart des mutations ont Ă©tĂ© tuĂ©s donne une meilleure confiance qu'un rapport de couverture traditionnel et le temps de configuration est similaire. +
    + +❌ **Autrement:** Tu seras dupĂ© en croyant que 85% de couverture de code signifie que tes tests dĂ©tecteront les bugs dans 85% du code + +
    + +
    ✏ Exemple de code + +
    + +### :thumbsdown: Exemple d'anti pattern: 100% de couverture, 0% testĂ© + +![](https://img.shields.io/badge/🔹%20Example%20using%20Stryker-blue.svg "Using Stryker") + +```javascript +function addNewOrder(newOrder) { + logger.log(`Adding new order ${newOrder}`); + DB.save(newOrder); + Mailer.sendMail(newOrder.assignee, `A new order was places ${newOrder}`); + + return { approved: true }; +} + +it("Test addNewOrder, don't use such test names", () => { + addNewOrder({ assignee: "John@mailer.com", price: 120 }); +}); //Triggers 100% code coverage, but it doesn't check anything +``` + +
    + +### :clap: Bien faire les choses, exemple: Un rapport Stryker, un outil pour les tests de mutations, qui détecte et compte la quantité de code qui n'est pas testé (Mutations) + +![alt text](assets/bp-20-yoni-goldberg-mutation-testing.jpeg "Stryker reports, a tool for mutation testing, detects and counts the amount of code that is not tested (Mutations)") + +
    + +

    + +## âšȘ 4.4 Éviter les problĂšmes dans le code de test avec les Test linters + +:white_check_mark: **À faire:** Un groupe de plugins ESLint ont Ă©tĂ© dĂ©veloppĂ©s spĂ©cifiquement pour inspecter le code de test et dĂ©tecter les problĂšmes. Par exemple, [eslint-plugin-mocha](https://www.npmjs.com/package/eslint-plugin-mocha) t'avertiras lorsqu'un test est Ă©crit Ă  un niveau global (pas un enfant d'une dĂ©claration describe()) ou lorsque les tests sont [sautĂ©s](https://mochajs.org/#inclusive-tests), ce qui peut conduire Ă  une fausse croyance que les tests passent. De façon similaire, [eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest) peut avertir lorsqu'un test n'a pas d'assertion (ne vĂ©rifie rien) +
    + +❌ **Autrement:** Voir 90% de couverture de code et 100% de tests verts fera apparaĂźtre un grand sourire sur ton visage, jusqu'Ă  ce que tu rĂ©alises que de nombreux tests ne vĂ©rifient rien et que plusieurs suites de tests ont Ă©tĂ© sautĂ©es. Avec un peu de chance, tu n'as rien dĂ©ployĂ© basĂ© sur de fausses observations + +
    +
    ✏ Exemple de code + +
    + +### :thumbsdown: Exemple d'anti pattern: Un cas de test plein d'erreur, heuresement toutes détectés par les Linters + +```javascript +describe("Too short description", () => { + const userToken = userService.getDefaultToken() // *error:no-setup-in-describe, use hooks (sparingly) instead + it("Some description", () => {});//* error: valid-test-description. Must include the word "Should" + at least 5 words +}); + +it.skip("Test name", () => {// *error:no-skipped-tests, error:error:no-global-tests. Put tests only under describe or suite + expect("somevalue"); // error:no-assert +}); + +it("Test name", () => {*//error:no-identical-title. Assign unique titles to tests +}); +``` + +
    + +

    + +# Section 5ïžâƒŁ: CI et autres mesures de qualitĂ© + +

    + +## âšȘ  5.1 Enrichir ses linter et annuler les builds qui ont des problĂšmes de lint + +:white_check_mark: **À faire:** Les linters sont un bonus gratuit, avec 5 minutes de configurations, tu as gratuitement un auto-pilote qui surveille ton code et repĂšre les problĂšmes pendant que tu tapes. Les jours oĂč les linters Ă©taient rĂ©servĂ©s Ă  l'esthĂ©tique sont terminĂ©s (pas de point-virgules!). De nos jours, les linters peuvent dĂ©tecter des problĂšmes sĂ©rieux comme des erreurs qui ne sont pas thrown correctement et les pertes d'informations. En plus de ta liste de rĂšgles basiques (like [ESLint standard](https://www.npmjs.com/package/eslint-plugin-standard) or [Airbnb style](https://www.npmjs.com/package/eslint-config-airbnb)), considĂšre d'ajouter des linters spĂ©cialisĂ©s comme [eslint-plugin-chai-expect](https://www.npmjs.com/package/eslint-plugin-chai-expect) qui peux dĂ©tecter les tests sans assertions, [eslint-plugin-promise](https://www.npmjs.com/package/eslint-plugin-promise?activeTab=readme) qui dĂ©tecte les promesses qui ne se resolvent pas (le code ne va jamais continuer), [eslint-plugin-security](https://www.npmjs.com/package/eslint-plugin-security?activeTab=readme) qui peut dĂ©couvrir les regex qui peuvent ĂȘtre utilisĂ© pour des attaques DOS, et [eslint-plugin-you-dont-need-lodash-underscore](https://www.npmjs.com/package/eslint-plugin-you-dont-need-lodash-underscore) qui est capable de t'indiquer lorsque le code utilise une mĂ©thode de librairie qui fait partie des mĂ©thodes du cƓur V8 comme Lodash.\_map(...) + +
    + +❌ **Autrement:** Imagine un jour de pluie ou ta production n'arrĂȘte pas de crasher mais les logs ne montrent aucune stack trace d'erreur. Qu'est-ce qu'il s'est passĂ© ? Ton code a malencontreusement Ă©mis un objet qui n'Ă©tait pas une erreur et la stack trace a Ă©tĂ© perdu, une bonne raison de se taper la tĂȘte contre les murs. 5 minutes de configuration d'un linter pourraient permettre de dĂ©tecter cette erreur et sauver la journĂ©e + +
    + +
    ✏ Exemple de code + +
    + +### :thumbsdown: Exemple d'anti pattern: Le mauvais objet d'erreur est émit par erreur, aucune stack-trace ne va apparaitre pour cette erreur. Heuresement, ESLint catch le prochain beug de production + +![alt text](assets/bp-21-yoni-goldberg-eslint.jpeg "The wrong Error object is thrown mistakenly, no stack-trace will appear for this error. Luckily, ESLint catches the next production bug") + +
    + +

    + +## âšȘ  5.2 Raccourcir la boucle de retours avec du CI local pour les dĂ©veloppeurs + +:white_check_mark: **À faire:** Tu utilises un outil de CI avec une bonne inspection de qualitĂ© comme des tests, du linting, des checks de vulnĂ©rabilitĂ©s, etc ? Aide les dĂ©veloppeurs Ă  lancer Ă©galement cette pipeline en local pour solliciter un retour instantanĂ© et raccourcir la [boucle de feedback](https://www.gocd.org/2016/03/15/are-you-ready-for-continuous-delivery-part-2-feedback-loops/). Pourquoi ? Un processus de tests efficace constitue de nombreuses boucles itĂ©ratives: (1) essai -> (2) retours -> (3) refactoriser. Plus le retour est rapide, plus le dĂ©veloppeur peut effectuer d'itĂ©rations d'amĂ©liorations par modules et perfectionner le rĂ©sultat. D'un autre cĂŽtĂ©, lorsque les retours sont lent Ă  arriver, moins d'amĂ©liorations peuvent ĂȘtre effectuĂ©es au sein d'une journĂ©e, l'Ă©quipe peut ĂȘtre dĂ©jĂ  passĂ©e Ă  un autre sujet/tache/module et peut ne pas ĂȘtre prĂȘte a affiner ce module. + +En pratique, certains fournisseurs de CI (Exemple: [CircleCI local CLI](https://circleci.com/docs/2.0/local-cli/)) autorisent le lancement de la pipeline en local. Certains outils commerciaux comme [wallaby fournissent des informations de valeur et des tests](https://wallabyjs.com/) pendant que le dĂ©veloppeur prototype (pas d'affiliation). Alternativement, tu peux simplement ajouter un script npm au package.json qui lance toute les commandes de qualitĂ©s (e.g. tests, lint, vulnĂ©rabilitĂ©s) - utilise des outils comme [concurrently](https://www.npmjs.com/package/concurrently) pour la parallĂ©lisation et des code de retour diffĂ©rents de 0 si l'un des outils Ă©choue. Maintenant le dĂ©veloppeur peut juste lancer une commande - e.g. 'npm run quality' - pour recevoir un retour instantanĂ©. Envisage Ă©galement d'annuler un commit si le contrĂŽle de qualitĂ© ne passe pas en utilisant githook ([husky can help](https://github.com/typicode/husky)) +
    + +❌ **Autrement:** Quand les rĂ©sultats de qualitĂ© arrivent le jour suivant le dĂ©veloppement, les tests ne sont pas une partie fluide du dĂ©veloppement mais plutĂŽt une Ă©tape formelle aprĂšs coup +
    + +
    ✏ Exemple de code + +
    + +### :clap: Bien faire les choses, exemple: Script npm qui effectue une inspection de la qualité du code, tout est lancé en parallÚle sur demande ou lorsque le développeur essaye de push du code + +```javascript +"scripts": { + "inspect:sanity-testing": "mocha **/**--test.js --grep \"sanity\"", + "inspect:lint": "eslint .", + "inspect:vulnerabilities": "npm audit", + "inspect:license": "license-checker --failOn GPLv2", + "inspect:complexity": "plato .", + + "inspect:all": "concurrently -c \"bgBlue.bold,bgMagenta.bold,yellow\" \"npm:inspect:quick-testing\" \"npm:inspect:lint\" \"npm:inspect:vulnerabilities\" \"npm:inspect:license\"" + }, + "husky": { + "hooks": { + "precommit": "npm run inspect:all", + "prepush": "npm run inspect:all" + } +} + +``` + +
    + +

    + +## âšȘ 5.3 Effectuer des tests e2e sur un vrai miroir de production + +:white_check_mark: **À faire:** Les tests end to end (E2E) sont le dĂ©fi principal de chaque pipeline CI - crĂ©er un miroir Ă©phĂ©mĂšre de la production Ă  la volĂ©e avec tous les services clouds liĂ© peut ĂȘtre fastidieux et coĂ»teux. Le jeu est de trouver le meilleur compromis: [Docker-compose](https://serverless.com/) permet de crĂ©er des environnement dockerisĂ©s isolĂ©s avec des containers identiques en utilisant un simple fichier text mais la technologie backend (e.g rĂ©seau, modĂšle de dĂ©ploiement) est diffĂ©rent de la vrai production. Tu peux l'associer Ă  [‘AWS Local’](https://github.com/localstack/localstack) pour travailler avec un stub des services AWS. Si tu es [serverless](https://serverless.com/), plusieurs frameworks comme serverless et [AWS SAM](https://docs.aws.amazon.com/lambda/latest/dg/serverless_app.html) permettent l'invocation locale de code FaaS. + +Le large Ă©cosystĂšme de Kubernetes doit encore formaliser un outil standard pour la mise en miroir locale et CI, bien que de nombreux nouveaux outils soient lancĂ©s frĂ©quemment. Une des approches est de lancer un 'minimized-Kubernetes' en utilisant des outils comme [Minikube](https://kubernetes.io/docs/setup/minikube/) et [MicroK8s](https://microk8s.io/). Une autre approche est de tester avec un 'vrai-Kebernetes' distant, certains fournisseurs CI (e.g. [Codefresh](https://codefresh.io/)) ont une intĂ©gration native avec l'environnement Kubernetes et rendent simple le lancement d'une pipeline CI sur le vrai environnement, d'autres permettent d'exĂ©cuter des scripts custom sur le Kubernetes distant. +
    + +❌ **Autrement:** Utiliser des technologies diffĂ©rentes pour la production et pour les tests demande de maintenir deux modĂšles de dĂ©ploiement et crĂ©er une sĂ©paration entre l'Ă©quipe dev et l'Ă©quipe ops. +
    + +
    ✏ Exemple de code + +
    + +### :clap: Exemple: Une pipeline CI qui génÚre un cluster Kubernetes à la volée ([Credit: Dynamic-environments Kubernetes](https://container-solutions.com/dynamic-environments-kubernetes/)) + +
    deploy:
    stage: deploy
    image: registry.gitlab.com/gitlab-examples/kubernetes-deploy
    script:
    - ./configureCluster.sh $KUBE_CA_PEM_FILE $KUBE_URL $KUBE_TOKEN
    - kubectl create ns $NAMESPACE
    - kubectl create secret -n $NAMESPACE docker-registry gitlab-registry --docker-server="$CI_REGISTRY" --docker-username="$CI_REGISTRY_USER" --docker-password="$CI_REGISTRY_PASSWORD" --docker-email="$GITLAB_USER_EMAIL"
    - mkdir .generated
    - echo "$CI_BUILD_REF_NAME-$CI_BUILD_REF"
    - sed -e "s/TAG/$CI_BUILD_REF_NAME-$CI_BUILD_REF/g" templates/deals.yaml | tee ".generated/deals.yaml"
    - kubectl apply --namespace $NAMESPACE -f .generated/deals.yaml
    - kubectl apply --namespace $NAMESPACE -f templates/my-sock-shop.yaml
    environment:
    name: test-for-ci
    + +
    + +

    + +## âšȘ 5.4 ParallĂ©liser l'exĂ©cution des tests + +:white_check_mark: **À faire:** Lorsque c'est fait correctement, les tests sont tes amis 24/7 en fournissant un retour quasi instantanĂ©. En pratique, exĂ©cuter 500 tests unitaires liĂ©s au processeur sur un seul thread peut prendre trop longtemps. Heureusement, les outils de tests et les plateformes CI moderne (comme [Jest](https://github.com/facebook/jest), [AVA](https://github.com/avajs/ava) et [Mocha extensions](https://github.com/yandex/mocha-parallel-tests)) peuvent parallĂ©liser les tests sur plusieurs processus et amĂ©liorer significativement le temps de retour. Certains fournisseurs CI font Ă©galement de la parallĂ©lisation de tests Ă  travers des containers (!) ce qui raccourcis encore plus la boucle de retour. Que ce soit localement sur plusieurs processus, ou sur un serveur Cloud avec plusieurs machines - parallĂ©liser demande de garder les tests autonomes puisqu'ils peuvent tourner sur diffĂ©rents processus. + +❌ **Autrement:** Obtenir les rĂ©sultats de tests 1h aprĂšs avoir publiĂ© du nouveau code, pendant que tu es dĂ©jĂ  en train de coder la fonctionnalitĂ© suivante, est une bonne recette pour rendre les tests moins pertinents +
    + +
    ✏ Exemple de code + +
    + +### :clap: Bien faire les choses, exemple: Mocha parallel & Jest distancent facilement le Mocha traditionnel grace à la parallélisation ([Credit: JavaScript Test-Runners Benchmark](https://medium.com/dailyjs/javascript-test-runners-benchmark-3a78d4117b4)) + +![alt text](assets/bp-24-yonigoldberg-jest-parallel.png "Mocha parallel & Jest easily outrun the traditional Mocha thanks to testing parallelization (Credit: JavaScript Test-Runners Benchmark)") + +
    + +

    + +## âšȘ 5.5 Rester loin des problĂšmes lĂ©gaux en utilisants des vĂ©rifications de license et de plagiat + +:white_check_mark: **À faire:** Les problĂšmes de licences et de plagiat ne sont probablement pas au centre de votre attention pour l'instant, mais pourquoi ne pas cocher Ă©galement cette case en 10 minutes ? Plusieurs packages npm comme [license check](https://www.npmjs.com/package/license-checker) et [plagiarism check](https://www.npmjs.com/package/plagiarism-checker) (commerciaux avec un essai gratuit) peuvent ĂȘtre facilement intĂ©grĂ© dans ta pipeline CI et inspecter les problĂšmes tels que les dĂ©pendances avec des licences restrictives ou du code qui a Ă©tĂ© copiĂ©-collĂ© Ă  partir de Stack Overflow et qui violerai certains droits d'auteur + +❌ **Autrement:** Involontairement, les dĂ©veloppeurs peuvent utiliser un package avec une license inappropriĂ©, ou copier/coller du code commercial et tomber sur des problĂšmes lĂ©gaux +
    + +
    ✏ Exemple de code + +
    + +### :clap: Bien faire les choses, exemple: + +```javascript +//install license-checker in your CI environment or also locally +npm install -g license-checker + +//ask it to scan all licenses and fail with exit code other than 0 if it found unauthorized license. The CI system should catch this failure and stop the build +license-checker --summary --failOn BSD + +``` + +
    + +![alt text](assets/bp-25-nodejs-licsense.png) + +
    + +

    + +## âšȘ 5.6 Inspecter constamment les dĂ©pendences vulnĂ©rables + +:white_check_mark: **À faire:** MĂȘme les dĂ©pendances les plus rĂ©putĂ©es comme Express ont des vulnĂ©rabilitĂ©s connues. Cela peut ĂȘtre apprivoisĂ© facilement avec des outils de la communautĂ© comme [npm audit](https://docs.npmjs.com/getting-started/running-a-security-audit), ou des outils commerciaux comme [snyk](https://snyk.io/) (qui offre Ă©galement une version de la communautĂ© gratuite). Les deux peuvent ĂȘtre appelĂ©s depuis ton CI Ă  chaque build + +❌ **Autrement:** Garder ton code exempt de vulnĂ©rabilitĂ©s sans les outils appropriĂ©s demande de suivre constamment les publications en ligne Ă  propos des nouvelles menaces. PlutĂŽt fastidieux. + +
    + +
    ✏ Exemple de code + +
    + +### :clap: Exemple: Résultat d'audit Npm + +![alt text](assets/bp-26-npm-audit-snyk.png "NPM Audit result") + +
    + +

    + +## âšȘ 5.7 Automatiser les mises-Ă -jour de dĂ©pendences + +:white_check_mark: **À faire:** L'introduction rĂ©cente du package-lock.json par Yarn et npm Ă  introduit un vrai dĂ©fi (la route vers l'enfer est pavĂ©e de bonnes intentions) - par dĂ©faut maintenant, les packages ne sont pas mis Ă  jour. MĂȘme une Ă©quipe qui lance plusieurs nouveaux dĂ©ploiements avec 'npm install' & 'npm update' n'aura pas de nouvelles mise Ă  jour. Cela conduit au mieux, Ă  des dĂ©pendances Ă  des packages de qualitĂ© infĂ©rieure, au pire Ă  du code vulnĂ©rable. Les Ă©quipes dĂ©pendent maintenant de la bonne volontĂ© et de la mĂ©moire des dĂ©veloppeur pour mettre Ă  jour manuellement le package.json ou utiliser des outils [comme ncu](https://www.npmjs.com/package/npm-check-updates). Une mĂ©thode plus fiable pourrait ĂȘtre d'automatiser le processus de rĂ©cupĂ©ration des versions de dĂ©pendances les plus fiables, bien qu'il n'y ait pas de solutions miracle, il y a deux possibilitĂ©s d'automatisation: + +(1) Le CI peut faire Ă©chouer les builds qui ont des dĂ©pendances obsolĂštes - en utilisant des outils comme [‘npm outdated’](https://docs.npmjs.com/cli/outdated) ou 'npm-check-updates (ncu)'. Faire ça forcera les dĂ©veloppeurs Ă  mettre Ă  jour les dĂ©pendances. + +(2) Utiliser un outil commercial qui peut scanner le code et envoyer automatiquement une pull-request avec les dĂ©pendances mises Ă  jour. La question intĂ©ressante restante est, quel devrait ĂȘtre la politique de mises Ă  jour - Mettre Ă  jour chaque patch gĂ©nĂšre trop de surcharge, mettre Ă  jour juste aprĂšs une release majeure peut introduire une version instable (de nombreuses vulnĂ©rabilitĂ©s sont dĂ©couverte dans les premiers jours aprĂšs la release, [voir l'incident](https://nodesource.com/blog/a-high-level-post-mortem-of-the-eslint-scope-security-incident/) eslint-scope). + +Une politique de mise Ă  jour efficace peut autoriser une 'pĂ©riode d'acquisition' - laisser le code en retard par rapport Ă  @latest pour quelque temps et versions avant de considĂ©rer la copie locale comme obsolĂšte (e.g la version locale est 1.3.1 et la version du repo est 1.3.8) +
    + +❌ **Autrement:** Ta production utilisera des packages qui ont Ă©tĂ© taggĂ© explicitement par leurs auteurs comme risquĂ©es + +
    + +
    ✏ Exemple de code + +
    + +### :clap: Exemple: [ncu](https://www.npmjs.com/package/npm-check-updates) peut ĂȘtre utilisĂ© manuellement ou dans une pipeline CI pour dĂ©tecter Ă  quel point le code est en retard vis a vis des derniĂšres versions + +![alt text](assets/bp-27-yoni-goldberg-npm.png "ncu can be used manually or within a CI pipeline to detect to which extent the code lag behind the latest versions") + +
    + +

    + +## âšȘ  5.8 Autres conseils CI, sans rapports avec Node + +:white_check_mark: **À faire:** Ce post se concentre sur les conseils de tests qui sont en lien avec, ou peuvent ĂȘtre illustrĂ©s, avec Node JS. Ce point, cependant, regroupe quelques conseils sans rapports avec Node qui sont bien connus + +
    1. Utilise une syntaxe déclarative. C'est la seule option pour la plupart des fournisseurs mais d'anciennes versions de Jenkins autorisent l'utilisation du code ou de l'UI
    2. Choisis un fournisseur qui a une intégration Docker native
    3. Échoue rapidement, lance les tests les plus rapides d'abord. CrĂ©e des 'tests de fumĂ©e' pour certaines Ă©tapes qui regroupe plusieurs inspections rapide (e.g liting, tests unitaires) et fourni des commentaires rapides Ă  celui qui commit le code
    4. Facilite le parcours des informations de build, cela inclut les rapports de tests, de couverture, de mutation, les logs ..etc
    5. Crée plusieurs pipelines/jobs pour chaque événement, réutiliser les étapes entre eux. Par exemple, configure un job pour les commits de features sur une branche et un différent pour une PR sur master. Laisse chacun réutiliser la logique en utilisant des étapes partagées (la plupart des fournisseurs ont des mécanismes pour réutiliser le code)
    6. Ne mets jamais de secrets dans la déclaration du job, récupÚre-les depuis un secret store ou depuis les configurations du job
    7. Augmente explicitement la version dans un build de release, ou au moins vérifie que le développeur l'a fait
    8. Build une fois et effectue toute les inspections sur l'artefact de build (e.g. Docker image)
    9. Test dans un environnement Ă©phĂ©mĂšre qui ne change pas d'Ă©tat entre les builds. Le cache des nodes peut ĂȘtre la seule exception
    +
    + +❌ **Autrement:** Tu rateras des annĂ©es de sagesse + +

    + +## âšȘ  5.9 Structure de build: Lancer les mĂȘmes Ă©tapes CI sur plusieurs versions de Node + +:white_check_mark: **À faire:** Le contrĂŽle de qualitĂ© est un jeu de hasard, plus tu couvres de terrain, plus tu as de chance de dĂ©tecter les problĂšmes rapidement. Quand tu dĂ©veloppes des packages rĂ©utilisable ou que tu lances une production avec plusieurs clients qui ont diffĂ©rentes configurations et versions de Node, le CI doit lancer la pipeline de tests sur toutes les configurations possible. Par exemple, imaginons qu'on utilise MySQL pour certains clients et Postgres pour d'autres - Certains fournisseurs CI supportent une fonctionnalitĂ©e appelĂ©e 'Matrix' qui permet de lancer les tests contre toutes les permutations de MySQL, Postgres et plusieurs versions de Node comme 8, 9 et 10. Cela peut se faire en utilisant seulement des configurations sans efforts supplĂ©mentaire (en considĂ©rant que tu as des tests ou d'autres contrĂŽles de qualitĂ©s). D'autres CIs qui ne supportent pas Matrix peuvent avoir des extensions qui permettent ça +
    + +❌ **Autrement:** AprĂšs avoir fait tout le travail d'Ă©crire des tests, va-t-on laisser passer des bugs seulement Ă  cause de problĂšmes de configurations ? + +
    + +
    ✏ Exemple de code + +
    + +### :clap: Exemple: Utiliser les dĂ©finition de build de Travis (fournisseur CI) pour lancer le mĂȘme test sur plusieurs versions de Node + +
    language: node_js
    node_js:
    - "7"
    - "6"
    - "5"
    - "4"
    install:
    - npm install
    script:
    - npm run test
    +
    + +

    + +# L'équipe + +## Yoni Goldberg + +
    + +
    + +**RĂŽle:** Auteur + +**À propos:** Je suis un consultant indĂ©pendant qui travaille avec des entreprises Fortune 500 et des startups pour peaufiner leurs applications JS et Node.JS. Plus qu'aucun autre sujet, je suis fascinĂ© par et vise Ă  maĂźtriser l'art du test. Je suis aussi l'auteur de [Node.js Best Practices](https://github.com/goldbergyoni/nodebestpractices) + +**📗 Cours en ligne:** Tu aimes ce guide et tu veux pousser tes compĂ©tences de test Ă  l'extreme ? Pense Ă  regarder mon cours complet [Testing Node.js & JavaScript From A To Z](https://www.testjavascript.com) + +
    + +**Me suivre:** + +- [🐩 Twitter](https://twitter.com/goldbergyoni/) +- [📞 Contact](https://testjavascript.com/contact-2/) +- [✉ Newsletter](https://testjavascript.com/newsletter//) + +
    +
    +
    + +## [Bruno Scheufler](https://github.com/BrunoScheufler) + +**RĂŽle:** RĂ©viseur et conseiller technique + +A pris soin de revoir, amĂ©liorer, linter et peaufiner tout les textes + +**À propos:** IngĂ©nieur web full-stack, passionĂ© par Node.js et GraphQL + +
    +
    + +## [Ido Richter](https://github.com/idori) + +**RĂŽle:** Concept, design et bons conseils + +**À propos:** Un dĂ©veloppeur front-end averti, expert CSS et maniaque des Ă©mojis + +## [Kyle Martin](https://github.com/js-kyle) + +**RĂŽle:** Aide Ă  faire tourner ce projet, et revois les pratiques liĂ©s Ă  la sĂ©curitĂ© + +**À propos:** Aime travailler sur des projets Node.JS et la sĂ©curitĂ© des applications web. + +## Contributors ✹ + +Merci Ă  ces merveilleuses personnes qui ont contribuĂ© Ă  ce repo! + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    Scott Davis

    🖋

    Adrien REDON

    🖋

    Stefano Magni

    🖋

    Yeoh Joer

    🖋

    Jhonny Moreira

    🖋

    Ian Germann

    🖋

    Hafez

    🖋

    Ruxandra Fediuc

    🖋

    Jack

    🖋

    Peter Carrero

    🖋

    Huhgawz

    🖋

    Haakon Borch

    🖋

    Jaime Mendoza

    🖋

    Cameron Dunford

    🖋

    John Gee

    🖋

    Aurelijus RoĆŸÄ—nas

    🖋

    Aaron

    🖋

    Tom Nagle

    🖋

    Yves yao

    🖋

    Userbit

    🖋

    Glaucia Lemos

    🚧

    koooge

    🖋

    Michal

    🖋

    roywalker

    🖋

    dangen

    🖋

    biesiadamich

    🖋

    Yanlin Jiang

    🖋

    sanguino

    🖋

    Morgan

    🖋

    Lukas Bischof

    ⚠ 🖋

    JuanMa Ruiz

    🖋

    Luís Ângelo Rodrigues Jr.

    🖋

    José Fernåndez

    🖋

    Alejandro Gutierrez Barcenilla

    🖋

    Jason

    🖋

    Otavio Araujo

    ⚠ 🖋

    Alex Ivanov

    🖋
    + + + + + diff --git a/readme-pl.md b/readme-pl.md index 499f5e9d..eb217e2d 100644 --- a/readme-pl.md +++ b/readme-pl.md @@ -1,12 +1,14 @@ +## 🎊 OgƂoszenie - KwiecieƄ 2022: WƂaƛnie ukazaƂa się nowa edycja z 5 nowymi najlepszymi praktykami, wieloma przykƂadami kodu i 4 nowymi tƂumaczeniami językowymi +
    # 👇 Powody dla ktĂłrych ten przewodnik moĆŒe przenieƛć twoje umiejętnoƛci testowania na wyĆŒszy poziom
    -## 📗 46+ najlepszych praktyk: super kompleksowe i wyczerpujące +## 📗 50+ najlepszych praktyk: super kompleksowe i wyczerpujące Jest to przewodnik po niezawodnoƛci JavaScript i Node.js od A-Z. Podsumowuje i przygotowuje dla Ciebie dziesiątki najlepszych postĂłw na blogu, ksiÄ…ĆŒek i narzędzi dostępnych na rynku @@ -33,6 +35,12 @@ Zacznij od zrozumienia wszechobecnych praktyk testowania, ktĂłre są podstawą k - 🇹🇳[Chinese](readme-zh-CN.md) - dzięki uprzejmoƛci [Yves yao](https://github.com/yvesyao) - đŸ‡°đŸ‡·[Korean](readme.kr.md) - dzięki uprzejmoƛci [Rain Byun](https://github.com/ragubyun) - đŸ‡”đŸ‡±[Polish](readme.pl.md) - dzięki uprzejmoƛci [Michal Biesiada](https://github.com/mbiesiad) +- đŸ‡Ș🇾[Spanish](readme-es.md) - dzięki uprzejmoƛci [Miguel G. Sanguino](https://github.com/sanguino) +- đŸ‡§đŸ‡·[Portuguese-BR](readme-pt-br.md) - dzięki uprzejmoƛci [Iago Angelim Costa Cavalcante](https://github.com/iagocavalcante) , [Douglas Mariano Valero](https://github.com/DouglasMV) oraz [koooge](https://github.com/koooge) +- đŸ‡«đŸ‡·[French](readme-fr.md) - dzięki uprzejmoƛci [Mathilde El Mouktafi](https://github.com/mel-mouk) +- đŸ‡ŻđŸ‡”[Japanese (draft)](https://github.com/yuichkun/javascript-testing-best-practices/blob/master/readme-jp.md) - dzięki uprzejmoƛci [Yuichi Yogo](https://github.com/yuichkun) oraz [ryo](https://github.com/kawamataryo) +- đŸ‡čđŸ‡Œ[Traditional Chinese](readme-zh-TW.md) - dzięki uprzejmoƛci [Yubin Hsu](https://github.com/yubinTW) +- đŸ‡ș🇩[Ukrainian](readme-ua.md) - dzięki uprzejmoƛci [Serhii Shramko](https://github.com/Shramkoweb) - Chcesz przetƂumaczyć na swĂłj język? Proszę skorzystaj z issue 💜

    @@ -41,7 +49,7 @@ Zacznij od zrozumienia wszechobecnych praktyk testowania, ktĂłre są podstawą k #### [`Sekcja 0: ZƂota zasada`](#sekcja-0ïžâƒŁ-zƂota-zasada) -Jedna rada, ktĂłra inspiruje wszystkich innych (1 specjalny punkt) +Jedna rada, ktĂłra inspiruje wszystkie inne (1 punkt specjalny) #### [`Sekcja 1: Anatomia testu`](#sekcja-1-anatomia-testu-1) @@ -49,7 +57,7 @@ Podstawa - konstruowanie czystych testĂłw (12 wypunktowaƄ) #### [`Sekcja 2: Backend`](#sekcja-2ïžâƒŁ-backend-testing) -Pisanie backendu i wydajne testy MikroserwisĂłw (8 wypunktowaƄ) +Pisanie backendu i wydajne testy mikroserwisĂłw (13 wypunktowaƄ) #### [`Sekcja 3: Frontend`](#sekcja-3ïžâƒŁ-frontend-testing) @@ -57,7 +65,7 @@ Pisanie testĂłw dla webowego interfejsu uĆŒytkownika, w tym testy komponentĂłw i #### [`Sekcja 4: Pomiary skutecznoƛci testĂłw`](#sekcja-4%EF%B8%8F%E2%83%A3-pomiar-skuteczno%C5%9Bci-testu) -Watching the watchman - pomiar jakoƛci testu (4 wypunktowania) +Pilnowanie straĆŒnika - pomiar jakoƛci testu (4 wypunktowania) #### [`Sekcja 5: Continuous Integration`](#sekcja-5ïžâƒŁ-ci-oraz-inne-miary-jakoƛci) @@ -72,7 +80,7 @@ Wytyczne dla CI w ƛwiecie JS (9 wypunktowaƄ) ## âšȘ 0 ZƂota zasada: Projektowanie dla lean testing :white_check_mark: **Opis:** -Testowany kod nie przypomina kodu produkcyjnego - zaprojektuj go tak, by byƂ prosty, krĂłtki, pozbawiony abstrakcji, pƂaski, przyjemny w pracy, lean. Trzeba spojrzeć na test i natychmiast uzyskać cel. +Kod testowy nie jest kodem produkcyjnym - zaprojektuj go tak, aby byƂ krĂłtki, ƛmiertelnie prosty, pƂaski i przyjemny w pracy. NaleĆŒy spojrzeć na test i natychmiast uzyskać intencję. Nasz umysƂ jest przepeƂniony gƂównym kodem produkcyjnym, nie mamy 'przestrzeni roboczej' na dodatkową zƂoĆŒonoƛć. Jeƛli sprĂłbujemy wcisnąć kolejny trudny kod do naszego sƂabego mĂłzgu, spowolni to pracę zespoƂu, co dziaƂa wbrew temu, co testujemy. W praktyce wiele zespoƂów po prostu rezygnuje z testĂłw. @@ -140,6 +148,11 @@ describe('Products Service', function() { +
    +
    © Credits & read-more + 1. Roy Osherove - Naming standards for unit tests +
    +

    ## âšȘ  1.2 Struktura testĂłw wedƂug wzorca AAA @@ -518,12 +531,12 @@ it("When visiting TestJavaScript.com home page, a menu is displayed", () => {

    -## âšȘ 1.9 Unikaj globalnych test fixture i seeds, dodawaj dane na test +## âšȘ 1.9 Kopiuj kod, ale tylko to, co niezbędne -:white_check_mark: **Opis:** Kierując się zƂotą zasadą (punkt 0), kaĆŒdy test powinien dodawać i dziaƂać na swoim wƂasnym zestawie wierszy BD, aby zapobiec sprzÄ™ĆŒeniu i Ƃatwo uzasadnić przebieg testu. W rzeczywistoƛci jest to często naruszane przez testerĂłw, ktĂłrzy zapeƂniają bazę danych danymi przed uruchomieniem testĂłw ([znany rĂłwnieĆŒ jako ‘test fixture’](https://en.wikipedia.org/wiki/Test_fixture)) w celu poprawy wydajnoƛci. ChociaĆŒ wydajnoƛć jest istotnym problemem - moĆŒna ją zƂagodzić (patrz punkt „Testowanie komponentĂłw”), jednak zƂoĆŒonoƛć testĂłw jest bardzo bolesnym smutkiem, ktĂłry powinien rządzić innymi względami przez większoƛć czasu. Praktycznie spraw, aby kaĆŒdy przypadek testowy wyraĆșnie dodaƂ potrzebne rekordy BD i dziaƂaƂ tylko na tych rekordach. Jeƛli wydajnoƛć stanie się kluczowym problemem - zrĂłwnowaĆŒony kompromis moĆŒe przyjƛć w postaci inicjowania jedynego zestawu testĂłw, ktĂłre nie powodują mutacji danych (np. zapytania) +:white_check_mark: **Opis:** DoƂącz wszystkie niezbędne szczegóƂy, ktĂłre wpƂywają na wynik testu, ale nic więcej. Jako przykƂad rozwaĆŒ test, ktĂłry powinien rozƂoĆŒyć na czynniki 100 wierszy wejƛciowego JSON - wklejanie tego w kaĆŒdym teƛcie jest nuĆŒÄ…ce. Wyodrębnienie go na zewnątrz do transferFactory.getJSON() spowoduje, ĆŒe test będzie niejasny. Bez danych trudno jest skorelować wynik testu z przyczyną („dlaczego ma zwracać status 400?”). Klasyczna ksiÄ…ĆŒka wzorcĂłw x-unit nazwaƂa ten wzorzec „tajemniczym goƛciem” - coƛ niewidocznego wpƂynęƂo na wyniki naszych testĂłw, nie wiemy co dokƂadnie. MoĆŒemy zrobić lepiej, wyodrębniając powtarzalne dƂugie częƛci na zewnątrz ORAZ wyraĆșnie wspomnij, ktĂłre konkretne szczegóƂy mają znaczenie dla testu. Idąc z powyĆŒszym przykƂadem, test moĆŒe przekazać parametry, ktĂłre podkreƛlają to, co jest waĆŒne: transferFactory.getJSON({sender: undefined}). W tym przykƂadzie czytelnik powinien natychmiast wywnioskować, ĆŒe puste pole nadawcy jest powodem, dla ktĂłrego test powinien oczekiwać bƂędu walidacji lub innego podobnego odpowiedniego wyniku.
    -❌ **W przeciwnym razie:** Niewiele testĂłw koƄczy się niepowodzeniem, wdroĆŒenie zostaƂo przerwane, nasz zespóƂ spędza teraz cenny czas, czy mamy bƂąd? Zbadajmy, och nie - wydaje się, ĆŒe dwa testy mutowaƂy te same dane seed +❌ **W przeciwnym razie:** Kopiowanie 500 wierszy JSON spowoduje, ĆŒe Twoje testy nie będą mogƂy być konserwowane i będą nieczytelne. Wyniesienie wszystkiego na zewnątrz zakoƄczy się niejasnymi testami, ktĂłre są trudne do zrozumienia
    @@ -531,49 +544,46 @@ it("When visiting TestJavaScript.com home page, a menu is displayed", () => {
    -### :thumbsdown: PrzykƂad antywzorca: testy nie są niezaleĆŒne i polegają na pewnym globalnym hook do zasilania globalnych danych BD +### :thumbsdown: PrzykƂad antywzorca: niepowodzenie testu jest niejasne, poniewaĆŒ caƂa przyczyna jest zewnętrzna i ukryta w ogromnym formacie JSON -![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "Examples with Mocha") +![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "PrzykƂady z Mocha") ```javascript -before(() => { - //adding sites and admins data to our DB. Where is the data? outside. At some external json or migration framework - await DB.AddSeedDataFromJson('seed.json'); -}); -it("When updating site name, get successful confirmation", async () => { - //I know that site name "portal" exists - I saw it in the seed files - const siteToUpdate = await SiteService.getSiteByName("Portal"); - const updateNameResult = await SiteService.changeName(siteToUpdate, "newName"); - expect(updateNameResult).to.be(true); -}); -it("When querying by site name, get the right site", async () => { - //I know that site name "portal" exists - I saw it in the seed files - const siteToCheck = await SiteService.getSiteByName("Portal"); - expect(siteToCheck.name).to.be.equal("Portal"); //Failure! The previous test change the name :[ -}); +test("When no credit, then the transfer is declined", async() => { + // Arrange + const transferRequest = testHelpers.factorMoneyTransfer() //get back 200 lines of JSON; + const transferServiceUnderTest = new TransferService(); + // Act + const transferResponse = await transferServiceUnderTest.transfer(transferRequest); + + // Assert + expect(transferResponse.status).toBe(409);// But why do we expect failure: All seems perfectly valid in the test đŸ€” + }); ```
    -### :clap: PrzykƂad robienia tego dobrze: MoĆŒemy pozostać w teƛcie, kaĆŒdy test dziaƂa na wƂasny zestaw danych +### :clap: PrzykƂad robienia tego prawidƂowo: Test wskazuje, co jest przyczyną wyniku testu ```javascript -it("When updating site name, get successful confirmation", async () => { - //test is adding a fresh new records and acting on the records only - const siteUnderTest = await SiteService.addSite({ - name: "siteForUpdateTest" - }); - const updateNameResult = await SiteService.changeName(siteUnderTest, "newName"); +test("When no credit, then the transfer is declined ", async() => { + // Arrange + const transferRequest = testHelpers.factorMoneyTransfer({userCredit:100, transferAmount:200}) //obviously there is lack of credit + const transferServiceUnderTest = new TransferService({disallowOvercharge:true}); - expect(updateNameResult).to.be(true); -}); -``` + // Act + const transferResponse = await transferServiceUnderTest.transfer(transferRequest); + + // Assert + expect(transferResponse.status).toBe(409); // Obviously if the user has no credit it should fail + }); + ``` -
    +

    ## âšȘ  1.10 Nie wychwytuj bƂędĂłw, oczekuj ich @@ -767,6 +777,9 @@ SƂowo ostrzeĆŒenia: argument TDD w ƛwiecie oprogramowania ma typową faƂszyw :white_check_mark: **Opis:** KaĆŒdy test jednostkowy obejmuje niewielką częƛć aplikacji i jest to kosztowne, aby pokryć caƂoƛć, podczas gdy kompleksowe testy z Ƃatwoƛcią obejmują duĆŒo gruntu, ale są niestabilne i wolniejsze, dlaczego nie zastosować zrĂłwnowaĆŒonego podejƛcia i napisać testy, ktĂłre są większe niĆŒ testy jednostkowe, ale mniejsze niĆŒ testy kompleksowe? Testowanie komponentĂłw to nieoceniona piosenka ƛwiata testowego - zapewniają to, co najlepsze z obu ƛwiatĂłw: rozsądną wydajnoƛć i moĆŒliwoƛć zastosowania wzorcĂłw TDD + realistyczne i doskonaƂe pokrycie. Testy komponentĂłw koncentrują się na mikroserwisowej ‘jednostce’, dziaƂają przeciwko interfejsowi API, nie mockują niczego, co naleĆŒy do samego mikroserwisu (np. prawdziwa baza danych lub przynajmniej wersja tej bazy danych w pamięci), ale usuwają wszystko, co jest zewnętrzne, jak wywoƂania innych mikrousƂug. W ten sposĂłb testujemy to, co wdraĆŒamy, podchodzimy do aplikacji od zewnątrz do wewnątrz i zyskujemy duĆŒÄ… pewnoƛć w rozsądnym czasie. + +[Mamy peƂny przewodnik, ktĂłry jest poƛwięcony wyƂącznie pisaniu testĂłw komponentĂłw we wƂaƛciwy sposĂłb](https://github.com/testjavascript/nodejs-integration-tests-best-practices) +
    ❌ **W przeciwnym razie:** MoĆŒesz spędzać dƂugie dni na pisaniu testĂłw jednostkowych, aby dowiedzieć się, ĆŒe masz tylko 20% zasięgu systemu @@ -951,6 +964,164 @@ it("When updating site name, get successful confirmation", async () => { +
    + +## âšȘ 2.8 Wybierz przejrzystą strategię czyszczenia danych: po wszystkim (zalecane) lub po kaĆŒdym + +:white_check_mark: **Opis:** Moment, w ktĂłrym testy czyszczą bazę danych, okreƛla sposĂłb pisania testĂłw. Dwie najbardziej opƂacalne opcje to czyszczenie po wszystkich testach i czyszczenie po kaĆŒdym teƛcie. Wybierając tę drugą opcję, czyszczenie po kaĆŒdym teƛcie gwarantuje czyste tabele i buduje wygodne korzyƛci testowe dla programisty. Na początku testu nie istnieją ĆŒadne inne rekordy, moĆŒna mieć pewnoƛć, ktĂłre dane są odpytywane, a nawet moĆŒna pokusić się o zliczenie wierszy podczas asercji. Ma to powaĆŒne wady: podczas uruchamiania w trybie wieloprocesowym testy prawdopodobnie będą ze sobą kolidować. Podczas gdy proces-1 czyƛci tabele, w tej chwili proces-2 wysyƂa zapytania o dane i koƄczy się niepowodzeniem (poniewaĆŒ baza danych zostaƂa nagle usunięta przez proces-1). Ponadto trudniej jest rozwiązywać problemy z testami zakoƄczonymi niepowodzeniem — odwiedzenie bazy danych nie spowoduje wyƛwietlenia ĆŒadnych rekordĂłw. + +Drugą opcją jest czyszczenie po zakoƄczeniu wszystkich plikĂłw testowych (lub nawet codziennie!). Takie podejƛcie oznacza, ĆŒe ta sama baza danych z istniejącymi rekordami obsƂuguje wszystkie testy i procesy. Aby uniknąć nadepnięcia sobie nawzajem na palce, testy muszą dodawać i dziaƂać na okreƛlonych rekordach, ktĂłre dodaƂy. Chcesz sprawdzić, czy dodano jakiƛ rekord? ZaĆ‚ĂłĆŒmy, ĆŒe istnieją inne tysiące rekordĂłw i zapytaj o rekordy, ktĂłre zostaƂy dodane jawnie. Chcesz sprawdzić, czy rekord zostaƂ usunięty? Nie moĆŒna zaƂoĆŒyć, ĆŒe tabela jest pusta, sprawdĆș, czy nie ma tam tego konkretnego rekordu. Ta technika przynosi kilka potÄ™ĆŒnych korzyƛci: dziaƂa natywnie w trybie wieloprocesowym, gdy programista chce zrozumieć, co się staƂo — dane są tam i nie są usuwane. Zwiększa rĂłwnieĆŒ szansę na znalezienie bƂędĂłw, poniewaĆŒ baza danych jest peƂna rekordĂłw i nie jest sztucznie oprĂłĆŒniona. [Zobacz peƂną tabelę porĂłwnawczą tutaj](https://github.com/testjavascript/nodejs-integration-tests-best-practices/blob/master/graphics/db-clean-options.png). +
    + +❌ **W przeciwnym razie:** Bez strategii oddzielania rekordĂłw lub czyszczenia — testy będą stąpać po sobie nawzajem; Korzystanie z transakcji będzie dziaƂać tylko w przypadku relacyjnych baz danych i moĆŒe się skomplikować, gdy pojawią się transakcje wewnętrzne + +
    + +
    ✏ PrzykƂady kodu + +
    + +### :clap: Czyszczenie po WSZYSTKICH testach. Niekoniecznie po kaĆŒdym uruchomieniu. Im więcej danych mamy w trakcie testĂłw - tym bardziej przypomina to profity produkcyjne + +```javascript + // After-all clean up (recommended) +// global-teardown.js +module.exports = async () => { + // ... + if (Math.ceil(Math.random() * 10) === 10) { + await new OrderRepository().cleanup(); + } +}; +``` + +
    + +
    + +## âšȘ 2.9 Odizoluj komponent od ƛwiata za pomocą interceptora HTTP + +:white_check_mark: **Opis:** Odizoluj testowany skƂadnik, przechwytując wszystkie wychodzące ĆŒÄ…dania HTTP i dostarczając ĆŒÄ…daną odpowiedĆș, aby interfejs HTTP API wspóƂpracownika nie zostaƂ trafiony. Nock jest doskonaƂym narzędziem do tej misji, poniewaĆŒ zapewnia wygodną skƂadnię do definiowania zachowania usƂug zewnętrznych. Izolacja jest koniecznoƛcią, aby zapobiec szumowi i spowolnieniu dziaƂania, ale przede wszystkim aby symulować rĂłĆŒne scenariusze i reakcje. Dobry symulator lotu nie polega na malowaniu czystego bƂękitnego nieba, ale na sprowadzaniu bezpieczeƄstwa podczas burz i chaosu. Jest to wzmocnione w architekturze mikrousƂug, w ktĂłrej naleĆŒy zawsze koncentrować się na jednym komponencie bez angaĆŒowania reszty ƛwiata. ChociaĆŒ moĆŒliwe jest symulowanie zachowania usƂugi zewnętrznej za pomocą dublowania testowego (mockowanie), lepiej nie dotykać wdroĆŒonego kodu i dziaƂać na poziomie sieci, aby testy byƂy czysto typu black-box. Wadą izolacji jest nie wykrywanie zmian w komponencie wspóƂpracownika i brak zrozumienia nieporozumieƄ między dwiema usƂugami — pamiętaj, aby zrekompensować to za pomocą kilku testĂłw kontraktowych lub E2E +
    + +❌ **W przeciwnym razie:** NiektĂłre usƂugi udostępniają wersję fake, ktĂłra moĆŒe zostać wdroĆŒona lokalnie przez rozmĂłwcę, zwykle za pomocą Dockera — uƂatwi to konfigurację i zwiększy wydajnoƛć, ale nie pomoĆŒe w symulowaniu rĂłĆŒnych odpowiedzi; NiektĂłre usƂugi zapewniają ƛrodowisko „piaskownicy”, więc prawdziwa usƂuga zostaje trafiona, ale nie są wyzwalane ĆŒadne koszty ani skutki uboczne — zmniejszy to szum związany z konfiguracją usƂugi innej firmy, ale nie pozwoli rĂłwnieĆŒ na symulację scenariuszy + +
    + +
    ✏ PrzykƂady kodu + +
    + +### :clap: Zapobieganie poƂączeniom sieciowym z komponentami zewnętrznymi umoĆŒliwia tworzenie scenariuszy symulacji i minimalizowanie szumu + +```javascript +// Intercept requests for 3rd party APIs and return a predefined response +beforeEach(() => { + nock('http://localhost/user/').get(`/1`).reply(200, { + id: 1, + name: 'John', + }); +}); +``` +
    +
    + +## âšȘ 2.10 Przetestuj schemat odpowiedzi, gƂównie w przypadku pĂłl generowanych automatycznie + +:white_check_mark: **Opis:** Gdy nie moĆŒna potwierdzić konkretnych danych, sprawdĆș istnienie i typy pĂłl obowiązkowych. Czasami odpowiedĆș zawiera waĆŒne pola z danymi dynamicznymi, ktĂłrych nie moĆŒna przewidzieć podczas pisania testu, takie jak daty i rosnące liczby. Jeƛli kontrakt API obiecuje, ĆŒe te pola nie będą miaƂy wartoƛci null i będą zawierać odpowiednie typy, konieczne jest przetestowanie tego. Większoƛć bibliotek asercji obsƂuguje typy sprawdzania. Jeƛli odpowiedĆș jest maƂa, sprawdĆș dane zwrotne i wpisz razem w ramach tego samego potwierdzenia (patrz przykƂad kodu). Jeszcze jedną opcją jest zweryfikowanie caƂej odpowiedzi z dokumentem OpenAPI (Swagger). Większoƛć programĂłw uruchamiających testy ma rozszerzenia spoƂecznoƛci, ktĂłre weryfikują odpowiedzi API w oparciu o ich dokumentację. + + +
    + +❌ **W przeciwnym razie:** ChociaĆŒ wywoƂujący kod/API opiera się na jakimƛ polu z danymi dynamicznymi (np. ID, data), nie wrĂłci i nie zerwie umowy + +
    + +
    ✏ PrzykƂady kodu + +
    + +### :clap: Zapewnienie, ĆŒe pola z wartoƛcią dynamiczną istnieją i mają wƂaƛciwy typ + +```javascript + test('When adding a new valid order, Then should get back approval with 200 response', async () => { + // ... + //Assert + expect(receivedAPIResponse).toMatchObject({ + status: 200, + data: { + id: expect.any(Number), // Any number satisfies this test + mode: 'approved', + }, + }); +}); +``` + +
    + +
    + +## âšȘ 2.11 SprawdĆș corner cases integracji i chaos + +:white_check_mark: **Opis:** Sprawdzając integracje wyjdĆș poza happy i sad paths. SprawdĆș nie tylko bƂędne odpowiedzi (np. bƂąd HTTP 500), ale takĆŒe anomalie na poziomie sieci, takie jak powolne i przekroczone limity czasu odpowiedzi. Udowodni to, ĆŒe kod jest odporny i moĆŒe obsƂugiwać rĂłĆŒne scenariusze sieciowe, takie jak obieranie wƂaƛciwej ƛcieĆŒki po przekroczeniu limitu czasu, brak problemĂłw z prądem i czy zawiera wyƂącznik umoĆŒliwiający ponowną prĂłbę. Renomowane narzędzia przechwytujące mogą z Ƃatwoƛcią symulować rĂłĆŒne zachowania sieciowe, takie jak gorączkowa usƂuga, ktĂłra czasami koƄczy się niepowodzeniem. MoĆŒe nawet zdać sobie sprawę, kiedy domyƛlna wartoƛć limitu czasu klienta HTTP jest dƂuĆŒsza niĆŒ symulowany czas odpowiedzi i natychmiast zgƂosić wyjątek limitu czasu, bez czekania + + +
    + +❌ **W przeciwnym razie:** Wszystkie twoje testy przechodzą pomyƛlnie, tylko produkcja ulegnie awarii lub nie będzie poprawnie zgƂaszać bƂędów, gdy strony trzecie wyƛlą wyjątkowe odpowiedzi + +
    + +
    ✏ PrzykƂady kodu + +
    + +### :clap: Zapewnienie, ĆŒe w przypadku awarii sieci wyƂącznik moĆŒe uratować sytuację + +```javascript + test('When users service replies with 503 once and retry mechanism is applied, then an order is added successfully', async () => { + //Arrange + nock.removeInterceptor(userServiceNock.interceptors[0]) + nock('http://localhost/user/') + .get('/1') + .reply(503, undefined, { 'Retry-After': 100 }); + nock('http://localhost/user/') + .get('/1') + .reply(200); + const orderToAdd = { + userId: 1, + productId: 2, + mode: 'approved', + }; + + //Act + const response = await axiosAPIClient.post('/order', orderToAdd); + + //Assert + expect(response.status).toBe(200); +}); +``` + +
    + +
    + + +## âšȘ 2.12 Przetestuj pięć potencjalnych wynikĂłw + +:white_check_mark: **Opis:** Planując testy, rozwaĆŒ uwzględnienie pięciu typowych wynikĂłw przepƂywu. Kiedy twĂłj test uruchamia jakąƛ akcję (np. wywoƂanie API), dzieje się reakcja, dzieje się coƛ znaczącego i wzywa do testowania. Pamiętaj, ĆŒe nie dbamy o to, jak wszystko dziaƂa. Skupiamy się na wynikach, rzeczach, ktĂłre są zauwaĆŒalne z zewnątrz i mogą mieć wpƂyw na uĆŒytkownika. Te wyniki/reakcje moĆŒna podzielić na 5 kategorii: + +‱ OdpowiedĆș — test wywoƂuje akcję (np. przez API) i otrzymuje odpowiedĆș. Teraz zajmuje się sprawdzaniem poprawnoƛci danych odpowiedzi, schematu i statusu HTTP + +‱ Nowy stan — po wywoƂaniu akcji niektĂłre **publicznie dostępne** dane są prawdopodobnie modyfikowane + +‱ WywoƂania zewnętrzne — po wywoƂaniu akcji aplikacja moĆŒe wywoƂać skƂadnik zewnętrzny za poƛrednictwem protokoƂu HTTP lub dowolnego innego transportu. Na przykƂad poƂączenie w celu wysƂania SMS-a, e-maila lub obciÄ…ĆŒenia karty kredytowej + +‱ Kolejki wiadomoƛci — wynikiem przepƂywu moĆŒe być wiadomoƛć w kolejce + +‱ Obserwowalnoƛć — NiektĂłre rzeczy muszą być monitorowane, na przykƂad bƂędy lub niezwykƂe wydarzenia biznesowe. Gdy transakcja się nie powiedzie, oczekujemy nie tylko wƂaƛciwej reakcji, ale takĆŒe poprawnej obsƂugi bƂędĂłw i prawidƂowego logowania/metryk. Informacje te trafiają bezpoƛrednio do bardzo waĆŒnego uĆŒytkownika — uĆŒytkownika Ops (tj. produkcyjnego SRE/administratora) + + +

    # Sekcja 3ïžâƒŁ: Frontend Testing @@ -1918,38 +2089,59 @@ Podziękowania dla tych wspaniaƂych ludzi, ktĂłrzy przyczynili się do tego rep - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + + - - - - - - - + + + + + + + - + + + +

    Scott Davis

    🖋

    Adrien REDON

    🖋

    Stefano Magni

    🖋

    Yeoh Joer

    🖋

    Jhonny Moreira

    🖋

    Ian Germann

    🖋

    Hafez

    🖋

    Scott Davis

    🖋

    Adrien REDON

    🖋

    Stefano Magni

    🖋

    Yeoh Joer

    🖋

    Jhonny Moreira

    🖋

    Ian Germann

    🖋

    Hafez

    🖋

    Ruxandra Fediuc

    🖋

    Jack

    🖋

    Peter Carrero

    🖋

    Huhgawz

    🖋

    Haakon Borch

    🖋

    Jaime Mendoza

    🖋

    Cameron Dunford

    🖋

    John Gee

    🖋

    Aurelijus RoĆŸÄ—nas

    🖋

    Aaron

    🖋

    Tom Nagle

    🖋

    Yves yao

    🖋

    Userbit

    🖋

    Glaucia Lemos

    🚧

    Ruxandra Fediuc

    🖋

    Jack

    🖋

    Peter Carrero

    🖋

    Huhgawz

    🖋

    Haakon Borch

    🖋

    Jaime Mendoza

    🖋

    Cameron Dunford

    🖋

    koooge

    🖋

    Michal

    🖋

    roywalker

    🖋

    dangen

    🖋

    biesiadamich

    🖋

    Yanlin Jiang

    🖋

    sanguino

    🖋

    John Gee

    🖋

    Aurelijus RoĆŸÄ—nas

    🖋

    Aaron

    🖋

    Tom Nagle

    🖋

    Yves yao

    🖋

    Userbit

    🖋

    Glaucia Lemos

    🚧

    Morgan

    🖋

    Lukas Bischof

    ⚠ 🖋

    JuanMa Ruiz

    🖋

    Luís Ângelo Rodrigues Jr.

    🖋

    José Fernåndez

    🖋

    Alejandro Gutierrez Barcenilla

    🖋

    Jason

    🖋

    koooge

    🖋

    Otavio Araujo

    ⚠ 🖋

    Alex Ivanov

    🖋

    Yiqiao Xu

    🖋

    YuBin, Hsu

    🌍 đŸ’»
    - + diff --git a/readme-pr-fr.md b/readme-pr-fr.md new file mode 100644 index 00000000..20831337 --- /dev/null +++ b/readme-pr-fr.md @@ -0,0 +1,2055 @@ + + + +
    + +# 👇 چ۱ۧ Ű§ÛŒÙ† Ű±Ű§Ù‡Ù†Ù…Ű§ می ŰȘÙˆŰ§Ù†ŰŻ Ù…Ù‡Ű§Ű±ŰȘ Ù‡Ű§ÛŒ ŰąŰČÙ…Ű§ÛŒŰŽÛŒ ŰŽÙ…Ű§ ۱ۧ ŰšÙ‡ ۳۷ۭ ŰšŰčŰŻÛŒ ŰšŰ±ŰłŰ§Ù†ŰŻ + +
    + +## 📗 50+ ŰȘۧ ۧŰČ ŰšÙ‡ŰȘŰ±ÛŒÙ† ŰŽÛŒÙˆÙ‡ Ù‡Ű§: فوق Ű§Ù„ŰčŰ§ŰŻÙ‡ ŰŹŰ§Ù…Űč و ŰŹŰ§Ù…Űč + +Ű§ÛŒÙ† Ű±Ű§Ù‡Ù†Ù…Ű§ÛŒ Ù‚Ű§ŰšÙ„ÛŒŰȘ Ű§Ű·Ù…ÛŒÙ†Ű§Ù† JavaScript & Node.js ۧŰČ A-Z ۧ۳ŰȘ. ŰŻÙ‡â€ŒÙ‡Ű§ Ù…ÙˆŰ±ŰŻ ۧŰČ ŰšÙ‡ŰȘŰ±ÛŒÙ† ÙŸŰłŰȘâ€ŒÙ‡Ű§ŰŒ Ú©ŰȘŰ§Űšâ€ŒÙ‡Ű§ و ۧۚŰČŰ§Ű±Ù‡Ű§ÛŒÛŒ که ۚۧŰČۧ۱ Ű§Ű±Ű§ŰŠÙ‡ Ù…ÛŒâ€ŒŰŻÙ‡ŰŻ ۱ۧ ŰšŰ±Ű§ÛŒ ŰŽÙ…Ű§ ŰźÙ„Ű§Ű”Ù‡ و ÚŻŰ±ŰŻŰąÙˆŰ±ÛŒ Ù…ÛŒâ€ŒÚ©Ù†ŰŻ. + +## 🚱 ÙŸÛŒŰŽŰ±ÙŰȘه: 10000 Ù…Ű§ÛŒÙ„ ÙŰ±Ű§ŰȘ۱ ۧŰČ Ű§Ű”ÙˆÙ„ Ű§ÙˆÙ„ÛŒÙ‡ می Ű±ÙˆŰŻ + +ŰšÙ‡ ŰłÙŰ±ÛŒ ŰšŰ±ÙˆÛŒŰŻ که ŰšŰłÛŒŰ§Ű± ÙŰ±Ű§ŰȘ۱ ۧŰČ Ű§Ű”ÙˆÙ„ Ű§ÙˆÙ„ÛŒÙ‡ ŰšÙ‡ Ù…ÙˆŰ¶ÙˆŰčۧŰȘ ÙŸÛŒŰŽŰ±ÙŰȘه Ű§ÛŒ Ù…Ű§Ù†Ù†ŰŻ ŰąŰČÙ…Ű§ÛŒŰŽ ۯ۱ ŰȘÙˆÙ„ÛŒŰŻŰŒ ŰąŰČÙ…Ű§ÛŒŰŽ ŰŹÙ‡ŰŽŰŒ ŰąŰČÙ…Ű§ÛŒŰŽ Ù…ŰšŰȘنی ۚ۱ ŰŻŰ§Ű±Ű§ÛŒÛŒ و ŰšŰłÛŒŰ§Ű±ÛŒ ۧŰČ Ű§ŰšŰČŰ§Ű±Ù‡Ű§ÛŒ ۧ۳ŰȘ۱ۧŰȘژیک و Ű­Ű±ÙÙ‡ Ű§ÛŒ ŰŻÛŒÚŻŰ± ŰłÙŰ± می Ú©Ù†ŰŻ. ۧگ۱ ŰȘÙ…Ű§Ù… Ú©Ù„Ù…Ű§ŰȘ Ű§ÛŒÙ† Ű±Ű§Ù‡Ù†Ù…Ű§ ۱ۧ ŰšŰźÙˆŰ§Ù†ÛŒŰŻŰŒ ۭۧŰȘÙ…Ű§Ù„Ű§Ù‹ Ù…Ù‡Ű§Ű±ŰȘ Ù‡Ű§ÛŒ ŰȘŰłŰȘ ŰČنی ŰŽÙ…Ű§ ŰšŰłÛŒŰ§Ű± ŰšŰ§Ù„Ű§ŰȘ۱ ۧŰČ Ű­ŰŻ مŰȘÙˆŰłŰ· ŰźÙˆŰ§Ù‡ŰŻ ŰšÙˆŰŻ + +## 🌐 Full-stack: front, backend, CI, Ù‡Ű± چیŰČی + +ۚۧ ۯ۱ک Ű±ÙˆŰŽâ€ŒÙ‡Ű§ÛŒ ŰȘŰłŰȘ همه ۏۧ ۭۧ۶۱ که ÙŸŰ§ÛŒÙ‡ و ۧ۳ۧ۳ Ù‡Ű± ۳۷ۭ ŰšŰ±Ù†Ű§Ù…Ù‡ Ù‡ŰłŰȘÙ†ŰŻŰŒ ŰŽŰ±ÙˆŰč Ú©Ù†ÛŒŰŻ. ŰłÙŸŰłŰŒ ŰšÙ‡ Ű­ÙˆŰČه Ű§Ù†ŰȘŰźŰ§ŰšÛŒ ŰźÙˆŰŻ ŰšÙŸŰ±ŰŻŰ§ŰČÛŒŰŻ: frontend/UIی backendی CI ÛŒŰ§ ŰŽŰ§ÛŒŰŻ همه ŰąÙ†Ù‡Ű§ŰŸ + +
    + +### Yoni Goldberg Ù†ÙˆŰŽŰȘه ŰŽŰŻÙ‡ ŰȘÙˆŰłŰ· + +- JavaScript & Node.js Ù…ŰŽŰ§ÙˆŰ±Ù‡ +- 📗 [ŰȘŰłŰȘ Ú©Ű±ŰŻÙ† Node.js & JavaScript ۧŰČ A ŰȘۧ Z](https://www.testjavascript.com) - ŰŻÙˆŰ±Ù‡ ŰŹŰ§Ù…Űč ŰąÙ†Ù„Ű§ÛŒÙ† من ۚۧ ŰšÛŒŰŽ ۧŰČ [7 ۳ۧŰčŰȘ ÙˆÛŒŰŻÛŒÙˆ](https://www.testjavascript.com), 14 Ű§Ù†ÙˆŰ§Űč ŰȘŰłŰȘ و ŰšÛŒŰŽ ۧŰČ 40 ŰšÙ‡ŰȘŰ±ÛŒÙ† Ű±ÙˆŰŽ +- [من ۱ۧ ۯ۱ ŰȘوییŰȘ۱ ŰŻÙ†ŰšŰ§Ù„ Ú©Ù†ÛŒŰŻ](https://twitter.com/goldbergyoni/) +- [Ú©Ű§Ű±ÚŻŰ§Ù‡ ŰšŰčŰŻÛŒ: ÙˆŰ±ÙˆÙ†Ű§ŰŒ Ű§ÛŒŰȘŰ§Ù„ÛŒŰ§ 🇼đŸ‡čی 20 ŰąÙˆŰ±ÛŒÙ„](https://2022.jsday.it/workshop/nodejs_testing.html) + +
    + + +## `ÙÙ‡Ű±ŰłŰȘ Ù…Ű·Ű§Ù„Űš` + +#### [`ۚ۟ێ 0: Ù‚Ű§ŰčŰŻÙ‡ Ű·Ù„Ű§ÛŒÛŒ`](#section-0ïžâƒŁ-the-golden-rule) + +یک ŰȘÙˆŰ”ÛŒÙ‡ ÙˆŰ§Ű­ŰŻ که Ű§Ù„Ù‡Ű§Ù… ۚ۟ێ ŰšÙ‚ÛŒÙ‡ ۧ۳ŰȘ (1 ÚŻÙ„ÙˆÙ„Ù‡ ویژه) + +#### [`ۚ۟ێ 1: ŰąÙ†Ű§ŰȘومی ŰȘŰłŰȘ`](#section-1-the-test-anatomy-1) + +ÙÙˆÙ†ŰŻŰ§ŰłÛŒÙˆÙ† - ŰȘŰłŰȘ Ù‡Ű§ÛŒ ŰȘمیŰČ ŰłŰ§ŰźŰȘŰ§Ű±ÛŒ (12 ÚŻÙ„ÙˆÙ„Ù‡) + +#### [`Backend :ۚ۟ێ 2`](#section-2ïžâƒŁ-backend-testing) + +Ù†ÙˆŰŽŰȘن ŰȘŰłŰȘ Ù‡Ű§ÛŒ backend و Ù…ÛŒÚ©Ű±ÙˆŰłŰ±ÙˆÛŒŰł ŰšÙ‡ Ű·ÙˆŰ± Ù…ÙˆŰ«Ű± (13 ÚŻÙ„ÙˆÙ„Ù‡) + +#### [`Frontend :ۚ۟ێ 3`](#section-3ïžâƒŁ-frontend-testing) + +Ù†ÙˆŰŽŰȘن ŰȘŰłŰȘ Ù‡Ű§ ŰšŰ±Ű§ÛŒ ۱ۧۚ۷ Ú©Ű§Ű±ŰšŰ±ÛŒ ÙˆŰš ŰŽŰ§Ù…Ù„ ŰȘŰłŰȘ Ù‡Ű§ÛŒ Ú©Ű§Ù…ÙŸÙˆÙ†Ù†ŰȘ و E2E +(11 ÚŻÙ„ÙˆÙ„Ù‡) + +#### [`ۚ۟ێ 4: Ű§Ù†ŰŻŰ§ŰČه ÚŻÛŒŰ±ÛŒ Ű§Ű«Ű±ŰšŰźŰŽÛŒ ŰąŰČمون Ù‡Ű§`](#section-4ïžâƒŁ-measuring-test-effectiveness) + +ŰȘÙ…Ű§ŰŽŰ§ÛŒ Ù†ÚŻÙ‡ŰšŰ§Ù† - Ű§Ù†ŰŻŰ§ŰČه ÚŻÛŒŰ±ÛŒ کیفیŰȘ ŰȘŰłŰȘ (4 ÚŻÙ„ÙˆÙ„Ù‡) + +#### [`ۚ۟ێ 5: ÛŒÚ©ÙŸŰ§Ű±Ú†Ù‡ ۳ۧŰČی Ù…ŰŻŰ§ÙˆÙ…`](#section-5ïžâƒŁ-ci-and-other-quality-measures) + +ŰŻŰłŰȘÙˆŰ±Ű§Ù„Űčمل Ù‡Ű§ÛŒ CI ۯ۱ ŰŻÙ†ÛŒŰ§ÛŒ JS (9 ÚŻÙ„ÙˆÙ„Ù‡) + +

    + +# ۚ۟ێ 0ïžâƒŁ: Ù‚Ű§ŰčŰŻÙ‡ Ű·Ù„Ű§ÛŒÛŒ + +
    + +## âšȘ 0 Ù‚Ű§Ù†ÙˆÙ† Ű·Ù„Ű§ÛŒÛŒ: Ű·Ű±Ű§Ű­ÛŒ ŰšŰ±Ű§ÛŒ ŰȘŰłŰȘ Ù†Ű§Űš + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** + +ŰȘŰłŰȘ Ú©Ű±ŰŻÙ† Ú©ŰŻ Ű”Ű±ÙŰ§ Ù†ÙˆŰŽŰȘن Ú©ŰŻ Ù†ÛŒŰłŰȘ - ŰąÙ† ۱ۧ Ű·ÙˆŰ±ÛŒ Ű·Ű±Ű§Ű­ÛŒ Ú©Ù†ÛŒŰŻ که کوŰȘŰ§Ù‡ŰŒ ŰłŰ§ŰŻÙ‡ŰŒ Ù…ŰłŰ·Ű­ŰŒ و کۧ۱ Ú©Ű±ŰŻÙ† ۚۧ ŰąÙ† Ù„Ű°ŰȘ ۚ۟ێ ۚۧێۯ. ŰšŰ§ÛŒŰŻ ŰšÙ‡ یک ŰȘŰłŰȘ Ù†ÚŻŰ§Ù‡ ک۱ۯ و ÙÙˆŰ±Ű§Ù‹ Ù‡ŰŻÙ ۱ۧ ŰŻŰ±ÛŒŰ§ÙŰȘ ک۱ۯ. + +ŰšŰšÛŒÙ†ÛŒŰŻŰŒ Ű°Ù‡Ù† Ù…Ű§ ۧŰČ Ù‚ŰšÙ„ Ù…ŰŽŰșول کۧ۱ Ű§Ű”Ù„ÛŒ Ù…Ű§ - Ù†ÙˆŰŽŰȘن Ú©ŰŻ ۧ۳ŰȘ. ŰšŰ±Ű§ÛŒ ÙŸÛŒÚ†ÛŒŰŻÚŻÛŒ Ű§Ű¶Ű§ÙÛŒŰŒ هیچ ÙŰ¶Ű§ÛŒÛŒ ÙˆŰŹÙˆŰŻ Ù†ŰŻŰ§Ű±ŰŻ. ۧگ۱ ŰšŰźÙˆŰ§Ù‡ÛŒÙ… یک ŰłÛŒŰłŰȘم sus-system ŰŻÛŒÚŻŰ± ۱ۧ ۯ۱ مŰșŰČ Ű¶Űčیف ŰźÙˆŰŻ ŰšÙŰŽŰ§Ű±ÛŒÙ…ŰŒ ۳۱ŰčŰȘ ŰȘیم ۱ۧ Ú©Ű§Ù‡ŰŽ Ù…ÛŒâ€ŒŰŻÙ‡ŰŻ که ŰšŰ±ŰźÙ„Ű§Ù ŰŻÙ„ÛŒÙ„ÛŒ که Ù…Ű§ ŰȘŰłŰȘ می‌کنیم Űčمل Ù…ÛŒâ€ŒÚ©Ù†ŰŻ. ŰčÙ…Ù„Ű§Ù‹ Ű§ÛŒÙ†ŰŹŰ§ŰłŰȘ که ŰšŰłÛŒŰ§Ű±ÛŒ ۧŰČ ŰȘÛŒÙ…â€ŒÙ‡Ű§ ŰȘŰłŰȘ ۱ۧ Ű±Ù‡Ű§ Ù…ÛŒâ€ŒÚ©Ù†Ù†ŰŻ. + +ŰȘŰłŰȘ Ù‡Ű§ ÙŰ±Ű”ŰȘی ŰšŰ±Ű§ÛŒ چیŰČ ŰŻÛŒÚŻŰ±ÛŒ Ù‡ŰłŰȘÙ†ŰŻ - یک ŰŻŰłŰȘÛŒŰ§Ű± ŰŻÙˆŰłŰȘŰ§Ù†Ù‡ŰŒ کمک ŰźÙ„ŰšŰ§Ù†ŰŒ که ۧ۱ŰČŰŽ ŰČÛŒŰ§ŰŻÛŒ ŰšŰ±Ű§ÛŒ یک ŰłŰ±Ù…Ű§ÛŒÙ‡ ÚŻŰ°Ű§Ű±ÛŒ کوچک Ű§Ű±Ű§ŰŠÙ‡ می ŰŻÙ‡ŰŻ. Űčلم ŰšÙ‡ Ù…Ű§ می ÚŻÙˆÛŒŰŻ که Ù…Ű§ ŰŻÙˆ ŰłÛŒŰłŰȘم مŰșŰČی ŰŻŰ§Ű±ÛŒÙ…: ŰłÛŒŰłŰȘم 1 ŰšŰ±Ű§ÛŒ فŰčŰ§Ù„ÛŒŰȘ Ù‡Ű§ÛŒ ŰšÛŒ ۯ۱ۯ۳۱ Ù…Ű§Ù†Ù†ŰŻ Ű±Ű§Ù†Ù†ŰŻÚŻÛŒ Ù…Ű§ŰŽÛŒÙ† ۯ۱ یک ŰŹŰ§ŰŻÙ‡ ŰźŰ§Ù„ÛŒ و ŰłÛŒŰłŰȘم 2 که ŰšŰ±Ű§ÛŒ ŰčÙ…Ù„ÛŒŰ§ŰȘ ÙŸÛŒÚ†ÛŒŰŻÙ‡ و ŰąÚŻŰ§Ù‡Ű§Ù†Ù‡ Ù…Ű§Ù†Ù†ŰŻ Ű­Ù„ یک مŰčŰ§ŰŻÙ„Ù‡ Ű±ÛŒŰ§Ű¶ÛŒ ۧ۳ŰȘÙŰ§ŰŻÙ‡ می ŰŽÙˆŰŻ. ŰȘŰłŰȘ ŰźÙˆŰŻ ۱ۧ ŰšŰ±Ű§ÛŒ ŰłÛŒŰłŰȘم 1 Ű·Ű±Ű§Ű­ÛŒ Ú©Ù†ÛŒŰŻŰŒ ŰČÙ…Ű§Ù†ÛŒ که ŰšÙ‡ Ú©ŰŻ ŰȘŰłŰȘ Ù†ÚŻŰ§Ù‡ می Ú©Ù†ÛŒŰŻŰŒ ŰšŰ§ÛŒŰŻ ŰšÙ‡ ۱ۭۧŰȘی یک ŰłÙ†ŰŻ HTML ۱ۧ ŰȘŰșÛŒÛŒŰ± ŰŻÙ‡ÛŒŰŻŰŒ نه Ù…Ű§Ù†Ù†ŰŻ Ű­Ù„ 2X (17 × 24). + +Ű§ÛŒÙ† ۱ۧ می ŰȘÙˆŰ§Ù† ۚۧ ŰȘکنیک Ù‡Ű§ÛŒ Ű§Ù†ŰȘŰźŰ§ŰšÛŒ Ű§Ù†ŰȘ۟ۧۚ ÚŻÛŒÙ„Ű§ŰłŰŒ ۧۚŰČŰ§Ű±Ù‡Ű§ و Ű§Ù‡ŰŻŰ§Ù ŰąŰČÙ…Ű§ÛŒŰŽÛŒ که Ù…Ù‚Ű±ÙˆÙ† ŰšÙ‡ Ű”Ű±ÙÙ‡ Ù‡ŰłŰȘÙ†ŰŻ و ROI ŰčŰ§Ù„ÛŒ Ű§Ű±Ű§ŰŠÙ‡ می ŰŻÙ‡Ù†ŰŻŰŒ ŰšÙ‡ ŰŻŰłŰȘ ŰąÙˆŰ±ŰŻ. ÙÙ‚Ű· ŰšÙ‡ Ű§Ù†ŰŻŰ§ŰČه Ù†ÛŒŰ§ŰČ ŰȘŰłŰȘ Ú©Ù†ÛŒŰŻŰŒ ŰłŰčی Ú©Ù†ÛŒŰŻ ŰąÙ† ۱ۧ ŰČÛŒŰ±Ú© Ù†ÚŻÙ‡ ŰŻŰ§Ű±ÛŒŰŻŰŒ ÚŻŰ§Ù‡ÛŒ Ű§ÙˆÙ‚Ű§ŰȘ Ű­ŰȘی ۧ۱ŰČŰŽ ŰąÙ† ۱ۧ ۯۧ۱ۯ که ŰšŰ±ŰźÛŒ ۧŰČ ŰȘŰłŰȘ Ù‡Ű§ ۱ۧ Ú©Ù†Ű§Ű± ŰšÚŻŰ°Ű§Ű±ÛŒŰŻ و Ù‚Ű§ŰšÙ„ÛŒŰȘ Ű§Ű·Ù…ÛŒÙ†Ű§Ù† ۱ۧ ŰšŰ±Ű§ÛŒ Ú†Ű§ŰšÚ©ÛŒ و ŰłŰ§ŰŻÚŻÛŒ مŰčŰ§Ù…Ù„Ù‡ Ú©Ù†ÛŒŰŻ. + +![alt text](/assets/headspace.png "We have no head room for additional complexity") + +ŰšÛŒŰŽŰȘ۱ ŰȘÙˆŰ”ÛŒÙ‡ Ù‡Ű§ÛŒ ŰČÛŒŰ± Ù…ŰŽŰȘÙ‚Ű§ŰȘ Ű§ÛŒÙ† Ű§Ű”Ù„ ۧ۳ŰȘ. + +### ŰąÙ…Ű§ŰŻÙ‡ ŰšŰ±Ű§ÛŒ ŰŽŰ±ÙˆŰč۟ + +

    + +# ۚ۟ێ 1: ŰąÙ†Ű§ŰȘومی ŰȘŰłŰȘ + +
    + +## âšȘ  1.1 ŰŽŰ§Ù…Ù„ 3 Ù‚ŰłÙ…ŰȘ ۯ۱ Ù‡Ű± Ù†Ű§Ù… ŰąŰČمون + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** یک ÚŻŰČۧ۱ێ ŰąŰČÙ…Ű§ÛŒŰŽÛŒ ŰšŰ§ÛŒŰŻ ŰšÚŻÙˆÛŒŰŻ که ŰąÛŒŰ§ ۚۧŰČŰšÛŒÙ†ÛŒ ŰšŰ±Ù†Ű§Ù…Ù‡ فŰčلی Ű§Ù„ŰČŰ§Ù…Ű§ŰȘ Ű§ÙŰ±Ű§ŰŻÛŒ ۱ۧ که لŰČÙˆÙ…Ű§Ù‹ ۚۧ Ú©ŰŻ ŰąŰŽÙ†Ű§ Ù†ÛŒŰłŰȘÙ†ŰŻ ŰšŰ±ŰąÙˆŰ±ŰŻÙ‡ می Ú©Ù†ŰŻ: ŰȘŰłŰȘ Ú©Ù†Ù†ŰŻÙ‡ ی Ù…Ù‡Ù†ŰŻŰł DevOps که ۯ۱ Ű­Ű§Ù„ ۧ۳ŰȘÙ‚Ű±Ű§Ű± ۧ۳ŰȘ و ŰŽÙ…Ű§ ŰąÛŒÙ†ŰŻÙ‡ ŰŻÙˆ ŰłŰ§Ù„ ŰšŰčŰŻ. ۧگ۱ ŰąŰČمون Ù‡Ű§ ۯ۱ ۳۷ۭ Ű§Ù„ŰČŰ§Ù…Ű§ŰȘ ۔ۭۚŰȘ Ú©Ù†Ù†ŰŻ و ŰŽŰ§Ù…Ù„ 3 ۚ۟ێ ŰšŰ§ŰŽÙ†ŰŻŰŒ می ŰȘÙˆŰ§Ù† ŰšÙ‡ ŰšÙ‡ŰȘŰ±ÛŒÙ† ÙˆŰŹÙ‡ ŰšÙ‡ Ű§ÛŒÙ† Ű§Ù…Ű± ŰŻŰłŰȘ ÛŒŰ§ÙŰȘ: + +(1) چه چیŰČی ۯ۱ Ű­Ű§Ù„ ŰąŰČÙ…Ű§ÛŒŰŽ ۧ۳ŰȘ۟ ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† Ù…Ű«Ű§Ù„ŰŒ مŰȘŰŻ ProductsService.addNewProduct + +(2) ۯ۱ چه ŰŽŰ±Ű§ÛŒŰ· و ŰłÙ†Ű§Ű±ÛŒÙˆÛŒÛŒŰŸ ŰšŰ±Ű§ÛŒ Ù…Ű«Ű§Ù„ هیچ قیمŰȘی ŰšÙ‡ مŰȘŰŻ ŰŻŰ§ŰŻÙ‡ نمی ŰŽÙˆŰŻ + +(3) نŰȘÛŒŰŹÙ‡ ی Ù‚Ű§ŰšÙ„ Ű§Ù†ŰȘ۞ۧ۱ Ú†ÛŒŰłŰȘ۟ ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† Ù…Ű«Ű§Ù„ŰŒ Ù…Ű­Ű”ÙˆÙ„ ŰŹŰŻÛŒŰŻ ŰȘŰ§ÛŒÛŒŰŻ Ù†ŰŽŰŻÙ‡ ۧ۳ŰȘ + +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** یک ۧ۳ŰȘÙ‚Ű±Ű§Ű± ŰšÙ‡â€ŒŰȘۧŰČÚŻÛŒ Ù†Ű§Ù…ÙˆÙÙ‚ ŰšÙˆŰŻŰŒ ŰȘŰłŰȘ ۚۧ Ù†Ű§Ù… Â«Ű§ÙŰČÙˆŰŻÙ† Ù…Ű­Ű”ÙˆÙ„Â» Ù†Ű§Ù…ÙˆÙÙ‚ ŰšÙˆŰŻ. ŰąÛŒŰ§ Ű§ÛŒÙ† ŰšÙ‡ ŰŽÙ…Ű§ می ÚŻÙˆÛŒŰŻ که ŰŻÙ‚ÛŒÙ‚Ű§Ù‹ چه چیŰČی ۟۱ۧۚ ۧ۳ŰȘ۟ + +
    + +**👇 ŰȘÙˆŰŹÙ‡ ۯۧێŰȘه ŰšŰ§ŰŽÛŒŰŻ:** Ù‡Ű± ÚŻÙ„ÙˆÙ„Ù‡ ŰŻŰ§Ű±Ű§ÛŒ نمونه Ú©ŰŻ و ÚŻŰ§Ù‡ÛŒ Ű§ÙˆÙ‚Ű§ŰȘ یک ŰȘŰ”ÙˆÛŒŰ± ŰȘŰ”ÙˆÛŒŰ±ÛŒ نیŰČ Ù…ÛŒ ۚۧێۯ. ŰšŰ±Ű§ÛŒ ÚŻŰłŰȘ۱ێ کلیک Ú©Ù†ÛŒŰŻ +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :clap: Ù…Ű«Ű§Ù„ ۯ۱۳ŰȘ: Ù†Ű§Ù… ŰȘŰłŰȘی که ۧŰČ 3 Ù‚ŰłÙ…ŰȘ ŰȘŰŽÚ©ÛŒÙ„ ŰŽŰŻÙ‡ ۧ۳ŰȘ + +![](https://img.shields.io/badge/🔹%20Example%20using%20Mocha-blue.svg "Using Mocha to illustrate the idea") + +```javascript +//1. ÙˆŰ§Ű­ŰŻ ۯ۱ Ű­Ű§Ù„ ŰȘŰłŰȘ +describe('ŰźŰŻÙ…Ű§ŰȘ Ù…Ű­Ű”ÙˆÙ„Ű§ŰȘ', function() { + describe('Ű§ÙŰČÙˆŰŻÙ† Ù…Ű­Ű”ÙˆÙ„ ŰŹŰŻÛŒŰŻ', function() { + //2. ŰłÙ†Ű§Ű±ÛŒÙˆ و 3. Ű§Ù†ŰȘ۞ۧ۱ + it('Ù‡Ù†ÚŻŰ§Ù…ÛŒ که قیمŰȘی Ù…ŰŽŰźŰ” Ù†ŰŽŰŻÙ‡ ۧ۳ŰȘی ÙˆŰ¶ŰčیŰȘ Ù…Ű­Ű”ÙˆÙ„ ۯ۱ Ű§Ù†ŰȘ۞ۧ۱ ŰȘŰ§ÛŒÛŒŰŻ ۧ۳ŰȘ', ()=> { + const newProduct = new ProductService().add(...); + expect(newProduct.status).to.equal('pendingApproval'); + }); + }); +}); + +``` + +
    + +### :clap: Ù…Ű«Ű§Ù„ ۯ۱۳ŰȘ: Ù†Ű§Ù… ŰąŰČÙ…Ű§ÛŒŰŽÛŒ که ۧŰČ 3 Ù‚ŰłÙ…ŰȘ ŰȘŰŽÚ©ÛŒÙ„ ŰŽŰŻÙ‡ ۧ۳ŰȘ + +![alt text](/assets/bp-1-3-parts.jpeg "A test name that constitutes 3 parts") + +
    + + +## âšȘ  1.2 ŰȘŰłŰȘ Ù‡Ű§ÛŒ ۳ۧ۟ŰȘۧ۱ ۚۧ Ű§Ù„ÚŻÙˆÛŒ AAA + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** ۳ۧ۟ŰȘۧ۱ ŰȘŰłŰȘ Ù‡Ű§ÛŒ ŰźÙˆŰŻ ۱ۧ ۚۧ 3 ۚ۟ێ ŰšÙ‡ ŰźÙˆŰšÛŒ ۏۯۧ ŰŽŰŻÙ‡ Ù…ÙŰŻŰ§Ű± ŰŻÙ‡ÛŒ Ú©Ù†ÛŒŰŻŰŒ ۧۏ۱ۧ Ú©Ù†ÛŒŰŻ و Ù…Ù‚Ű§ÛŒŰłÙ‡ Ú©Ù†ÛŒŰŻ (AAA). ÙŸÛŒŰ±ÙˆÛŒ ۧŰČ Ű§ÛŒÙ† ۳ۧ۟ŰȘۧ۱ ŰȘŰ¶Ù…ÛŒÙ† می Ú©Ù†ŰŻ که ŰźÙˆŰ§Ù†Ù†ŰŻÙ‡ هیچ CPU مŰșŰČی ۱ۧ ŰšŰ±Ű§ÛŒ ۯ۱ک ŰšŰ±Ù†Ű§Ù…Ù‡ ŰąŰČÙ…Ű§ÛŒŰŽÛŒ ۟۱ۏ نمی Ú©Ù†ŰŻ: + +Ű§ÙˆÙ„ A - Ù…ÙŰŻŰ§Ű± ŰŻŰ§ŰŻÙ†: ŰȘÙ…Ű§Ù… Ú©ŰŻÙ‡Ű§ÛŒ Ű±Ű§Ù‡ Ű§Ù†ŰŻŰ§ŰČی ŰšŰ±Ű§ÛŒ Ű±ŰłŰ§Ù†ŰŻÙ† ŰłÛŒŰłŰȘم ŰšÙ‡ ŰłÙ†Ű§Ű±ÛŒÙˆÛŒÛŒ که ŰȘŰłŰȘ Ù‡ŰŻÙ ŰąÙ† ŰŽŰšÛŒÙ‡ ۳ۧŰČی ۧ۳ŰȘ. Ű§ÛŒÙ† ممکن ۧ۳ŰȘ ŰŽŰ§Ù…Ù„ نمونه ۳ۧŰČی ÙˆŰ§Ű­ŰŻ ۯ۱ Ű­Ű§Ù„ ŰąŰČÙ…Ű§ÛŒŰŽ ۳ۧŰČÙ†ŰŻÙ‡ŰŒ Ű§Ű¶Ű§ÙÙ‡ Ú©Ű±ŰŻÙ† Ű±Ú©ÙˆŰ±ŰŻÙ‡Ű§ÛŒ DBی mocking/stubbing Ű§ŰŽÛŒŰ§ و Ù‡Ű± Ú©ŰŻ ŰąÙ…Ű§ŰŻÙ‡ ۳ۧŰČی ŰŻÛŒÚŻŰ± ۚۧێۯ. + +ŰŻÙˆÙ… A - ۧۏ۱ۧ: ÙˆŰ§Ű­ŰŻ ŰȘŰ­ŰȘ ŰȘŰłŰȘ ۱ۧ ۧۏ۱ۧ Ú©Ù†ÛŒŰŻ. مŰčÙ…ÙˆÙ„Ű§ 1 ۟۷ Ú©ŰŻ + +ŰłÙˆÙ… A - Ù…ÙŰ§ÛŒŰłÙ‡: Ű§Ű·Ù…ÛŒÙ†Ű§Ù† Ű­Ű§Ű”Ù„ Ú©Ù†ÛŒŰŻ که ۧ۱ŰČŰŽ ŰŻŰ±ÛŒŰ§ÙŰȘی Ű§Ù†ŰȘ۞ۧ۱ۧŰȘ ۱ۧ ŰšŰ±ŰąÙˆŰ±ŰŻÙ‡ می Ú©Ù†ŰŻ. مŰčÙ…ÙˆÙ„Ű§ 1 ۟۷ Ú©ŰŻ + +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** نه ŰȘÙ†Ù‡Ű§ ۳ۧŰčŰȘ Ù‡Ű§ ŰšŰ±Ű§ÛŒ ۯ۱ک Ú©ŰŻ Ű§Ű”Ù„ÛŒ وقŰȘ می ÚŻŰ°Ű§Ű±ÛŒŰŻŰŒ ŰšÙ„Ú©Ù‡ چیŰČی که ŰšŰ§ÛŒŰŻ ŰłŰ§ŰŻÙ‡ ŰȘŰ±ÛŒÙ† Ù‚ŰłÙ…ŰȘ Ű±ÙˆŰČ ŰšŰ§ŰŽŰŻ (ŰȘŰłŰȘ) مŰșŰČ ŰŽÙ…Ű§ ۱ۧ Ú©ŰŽ می ŰŻÙ‡ŰŻ. + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :clap: Ù…Ű«Ű§Ù„ ۯ۱۳ŰȘ: ŰȘŰłŰȘی که ۚۧ Ű§Ù„ÚŻÙˆÛŒ AAA ۳ۧ۟ŰȘۧ۱ ÛŒŰ§ÙŰȘه ۧ۳ŰȘ + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Jest") ![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "Examples with Mocha") + +```javascript +describe("Ű·ŰšÙ‚Ù‡ ŰšÙ†ŰŻÛŒ Ù…ŰŽŰȘŰ±ÛŒ", () => { + test("Ù‡Ù†ÚŻŰ§Ù…ÛŒ که Ù…ŰŽŰȘŰ±ÛŒ ŰšÛŒŰŽ ۧŰČ 500 ŰŻÙ„Ű§Ű± هŰČینه Ú©Ű±ŰŻŰŒ ŰšŰ§ÛŒŰŻ ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† Ű­Ù‚ ŰšÛŒÙ…Ù‡ Ű·ŰšÙ‚Ù‡ ŰšÙ†ŰŻÛŒ ŰŽÙˆŰŻ", () => { + //Ù…ÙŰŻŰ§Ű± ŰŻÙ‡ÛŒ Ú©Ű±ŰŻÙ† + const customerToClassify = { spent: 505, joined: new Date(), id: 1 }; + const DBStub = sinon.stub(dataAccess, "getCustomer").reply({ id: 1, classification: "regular" }); + + //ۧۏ۱ۧ Ú©Ű±ŰŻÙ† + const receivedClassification = customerClassifier.classifyCustomer(customerToClassify); + + //Ù…ÙŰ§ŰšŰłÙ‡ Ú©Ű±ŰŻÙ† + expect(receivedClassification).toMatch("premium"); + }); +}); +``` + +
    + +### :thumbsdown: Ù…Ű«Ű§Ù„ ۶ۯ Ű§Ù„ÚŻÙˆ: ŰšŰŻÙˆÙ† ŰŹŰŻŰ§ÛŒÛŒŰŒ یک Ű§Ù†ŰšÙˆÙ‡ŰŒ ۳۟ŰȘ ŰȘ۱ ŰšŰ±Ű§ÛŒ ŰȘÙŰłÛŒŰ± + +```javascript +test("ŰšŰ§ÛŒŰŻ ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† Ű­Ù‚ ŰšÛŒÙ…Ù‡ Ű·ŰšÙ‚Ù‡ ŰšÙ†ŰŻÛŒ ŰŽÙˆŰŻ", () => { + const customerToClassify = { spent: 505, joined: new Date(), id: 1 }; + const DBStub = sinon.stub(dataAccess, "getCustomer").reply({ id: 1, classification: "regular" }); + const receivedClassification = customerClassifier.classifyCustomer(customerToClassify); + expect(receivedClassification).toMatch("premium"); +}); +``` + +
    + +

    + +## âšȘ 1.3 Ű§Ù†ŰȘ۞ۧ۱ۧŰȘ ۱ۧ ŰšÙ‡ ŰČŰšŰ§Ù† Ù…Ű­Ű”ÙˆÙ„ ŰȘÙˆŰ”ÛŒÙ Ú©Ù†ÛŒŰŻ: ۧŰČ Ű§ŰŻŰčŰ§Ù‡Ű§ÛŒ ۳ۚک BDD ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ú©Ù†ÛŒŰŻ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** Ú©ŰŻÙ†ÙˆÛŒŰłÛŒ ŰȘŰłŰȘâ€ŒÙ‡Ű§ÛŒ ŰźÙˆŰŻ ŰšÙ‡ ۳ۚک ŰšÛŒŰ§Ù†ÛŒ ŰšÙ‡ ŰźÙˆŰ§Ù†Ù†ŰŻÙ‡ Ű§ÛŒÙ† Ű§Ù…Ú©Ű§Ù† ۱ۧ Ù…ÛŒâ€ŒŰŻÙ‡ŰŻ ŰȘۧ ŰšŰŻÙˆÙ† Ű”Ű±Ù Ű­ŰȘی یک Ú†Ű±ŰźÙ‡ مŰșŰČ - CPUی ÙÙˆŰ±Ű§Ù‹ ŰšÙ‡ نŰȘÛŒŰŹÙ‡ ۚ۱۳ۯ. وقŰȘی Ú©ŰŻÙ‡Ű§ÛŒ Ű¶Ű±ÙˆŰ±ÛŒ ۱ۧ می Ù†ÙˆÛŒŰłÛŒŰŻ که مملو ۧŰČ Ù…Ù†Ű·Ù‚ ŰŽŰ±Ű·ÛŒ ۧ۳ŰȘی ŰźÙˆŰ§Ù†Ù†ŰŻÙ‡ Ù…ŰŹŰšÙˆŰ± می ŰŽÙˆŰŻ Ú†Ű±ŰźÙ‡ Ù‡Ű§ÛŒ مŰșŰČ - CPU ŰšÛŒŰŽŰȘŰ±ÛŒ ۧŰčÙ…Ű§Ù„ Ú©Ù†ŰŻ. ۯ۱ Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘی Ű§Ù†ŰȘ۞ۧ۱ۧŰȘ ۱ۧ ŰšÙ‡ ŰČŰšŰ§Ù†ÛŒ ŰŽŰšÛŒÙ‡ ŰšÙ‡ Ű§Ù†ŰłŰ§Ù†ŰŒ ŰšÙ‡ ۳ۚک BDD ۧŰčÙ„Ű§Ù†ÛŒ ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ Â«Ű§Ù†ŰȘ۞ۧ۱» ÛŒŰ§ Â«ŰšŰ§ÛŒŰŻÂ» و ŰšŰŻÙˆÙ† ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ Ú©ŰŻ ŰłÙŰ§Ű±ŰŽÛŒ Ú©ŰŻÙ†ÙˆÛŒŰłÛŒ Ú©Ù†ÛŒŰŻ. ۧگ۱ Chai & Jest ŰŽŰ§Ù…Ù„ ۧۯŰčŰ§ÛŒ Ù…ÙˆŰ±ŰŻ Ù†ŰžŰ± Ù†ÛŒŰłŰȘ و ŰšŰłÛŒŰ§Ű± Ù‚Ű§ŰšÙ„ ŰȘک۱ۧ۱ ۧ۳ŰȘی ۯ۱ Ù†ŰžŰ± ŰšÚŻÛŒŰ±ÛŒŰŻ [extending Jest matcher (Jest)](https://jestjs.io/docs/en/expect#expectextendmatchers) ÛŒŰ§ Ù†ÙˆŰŽŰȘن یک [ÙŸÙ„Ű§ÚŻÛŒÙ† Chai ŰłÙŰ§Ű±ŰŽÛŒ](https://www.chaijs.com/guide/plugins/) +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** ŰȘیم ŰȘŰłŰȘ Ù‡Ű§ÛŒ کمŰȘŰ±ÛŒ می Ù†ÙˆÛŒŰłŰŻ و ŰȘŰłŰȘ Ù‡Ű§ÛŒ مŰČŰ§Ű­Ù… ۱ۧ ۚۧ .skip ŰȘŰČŰŠÛŒÙ† می Ú©Ù†ŰŻ() + +
    + +
    ✏ نمونه Ú©ŰŻ
    + +![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "Examples with Mocha & Chai") ![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Jest") + +### :thumbsdown: Ù…Ű«Ű§Ù„ ۶ۯ Ű§Ù„ÚŻÙˆ: ŰźÙˆŰ§Ù†Ù†ŰŻÙ‡ ÙÙ‚Ű· ŰšŰ±Ű§ÛŒ ŰŻŰ±ÛŒŰ§ÙŰȘ ۯۧ۳ŰȘŰ§Ù† ŰȘŰłŰȘی که ŰšŰ§ÛŒŰŻ Ú©ŰŻÙ‡Ű§ÛŒ نه Ú†Ù†ŰŻŰ§Ù† کوŰȘŰ§Ù‡ و Ű¶Ű±ÙˆŰ±ÛŒ ۱ۧ Ù…Ű±ÙˆŰ± Ú©Ù†ŰŻ. + +```javascript +test("Ù‡Ù†ÚŻŰ§Ù… ŰŻŰ±ŰźÙˆŰ§ŰłŰȘ Ű§ŰŻÙ…ÛŒÙ†ŰŒ Ù…Ű·Ù…ŰŠÙ† ŰŽÙˆÛŒŰŻ که ۯ۱ نŰȘŰ§ÛŒŰŹ ÙÙ‚Ű· Ù…ŰŻÛŒŰ±Ű§Ù† ŰłÙŰ§Ű±ŰŽ ŰŻŰ§ŰŻÙ‡ ŰŽŰŻÙ‡ Ù‡ŰłŰȘÙ†ŰŻ", () => { + //ۚۧ ÙŰ±Ű¶ Ű§ÛŒÙ†Ú©Ù‡ Ù…Ű§ ŰŻÙˆ Ű§ŰŻÙ…ÛŒÙ† "admin1"ی "admin2" و "user1" ۱ۧ ŰšÙ‡ Ű§ÛŒÙ†ŰŹŰ§ Ű§Ű¶Ű§ÙÙ‡ Ú©Ű±ŰŻÙ‡ Ű§ÛŒÙ…. + const allAdmins = getUsers({ adminOnly: true }); + + let admin1Found, + adming2Found = false; + + allAdmins.forEach(aSingleUser => { + if (aSingleUser === "user1") { + assert.notEqual(aSingleUser, "user1", "A user was found and not admin"); + } + if (aSingleUser === "admin1") { + admin1Found = true; + } + if (aSingleUser === "admin2") { + admin2Found = true; + } + }); + + if (!admin1Found || !admin2Found) { + throw new Error("Not all admins were returned"); + } +}); +``` + +
    + +### :clap: Ù…Ű«Ű§Ù„ ۯ۱۳ŰȘ: Ù…Ű±ÙˆŰ± ŰȘŰłŰȘ ۧŰčÙ„Ű§Ù†ÛŒ ŰČÛŒŰ± یک Ù†ŰłÛŒÙ… ۧ۳ŰȘ + +```javascript +it("When asking for an admin, ensure only ordered admins in results", () => { + //ۚۧ ÙŰ±Ű¶ Ű§ÛŒÙ†Ú©Ù‡ Ù…Ű§ ŰšÙ‡ Ű§ÛŒÙ†ŰŹŰ§ ŰŻÙˆ Ű§ŰŻÙ…ÛŒÙ† Ű§Ű¶Ű§ÙÙ‡ Ú©Ű±ŰŻÙ‡ Ű§ÛŒÙ… + const allAdmins = getUsers({ adminOnly: true }); + + expect(allAdmins) + .to.include.ordered.members(["admin1", "admin2"]) + .but.not.include.ordered.members(["user1"]); +}); +``` + +
    + +

    + +## âšȘ  1.4 ŰšÙ‡ ŰȘŰłŰȘ ŰŹŰčŰšÙ‡ ŰłÛŒŰ§Ù‡ ÙŸŰ§ÛŒŰšÙ†ŰŻ ŰšŰ§ŰŽÛŒŰŻ: ÙÙ‚Ű· مŰȘŰŻ Ù‡Ű§ÛŒ Űčمومی ۱ۧ ŰąŰČÙ…Ű§ÛŒŰŽ Ú©Ù†ÛŒŰŻ + +:white_check_mark: **ۧŰȘŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** ŰąŰČÙ…Ű§ÛŒŰŽ Ù‚Ű·ŰčۧŰȘ ŰŻŰ§ŰźÙ„ÛŒ ŰȘÙ‚Ű±ÛŒŰšŰ§Ù‹ هیچ هŰČینه ŰČÛŒŰ§ŰŻÛŒ ۱ۧ ŰšÙ‡ Ù‡Ù…Ű±Ű§Ù‡ Ù†ŰŻŰ§Ű±ŰŻ. ۧگ۱ Ú©ŰŻ/API ŰŽÙ…Ű§ نŰȘŰ§ÛŒŰŹ ۯ۱۳ŰȘی ۱ۧ Ű§Ű±Ű§ŰŠÙ‡ Ù…ÛŒâ€ŒŰŻÙ‡ŰŻŰŒ ŰąÛŒŰ§ ÙˆŰ§Ù‚ŰčŰ§Ù‹ ŰšŰ§ÛŒŰŻ 3 ۳ۧŰčŰȘ ŰąÛŒÙ†ŰŻÙ‡ ŰźÙˆŰŻ ۱ۧ ŰšŰ±Ű§ÛŒ ŰąŰČÙ…Ű§ÛŒŰŽ Ù†Ű­ÙˆÙ‡ ŰčÙ…Ù„Ú©Ű±ŰŻ ŰŻŰ§ŰźÙ„ÛŒ ŰąÙ† ŰłŰ±Ù…Ű§ÛŒÙ‡â€ŒÚŻŰ°Ű§Ű±ÛŒ Ú©Ù†ÛŒŰŻ و ŰłÙŸŰł Ű§ÛŒÙ† ŰȘŰłŰȘâ€ŒÙ‡Ű§ÛŒ ŰŽÚ©Ù†Ù†ŰŻÙ‡ ۱ۧ Ű­ÙŰž Ú©Ù†ÛŒŰŻŰŸ Ù‡Ű± ŰČÙ…Ű§Ù† که یک Ű±ÙŰȘۧ۱ Űčمومی ŰšŰ±Ű±ŰłÛŒ Ù…ÛŒâ€ŒŰŽÙˆŰŻŰŒ ÙŸÛŒŰ§ŰŻÙ‡â€ŒŰłŰ§ŰČی ŰźŰ”ÙˆŰ”ÛŒ نیŰČ ŰšÙ‡ Ű·ÙˆŰ± Ű¶Ù…Ù†ÛŒ ŰąŰČÙ…Ű§ÛŒŰŽ Ù…ÛŒâ€ŒŰŽÙˆŰŻ و ŰąŰČÙ…Ű§ÛŒŰŽâ€ŒÙ‡Ű§ÛŒ ŰŽÙ…Ű§ ŰȘÙ†Ù‡Ű§ ۯ۱ Ű”ÙˆŰ±ŰȘ ÙˆŰŹÙˆŰŻ Ù…ŰŽÚ©Ù„ ŰźŰ§Ű”ÛŒ (ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† Ù…Ű«Ű§Ù„ ŰźŰ±ÙˆŰŹÛŒ ۧێŰȘŰšŰ§Ù‡) ŰŽÚ©ŰłŰȘه Ù…ÛŒâ€ŒŰŽÙˆÙ†ŰŻ. Ű§ÛŒÙ† Ű±ÙˆÛŒÚ©Ű±ŰŻ ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† "ŰąŰČÙ…Ű§ÛŒŰŽ Ű±ÙŰȘۧ۱" نیŰČ Ù†Ű§Ù…ÛŒŰŻÙ‡ می ŰŽÙˆŰŻ. ۧŰČ Ű·Ű±Ù ŰŻÛŒÚŻŰ±ŰŒ ۧگ۱ Ù‚Ű·ŰčۧŰȘ ŰŻŰ§ŰźÙ„ÛŒ ۱ۧ ŰąŰČÙ…Ű§ÛŒŰŽ Ú©Ù†ÛŒŰŻ (Ű±ÙˆÛŒÚ©Ű±ŰŻ ŰŹŰčŰšÙ‡ ŰłÙÛŒŰŻ) - ŰȘÙ…Ű±Ú©ŰČ ŰŽÙ…Ű§ ۧŰČ ŰšŰ±Ù†Ű§Ù…Ù‡ Ű±ÛŒŰČی نŰȘÛŒŰŹÙ‡ Ú©Ű§Ù…ÙŸÙˆÙ†Ù†ŰȘ ŰšÙ‡ ŰŹŰČŰŠÛŒŰ§ŰȘ ŰŻÙ‚ÛŒÙ‚ ŰȘŰșÛŒÛŒŰ± می Ú©Ù†ŰŻ و ممکن ۧ۳ŰȘ ŰȘŰłŰȘ ŰŽÙ…Ű§ ŰšÙ‡ ŰŻÙ„ÛŒÙ„ Ű§Ű”Ù„Ű§Ű­ Ú©Ù†Ù†ŰŻÙ‡ Ù‡Ű§ÛŒ ŰŹŰČŰŠÛŒ Ú©ŰŻ ŰŽÚ©ŰłŰȘه ŰŽÙˆŰŻŰŒ Ű§ÚŻŰ±Ú†Ù‡ نŰȘŰ§ÛŒŰŹ ŰźÙˆŰš Ù‡ŰłŰȘÙ†ŰŻ - Ű§ÛŒÙ† ŰšÙ‡ Ű·ÙˆŰ± Ú†ŰŽÙ…ÚŻÛŒŰ±ÛŒ ŰȘŰčÙ…ÛŒŰ± و Ù†ÚŻÙ‡ŰŻŰ§Ű±ÛŒ ۱ۧ Ű§ÙŰČŰ§ÛŒŰŽ می ŰŻÙ‡ŰŻ. ۚۧ۱ +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** ŰȘŰłŰȘ Ù‡Ű§ÛŒ ŰŽÙ…Ű§ Ù…Ű§Ù†Ù†ŰŻ [ÙŸŰłŰ±ÛŒ Ù‡ŰłŰȘ که Űčین گ۱گ ÙŰ±ÛŒŰ§ŰŻ می ŰČÙ†ŰŻ](https://en.wikipedia.org/wiki/The_Boy_Who_Cried_Wolf): ÙŰ±ÛŒŰ§ŰŻ ŰČŰŻÙ† ۚۧ ÙŰ±ÛŒŰ§ŰŻÙ‡Ű§ÛŒ Ù…Ű«ŰšŰȘ کۧ۰ۚ (Ù…Ű«Ù„Ű§Ù‹ŰŒ یک ŰąŰČÙ…Ű§ÛŒŰŽ ŰšÙ‡ ŰŻÙ„ÛŒÙ„ ŰȘŰșÛŒÛŒŰ± Ù†Ű§Ù… یک مŰȘŰșÛŒŰ± ŰźŰ”ÙˆŰ”ÛŒ Ù†Ű§Ù…ÙˆÙÙ‚ ŰšÙˆŰŻ). ŰŹŰ§ÛŒ ŰȘŰčŰŹŰš Ù†ÛŒŰłŰȘ که Ù…Ű±ŰŻÙ… ŰšÙ‡ ŰČÙˆŰŻÛŒ ۧŰčÙ„Ű§Ù†â€ŒÙ‡Ű§ÛŒ CI ۱ۧ Ù†Ű§ŰŻÛŒŰŻÙ‡ Ù…ÛŒâ€ŒÚŻÛŒŰ±Ù†ŰŻ ŰȘۧ Ű§ÛŒÙ†Ú©Ù‡ Ű±ÙˆŰČی یک ۚۧگ ÙˆŰ§Ù‚Űčی Ù†Ű§ŰŻÛŒŰŻÙ‡ ÚŻŰ±ÙŰȘه ŰŽÙˆŰŻâ€Š + +
    +
    ✏ نمونه Ú©ŰŻ + +
    + +### :thumbsdown: Ù…Ű«Ű§Ù„ ۶ۯ Ű§Ù„ÚŻÙˆ: یک Ù…ÙˆŰ±ŰŻ ŰȘŰłŰȘی ŰšŰŻÙˆÙ† هیچ ŰŻÙ„ÛŒÙ„ Ù…ÙˆŰŹÙ‡ÛŒ Ù‚Ű·ŰčۧŰȘ ŰŻŰ§ŰźÙ„ÛŒ ۱ۧ ŰȘŰłŰȘ می Ú©Ù†ŰŻ + +![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "Examples with Mocha & Chai") + +```javascript +class ProductService { + //Ű§ÛŒÙ† مŰȘ۰ ÙÙ‚Ű· ۯ۱ ŰŻŰ§ŰźÙ„ ۧ۳ŰȘÙŰ§ŰŻÙ‡ می ŰŽÙˆŰŻ + //ŰȘŰșÛŒÛŒŰ± Ű§ÛŒÙ† Ù†Ű§Ù… ۚۧŰčŰ« می ŰŽÙˆŰŻ که ŰąŰČمون Ù‡Ű§ ۚۧ ŰŽÚ©ŰłŰȘ Ù…ÙˆŰ§ŰŹÙ‡ ŰŽÙˆÙ†ŰŻ + calculateVATAdd(priceWithoutVAT) { + return { finalPrice: priceWithoutVAT * 1.2 }; + //ŰȘŰșÛŒÛŒŰ± ÙŰ±Ù…ŰȘ نŰȘÛŒŰŹÙ‡ ÛŒŰ§ Ù†Ű§Ù… Ú©Ù„ÛŒŰŻ ŰšŰ§Ù„Ű§ ۚۧŰčŰ« ŰŽÚ©ŰłŰȘ ŰȘŰłŰȘ Ù‡Ű§ می ŰŽÙˆŰŻ + } + //مŰȘŰŻ Űčمومی + getPrice(productId) { + const desiredProduct = DB.getProduct(productId); + finalPrice = this.calculateVATAdd(desiredProduct.price).finalPrice; + return finalPrice; + } +} + +it("ŰȘŰłŰȘ ŰŹŰčŰšÙ‡ ŰłÙÛŒŰŻ: Ù‡Ù†ÚŻŰ§Ù…ÛŒ که Ű±ÙˆŰŽ Ù‡Ű§ÛŒ ŰŻŰ§ŰźÙ„ÛŒ 0 vat ŰŻŰ±ÛŒŰ§ÙŰȘ می Ú©Ù†Ù†ŰŻŰŒ 0 ÙŸŰ§ŰłŰź می ŰŻÙ‡ŰŻ", async () => { + //هیچ Ű§Ù„ŰČŰ§Ù…ÛŒ ŰšŰ±Ű§ÛŒ ۧۏۧŰČه ŰŻŰ§ŰŻÙ† ŰšÙ‡ Ú©Ű§Ű±ŰšŰ±Ű§Ù† ŰšŰ±Ű§ÛŒ Ù…Ű­Ű§ŰłŰšÙ‡ Ù…Ű§Ù„ÛŒŰ§ŰȘ ۚ۱ ۧ۱ŰČŰŽ Ű§ÙŰČÙˆŰŻÙ‡ ÙˆŰŹÙˆŰŻ Ù†ŰŻŰ§Ű±ŰŻŰŒ ÙÙ‚Ű· قیمŰȘ Ù†Ù‡Ű§ÛŒÛŒ ۱ۧ Ù†ŰŽŰ§Ù† می ŰŻÙ‡ŰŻ. ۚۧ Ű§ÛŒÙ† ÙˆŰŹÙˆŰŻŰŒ Ù…Ű§ ŰšÙ‡ ŰŻŰ±ÙˆŰș ۧ۔۱ۧ۱ ŰŻŰ§Ű±ÛŒÙ… که ŰŻŰ±ÙˆÙ†ÛŒ Ú©Ù„Ű§Űł ۱ۧ ŰąŰČÙ…Ű§ÛŒŰŽ کنیم + expect(new ProductService().calculateVATAdd(0).finalPrice).to.equal(0); +}); +``` + +
    + +

    + +## âšȘ  1.5 ŰŻÙˆ ۚ۱ۧۚ۱ ŰȘŰłŰȘ Ù…Ù†Ű§ŰłŰš ۱ۧ Ű§Ù†ŰȘ۟ۧۚ Ú©Ù†ÛŒŰŻ: ۧŰČ ŰȘÙ…ŰłŰźŰ± ŰšÙ‡ نفŰč ۟۱ۯ و ŰŹŰ§ŰłÙˆŰł ŰźÙˆŰŻŰŻŰ§Ű±ÛŒ Ú©Ù†ÛŒŰŻ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** ŰȘŰłŰȘ Ù‡Ű§ÛŒ ŰŻÙˆŰšÙ„ یک ێ۱ Ű¶Ű±ÙˆŰ±ÛŒ Ù‡ŰłŰȘÙ†ŰŻ ŰČÛŒŰ±Ű§ ۚۧ ۧۏŰČŰ§ÛŒ ŰŻŰ§ŰźÙ„ÛŒ ŰšŰ±Ù†Ű§Ù…Ù‡ Ù‡Ù…Ű±Ű§Ù‡ Ù‡ŰłŰȘÙ†ŰŻŰŒ Ű§Ù…Ű§ ŰšŰ±ŰźÛŒ ۧŰČ ŰąÙ†Ù‡Ű§ ۧ۱ŰČŰŽ ŰšŰłÛŒŰ§Ű± ŰČÛŒŰ§ŰŻÛŒ ۱ۧ Ű§Ű±Ű§ŰŠÙ‡ می ŰŻÙ‡Ù†ŰŻ ([ۯ۱ Ű§ÛŒÙ†ŰŹŰ§ یک ÛŒŰ§ŰŻŰąÙˆŰ±ÛŒ ۯ۱ Ù…ÙˆŰ±ŰŻ ŰȘŰłŰȘ ŰŻÙˆŰšÙ„ ŰšŰźÙˆŰ§Ù†ÛŒŰŻ: mocks vs stubs vs spies](https://martinfowler.com/articles/mocksArentStubs.html)). + +Ù‚ŰšÙ„ ۧŰČ Ű§ŰłŰȘÙŰ§ŰŻÙ‡ ۧŰČ ŰȘŰłŰȘ ŰŻÙˆŰšÙ„ŰŒ یک ŰłÙˆŰ§Ù„ ŰšŰłÛŒŰ§Ű± ŰłŰ§ŰŻÙ‡ ŰšÙŸŰ±ŰłÛŒŰŻ: ŰąÛŒŰ§ ۧŰČ ŰąÙ† ŰšŰ±Ű§ÛŒ ŰȘŰłŰȘ ŰčÙ…Ù„Ú©Ű±ŰŻÛŒ که ۯ۱ ŰłÙ†ŰŻ Ű§Ù„ŰČŰ§Ù…Ű§ŰȘ ŰžŰ§Ù‡Ű± می ŰŽÙˆŰŻ ÛŒŰ§ می ŰȘÙˆŰ§Ù†ŰŻ ŰžŰ§Ù‡Ű± ŰŽÙˆŰŻ ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ú©Ù†Ù…ŰŸ ۧگ۱ Ù†Ù‡ŰŒ ŰšÙˆÛŒ ŰȘŰłŰȘ ŰŹŰčŰšÙ‡ ŰłÙÛŒŰŻ ۧ۳ŰȘ. + +ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† Ù…Ű«Ű§Ù„ŰŒ ۧگ۱ Ù…ÛŒâ€ŒŰźÙˆŰ§Ù‡ÛŒŰŻ ŰąŰČÙ…Ű§ÛŒŰŽ Ú©Ù†ÛŒŰŻ که ŰšŰ±Ù†Ű§Ù…Ù‡â€ŒŰȘŰ§Ù† ۯ۱ Ù‡Ù†ÚŻŰ§Ù… Ù‚Ű·Űč ŰłŰ±ÙˆÛŒŰł ÙŸŰ±ŰŻŰ§ŰźŰȘی Ű±ÙŰȘۧ۱ مŰčقولی ŰŻŰ§Ű±ŰŻŰŒ ممکن ۧ۳ŰȘ ŰłŰ±ÙˆÛŒŰł ÙŸŰ±ŰŻŰ§ŰźŰȘ ۱ۧ ŰźŰ§Ù…ÙˆŰŽ Ú©Ù†ÛŒŰŻ و Ù…Ù‚ŰŻŰ§Ű±ÛŒ Â«ŰšŰŻÙˆÙ† ÙŸŰ§ŰłŰźÂ» ۱ۧ فŰčŰ§Ù„ Ú©Ù†ÛŒŰŻ ŰȘۧ Ù…Ű·Ù…ŰŠÙ† ŰŽÙˆÛŒŰŻ که ÙˆŰ§Ű­ŰŻ Ù…ÙˆŰ±ŰŻ ŰąŰČÙ…Ű§ÛŒŰŽ Ù…Ù‚ŰŻŰ§Ű± Ù…Ù†Ű§ŰłŰš ۱ۧ ŰšŰ±Ù…ÛŒâ€ŒÚŻŰ±ŰŻŰ§Ù†ŰŻ. Ű§ÛŒÙ† Ű±ÙŰȘۧ۱ / ÙŸŰ§ŰłŰź / نŰȘÛŒŰŹÙ‡ ŰšŰ±Ù†Ű§Ù…Ù‡ Ù…Ű§ ۱ۧ ŰȘŰ­ŰȘ ŰłÙ†Ű§Ű±ÛŒÙˆÙ‡Ű§ÛŒ ŰźŰ§Ű”ÛŒ ŰšŰ±Ű±ŰłÛŒ می Ú©Ù†ŰŻ. همچنین ممکن ۧ۳ŰȘ ۧŰČ ŰŹŰ§ŰłÙˆŰłÛŒ ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ú©Ù†ÛŒŰŻ ŰȘۧ ۧۯŰčۧ Ú©Ù†ÛŒŰŻ که Ű§ÛŒÙ…ÛŒÙ„ÛŒ ŰČÙ…Ű§Ù†ÛŒ Ű§Ű±ŰłŰ§Ù„ ŰŽŰŻÙ‡ ۧ۳ŰȘ که ŰąÙ† ŰłŰ±ÙˆÛŒŰł ۧŰČ Ú©Ű§Ű± Ű§ÙŰȘŰ§ŰŻÙ‡ ۧ۳ŰȘâ€”â€”â€”Ű§ÛŒÙ† ŰŻÙˆŰšŰ§Ű±Ù‡ یک ŰšŰ±Ű±ŰłÛŒ Ű±ÙŰȘŰ§Ű±ÛŒ ۧ۳ŰȘ که ۭۧŰȘÙ…Ű§Ù„Ű§Ù‹ ۯ۱ ŰłÙ†ŰŻ Ű§Ù„ŰČŰ§Ù…Ű§ŰȘ ŰžŰ§Ù‡Ű± Ù…ÛŒâ€ŒŰŽÙˆŰŻ («ۧگ۱ ÙŸŰ±ŰŻŰ§ŰźŰȘ Ű§Ù…Ú©Ű§Ù† Ű°ŰźÛŒŰ±Ù‡ Ù†ŰŽŰŻÂ» Ű±Ű§ÛŒŰ§Ù†Ű§Ù…Ù‡ Ű§Ű±ŰłŰ§Ù„ Ú©Ù†ÛŒŰŻ. ۧŰČ Ű·Ű±Ù ŰŻÛŒÚŻŰ±ŰŒ ۧگ۱ ŰłŰ±ÙˆÛŒŰł ÙŸŰ±ŰŻŰ§ŰźŰȘ ۱ۧ Ù…ŰłŰźŰ±Ù‡ Ù…ÛŒâ€ŒÚ©Ù†ÛŒŰŻ و Ù…Ű·Ù…ŰŠÙ† Ù…ÛŒâ€ŒŰŽÙˆÛŒŰŻ که ۚۧ Ű§Ù†ÙˆŰ§Űč ŰŹŰ§ÙˆŰ§ Ű§ŰłÚ©Ű±ÛŒÙŸŰȘ Ù…Ù†Ű§ŰłŰš ÙŰ±Ű§ŰźÙˆŰ§Ù†ÛŒ ŰŽŰŻÙ‡ ۧ۳ŰȘ — ۱ŰČÙ…Ű§ÛŒŰŽ ŰŽÙ…Ű§ Ű±ÙˆÛŒ چیŰČÙ‡Ű§ÛŒ ŰŻŰ§ŰźÙ„ÛŒ مŰȘÙ…Ű±Ú©ŰČ Ű§ŰłŰȘ که هیچ Ű±ŰšŰ·ÛŒ ŰšÙ‡ ŰčÙ…Ù„Ú©Ű±ŰŻ ŰšŰ±Ù†Ű§Ù…Ù‡ Ù†ŰŻŰ§Ű±Ù†ŰŻ و ۭۧŰȘÙ…Ű§Ù„Ű§Ù‹ ۧŰșÙ„Űš ŰȘŰșÛŒÛŒŰ± Ù…ÛŒâ€ŒÚ©Ù†Ù†ŰŻ. +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** Ù‡Ű± ÚŻÙˆÙ†Ù‡ ۚۧŰČ۳ۧŰČي ÙƒŰŻŰŒ ŰŹŰłŰȘ و ŰŹÙˆÙŠ ŰȘÙ…Ű§Ù…ÙŠ ۳ۧ۟ŰȘÚŻÙŠ Ù‡Ű§ÙŠ Ù…ÙˆŰŹÙˆŰŻ ۯ۱ ÙƒŰŻ و ŰšÙ‡ Ű±ÙˆŰČ ÙƒŰ±ŰŻÙ† ŰąÙ† ۱ۧ Ű§Ù„ŰČŰ§Ù…ÙŠ مي ÙƒÙ†ŰŻ. ŰȘŰłŰȘ Ù‡Ű§ ŰšÙ‡ ŰŹŰ§ÛŒ یک ŰŻÙˆŰłŰȘ Ù…ÙÛŒŰŻ ŰȘŰšŰŻÛŒÙ„ ŰšÙ‡ یک ۚۧ۱ می ŰŽÙˆÙ†ŰŻ + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :thumbsdown: Ù…Ű«Ű§Ù„ ۶ۯ Ű§Ù„ÚŻÙˆ: mock Ù‡Ű§ ۚ۱ Ű±ÙˆÛŒ ŰŻŰ§ŰźÙ„ÛŒ ŰȘÙ…Ű±Ú©ŰČ Ù…ÛŒ Ú©Ù†Ù†ŰŻ + +![](https://img.shields.io/badge/🔧%20Example%20using%20Sinon-blue.svg "Examples with Sinon") + +```javascript +it("Ù‡Ù†ÚŻŰ§Ù…ÛŒ که یک Ù…Ű­Ű”ÙˆÙ„ مŰčŰȘۚ۱ ۯ۱ ŰŽŰ±Ù Ű­Ű°Ù ۧ۳ŰȘی Ù…Ű·Ù…ŰŠÙ† ŰŽÙˆÛŒŰŻ که DAL یک ۚۧ۱ ۚۧ Ù…Ű­Ű”ÙˆÙ„ Ù…Ù†Ű§ŰłŰš و ÙŸÛŒÚ©Ű±ŰšÙ†ŰŻÛŒ Ù…Ù†Ű§ŰłŰš ŰȘÙ…Ű§Űł ÚŻŰ±ÙŰȘه ŰŽŰŻÙ‡ ۧ۳ŰȘ.", async () => { + //ÙŰ±Ű¶ Ú©Ù†ÛŒŰŻ Ù‚ŰšÙ„Ű§Ù‹ یک Ù…Ű­Ű”ÙˆÙ„ Ű§Ű¶Ű§ÙÙ‡ Ú©Ű±ŰŻÙ‡ Ű§ÛŒÙ… + const dataAccessMock = sinon.mock(DAL); + //هومممم ۚۯ ۧ۳ŰȘ: ŰȘŰłŰȘ Ù‚Ű·ŰčۧŰȘ ŰŻŰ§ŰźÙ„ÛŒ ۯ۱ ÙˆŰ§Ù‚Űč Ù‡ŰŻÙ Ű§Ű”Ù„ÛŒ Ù…Ű§ ۯ۱ Ű§ÛŒÙ†ŰŹŰ§ ۧ۳ŰȘی نه ÙÙ‚Ű· یک side effect + dataAccessMock + .expects("deleteProduct") + .once() + .withArgs(DBConfig, theProductWeJustAdded, true, false); + new ProductService().deletePrice(theProductWeJustAdded); + dataAccessMock.verify(); +}); +``` + +
    + +### :clap:Ù…Ű«Ű§Ù„ ۯ۱۳ŰȘ: ŰŹŰ§ŰłÙˆŰłŰ§Ù† ۚ۱ Ű±ÙˆÛŒ ŰȘŰłŰȘ Ű§Ù„ŰČŰ§Ù…Ű§ŰȘ مŰȘÙ…Ű±Ú©ŰČ Ù‡ŰłŰȘÙ†ŰŻŰŒ Ű§Ù…Ű§ ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† یک side effectی ŰšÙ‡ Ű·ÙˆŰ± ۧۏŰȘÙ†Ű§Űš Ù†Ű§ÙŸŰ°ÛŒŰ±ÛŒ ŰšÙ‡ ŰŻŰ±ÙˆÙ†ÛŒ Ù‡Ű§ ŰŻŰłŰȘ می ŰČÙ†Ù†ŰŻ. + +```javascript +it("Ù‡Ù†ÚŻŰ§Ù…ÛŒ که یک Ù…Ű­Ű”ÙˆÙ„ مŰčŰȘۚ۱ ۯ۱ ŰŽŰ±Ù Ű­Ű°Ù ۧ۳ŰȘی Ù…Ű·Ù…ŰŠÙ† ŰŽÙˆÛŒŰŻ که یک Ű§ÛŒÙ…ÛŒÙ„ Ű§Ű±ŰłŰ§Ù„ ŰŽŰŻÙ‡ ۧ۳ŰȘ", async () => { + //ÙŰ±Ű¶ Ú©Ù†ÛŒŰŻ Ù‚ŰšÙ„Ű§Ù‹ یک Ù…Ű­Ű”ÙˆÙ„ ۱ۧ ۯ۱ Ű§ÛŒÙ†ŰŹŰ§ Ű§Ű¶Ű§ÙÙ‡ Ú©Ű±ŰŻÙ‡ Ű§ÛŒÙ… + const spy = sinon.spy(Emailer.prototype, "sendEmail"); + new ProductService().deletePrice(theProductWeJustAdded); + //هومممم ŰźÙˆŰš ۧ۳ŰȘ: Ù…Ű§ ۚۧ ŰŻŰ§ŰźÙ„ÛŒ ŰłŰ±ÙˆÚ©Ű§Ű± ŰŻŰ§Ű±ÛŒÙ…ŰŸ ŰšÙ„Ù‡ŰŒ Ű§Ù…Ű§ ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† یک side effect ŰȘŰłŰȘ Ű§Ù„ŰČŰ§Ù…Ű§ŰȘ (Ű§Ű±ŰłŰ§Ù„ Ű§ÛŒÙ…ÛŒÙ„) + expect(spy.calledOnce).to.be.true; +}); +``` + +
    + +

    + +## 📗 ŰąÛŒŰ§ می ŰźÙˆŰ§Ù‡ÛŒŰŻ همه Ű§ÛŒÙ† ŰȘÙ…Ű±ÛŒÙ† Ù‡Ű§ ۱ۧ ۚۧ ÙˆÛŒŰŻÛŒÙˆÛŒ ŰČÙ†ŰŻÙ‡ ÛŒŰ§ŰŻ ŰšÚŻÛŒŰ±ÛŒŰŻŰŸ + +###ۧŰČ ŰŻÙˆŰ±Ù‡ ŰąÙ†Ù„Ű§ÛŒÙ† من ŰŻÛŒŰŻÙ† Ú©Ù†ÛŒŰŻ [Testing Node.js & JavaScript ۧŰČ A ŰȘۧ Z](https://www.testjavascript.com) + +

    + +## âšȘ 1.6 Â«ÚŻÙˆÙ„ نŰČÙ†ÛŒŰŻÂ»ŰŒ ۧŰČ ŰŻŰ§ŰŻÙ‡ Ù‡Ű§ÛŒ ÙˆŰ±ÙˆŰŻÛŒ ÙˆŰ§Ù‚Űčی ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ú©Ù†ÛŒŰŻ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** ۧŰșÙ„Űš Ű§ŰŽÚ©Ű§Ù„Ű§ŰȘ ŰȘÙˆÙ„ÛŒŰŻ ŰȘŰ­ŰȘ ŰšŰ±ŰźÛŒ ÙˆŰ±ÙˆŰŻÛŒâ€ŒÙ‡Ű§ÛŒ ŰšŰłÛŒŰ§Ű± ۟ۧ۔ و ŰŽÚŻÙŰȘâ€ŒŰ§Ù†ÚŻÛŒŰČ ŰąŰŽÚ©Ű§Ű± Ù…ÛŒâ€ŒŰŽÙˆÙ†ŰŻâ€”â€ŠÙ‡Ű±Ú†Ù‡ ÙˆŰ±ÙˆŰŻÛŒ ŰąŰČÙ…Ű§ÛŒŰŽ ÙˆŰ§Ù‚Űčی‌ŰȘ۱ ۹ۧۮۯی ŰŽŰ§Ù†Űł ŰȘŰŽŰźÛŒŰ” ŰČÙˆŰŻÙ‡Ù†ÚŻŰ§Ù… ŰšŰ§ÚŻâ€ŒÙ‡Ű§ ŰšÛŒŰŽŰȘ۱ Ù…ÛŒâ€ŒŰŽÙˆŰŻ. ۧŰČ Ú©ŰȘŰ§ŰšŰźŰ§Ù†Ù‡â€ŒÙ‡Ű§ÛŒ ۧ۟ŰȘŰ”Ű§Ű”ÛŒ Ù…Ű§Ù†Ù†ŰŻ [Chance](https://github.com/chancejs/chancejs) ÛŒŰ§ [Faker](https://www.npmjs.com/package/faker) ŰšŰ±Ű§ÛŒ ŰȘÙˆÙ„ÛŒŰŻ ŰŻŰ§ŰŻÙ‡â€ŒÙ‡Ű§ÛŒ ŰŽŰšÙ‡ ÙˆŰ§Ù‚Űčی که ŰŽŰšÛŒÙ‡ Ű§Ù†ÙˆŰ§Űč و Ű§Ù‚ŰłŰ§Ù… ŰąÙ†â€ŒÙ‡Ű§ Ù‡ŰłŰȘÙ†ŰŻŰŒ ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ú©Ù†ÛŒŰŻ. ŰŽÚ©Ù„ ŰŻŰ§ŰŻÙ‡ Ù‡Ű§ÛŒ ŰȘÙˆÙ„ÛŒŰŻ ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† Ù…Ű«Ű§Ù„ŰŒ چنین Ú©ŰȘŰ§ŰšŰźŰ§Ù†Ù‡â€ŒÙ‡Ű§ÛŒÛŒ می‌ŰȘÙˆŰ§Ù†Ù†ŰŻ ŰŽÙ…Ű§Ű±Ù‡ ŰȘÙ„ÙÙ†ŰŒ Ù†Ű§Ù… Ú©Ű§Ű±ŰšŰ±ÛŒŰŒ کۧ۱ŰȘ ۧŰčŰȘŰšŰ§Ű±ÛŒŰŒ Ù†Ű§Ù… ێ۱کŰȘ و Ű­ŰȘی مŰȘن «lorem ipsum» ÙˆŰ§Ù‚Űčی ۱ۧ ŰȘÙˆÙ„ÛŒŰŻ Ú©Ù†Ù†ŰŻ. همچنین می‌ŰȘÙˆŰ§Ù†ÛŒŰŻ ŰąŰČÙ…Ű§ÛŒŰŽâ€ŒÙ‡Ű§ÛŒÛŒ (ۯ۱ ŰšŰ§Ù„Ű§ÛŒ ŰąŰČÙ…Ű§ÛŒŰŽâ€ŒÙ‡Ű§ÛŒ ÙˆŰ§Ű­ŰŻŰŒ نه ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† ŰŹŰ§ÛŒÚŻŰČین) Ű§ÛŒŰŹŰ§ŰŻ Ú©Ù†ÛŒŰŻ که ŰŻŰ§ŰŻÙ‡â€ŒÙ‡Ű§ÛŒ ŰŹŰčلی ۱ۧ ŰȘŰ”Ű§ŰŻÙÛŒ Ù…ÛŒâ€ŒÚ©Ù†ŰŻ ŰȘۧ ÙˆŰ§Ű­ŰŻ ŰŽÙ…Ű§ ŰȘŰ­ŰȘ ŰąŰČÙ…Ű§ÛŒŰŽ Ú©ŰŽÛŒŰŻÙ‡ ŰŽÙˆŰŻ ÛŒŰ§ Ű­ŰȘی ŰŻŰ§ŰŻÙ‡â€ŒÙ‡Ű§ÛŒ ÙˆŰ§Ù‚Űčی ۱ۧ ۧŰČ Ù…Ű­ÛŒŰ· ŰȘÙˆÙ„ÛŒŰŻ ŰŽÙ…Ű§ ÙˆŰ§Ű±ŰŻ Ú©Ù†ŰŻ. می ŰźÙˆŰ§Ù‡ÛŒŰŻ ŰąÙ† ۱ۧ ŰšÙ‡ ۳۷ۭ ŰšŰčŰŻÛŒ ŰšŰšŰ±ÛŒŰŻŰŸ ÚŻÙ„ÙˆÙ„Ù‡ ŰšŰčŰŻÛŒ (ŰȘŰłŰȘ Ù…ŰšŰȘنی ۚ۱ ÙˆÛŒÚ˜ÚŻÛŒ) ۱ۧ ŰšŰšÛŒÙ†ÛŒŰŻ. +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** وقŰȘی ۧŰČ ÙˆŰ±ÙˆŰŻÛŒ Ù‡Ű§ÛŒ Ù…Ű”Ù†ÙˆŰčی Ù…Ű§Ù†Ù†ŰŻ "Foo" ۧ۳ŰȘÙŰ§ŰŻÙ‡ می Ú©Ù†ÛŒŰŻŰŒ ŰȘÙ…Ű§Ù… ŰąŰČÙ…Ű§ÛŒŰŽ Ù‡Ű§ÛŒ ŰȘÙˆŰłŰčه ŰŽÙ…Ű§ ŰšÙ‡ ۧێŰȘŰšŰ§Ù‡ ۳ۚŰČ Ù†ŰŽŰ§Ù† ŰŻŰ§ŰŻÙ‡ می ŰŽÙˆÙ†ŰŻŰŒ Ű§Ù…Ű§ ŰČÙ…Ű§Ù†ÛŒ که Ù‡Ú©Ű± ۱ێŰȘه ŰšŰŻÛŒ Ù…Ű§Ù†Ù†ŰŻ "@3e2ddsf" ۱ۧ ŰčŰšÙˆŰ± می ŰŻÙ‡ŰŻŰŒ ممکن ۧ۳ŰȘ ŰȘÙˆÙ„ÛŒŰŻ Ù‚Ű±Ù…ŰČ ŰŽÙˆŰŻ. ##' 1 fdsfds . fds432 AAAA” + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :thumbsdown: Ù…Ű«Ű§Ù„ ۶ۯ Ű§Ù„ÚŻÙˆ: Ù…ŰŹÙ…ÙˆŰčه ŰąŰČÙ…Ű§ÛŒŰŽÛŒ که ŰšÙ‡ ŰŻÙ„ÛŒÙ„ ŰŻŰ§ŰŻÙ‡ Ù‡Ű§ÛŒ ŰșÛŒŰ±ÙˆŰ§Ù‚Űčی Ù‚ŰšÙˆÙ„ می ŰŽÙˆŰŻ + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Jest") + +```javascript +const addProduct = (name, price) => { + const productNameRegexNoSpace = /^\S*$/; //space Ù…ŰŹŰ§ŰČ Ù†ÛŒŰłŰȘ + + if (!productNameRegexNoSpace.test(name)) return false; //Ű§ÛŒÙ† ێ۱۷ ŰšÙ‡ ŰŻÙ„ÛŒÙ„ ÙˆŰ±ÙˆŰŻÛŒ Ù†Ű§ŰŻŰ±ŰłŰȘ Ù‡Ű±ÚŻŰČ Ű§ŰŹŰ±Ű§ نمی ŰŽÙˆŰŻ + + //ŰšÙ‚ÛŒÙ‡ Ú©ŰŻ Ù‡Ű§ + return true; +}; + +test("ۧێŰȘŰšŰ§Ù‡: Ù‡Ù†ÚŻŰ§Ù… Ű§ÙŰČÙˆŰŻÙ† Ù…Ű­Ű”ÙˆÙ„ ŰŹŰŻÛŒŰŻ ۚۧ ÙˆÛŒÚ˜ÚŻÛŒ Ù‡Ű§ÛŒ مŰčŰȘŰšŰ±ŰŒ ŰȘŰŁÛŒÛŒŰŻ موفقیŰȘ ŰąÙ…ÛŒŰČ ŰŻŰ±ÛŒŰ§ÙŰȘ Ú©Ù†ÛŒŰŻ", async () => { + //۱ێŰȘه "Foo" که ۯ۱ همه ŰȘŰłŰȘ Ù‡Ű§ ۧ۳ŰȘÙŰ§ŰŻÙ‡ می ŰŽÙˆŰŻŰŒ Ù‡Ű±ÚŻŰČ Ù†ŰȘÛŒŰŹÙ‡ Ù†Ű§ŰŻŰ±ŰłŰȘی ۱ۧ Ű§ÛŒŰŹŰ§ŰŻ نمی Ú©Ù†ŰŻ + const addProductResult = addProduct("Foo", 5); + expect(addProductResult).toBe(true); + //Ù…Ű«ŰšŰȘ-کۧ۰ۚ: ŰčÙ…Ù„ÛŒŰ§ŰȘ موفقیŰȘ ŰąÙ…ÛŒŰČ ŰšÙˆŰŻ ŰČÛŒŰ±Ű§ Ù…Ű§ Ù‡Ű±ÚŻŰČ ŰȘÙ„Ű§ŰŽ ŰČÛŒŰ§ŰŻÛŒ Ù†Ú©Ű±ŰŻÛŒÙ… + //Ù†Ű§Ù… Ù…Ű­Ű”ÙˆÙ„ ŰŽŰ§Ù…Ù„ ÙŰ§Ű”Ù„Ù‡ Ù‡Ű§ +}); +``` + +
    + +### :clap:Ù…Ű«Ű§Ù„ ۯ۱۳ŰȘ:ŰȘŰ”Ű§ŰŻÙÛŒ ۳ۧŰČی ÙˆŰ±ÙˆŰŻÛŒ ÙˆŰ§Ù‚Űčی + +```javascript +it("ŰšÙ‡ŰȘ۱: Ù‡Ù†ÚŻŰ§Ù… Ű§ÙŰČÙˆŰŻÙ† Ù…Ű­Ű”ÙˆÙ„ مŰčŰȘۚ۱ ŰŹŰŻÛŒŰŻŰŒ ŰȘŰŁÛŒÛŒŰŻ موفقیŰȘ ŰąÙ…ÛŒŰČ ŰŻŰ±ÛŒŰ§ÙŰȘ Ú©Ù†ÛŒŰŻ", async () => { + const addProductResult = addProduct(faker.commerce.productName(), faker.random.number()); + //ÙˆŰ±ÙˆŰŻÛŒ ŰȘŰ”Ű§ŰŻÙÛŒ Ű§ÛŒŰŹŰ§ŰŻ ŰŽŰŻÙ‡: {'Sleek Cotton Computer', 85481} + expect(addProductResult).to.be.true; + //ŰȘŰłŰȘ Ù†Ű§Ù…ÙˆÙÙ‚ ŰšÙˆŰŻŰŒ ÙˆŰ±ÙˆŰŻÛŒ ŰȘŰ”Ű§ŰŻÙÛŒ Ù…ŰłÛŒŰ±ÛŒ ۱ۧ ŰąŰșۧŰČ Ú©Ű±ŰŻ که Ù…Ű§ Ù‡Ű±ÚŻŰČ ŰšŰ±Ű§ÛŒ ŰąÙ† ŰšŰ±Ù†Ű§Ù…Ù‡ Ű±ÛŒŰČی Ù†Ú©Ű±ŰŻÙ‡ ŰšÙˆŰŻÛŒÙ…. + //Ù…Ű§ یک ۚۧگ ۱ۧ ŰČÙˆŰŻ Ú©ŰŽÙ Ú©Ű±ŰŻÛŒÙ…! +}); +``` + +
    + +

    + +## âšȘ  1.7 ŰšŰłÛŒŰ§Ű±ÛŒ ۧŰČ ŰȘŰ±Ú©ÛŒŰšŰ§ŰȘ ÙˆŰ±ÙˆŰŻÛŒ ۱ۧ ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ ŰȘŰłŰȘ Ù…ŰšŰȘنی ۚ۱ ÙˆÛŒÚ˜ÚŻÛŒ ŰȘŰłŰȘ Ú©Ù†ÛŒŰŻ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** مŰčÙ…ÙˆÙ„Ű§Ù‹ ŰšŰ±Ű§ÛŒ Ù‡Ű± ŰąŰČمون Ú†Ù†ŰŻ نمونه ÙˆŰ±ÙˆŰŻÛŒ Ű§Ù†ŰȘ۟ۧۚ می کنیم. Ű­ŰȘی ŰČÙ…Ű§Ù†ÛŒ که Ù‚Ű§Ù„Űš ÙˆŰ±ÙˆŰŻÛŒ ŰŽŰšÛŒÙ‡ ŰŻŰ§ŰŻÙ‡ Ù‡Ű§ÛŒ ŰŻÙ†ÛŒŰ§ÛŒ ÙˆŰ§Ù‚Űčی ۚۧێۯ (ŰšÙ‡ ÚŻÙ„ÙˆÙ„Ù‡ Ù…Ű±Ű§ŰŹŰčه Ú©Ù†ÛŒŰŻ [â€˜ÚŻÙˆÙ„ نŰČن’](https://github.com/goldbergyoni/javascript-testing-best-practices#-%EF%B8%8F16-dont-foo-use-realistic-input-data)), Ù…Ű§ ÙÙ‚Ű· Ú†Ù†ŰŻ ŰȘŰ±Ú©ÛŒŰš ÙˆŰ±ÙˆŰŻÛŒ ۱ۧ ÙŸÙˆŰŽŰŽ Ù…ÛŒâ€ŒŰŻÙ‡ÛŒÙ… (method(''ی trueی 1)ی Ű±ÙˆŰŽ ("string"ی falseی 0))ی ۚۧ Ű§ÛŒÙ† Ű­Ű§Ù„ŰŒ ۯ۱ ŰȘÙˆÙ„ÛŒŰŻŰŒ یک API که ۚۧ 5 ÙŸŰ§Ű±Ű§Ù…ŰȘ۱ ÙŰ±Ű§ŰźÙˆŰ§Ù†ÛŒ می ŰŽÙˆŰŻ ۱ۧ می ŰȘÙˆŰ§Ù† ۚۧ هŰČŰ§Ű±Ű§Ù† ÙŸŰ§Ű±Ű§Ù…ŰȘ۱ Ù…ŰźŰȘلف ÙŰ±Ű§ŰźÙˆŰ§Ù†ÛŒ ک۱ۯ. ŰŹŰ§ÛŒÚŻŰŽŰȘی یکی ۧŰČ ŰąÙ†Ù‡Ű§ ممکن ۧ۳ŰȘ ÙŰ±ŰąÛŒÙ†ŰŻ Ù…Ű§ ۱ۧ ÙŸŰ§ÛŒÛŒÙ† ŰšÛŒŰ§ÙˆŰ±ŰŻ ([ŰȘŰłŰȘ ÙŰ§ŰČ Ű±Ű§ ŰšŰšÛŒÙ†ÛŒŰŻ](https://en.wikipedia.org/wiki/Fuzzing)). ۧگ۱ ŰšŰȘÙˆŰ§Ù†ÛŒŰŻ یک ŰȘŰłŰȘ ŰšÙ†ÙˆÛŒŰłÛŒŰŻ که 1000 ŰŹŰ§ÛŒÚŻŰŽŰȘ ۧŰČ ÙˆŰ±ÙˆŰŻÛŒ Ù‡Ű§ÛŒ Ù…ŰźŰȘلف ۱ۧ ŰšÙ‡ Ű·ÙˆŰ± ŰźÙˆŰŻÚ©Ű§Ű± Ű§Ű±ŰłŰ§Ù„ می Ú©Ù†ŰŻ و Ú©ŰŻ Ù…Ű§ ŰšŰ±Ű§ÛŒ Ú©ŰŻŰ§Ù… ÙˆŰ±ÙˆŰŻÛŒ ÙŸŰ§ŰłŰź Ù…Ù†Ű§ŰłŰš ۱ۧ نمی ŰŻÙ‡ŰŻŰŸ ŰȘŰłŰȘ Ù…ŰšŰȘنی ۚ۱ ÙˆÛŒÚ˜ÚŻÛŒ ŰȘکنیکی ۧ۳ŰȘ که ŰŻÙ‚ÛŒÙ‚Ű§Ù‹ همین کۧ۱ ۱ۧ Ű§Ù†ŰŹŰ§Ù… می ŰŻÙ‡ŰŻ: ۚۧ Ű§Ű±ŰłŰ§Ù„ ŰȘÙ…Ű§Ù… ŰȘŰ±Ú©ÛŒŰšŰ§ŰȘ ÙˆŰ±ÙˆŰŻÛŒ ممکن ŰšÙ‡ ÙˆŰ§Ű­ŰŻ ŰȘŰ­ŰȘ ŰąŰČÙ…Ű§ÛŒŰŽ ŰŽÙ…Ű§ŰŒ Ù…ŰŽÚ©Ù„ ÛŒŰ§ÙŰȘن یک ۚۧگ ۱ۧ Ű§ÙŰČŰ§ÛŒŰŽ می ŰŻÙ‡ŰŻ. ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† Ù…Ű«Ű§Ù„ŰŒ ۚۧ ŰȘÙˆŰŹÙ‡ ŰšÙ‡ یک مŰȘŰŻ —  addNewProduct(id, name, isDiscount)—— کŰȘŰ§ŰšŰźŰ§Ù†Ù‡ Ù‡Ű§ÛŒ ÙŸŰŽŰȘÛŒŰšŰ§Ù†ÛŒ Ú©Ù†Ù†ŰŻÙ‡ Ű§ÛŒÙ† مŰȘŰŻ ۱ۧ ۚۧ ŰȘŰ±Ú©ÛŒŰš Ù‡Ű§ÛŒ ŰČÛŒŰ§ŰŻÛŒ ۧŰČ (Űčۯۯی ۱ێŰȘÙ‡ŰŒ ŰšÙˆÙ„ÛŒ) Ù…Ű§Ù†Ù†ŰŻ (1ی "iPhone"ی false)ی (2ی "Galaxy" ÙŰ±Ű§ŰźÙˆŰ§Ù†ÛŒ می Ú©Ù†Ù†ŰŻ. "ی ۯ۱۳ŰȘ ۧ۳ŰȘی ÙˆŰ§Ù‚Űčی). ŰŽÙ…Ű§ می ŰȘÙˆŰ§Ù†ÛŒŰŻ ŰȘŰłŰȘ Ù…ŰšŰȘنی ۚ۱ ÙˆÛŒÚ˜ÚŻÛŒ ۱ۧ ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ ŰšŰ±Ù†Ű§Ù…Ù‡ ŰąŰČÙ…Ű§ÛŒŰŽÛŒ Ù…ÙˆŰ±ŰŻ ŰčÙ„Ű§Ù‚Ù‡ ŰźÙˆŰŻ (Ù…ÙˆÚ©Ű§ŰŒ ŰŹŰłŰȘ و ŰșÛŒŰ±Ù‡) ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ Ú©ŰȘŰ§ŰšŰźŰ§Ù†Ù‡ Ù‡Ű§ÛŒÛŒ Ù…Ű§Ù†Ù†ŰŻ [js-verify](https://github.com/jsverify/jsverify) ÛŒŰ§ [ŰšŰ±Ű±ŰłÛŒ ŰȘŰłŰȘ](https://github.com/leebyron/testcheck-js) (Ù…ŰłŰȘÙ†ŰŻŰ§ŰȘ ŰźÛŒÙ„ÛŒ ŰšÙ‡ŰȘ۱). ŰšÙ‡ Ű±ÙˆŰČ Ű±ŰłŰ§Ù†ÛŒ: Nicolas Dubien ۯ۱ Ù†ŰžŰ±Ű§ŰȘ ŰČÛŒŰ± ŰšÙ‡[ŰȘŰłÙˆÛŒÙ‡ ۭ۳ۧۚ ŰłŰ±ÛŒŰč](https://github.com/dubzzz/fast-check#readme) که ŰšÙ‡ Ù†ŰžŰ± می ۱۳ۯ ŰšŰ±ŰźÛŒ ۧŰČ ÙˆÛŒÚ˜ÚŻÛŒ Ù‡Ű§ÛŒ Ű§Ű¶Ű§ÙÛŒ ۱ۧ Ű§Ű±Ű§ŰŠÙ‡ می ŰŻÙ‡ŰŻ و همچنین ŰšÙ‡ Ű·ÙˆŰ± فŰčŰ§Ù„ Ű­ÙŰž می ŰŽÙˆŰŻ +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** Ù†Ű§ŰźÙˆŰŻŰąÚŻŰ§Ù‡ŰŒ ÙˆŰ±ÙˆŰŻÛŒâ€ŒÙ‡Ű§ÛŒ ŰȘŰłŰȘ ۱ۧ Ű§Ù†ŰȘ۟ۧۚ Ù…ÛŒâ€ŒÚ©Ù†ÛŒŰŻ که ÙÙ‚Ű· Ù…ŰłÛŒŰ±Ù‡Ű§ÛŒ Ú©ŰŻÛŒ ۱ۧ ÙŸÙˆŰŽŰŽ Ù…ÛŒâ€ŒŰŻÙ‡Ù†ŰŻ که ŰšÙ‡ ŰźÙˆŰšÛŒ کۧ۱ Ù…ÛŒâ€ŒÚ©Ù†Ù†ŰŻ. مŰȘŰŁŰłÙŰ§Ù†Ù‡ŰŒ Ű§ÛŒÙ† Ú©Ű§Ű±Ű§ÛŒÛŒ ŰȘŰłŰȘ ۱ۧ ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† ÙˆŰłÛŒÙ„Ù‡ Ű§ÛŒ ŰšŰ±Ű§ÛŒ Ű§ÙŰŽŰ§ÛŒ Ű§ŰŽÚ©Ű§Ù„Ű§ŰȘ Ú©Ű§Ù‡ŰŽ می ŰŻÙ‡ŰŻ + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :clap: Ù…Ű«Ű§Ù„ ۯ۱۳ŰȘ: ŰąŰČÙ…Ű§ÛŒŰŽ ŰšŰłÛŒŰ§Ű±ÛŒ ۧŰČ ŰŹŰ§ÛŒÚŻŰŽŰȘ Ù‡Ű§ÛŒ ÙˆŰ±ÙˆŰŻÛŒ ۚۧ "ŰšŰ±Ű±ŰłÛŒ ŰłŰ±ÛŒŰč" + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Jest") + +```javascript +import fc from "fast-check"; + +describe("ŰźŰŻÙ…Ű§ŰȘ Ù…Ű­Ű”ÙˆÙ„", () => { + describe("Ű§ÙŰČÙˆŰŻÙ† ŰŹŰŻÛŒŰŻ", () => { + //Ű§ÛŒÙ† 100 ۚۧ۱ ۚۧ ÙˆÛŒÚ˜ÚŻÛŒ Ù‡Ű§ÛŒ ŰȘŰ”Ű§ŰŻÙÛŒ Ù…ŰźŰȘلف ۧۏ۱ۧ می ŰŽÙˆŰŻ + it("Ù…Ű­Ű”ÙˆÙ„ ŰŹŰŻÛŒŰŻ ۚۧ ÙˆÛŒÚ˜ÚŻÛŒ Ù‡Ű§ÛŒ ŰȘŰ”Ű§ŰŻÙÛŒ و ۯ۱ Űčین Ű­Ű§Ù„ مŰčŰȘŰšŰ±ŰŒ Ù‡Ù…ÛŒŰŽÙ‡ موفق Ű§Ű¶Ű§ÙÙ‡ Ú©Ù†ÛŒŰŻ", () => + fc.assert( + fc.property(fc.integer(), fc.string(), (id, name) => { + expect(addNewProduct(id, name).status).toEqual("approved"); + }) + )); + }); +}); +``` + +
    + +

    + +## âšȘ  1.8 ۯ۱ Ű”ÙˆŰ±ŰȘ Ù†ÛŒŰ§ŰČی ÙÙ‚Ű· ۧŰČ snapshots کوŰȘŰ§Ù‡ و ŰŻŰ±ÙˆÙ† ŰźŰ·ÛŒ ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ú©Ù†ÛŒŰŻ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** ŰČÙ…Ű§Ù†ÛŒ که Ù†ÛŒŰ§ŰČ ŰšŰ§ŰŽŰŻ [snapshot ŰȘŰłŰȘ Ú©Ű±ŰŻÙ†](https://jestjs.io/docs/en/snapshot-testing), ÙÙ‚Ű· ۧŰČ ŰčÚ©Űł Ù‡Ű§ÛŒ ÙÙˆŰ±ÛŒ کوŰȘŰ§Ù‡ و مŰȘÙ…Ű±Ú©ŰČ Ű§ŰłŰȘÙŰ§ŰŻÙ‡ Ú©Ù†ÛŒŰŻ (i.e. 3-7 ۟۷) که ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† ŰšŰźŰŽÛŒ ۧŰČ ŰąŰČمون ÚŻÙ†ŰŹŰ§Ù†ŰŻÙ‡ ŰŽŰŻÙ‡ ۧ۳ŰȘ ([Inline Snapshot](https://jestjs.io/docs/en/snapshot-testing#inline-snapshots)) و نه ۯ۱ ÙŰ§ÛŒÙ„ Ù‡Ű§ÛŒ ŰźŰ§Ű±ŰŹÛŒ. ۱ŰčŰ§ÛŒŰȘ Ű§ÛŒÙ† ŰŻŰłŰȘÙˆŰ±Ű§Ù„Űčمل ŰȘŰ¶Ù…ÛŒÙ† Ù…ÛŒâ€ŒÚ©Ù†ŰŻ که ŰȘŰłŰȘâ€ŒÙ‡Ű§ÛŒ ŰŽÙ…Ű§ Ù‚Ű§ŰšÙ„ ŰȘÙˆŰ¶ÛŒŰ­ و کمŰȘ۱ ŰŽÚ©Ù†Ù†ŰŻÙ‡ Ù‡ŰłŰȘÙ†ŰŻ. + +ۧŰČ ŰłÙˆÛŒ ŰŻÛŒÚŻŰ±ŰŒ ŰąÙ…ÙˆŰČŰŽâ€ŒÙ‡Ű§ و ۧۚŰČŰ§Ű±Ù‡Ű§ÛŒ «snapshot Ú©Ù„Ű§ŰłÛŒÚ©Â»ŰŒ Ű°ŰźÛŒŰ±Ù‡ ÙŰ§ÛŒÙ„â€ŒÙ‡Ű§ÛŒ ŰšŰČ۱گ (Ù…Ű§Ù†Ù†ŰŻ Ù†ŰŽŰ§Ù†Ù‡â€ŒÚŻŰ°Ű§Ű±ÛŒ Ű±Ù†ŰŻŰ± Ù…Ű€Ù„ÙÙ‡ŰŒ نŰȘÛŒŰŹÙ‡ API JSON) ۱ۧ ۚ۱ Ű±ÙˆÛŒ ŰšŰ±ŰźÛŒ Ű±ŰłŰ§Ù†Ù‡â€ŒÙ‡Ű§ÛŒ ŰźŰ§Ű±ŰŹÛŒ ŰȘŰŽÙˆÛŒÙ‚ Ù…ÛŒâ€ŒÚ©Ù†Ù†ŰŻ و Ű§Ű·Ù…ÛŒÙ†Ű§Ù† Ű­Ű§Ű”Ù„ Ù…ÛŒâ€ŒÚ©Ù†Ù†ŰŻ که Ù‡Ű± ۚۧ۱ Ù‡Ù†ÚŻŰ§Ù… Ű§ŰŹŰ±Ű§ÛŒ ŰȘŰłŰȘی نŰȘÛŒŰŹÙ‡ ŰŻŰ±ÛŒŰ§ÙŰȘâ€ŒŰŽŰŻÙ‡ ۚۧ Ù†ŰłŰźÙ‡ Ű°ŰźÛŒŰ±Ù‡â€ŒŰŽŰŻÙ‡ Ù…Ù‚Ű§ÛŒŰłÙ‡ ŰŽÙˆŰŻ. ŰšŰ±Ű§ÛŒ Ù…Ű«Ű§Ù„ŰŒ Ű§ÛŒÙ† می ŰȘÙˆŰ§Ù†ŰŻ ŰšÙ‡ Ű·ÙˆŰ± Ű¶Ù…Ù†ÛŒ ŰąŰČمون Ù…Ű§ ۱ۧ ŰšÙ‡ 1000 ۟۷ ۚۧ 3000 Ù…Ù‚ŰŻŰ§Ű± ŰŻŰ§ŰŻÙ‡ که Ù†ÙˆÛŒŰłÙ†ŰŻÙ‡ ŰąŰČمون Ù‡Ű±ÚŻŰČ Ù†ŰźÙˆŰ§Ù†ŰŻÙ‡ و ŰŻŰ±ŰšŰ§Ű±Ù‡ ŰąÙ† ۧ۳ŰȘŰŻÙ„Ű§Ù„ Ù†Ú©Ű±ŰŻÙ‡ ۧ۳ŰȘی Ù…Ű±ŰȘۚ۷ Ú©Ù†ŰŻ. چ۱ۧ Ű§ÛŒÙ† ۧێŰȘŰšŰ§Ù‡ ۧ۳ŰȘ۟ ۚۧ Ű§Ù†ŰŹŰ§Ù… Ű§ÛŒÙ† Ú©Ű§Ű±ŰŒ 1000 ŰŻÙ„ÛŒÙ„ ŰšŰ±Ű§ÛŒ ŰŽÚ©ŰłŰȘ ŰȘŰłŰȘ ŰŽÙ…Ű§ ÙˆŰŹÙˆŰŻ ۯۧ۱ۯ - Ú©Ű§ÙÛŒ ۧ۳ŰȘ یک ۟۷ ŰȘŰșÛŒÛŒŰ± Ú©Ù†ŰŻ ŰȘۧ snapshot Ù†Ű§Ù…ŰčŰȘۚ۱ ŰŽÙˆŰŻ و Ű§ÛŒÙ† ۭۧŰȘÙ…Ű§Ù„Ű§Ù‹ ŰČÛŒŰ§ŰŻ ۧŰȘÙŰ§Ù‚ می Ű§ÙŰȘŰŻ. Ú†Ù†ŰŻ ŰšŰ§Ű±ŰŸ ŰšŰ±Ű§ÛŒ Ù‡Ű± ÙŰ¶Ű§ŰŒ Ù†ŰžŰ± ÛŒŰ§ ŰȘŰșÛŒÛŒŰ± ŰŹŰČŰŠÛŒ CSS/HTML. نه ŰȘÙ†Ù‡Ű§ Ű§ÛŒÙ†ŰŒ Ù†Ű§Ù… ŰąŰČمون ŰłŰ±Ù†ŰźÛŒ ۯ۱ Ù…ÙˆŰ±ŰŻ ŰŽÚ©ŰłŰȘ نمی ŰŻÙ‡ŰŻŰŒ ŰČÛŒŰ±Ű§ ÙÙ‚Ű· ŰšŰ±Ű±ŰłÛŒ می Ú©Ù†ŰŻ که 1000 ۟۷ ŰȘŰșÛŒÛŒŰ± Ù†Ú©Ű±ŰŻÙ‡ ۧ۳ŰȘی همچنین Ù†ÙˆÛŒŰłÙ†ŰŻÙ‡ ŰąŰČمون ۱ۧ ŰȘŰŽÙˆÛŒÙ‚ می Ú©Ù†ŰŻ که ŰłÙ†ŰŻ Ű·ÙˆÙ„Ű§Ù†ÛŒ ۱ۧ که نمی ŰȘÙˆŰ§Ù†ŰŻ ŰšŰ±Ű±ŰłÛŒ Ú©Ù†ŰŻŰŒ ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† ÙˆŰ§Ù‚Űčی Ù…ÙˆŰ±ŰŻ Ù†ŰžŰ± ŰšÙŸŰ°ÛŒŰ±ŰŻ. ŰȘŰ§ÛŒÛŒŰŻ Ú©Ù†ÛŒŰŻ. همه Ű§ÛŒÙ†Ù‡Ű§ ŰčÙ„Ű§ŰŠÙ… ŰąŰČمون Ù…ŰšÙ‡Ù… و Ù…ŰŽŰȘŰ§Ù‚ ۧ۳ŰȘ که مŰȘÙ…Ű±Ú©ŰČ Ù†ÛŒŰłŰȘ و Ù‡ŰŻÙ ŰąÙ† ŰŻŰłŰȘÛŒŰ§ŰšÛŒ ŰšÙ‡ ŰšÛŒŰŽ ۧŰČ Ű­ŰŻ ۧ۳ŰȘ + +ŰŽŰ§ÛŒŰ§Ù† ۰ک۱ ۧ۳ŰȘ که Ù…ÙˆŰ§Ű±ŰŻ کمی ÙˆŰŹÙˆŰŻ ۯۧ۱ۯ که snapshotÙ‡Ű§ÛŒ Ű·ÙˆÙ„Ű§Ù†ÛŒ و ŰźŰ§Ű±ŰŹÛŒ Ù‚Ű§ŰšÙ„ Ù‚ŰšÙˆÙ„ ۚۧێۯ - Ù‡Ù†ÚŻŰ§Ù… ۧۯŰčۧ ۚ۱ Ű±ÙˆÛŒ ۷۱ۭ و نه ŰŻŰ§ŰŻÙ‡ (ۧ۳ŰȘ۟۱ۧۏ Ù…Ù‚Ű§ŰŻÛŒŰ± و ŰȘÙ…Ű±Ú©ŰČ ŰšŰ± Ű±ÙˆÛŒ ÙÛŒÙ„ŰŻÙ‡Ű§) ÛŒŰ§ ŰČÙ…Ű§Ù†ÛŒ که ŰłÙ†ŰŻ ŰŻŰ±ÛŒŰ§ÙŰȘی ŰšÙ‡ Ù†ŰŻŰ±ŰȘ ŰȘŰșÛŒÛŒŰ± Ù…ÛŒâ€ŒÚ©Ù†ŰŻ. +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** ŰȘŰłŰȘ ۱ۧۚ۷ Ú©Ű§Ű±ŰšŰ±ÛŒ Ù†Ű§Ù…ÙˆÙÙ‚ ۧ۳ŰȘ. ŰšÙ‡ Ù†ŰžŰ± می ۱۳ۯ Ú©ŰŻ ۯ۱۳ŰȘ ۧ۳ŰȘی Ű”ÙŰ­Ù‡ Ù†Ù…Ű§ÛŒŰŽ ÙŸÛŒÚ©ŰłÙ„ Ù‡Ű§ÛŒ ŰčŰ§Ù„ÛŒ ۱ۧ Ű§Ű±Ű§ŰŠÙ‡ می ŰŻÙ‡ŰŻŰŒ چه ۧŰȘÙŰ§Ù‚ÛŒ Ű§ÙŰȘŰ§ŰŻÙ‡ ۧ۳ŰȘ۟ ŰȘŰłŰȘ snapshot ŰŽÙ…Ű§ ŰȘÙŰ§ÙˆŰȘی ŰšÛŒÙ† ŰłÙ†ŰŻ Ù…ŰšŰŻŰŁ ۚۧ ŰłÙ†ŰŻ ŰŻŰ±ÛŒŰ§ÙŰȘی فŰčلی ÙŸÛŒŰŻŰ§ ک۱ۯ - یک کۧ۱ۧکŰȘ۱ ÙŰ§Ű”Ù„Ù‡ ŰšÙ‡ ŰčÙ„Ű§Ù…ŰȘ ÚŻŰ°Ű§Ű±ÛŒ Ű§Ű¶Ű§ÙÙ‡ ŰŽŰŻ... + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :thumbsdown: Ù…Ű«Ű§Ù„ ۶ۯ Ű§Ù„ÚŻÙˆ: ŰŹÙŰȘ Ú©Ű±ŰŻÙ† ŰȘŰłŰȘ Ù…Ű§ ŰšÙ‡ 2000 ۟۷ Ú©ŰŻ + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Jest") + +```javascript +it("TestJavaScript.com ŰšÙ‡ ۯ۱۳ŰȘی Ű§Ű±Ű§ŰŠÙ‡ ŰŽŰŻÙ‡ ۧ۳ŰȘ", () => { + //Ù…ÙŰŻŰ§Ű± ŰŻÙ‡ÛŒ Ú©Ű±ŰŻÙ† + + //ۧۏ۱ۧ Ú©Ű±ŰŻÙ† + const receivedPage = renderer + .create( Test JavaScript ) + .toJSON(); + + //Ù…Ù‚Ű§ÛŒŰłÙ‡ Ú©Ű±ŰŻÙ† + expect(receivedPage).toMatchSnapshot(); + //Ù…Ű§ Ű§Ú©Ù†ÙˆÙ† ŰšÙ‡ Ű·ÙˆŰ± Ű¶Ù…Ù†ÛŒ یک ŰłÙ†ŰŻ 2000 ŰźŰ·ÛŒ ۱ۧ Ű­ÙŰž می کنیم + //Ù‡Ű± ŰŽÚ©ŰłŰȘ ۟۷ ÛŒŰ§ comment Ű§Ű¶Ű§ÙÛŒ - Ű§ÛŒÙ† ŰȘŰłŰȘ ۱ۧ ŰŽÚ©ŰłŰȘه ŰźÙˆŰ§Ù‡ŰŻ ک۱ۯ +}); +``` + +
    + +### :clap: Ù…Ű«Ű§Ù„ ۯ۱۳ŰȘ: Ű§Ù†ŰȘ۞ۧ۱ۧŰȘ Ù‚Ű§ŰšÙ„ Ù…ŰŽŰ§Ù‡ŰŻÙ‡ و مŰȘÙ…Ű±Ú©ŰČ Ù‡ŰłŰȘÙ†ŰŻ + +```javascript +it("Ù‡Ù†ÚŻŰ§Ù… ۚۧŰČŰŻÛŒŰŻ ۧŰČ Ű”ÙŰ­Ù‡ Ű§Ű”Ù„ÛŒ TestJavaScript.comی یک منو Ù†Ù…Ű§ÛŒŰŽ ŰŻŰ§ŰŻÙ‡ می ŰŽÙˆŰŻ", () => { + //Ù…ÙŰŻŰ§Ű± ŰŻÙ‡ÛŒ Ú©Ű±ŰŻÙ† + + //ۧۏ۱ۧ Ú©Ű±ŰŻÙ† + const receivedPage = renderer + .create( Test JavaScript ) + .toJSON(); + + //Ù…ÙŰ§ÛŒŰłÙ‡ Ú©Ű±ŰŻÙ† + + const menu = receivedPage.content.menu; + expect(menu).toMatchInlineSnapshot(` + +`); +}); +``` + +
    + +

    + +## âšȘ ïžÚ©ŰŻ ۱ۧ Ú©ÙŸÛŒ Ú©Ù†ÛŒŰŻŰŒ Ű§Ù…Ű§ ÙÙ‚Ű· ŰąÙ†Ú†Ù‡ Ù„Ű§ŰČم ۧ۳ŰȘ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** ŰȘÙ…Ű§Ù… ŰŹŰČŰŠÛŒŰ§ŰȘ Ù„Ű§ŰČم ۱ۧ که ۚ۱ نŰȘÛŒŰŹÙ‡ ŰąŰČÙ…Ű§ÛŒŰŽ ŰȘŰŁŰ«ÛŒŰ± می ÚŻŰ°Ű§Ű±ŰŻŰŒ ÙˆŰ§Ű±ŰŻ Ú©Ù†ÛŒŰŻŰŒ Ű§Ù…Ű§ نه ŰšÛŒŰŽŰȘ۱. ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† Ù…Ű«Ű§Ù„ŰŒ ŰȘŰłŰȘی ۱ۧ ۯ۱ Ù†ŰžŰ± ŰšÚŻÛŒŰ±ÛŒŰŻ که ŰšŰ§ÛŒŰŻ 100 ۟۷ ÙˆŰ±ÙˆŰŻÛŒ JSON ۱ۧ ۯ۱ Ù†ŰžŰ± ŰšÚŻÛŒŰ±ÛŒŰŻâ€Š-â€ŠÚ†ŰłŰšŰ§Ù†ŰŻÙ† ŰąÙ† ۯ۱ Ù‡Ű± ŰȘŰłŰȘ ۟۳ŰȘه Ú©Ù†Ù†ŰŻÙ‡ ۧ۳ŰȘ. ۧ۳ŰȘ۟۱ۧۏ ŰąÙ† ۧŰČ ŰźŰ§Ű±ŰŹ ŰšÙ‡ transferFactory.getJSON() ۚۧŰčŰ« Ù…ÛŒâ€ŒŰŽÙˆŰŻ ŰȘŰłŰȘ Ù…ŰšÙ‡Ù… ŰšŰ§Ù‚ÛŒ ŰšÙ…Ű§Ù†ŰŻâ€Š-â€ŠŰšŰŻÙˆÙ† ŰŻŰ§ŰŻÙ‡ŰŒ ۧ۱ŰȘۚۧ۷ نŰȘÛŒŰŹÙ‡ ŰąŰČÙ…Ű§ÛŒŰŽ ۚۧ ŰčلŰȘ ŰŻŰŽÙˆŰ§Ű± ۧ۳ŰȘ ("چ۱ۧ Ù‚Ű±Ű§Ű± ۧ۳ŰȘ ÙˆŰ¶ŰčیŰȘ 400 ۱ۧ ŰšŰ±ÚŻŰ±ŰŻŰ§Ù†ŰŻŰŸ"). Ú©ŰȘۧۚ Ú©Ù„Ű§ŰłÛŒÚ© Ű§Ù„ÚŻÙˆÙ‡Ű§ÛŒ ÙˆŰ§Ű­ŰŻ x Ù†Ű§Ù… Ű§ÛŒÙ† Ű§Ù„ÚŻÙˆ ۱ۧ Â«Ù…Ù‡Ù…Ű§Ù† Ű§ŰłŰ±Ű§Ű±ŰąÙ…ÛŒŰČ» گ۰ۧێŰȘÙ†ŰŻ -  چیŰČی Ù†Ű§ŰŻÛŒŰŻÙ‡ ۚ۱ نŰȘŰ§ÛŒŰŹ ŰąŰČÙ…Ű§ÛŒŰŽ Ù…Ű§ ŰȘŰŁŰ«ÛŒŰ± گ۰ۧێŰȘی Ù…Ű§ ŰŻÙ‚ÛŒÙ‚Ű§Ù‹ Ù†Ù…ÛŒâ€ŒŰŻŰ§Ù†ÛŒÙ… چه چیŰČی. Ù…Ű§ می‌ŰȘÙˆŰ§Ù†ÛŒÙ… ۚۧ ۧ۳ŰȘ۟۱ۧۏ Ù‚Ű·ŰčۧŰȘ Ű·ÙˆÙ„Ű§Ù†ÛŒ Ù‚Ű§ŰšÙ„ ŰȘک۱ۧ۱ ۯ۱ ۟ۧ۱ۏ و ŰšÙ‡ ۔۱ۭۧŰȘ Ű§ŰŽŰ§Ű±Ù‡ کنیم که Ú©ŰŻŰ§Ù… ŰŹŰČŰŠÛŒŰ§ŰȘ ۟ۧ۔ ŰšŰ±Ű§ÛŒ ŰąŰČÙ…Ű§ÛŒŰŽ مهم Ù‡ŰłŰȘÙ†ŰŻŰŒ ŰšÙ‡ŰȘ۱ Űčمل کنیم. ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ Ù…Ű«Ű§Ù„ ŰšŰ§Ù„Ű§ŰŒ ŰąŰČمون می‌ŰȘÙˆŰ§Ù†ŰŻ ÙŸŰ§Ű±Ű§Ù…ŰȘŰ±Ù‡Ű§ÛŒÛŒ ۱ۧ ÙŸŰ§Űł Ú©Ù†ŰŻ که ŰąÙ†Ú†Ù‡ مهم ۧ۳ŰȘ ۱ۧ ۚ۱ۏ۳ŰȘه Ù…ÛŒâ€ŒÚ©Ù†ŰŻ: transferFactory.getJSON({sender: undefined}). ۯ۱ Ű§ÛŒÙ† Ù…Ű«Ű§Ù„ŰŒ ŰźÙˆŰ§Ù†Ù†ŰŻÙ‡ ŰšŰ§ÛŒŰŻ ÙÙˆŰ±Ű§Ù‹ ۧ۳ŰȘÙ†ŰšŰ§Ű· Ú©Ù†ŰŻ که Ù‚ŰłÙ…ŰȘ ŰźŰ§Ù„ÛŒ ÙŰ±ŰłŰȘÙ†ŰŻÙ‡ ŰŻÙ„ÛŒÙ„ÛŒ ۧ۳ŰȘ که ŰąŰČمون ŰšŰ§ÛŒŰŻ منŰȘ۞۱ ŰźŰ·Ű§ÛŒ ۧŰčŰȘŰšŰ§Ű±ŰłÙ†ŰŹÛŒ ÛŒŰ§ Ù‡Ű± نŰȘÛŒŰŹÙ‡ Ú©Ű§ÙÛŒ Ù…ŰŽŰ§ŰšÙ‡ ŰŻÛŒÚŻŰ±ÛŒ ۚۧێۯ. +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** Ú©ÙŸÛŒ Ú©Ű±ŰŻÙ† 500 ۟۷ JSON ۚۧŰčŰ« Ù…ÛŒâ€ŒŰŽÙˆŰŻ که ŰȘŰłŰȘâ€ŒÙ‡Ű§ÛŒ ŰŽÙ…Ű§ Ù‚Ű§ŰšÙ„ Ù†ÚŻÙ‡ŰŻŰ§Ű±ÛŒ و ŰźÙˆŰ§Ù†ŰŻÙ† Ù†ŰšŰ§ŰŽÙ†ŰŻ. ŰŹŰ§ŰšŰŹŰ§ÛŒÛŒ همه چیŰČ ŰšÙ‡ ŰšÛŒŰ±ÙˆÙ† ۚۧ ŰąŰČمون Ù‡Ű§ÛŒ Ù…ŰšÙ‡Ù…ÛŒ که ۯ۱ک ŰąÙ†Ù‡Ű§ ۳۟ŰȘ ۧ۳ŰȘ ŰšÙ‡ ÙŸŰ§ÛŒŰ§Ù† می ۱۳ۯ + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :thumbsdown: Ù…Ű«Ű§Ù„ ۶ۯ Ű§Ù„ÚŻÙˆ: ŰŽÚ©ŰłŰȘ ŰȘŰłŰȘ Ù†Ű§Ù…ŰŽŰźŰ” ۧ۳ŰȘ ŰČÛŒŰ±Ű§ همه ŰčلŰȘ ŰźŰ§Ű±ŰŹÛŒ ۧ۳ŰȘ و ۯ۱ JSON ŰšŰČ۱گ ÙŸÙ†Ù‡Ű§Ù† می ŰŽÙˆŰŻ + +![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "Examples with Mocha") + +```javascript +test("وقŰȘی ۧŰčŰȘŰšŰ§Ű±ÛŒ ÙˆŰŹÙˆŰŻ Ù†ŰŻŰ§Ű±ŰŻŰŒ Ű§Ù†ŰȘÙ‚Ű§Ù„ ۱ۯ می ŰŽÙˆŰŻ", async() => { + // Ù…ÙŰŻŰ§Ű± ŰŻÙ‡ÛŒ Ú©Ű±ŰŻÙ† + const transferRequest = testHelpers.factorMoneyTransfer() //200 ۟۷ JSON ۱ۧ ŰšŰ±ÚŻŰ±ŰŻŰ§Ù†ÛŒŰŻ. + const transferServiceUnderTest = new TransferService(); + + // ۧۏ۱ۧ Ú©Ű±ŰŻÙ† + const transferResponse = await transferServiceUnderTest.transfer(transferRequest); + + // Ù…ÙŰ§ÛŒŰłÙ‡ Ú©Ű±ŰŻÙ† + expect(transferResponse.status).toBe(409);// Ű§Ù…Ű§ چ۱ۧ Ù…Ű§ Ű§Ù†ŰȘ۞ۧ۱ ŰŽÚ©ŰłŰȘ ۱ۧ ŰŻŰ§Ű±ÛŒÙ…: ŰšÙ‡ Ù†ŰžŰ± می ۱۳ۯ همه ۯ۱ ŰąŰČمون Ú©Ű§Ù…Ù„Ű§Ù‹ مŰčŰȘۚ۱ Ù‡ŰłŰȘÙ†ŰŻ đŸ€” + }); +``` + +
    + +### :clap: Ù…Ű«Ű§Ù„ ۯ۱۳ŰȘ: ŰąŰČÙ…Ű§ÛŒŰŽ Ù†ŰŽŰ§Ù† می ŰŻÙ‡ŰŻ که ŰčلŰȘ نŰȘÛŒŰŹÙ‡ ŰąŰČÙ…Ű§ÛŒŰŽ Ú†ÛŒŰłŰȘ + +```javascript + +test("وقŰȘی ۧŰčŰȘŰšŰ§Ű±ÛŒ ÙˆŰŹÙˆŰŻ Ù†ŰŻŰ§Ű±ŰŻŰŒ Ű§Ù†ŰȘÙ‚Ű§Ù„ ۱ۯ می ŰŽÙˆŰŻ", async() => { + // Ù…ÙŰŻŰ§Ű± ŰŻÙ‡ÛŒ Ú©Ű±ŰŻÙ† + const transferRequest = testHelpers.factorMoneyTransfer({userCredit:100, transferAmount:200}) //ŰšŰŻÛŒÙ‡ÛŒ ۧ۳ŰȘ که Ú©Ù…ŰšÙˆŰŻ ۧŰčŰȘۚۧ۱ ÙˆŰŹÙˆŰŻ ۯۧ۱ۯ + const transferServiceUnderTest = new TransferService({disallowOvercharge:true}); + + // ۧۏ۱ۧ Ú©Ű±ŰŻÙ† + const transferResponse = await transferServiceUnderTest.transfer(transferRequest); + + // Ù…Ù‚Ű§ÛŒŰłÙ‡ Ú©Ű±ŰŻÙ† + expect(transferResponse.status).toBe(409); //ŰšŰŻÛŒÙ‡ÛŒ ۧ۳ŰȘ که ۧگ۱ کۧ۱ۚ۱ ۧŰčŰȘŰšŰ§Ű±ÛŒ Ù†ŰŻŰ§ŰŽŰȘه ۹ۧۮۯی ŰšŰ§ÛŒŰŻ ŰŽÚ©ŰłŰȘ ŰšŰźÙˆŰ±ŰŻ + }); + ``` + +
    + +

    + +## âšȘ  1.10 ۧێŰȘŰšŰ§Ù‡Ű§ŰȘ ۱ۧ مŰȘÙˆŰŹÙ‡ Ù†ŰŽÙˆÛŒŰŻŰŒ منŰȘ۞۱ ŰąÙ†Ù‡Ű§ ŰšŰ§ŰŽÛŒŰŻ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** Ù‡Ù†ÚŻŰ§Ù…ÛŒ که Ù…ÛŒâ€ŒŰźÙˆŰ§Ù‡ÛŒŰŻ ۧۯŰčۧ Ú©Ù†ÛŒŰŻ که ŰšŰ±ŰźÛŒ ۧŰČ ÙˆŰ±ÙˆŰŻÛŒâ€ŒÙ‡Ű§ ۚۧŰčŰ« Ű§ÛŒŰŹŰ§ŰŻ ۟۷ۧ Ù…ÛŒâ€ŒŰŽÙˆÙ†ŰŻŰŒ ممکن ۧ۳ŰȘ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ try-catch-finally ۯ۱۳ŰȘ ŰšÙ‡ Ù†ŰžŰ± ۚ۱۳ۯ و ۧۯŰčۧ Ú©Ù†ŰŻ که ŰšÙ†ŰŻ catch ÙˆŰ§Ű±ŰŻ ŰŽŰŻÙ‡ ۧ۳ŰȘ. نŰȘÛŒŰŹÙ‡ یک Ù…ÙˆŰ±ŰŻ ŰȘŰłŰȘ Ù†Ű§Ù…Ù†Ű§ŰłŰš و Ù…ÙŰ”Ù„ ۧ۳ŰȘ (Ù…Ű«Ű§Ù„ ŰČÛŒŰ±) که Ù‡ŰŻÙ ŰąŰČمون ŰłŰ§ŰŻÙ‡ و Ű§Ù†ŰȘ۞ۧ۱ۧŰȘ نŰȘÛŒŰŹÙ‡ ۱ۧ ÙŸÙ†Ù‡Ű§Ù† می Ú©Ù†ŰŻ. + +یک ŰŹŰ§ÛŒÚŻŰČین ŰČÛŒŰšŰ§ŰȘ۱ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ Ű§ŰŻŰčŰ§ÛŒ ۧ۟ŰȘŰ”Ű§Ű”ÛŒ Chai یک ŰźŰ·ÛŒ ۧ۳ŰȘ: expect(method).to.throw (ÛŒŰ§ ۯ۱ Jest: expect(method).toThrow()). Ú©Ű§Ù…Ù„Ű§Ù‹ Ű§ŰŹŰšŰ§Ű±ÛŒ ۧ۳ŰȘ که Ű§Ű·Ù…ÛŒÙ†Ű§Ù† Ű­Ű§Ű”Ù„ ŰŽÙˆŰŻ که ۧ۳ŰȘŰ«Ù†Ű§ ŰŻŰ§Ű±Ű§ÛŒ ŰźŰ§Ű”ÛŒŰȘی ۧ۳ŰȘ که نوŰč ۟۷ۧ ۱ۧ ŰšÛŒŰ§Ù† می Ú©Ù†ŰŻŰŒ ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ ۚۧ ŰȘÙˆŰŹÙ‡ ŰšÙ‡ یک ŰźŰ·Ű§ÛŒ ŰčÙ…ÙˆÙ…ÛŒŰŒ ŰšŰ±Ù†Ű§Ù…Ù‡ نمی ŰȘÙˆŰ§Ù†ŰŻ ŰšÙ‡ ŰŹŰ§ÛŒ Ù†Ù…Ű§ÛŒŰŽ یک ÙŸÛŒŰ§Ù… Ù†Ű§Ű§Ù…ÛŒŰŻÚ©Ù†Ù†ŰŻÙ‡ ŰšÙ‡ Ú©Ű§Ű±ŰšŰ±ŰŒ کۧ۱ ŰČÛŒŰ§ŰŻÛŒ Ű§Ù†ŰŹŰ§Ù… ŰŻÙ‡ŰŻ. +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** ۧ۳ŰȘنŰȘۧۏ ۧŰČ ÚŻŰČŰ§Ű±ŰŽâ€ŒÙ‡Ű§ÛŒ ŰąŰČÙ…Ű§ÛŒŰŽÛŒ (Ù…Ű«Ù„Ű§Ù‹ ÚŻŰČŰ§Ű±ŰŽâ€ŒÙ‡Ű§ÛŒ CI) Ú†Ű§Ù„ŰŽ ŰšŰ±Ű§Ù†ÚŻÛŒŰČ ŰźÙˆŰ§Ù‡ŰŻ ŰšÙˆŰŻ + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :thumbsdown: Ù…Ű«Ű§Ù„ ۶ۯ Ű§Ù„ÚŻÙˆ: یک Ù…ÙˆŰ±ŰŻ ŰȘŰłŰȘ Ű·ÙˆÙ„Ű§Ù†ÛŒ که ŰłŰčی Ù…ÛŒâ€ŒÚ©Ù†ŰŻ ÙˆŰŹÙˆŰŻ ۟۷ۧ ۱ۧ ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ ŰąŰČمون ŰȘŰŁÛŒÛŒŰŻ Ú©Ù†ŰŻ. + +![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "Examples with Mocha") + +```javascript +it("Ù‡Ù†ÚŻŰ§Ù…ÛŒ که Ù†Ű§Ù… Ù…Ű­Ű”ÙˆÙ„ ÙˆŰŹÙˆŰŻ Ù†ŰŻŰ§Ű±ŰŻŰŒ ŰźŰ·Ű§ÛŒ 400 ۱ۧ Ù†ŰŽŰ§Ù† می ŰŻÙ‡ŰŻ", async () => { + let errorWeExceptFor = null; + try { + const result = await addNewProduct({}); + } catch (error) { + expect(error.code).to.equal("InvalidInput"); + errorWeExceptFor = error; + } + expect(errorWeExceptFor).not.to.be.null; + //ۧگ۱ Ű§ÛŒÙ† Ù…Ù‚Ű§ÛŒŰłÙ‡ Ù†Ű§Ù…ÙˆÙÙ‚ ۹ۧۮۯی نŰȘŰ§ÛŒŰŹ/ÚŻŰČŰ§Ű±ŰŽâ€ŒÙ‡Ű§ÛŒ ŰȘŰłŰȘ ÙÙ‚Ű· Ù†ŰŽŰ§Ù† ŰŻŰ§ŰŻÙ‡ Ù…ÛŒâ€ŒŰŽÙˆÙ†ŰŻ + //که Ù…Ù‚ŰŻŰ§Ű±ÛŒ ŰȘهی ۧ۳ŰȘی کلمه Ű§ÛŒ ۯ۱ Ù…ÙˆŰ±ŰŻ یک ۧ۳ŰȘŰ«Ù†Ű§ ÙˆŰŹÙˆŰŻ Ù†ŰźÙˆŰ§Ù‡ŰŻ ۯۧێŰȘ +}); +``` + +
    + +### :clap: Ù…Ű«Ű§Ù„ ۯ۱۳ŰȘ: Ű§Ù†ŰȘŰžŰ§Ű±ÛŒ Ù‚Ű§ŰšÙ„ ŰźÙˆŰ§Ù†ŰŻÙ† ŰšŰ±Ű§ÛŒ Ű§Ù†ŰłŰ§Ù† که ŰšÙ‡ ۱ۭۧŰȘی Ù‚Ű§ŰšÙ„ ۯ۱ک ۧ۳ŰȘی Ű­ŰȘی ممکن ۧ۳ŰȘ ŰȘÙˆŰłŰ· QA ÛŒŰ§ PM فنی + +```javascript +it("Ù‡Ù†ÚŻŰ§Ù…ÛŒ که Ù†Ű§Ù… Ù…Ű­Ű”ÙˆÙ„ ÙˆŰŹÙˆŰŻ Ù†ŰŻŰ§Ű±ŰŻŰŒ ŰźŰ·Ű§ÛŒ 400 ۱ۧ Ù†ŰŽŰ§Ù† می ŰŻÙ‡ŰŻ", async () => { + await expect(addNewProduct({})) + .to.eventually.throw(AppError) + .with.property("code", "InvalidInput"); +}); +``` + +
    + +

    + +## âšȘ  1.11 ŰȘŰłŰȘ Ù‡Ű§ÛŒ ŰźÙˆŰŻ ۱ۧ ŰȘÚŻ Ú©Ù†ÛŒŰŻ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** ŰȘŰłŰȘ Ù‡Ű§ÛŒ Ù…ŰźŰȘلف ŰšŰ§ÛŒŰŻ ۯ۱ ŰłÙ†Ű§Ű±ÛŒÙˆÙ‡Ű§ÛŒ Ù…ŰźŰȘلف ۧۏ۱ۧ ŰŽÙˆÙ†ŰŻ: ŰŻÙˆŰŻ ŰłŰ±ÛŒŰčی ŰšŰŻÙˆÙ† IOی ŰȘŰłŰȘ Ù‡Ű§ ŰšŰ§ÛŒŰŻ ŰČÙ…Ű§Ù†ÛŒ که یک ŰȘÙˆŰłŰčÙ‡â€ŒŰŻÙ‡Ù†ŰŻÙ‡ ÙŰ§ÛŒÙ„ÛŒ ۱ۧ Ű°ŰźÛŒŰ±Ù‡ ÛŒŰ§ مŰȘŰčÙ‡ŰŻ Ù…ÛŒâ€ŒÚ©Ù†ŰŻŰŒ ۧۏ۱ۧ Ù…ÛŒâ€ŒŰŽÙˆÙ†ŰŻŰŒ ŰȘŰłŰȘ Ù‡Ű§ÛŒ Ú©Ű§Ù…Ù„ ÙŸŰ§ÛŒŰ§Ù† ŰšÙ‡ Ű§Ù†ŰȘÙ‡Ű§ مŰčÙ…ÙˆÙ„Ű§Ù‹ Ù‡Ù†ÚŻŰ§Ù… Ű§Ű±ŰłŰ§Ù„ ŰŻŰ±ŰźÙˆŰ§ŰłŰȘ Ú©ŰŽŰŽ ŰŹŰŻÛŒŰŻ ۧۏ۱ۧ Ù…ÛŒâ€ŒŰŽÙˆÙ†ŰŻŰŒ و ŰșÛŒŰ±Ù‡. ۚۧ ۚ۱چ۳ۚ ÚŻŰ°Ű§Ű±ÛŒ ŰȘŰłŰȘ Ù‡Ű§ ۚۧ Ú©Ù„Ù…Ű§ŰȘ Ú©Ù„ÛŒŰŻÛŒ Ù…Ű§Ù†Ù†ŰŻ #cold #api #sanity ŰȘۧ ŰšŰȘÙˆŰ§Ù†ÛŒŰŻ ۚۧ Ù…Ù‡Ű§Ű± ŰȘŰłŰȘ ŰźÙˆŰŻ ŰŻŰłŰȘ ŰšÚŻÛŒŰ±ÛŒŰŻ و ŰČÛŒŰ± Ù…ŰŹÙ…ÙˆŰčه Ù…ÙˆŰ±ŰŻ Ù†ŰžŰ± ۱ۧ ÙŰ±Ű§ŰźÙˆŰ§Ù†ÛŒ Ú©Ù†ÛŒŰŻ. ŰšŰ±Ű§ÛŒ Ù…Ű«Ű§Ù„ŰŒ ŰšÙ‡ Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ ۧ۳ŰȘ که ÙÙ‚Ű· ÚŻŰ±ÙˆÙ‡ ŰȘŰłŰȘ ŰłÙ„Ű§Ù…ŰȘ Űčقل ۱ۧ ۚۧ Ù…ÙˆÚ©Ű§ ÙŰ±Ű§ Ù…ÛŒâ€ŒŰźÙˆŰ§Ù†ÛŒŰŻ: mocha — grep ‘sanity’ +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** Ű§ŰŹŰ±Ű§ÛŒ ŰȘÙ…Ű§Ù… ŰȘŰłŰȘâ€ŒÙ‡Ű§ŰŒ ۧŰČ ŰŹÙ…Ù„Ù‡ ŰȘŰłŰȘâ€ŒÙ‡Ű§ÛŒÛŒ که ŰŻÙ‡â€ŒÙ‡Ű§ ÙŸŰ±Űłâ€ŒÙˆŰŹÙˆ ۯ۱ DB ۱ۧ Ű§Ù†ŰŹŰ§Ù… Ù…ÛŒâ€ŒŰŻÙ‡Ù†ŰŻŰŒ Ù‡Ű± ŰČÙ…Ű§Ù†ÛŒ که یک ŰȘÙˆŰłŰčÙ‡â€ŒŰŻÙ‡Ù†ŰŻÙ‡ یک ŰȘŰșÛŒÛŒŰ± کوچک Ű§ÛŒŰŹŰ§ŰŻ Ú©Ù†ŰŻ می‌ŰȘÙˆŰ§Ù†ŰŻ ŰšŰłÛŒŰ§Ű± Ú©Ù†ŰŻ ۚۧێۯ و ŰȘÙˆŰłŰčÙ‡â€ŒŰŻÙ‡Ù†ŰŻÚŻŰ§Ù† ۱ۧ ۧŰČ Ű§ŰŹŰ±Ű§ÛŒ ŰȘŰłŰȘ Ù‡Ű§ ŰŻÙˆŰ± Ù†ÚŻÙ‡ ۯۧ۱ۯ. + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :clap: Ù…Ű«Ű§Ù„ ۯ۱۳ŰȘ: ۚ۱چ۳ۚ ÚŻŰ°Ű§Ű±ÛŒ ŰȘŰłŰȘ Ù‡Ű§ ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† "#cold-test" ŰšÙ‡ Ű§ŰŹŰ±Ű§Ú©Ù†Ù†ŰŻÙ‡ ŰąŰČمون ۧۏۧŰČه می ŰŻÙ‡ŰŻ ÙÙ‚Ű· ŰȘŰłŰȘ Ù‡Ű§ÛŒ ŰłŰ±ÛŒŰč ۱ۧ ۧۏ۱ۧ Ú©Ù†ŰŻ (ŰȘŰłŰȘ Ù‡Ű§ÛŒ ۳۱ۯ===ŰłŰ±ÛŒŰč که هیچ IO Ű§Ù†ŰŹŰ§Ù… نمی ŰŻÙ‡Ù†ŰŻ و Ű­ŰȘی ŰČÙ…Ű§Ù†ÛŒ که ŰȘÙˆŰłŰčه ŰŻÙ‡Ù†ŰŻÙ‡ ۯ۱ Ű­Ű§Ù„ ŰȘŰ§ÛŒÙŸ Ú©Ű±ŰŻÙ† ۧ۳ŰȘ می ŰȘÙˆŰ§Ù†Ù†ŰŻ Ù…Ú©Ű±Ű±Ű§ ۧۏ۱ۧ ŰŽÙˆÙ†ŰŻ) + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Jest") + +```javascript +//Ű§ÛŒÙ† ŰȘŰłŰȘ ŰłŰ±ÛŒŰč ۧ۳ŰȘ (ŰšŰŻÙˆÙ† DB) و Ù…Ű§ ŰąÙ† ۱ۧ Ù…Ű·Ű§ŰšÙ‚ ۚۧ ŰąÙ† ۚ۱چ۳ۚ ÚŻŰ°Ű§Ű±ÛŒ می کنیم +//Ű§Ú©Ù†ÙˆÙ† کۧ۱ۚ۱/CI می ŰȘÙˆŰ§Ù†ŰŻ ŰąÙ† ۱ۧ ŰšÙ‡ Ű·ÙˆŰ± Ù…Ú©Ű±Ű± ۧۏ۱ۧ Ú©Ù†ŰŻ +describe("ŰłÙŰ§Ű±ŰŽ ŰłŰ±ÙˆÛŒŰł", function() { + describe("Ű§Ű¶Ű§ÙÙ‡ Ú©Ű±ŰŻÙ† ŰłÙŰ§Ű±ŰŽ ŰŹŰŻÛŒŰŻ #ŰȘŰłŰȘ ۳۱ۯ #Űčقل", function() { + test("ŰłÙ†Ű§Ű±ÛŒÙˆ - هیچ ۧ۱ŰČی ŰčŰ±Ű¶Ù‡ Ù†ŰŽŰŻ. Ű§Ù†ŰȘ۞ۧ۱ - ۧŰČ Ű§Ű±ŰČ ÙŸÛŒŰŽ ÙŰ±Ű¶ #Űčقل ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ú©Ù†ÛŒŰŻ", function() { + //ŰšÙ‚ÛŒÙ‡ Ú©ŰŻÙ‡Ű§ + }); + }); +}); +``` + +
    + +

    + +## âšȘ  1.12 ŰȘŰłŰȘ Ù‡Ű§ ۱ۧ Ű­ŰŻŰ§Ù‚Ù„ ۯ۱ 2 ۳۷ۭ ŰŻŰłŰȘه ŰšÙ†ŰŻÛŒ Ú©Ù†ÛŒŰŻ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** ۳ۧ۟ŰȘŰ§Ű±ÛŒ ۱ۧ ۯ۱ Ù…ŰŹÙ…ÙˆŰčه ŰąŰČÙ…Ű§ÛŒŰŽÛŒ ŰźÙˆŰŻ ۧŰčÙ…Ű§Ù„ Ú©Ù†ÛŒŰŻ ŰȘۧ یک ۚۧŰČŰŻÛŒŰŻÚ©Ù†Ù†ŰŻÙ‡ ÚŻŰ§Ù‡ ŰšÙ‡ ÚŻŰ§Ù‡ ŰšŰȘÙˆŰ§Ù†ŰŻ ŰšÙ‡ ۱ۭۧŰȘی Ű§Ù„ŰČŰ§Ù…Ű§ŰȘ (ŰąŰČمون Ù‡Ű§ ŰšÙ‡ŰȘŰ±ÛŒÙ† Ù…ŰłŰȘÙ†ŰŻŰ§ŰȘ Ù‡ŰłŰȘÙ†ŰŻ) و ŰłÙ†Ű§Ű±ÛŒÙˆÙ‡Ű§ÛŒ Ù…ŰźŰȘلفی که ۯ۱ Ű­Ű§Ù„ ŰąŰČÙ…Ű§ÛŒŰŽ Ù‡ŰłŰȘÙ†ŰŻ ۱ۧ ۯ۱ک Ú©Ù†ŰŻ. یک Ű±ÙˆŰŽ مŰȘŰŻŰ§ÙˆÙ„ ŰšŰ±Ű§ÛŒ Ű§ÛŒÙ† کۧ۱ Ù‚Ű±Ű§Ű± ŰŻŰ§ŰŻÙ† Ű­ŰŻŰ§Ù‚Ù„ 2 ŰšÙ„ÙˆÚ© «ŰȘÙˆŰ¶ÛŒŰ­Â» ۯ۱ ŰšŰ§Ù„Ű§ÛŒ ŰȘŰłŰȘâ€ŒÙ‡Ű§ÛŒŰȘŰ§Ù† ۧ۳ŰȘ: Ű§ÙˆÙ„ÛŒ ŰšŰ±Ű§ÛŒ Ù†Ű§Ù… ÙˆŰ§Ű­ŰŻ ŰȘŰ­ŰȘ ŰąŰČÙ…Ű§ÛŒŰŽ و ŰŻÙˆÙ…ÛŒ ŰšŰ±Ű§ÛŒ ۳۷ۭ Ű§Ű¶Ű§ÙÛŒ Ű·ŰšÙ‚Ù‡â€ŒŰšÙ†ŰŻÛŒ Ù…Ű§Ù†Ù†ŰŻ ŰłÙ†Ű§Ű±ÛŒÙˆ ÛŒŰ§ ŰŻŰłŰȘÙ‡â€ŒÙ‡Ű§ÛŒ ŰłÙŰ§Ű±ŰŽÛŒ ۧ۳ŰȘ (Ù†Ù…ÙˆÙ†Ù‡â€ŒÙ‡Ű§ÛŒ Ú©ŰŻ ۱ۧ ŰšŰšÛŒÙ†ÛŒŰŻ و Ú†Ű§ÙŸ Ú©Ù†ÛŒŰŻ. Ű”ÙŰ­Ù‡ ŰČÛŒŰ±). Ű§Ù†ŰŹŰ§Ù… Ű§ÛŒÙ† کۧ۱ ÚŻŰČŰ§Ű±ŰŽâ€ŒÙ‡Ű§ÛŒ ŰąŰČمون ۱ۧ نیŰČ ŰšŰłÛŒŰ§Ű± ŰšÙ‡ŰšÙˆŰŻ Ù…ÛŒâ€ŒŰšŰźŰŽŰŻ: ŰźÙˆŰ§Ù†Ù†ŰŻÙ‡ ŰšÙ‡ ۱ۭۧŰȘی ŰŻŰłŰȘÙ‡â€ŒÙ‡Ű§ÛŒ ŰȘŰłŰȘâ€ŒÙ‡Ű§ ۱ۧ ۧ۳ŰȘÙ†ŰšŰ§Ű· Ù…ÛŒâ€ŒÚ©Ù†ŰŻŰŒ ۯ۱ ۚ۟ێ Ù…ÙˆŰ±ŰŻ Ù†ŰžŰ± Ú©Ű§ÙˆŰŽ Ù…ÛŒâ€ŒÚ©Ù†ŰŻ و ŰȘŰłŰȘâ€ŒÙ‡Ű§ÛŒ Ù…Ű±ŰŻÙˆŰŻÛŒ ۱ۧ ŰšÙ‡ هم Ù…Ű±ŰȘۚ۷ Ù…ÛŒâ€ŒÚ©Ù†ŰŻ. ŰčÙ„Ű§ÙˆÙ‡ ۚ۱ Ű§ÛŒÙ†ŰŒ ÙŸÛŒÙ…Ű§ÛŒŰŽ Ú©ŰŻ یک Ù…ŰŹÙ…ÙˆŰčه ۚۧ ŰąŰČÙ…Ű§ÛŒŰŽ Ù‡Ű§ÛŒ ŰČÛŒŰ§ŰŻ ŰšŰ±Ű§ÛŒ یک ŰȘÙˆŰłŰčه ŰŻÙ‡Ù†ŰŻÙ‡ ŰšŰłÛŒŰ§Ű± ŰąŰłŰ§Ù† ŰȘ۱ ŰźÙˆŰ§Ù‡ŰŻ ŰŽŰŻ. Ú†Ù†ŰŻÛŒÙ† ۳ۧ۟ŰȘۧ۱ ŰŹŰ§ÛŒÚŻŰČین ŰšŰ±Ű§ÛŒ Ù…ŰŹÙ…ÙˆŰčه ŰąŰČÙ…Ű§ÛŒŰŽÛŒ ÙˆŰŹÙˆŰŻ ۯۧ۱ۯ که می‌ŰȘÙˆŰ§Ù†ÛŒŰŻ ŰąÙ†Ù‡Ű§ ۱ۧ Ù…Ű§Ù†Ù†ŰŻ ŰąÙ†Ù‡Ű§ ۯ۱ Ù†ŰžŰ± ŰšÚŻÛŒŰ±ÛŒŰŻ [ŰŻŰ§ŰŻÙ‡ ŰŽŰŻÙ‡-وقŰȘی-ÙŸŰł](https://github.com/searls/jasmine-given) و [RITE](https://github.com/ericelliott/riteway) + +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** وقŰȘی ŰšÙ‡ ÚŻŰČŰ§Ű±ŰŽÛŒ ۚۧ ÙÙ‡Ű±ŰłŰȘی Ù…ŰłŰ·Ű­ و Ű·ÙˆÙ„Ű§Ù†ÛŒ ۧŰČ ŰąŰČÙ…ÙˆÙ†â€ŒÙ‡Ű§ Ù†ÚŻŰ§Ù‡ Ù…ÛŒâ€ŒÚ©Ù†ÛŒÙ…ŰŒ ŰźÙˆŰ§Ù†Ù†ŰŻÙ‡ ŰšŰ§ÛŒŰŻ مŰȘÙ†â€ŒÙ‡Ű§ÛŒ Ű·ÙˆÙ„Ű§Ù†ÛŒ ۱ۧ ŰšÙ‡ Ű·ÙˆŰ± Ú©Ű§Ù…Ù„ ŰšŰźÙˆŰ§Ù†ŰŻ ŰȘۧ ŰłÙ†Ű§Ű±ÛŒÙˆÙ‡Ű§ÛŒ Ű§Ű”Ù„ÛŒ ۱ۧ نŰȘÛŒŰŹÙ‡â€ŒÚŻÛŒŰ±ÛŒ Ú©Ù†ŰŻ و Ù…ŰŽŰȘ۱کۧŰȘ ۱ۯ ŰŽŰŻÙ† ۯ۱ ŰąŰČÙ…ÙˆÙ†â€ŒÙ‡Ű§ ۱ۧ ŰšÙ‡ هم Ù…Ű±ŰȘۚ۷ Ú©Ù†ŰŻ. Ù…ÙˆŰ±ŰŻ ŰČÛŒŰ± ۱ۧ ۯ۱ Ù†ŰžŰ± ŰšÚŻÛŒŰ±ÛŒŰŻ: Ù‡Ù†ÚŻŰ§Ù…ÛŒ که 7/100 ŰȘŰłŰȘ ŰŽÚ©ŰłŰȘ می ŰźÙˆŰ±Ù†ŰŻŰŒ Ù†ÚŻŰ§Ù‡ Ú©Ű±ŰŻÙ† ŰšÙ‡ یک Ù„ÛŒŰłŰȘ ۫ۧۚŰȘ Ù†ÛŒŰ§ŰČ ŰšÙ‡ ŰźÙˆŰ§Ù†ŰŻÙ† مŰȘن ŰȘŰłŰȘ Ù‡Ű§ÛŒ Ù…Ű±ŰŻÙˆŰŻ ۯۧ۱ۯ ŰȘۧ ŰšŰšÛŒÙ†ÛŒŰŻ Ú†ÚŻÙˆÙ†Ù‡ ŰąÙ†Ù‡Ű§ ۚۧ ÛŒÚ©ŰŻÛŒÚŻŰ± ۧ۱ŰȘۚۧ۷ ŰŻŰ§Ű±Ù†ŰŻ. ۚۧ Ű§ÛŒÙ† Ű­Ű§Ù„ŰŒ ۯ۱ یک ÚŻŰČۧ۱ێ ŰłÙ„ŰłÙ„Ù‡ Ù…Ű±Ű§ŰȘŰšÛŒŰŒ همه ŰąÙ†Ù‡Ű§ می ŰȘÙˆŰ§Ù†Ù†ŰŻ ŰȘŰ­ŰȘ یک ŰŹŰ±ÛŒŰ§Ù† ÛŒŰ§ ŰŻŰłŰȘه ŰšŰ§ŰŽÙ†ŰŻ و ŰźÙˆŰ§Ù†Ù†ŰŻÙ‡ ŰšÙ‡ ۳۱ŰčŰȘ ۧ۳ŰȘÙ†ŰšŰ§Ű· می Ú©Ù†ŰŻ که ŰčلŰȘ Ű§Ű”Ù„ÛŒ ŰŽÚ©ŰłŰȘ Ú†ÛŒŰłŰȘ ÛŒŰ§ Ű­ŰŻŰ§Ù‚Ù„ کۏۧ۳ŰȘ. + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :clap: Ù…Ű«Ű§Ù„ ۯ۱۳ŰȘ: Ù…ŰŹÙ…ÙˆŰčه ۳ۧ۟ŰȘŰ§Ű±ÛŒ ۚۧ Ù†Ű§Ù… ÙˆŰ§Ű­ŰŻ ŰȘŰ­ŰȘ ŰąŰČÙ…Ű§ÛŒŰŽ و ŰłÙ†Ű§Ű±ÛŒÙˆÙ‡Ű§ ŰšÙ‡ ÚŻŰČۧ۱ێ Ù…Ù†Ű§ŰłŰšÛŒ Ù…Ù†ŰŹŰ± می ŰŽÙˆŰŻ که ۯ۱ ŰČÛŒŰ± Ù†ŰŽŰ§Ù† ŰŻŰ§ŰŻÙ‡ ŰŽŰŻÙ‡ ۧ۳ŰȘ. + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Jest") + +```javascript +// ÙˆŰ§Ű­ŰŻ ۯ۱ Ű­Ű§Ù„ ŰȘŰłŰȘ +describe("ŰłŰ±ÙˆÛŒŰł Ű§Ù†ŰȘÙ‚Ű§Ù„", () => { + //ŰłÙ†Ű§Ű±ÛŒÙˆ + describe("ŰČÙ…Ű§Ù†ÛŒ که ۧŰčŰȘŰšŰ§Ű±ÛŒ ÙˆŰŹÙˆŰŻ Ù†ŰŻŰ§Ű±ŰŻ", () => { + //Ű§Ù†ŰȘ۞ۧ۱ + test("ŰłÙŸŰł ÙˆŰ¶ŰčیŰȘ ÙŸŰ§ŰłŰź ŰšŰ§ÛŒŰŻ Ú©Ű§Ù‡ŰŽ ÛŒŰ§ŰšŰŻ", () => {}); + + //Ű§Ù†ŰȘ۞ۧ۱ + test("ŰłÙŸŰł ŰšŰ§ÛŒŰŻ ŰšŰ±Ű§ÛŒ Ù…ŰŻÛŒŰ± Ű§ÛŒÙ…ÛŒÙ„ Ű§Ű±ŰłŰ§Ù„ ŰŽÙˆŰŻ", () => {}); + }); +}); +``` + +![alt text](assets/hierarchical-report.png) + +
    + +### :thumbsdown: Ù…Ű«Ű§Ù„ ۶ۯ Ű§Ù„ÚŻÙˆ: یک Ù„ÛŒŰłŰȘ Ù…ŰłŰ·Ű­ ۧŰČ ŰȘŰłŰȘ Ù‡Ű§ ŰŽÙ†Ű§ŰłŰ§ÛŒÛŒ ۯۧ۳ŰȘŰ§Ù† Ù‡Ű§ÛŒ کۧ۱ۚ۱ و ۧ۱ŰȘۚۧ۷ ŰšÛŒÙ† ŰȘŰłŰȘ Ù‡Ű§ÛŒ ŰŽÚ©ŰłŰȘ ŰźÙˆŰ±ŰŻÙ‡ ۱ۧ ŰšŰ±Ű§ÛŒ ŰźÙˆŰ§Ù†Ù†ŰŻÙ‡ ۳۟ŰȘ ŰȘ۱ می Ú©Ù†ŰŻ. + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Mocha") + +```javascript +test("ŰłÙŸŰł ÙˆŰ¶ŰčیŰȘ ÙŸŰ§ŰłŰź ŰšŰ§ÛŒŰŻ Ú©Ű§Ù‡ŰŽ ÛŒŰ§ŰšŰŻ", () => {}); + +test("ŰłÙŸŰł ŰšŰ§ÛŒŰŻ Ű§ÛŒÙ…ÛŒÙ„ ŰšÙŰ±ŰłŰȘŰŻ", () => {}); + +test("ۯ۱ Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ Ù†ŰšŰ§ÛŒŰŻ ŰłŰ§ŰšÙ‚Ù‡ نقل و Ű§Ù†ŰȘÙ‚Ű§Ù„Ű§ŰȘ ŰŹŰŻÛŒŰŻÛŒ ÙˆŰŹÙˆŰŻ ۯۧێŰȘه ۚۧێۯ", () => {}); +``` + +![alt text](assets/flat-report.png) + +
    + +
    + +

    + +## âšȘ 1.13 Ù…ÙˆŰ§Ű±ŰŻ ŰŻÛŒÚŻŰ± ŰšŰ±Ű§ÛŒ Ù†ÙˆŰŽŰȘن یک ŰȘŰłŰȘ ŰźÙˆŰš + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** Ű§ÛŒÙ† ÙŸŰłŰȘ Ű±ÙˆÛŒ ŰȘÙˆŰ”ÛŒÙ‡â€ŒÙ‡Ű§ÛŒ ŰąŰČÙ…Ű§ÛŒŰŽÛŒ مŰȘÙ…Ű±Ú©ŰČ ŰŽŰŻÙ‡ ۧ۳ŰȘی ÛŒŰ§ Ű­ŰŻŰ§Ù‚Ù„ می‌ŰȘÙˆŰ§Ù† ŰąÙ† ۱ۧ ۚۧ Node JS Ù…Ű«Ű§Ù„ ŰČŰŻ. ۚۧ Ű§ÛŒÙ† Ű­Ű§Ù„ŰŒ Ű§ÛŒÙ† ÚŻÙ„ÙˆÙ„Ù‡ŰŒ Ú†Ù†ŰŻ نکŰȘه ŰșÛŒŰ± Ù…Ű±ŰȘۚ۷ ۚۧ Node ۱ۧ که ŰšÙ‡ ŰźÙˆŰšÛŒ ŰŽÙ†Ű§ŰźŰȘه ŰŽŰŻÙ‡ Ű§Ù†ŰŻŰŒ ÚŻŰ±ÙˆÙ‡ ŰšÙ†ŰŻÛŒ می Ú©Ù†ŰŻ + +ÛŒŰ§ŰŻ ŰšÚŻÛŒŰ±ÛŒŰŻ و ŰȘÙ…Ű±ÛŒÙ† Ú©Ù†ÛŒŰŻ [TDD principles](https://www.sm-cloud.com/book-review-test-driven-development-by-example-a-tldr/)â€ŠŰąÙ†Ù‡Ű§ ŰšŰ±Ű§ÛŒ ŰšŰłÛŒŰ§Ű±ÛŒ ŰšŰłÛŒŰ§Ű± ۧ۱ŰČŰŽÙ…Ù†ŰŻ Ù‡ŰłŰȘÙ†ŰŻŰŒ Ű§Ù…Ű§ ۧگ۱ ۚۧ ۳ۚک ŰŽÙ…Ű§ ۳ۧŰČÚŻŰ§Ű±ÛŒ Ù†ŰŻŰ§Ű±Ù†ŰŻŰŒ نŰȘŰ±ŰłÛŒŰŻŰŒ ŰŽÙ…Ű§ ŰȘÙ†Ù‡Ű§ Ù†ÛŒŰłŰȘÛŒŰŻ. ŰȘŰłŰȘ Ù‡Ű§ ۱ۧ Ù‚ŰšÙ„ ۧŰČ Ú©ŰŻ ۯ۱ ŰšÙ†ÙˆÛŒŰłÛŒŰŻ [red-green-refactor style](https://blog.cleancoder.com/uncle-bob/2014/12/17/TheCyclesOfTDD.html), Ű§Ű·Ù…ÛŒÙ†Ű§Ù† Ű­Ű§Ű”Ù„ Ú©Ù†ÛŒŰŻ که Ù‡Ű± ŰȘŰłŰȘ ŰŻÙ‚ÛŒÙ‚Ű§Ù‹ یک چیŰČ Ű±Ű§ ŰšŰ±Ű±ŰłÛŒ می Ú©Ù†ŰŻŰŒ وقŰȘی یک ۚۧگ ÙŸÛŒŰŻŰ§ Ú©Ű±ŰŻÛŒŰŻâ€Šâ€”â€ŠÙ‚ŰšÙ„ ۧŰČ Ű±ÙŰč یک ŰȘŰłŰȘ ŰšÙ†ÙˆÛŒŰłÛŒŰŻ که Ű§ÛŒÙ† Ű§ŰŽÚ©Ű§Ù„ ۱ۧ ۯ۱ ŰąÛŒÙ†ŰŻÙ‡ ŰŽÙ†Ű§ŰłŰ§ÛŒÛŒ Ú©Ù†ŰŻŰŒ ۧۏۧŰČه ŰŻÙ‡ÛŒŰŻ Ù‡Ű± ŰȘŰłŰȘ Ű­ŰŻŰ§Ù‚Ù„ یک ۚۧ۱ Ù‚ŰšÙ„ ۧŰČ ŰłŰšŰČ ŰŽŰŻÙ† ŰŽÚ©ŰłŰȘ ŰšŰźÙˆŰ±ŰŻŰŒ ۚۧ Ù†ÙˆŰŽŰȘن یک Ú©ŰŻ ŰłŰ±ÛŒŰč و ŰłŰ§ŰŻÙ‡ŰŒ یک Ù…Ű§Ú˜ÙˆÙ„ ۱ۧ ŰŽŰ±ÙˆŰč Ú©Ù†ÛŒŰŻ. ŰąŰČÙ…Ű§ÛŒŰŽ ۱ۧ ŰšŰ±ŰąÙˆŰ±ŰŻÙ‡ می Ú©Ù†ŰŻ - ŰłÙŸŰł ŰšÙ‡ ŰȘŰŻŰ±ÛŒŰŹ Ű§Ű”Ù„Ű§Ű­ Ú©Ù†ÛŒŰŻ و ŰąÙ† ۱ۧ ŰšÙ‡ ۳۷ۭ ŰŻŰ±ŰŹÙ‡ ŰȘÙˆÙ„ÛŒŰŻ ŰšŰšŰ±ÛŒŰŻŰŒ ۧŰČ Ù‡Ű± ÚŻÙˆÙ†Ù‡ ÙˆŰ§ŰšŰłŰȘÚŻÛŒ ŰšÙ‡ Ù…Ű­ÛŒŰ· (Ù…ŰłÛŒŰ±Ù‡Ű§ŰŒ ŰłÛŒŰłŰȘم ŰčŰ§Ù…Ù„ و ŰșÛŒŰ±Ù‡) ۧۏŰȘÙ†Ű§Űš Ú©Ù†ÛŒŰŻ. +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** ŰŻÙ„ŰȘ ŰšŰ±Ű§ÛŒ Ù…Ű±ÙˆŰ§Ű±ÛŒŰŻÙ‡Ű§ÛŒ ŰźŰ±ŰŻÛŒ که ŰšŰ±Ű§ÛŒ Ú†Ù†ŰŻÛŒÙ† ŰŻÙ‡Ù‡ ŰŹÙ…Űč ŰąÙˆŰ±ÛŒ ŰŽŰŻÙ‡ Ű§Ù†ŰŻ ۱ۧ ۧŰČ ŰŻŰłŰȘ ŰźÙˆŰ§Ù‡ÛŒŰŻ ۯۧۯ + +

    + +# Section 2ïžâƒŁ: Backend ŰȘŰłŰȘ Ú©Ű±ŰŻÙ† + +## âšȘ 2.1 Ù…ŰŹÙ…ÙˆŰčه ŰȘŰłŰȘ ŰźÙˆŰŻ ۱ۧ ŰČÛŒŰ§ŰŻ Ú©Ù†ÛŒŰŻ: ÙŰ±Ű§ŰȘ۱ ۧŰČ Unit Test و Ù‡Ű±Ù… ŰȘŰłŰȘ Ù†Ű±Ù… Ű§ÙŰČۧ۱ Ù†ÚŻŰ§Ù‡ Ú©Ù†ÛŒŰŻ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** [ŰȘŰłŰȘ Ú©Ű±ŰŻÙ† Ù‡Ű±Ù…](https://martinfowler.com/bliki/TestPyramid.html), Ű§ÚŻŰ±Ú†Ù‡ 10 ŰłŰ§Ù„ ŰłÙ† ŰŻŰ§Ű±ŰŻŰŒ Ű§Ù…Ű§ یک Ù…ŰŻÙ„ ŰčŰ§Ù„ÛŒ و Ù…Ű±ŰȘۚ۷ ۧ۳ŰȘ که ŰłÙ‡ نوŰč ŰȘŰłŰȘ ۱ۧ ÙŸÛŒŰŽÙ†Ù‡Ű§ŰŻ می Ú©Ù†ŰŻ و ۚ۱ ۧ۳ŰȘ۱ۧŰȘژی ŰȘŰłŰȘ ۧک۫۱ ŰȘÙˆŰłŰčه ŰŻÙ‡Ù†ŰŻÚŻŰ§Ù† ŰȘŰŁŰ«ÛŒŰ± می گ۰ۧ۱ۯ. ۯ۱ Ù‡Ù…Ű§Ù† ŰČÙ…Ű§Ù†ŰŒ ŰšÛŒŰŽ ۧŰČ ŰȘŰčۯۧۯ Ű§Ù†ÚŻŰŽŰȘ ŰŽÙ…Ű§Ű±ÛŒ ۧŰČ ŰȘÚ©Ù†ÛŒÚ©â€ŒÙ‡Ű§ÛŒ ŰąŰČÙ…Ű§ÛŒŰŽÛŒ ŰŹŰŻÛŒŰŻ ŰšŰ±Ű§Ù‚ ŰžŰ§Ù‡Ű± ŰŽŰŻÙ†ŰŻ و ۯ۱ ŰłŰ§ÛŒÙ‡â€ŒÙ‡Ű§ÛŒ Ù‡Ű±Ù… ŰąŰČÙ…Ű§ÛŒŰŽ ÙŸÙ†Ù‡Ű§Ù† ŰŽŰŻÙ†ŰŻ. ۚۧ ŰȘÙˆŰŹÙ‡ ŰšÙ‡ ŰȘÙ…Ű§Ù… ŰȘŰșÛŒÛŒŰ±Ű§ŰȘ Ú†ŰŽÙ…ÚŻÛŒŰ±ÛŒ که ۯ۱ 10 ŰłŰ§Ù„ Ű§ŰźÛŒŰ± ŰŻÛŒŰŻÙ‡ Ű§ÛŒÙ… (ŰłŰ±ÙˆÛŒŰł Ù‡Ű§ÛŒ Ù…ÛŒÚ©Ű±ÙˆŰŒ Ű§ŰšŰ±ŰŒ ŰšŰŻÙˆÙ† ŰłŰ±ÙˆŰ±)ی ŰąÛŒŰ§ Ű­ŰȘی ممکن ۧ۳ŰȘ یک Ù…ŰŻÙ„ Ú©Ű§Ù…Ù„Ű§Ù‹ Ù‚ŰŻÛŒÙ…ÛŒ ŰšŰ±Ű§ÛŒ *همه* Ű§Ù†ÙˆŰ§Űč ŰšŰ±Ù†Ű§Ù…Ù‡ Ù‡Ű§ Ù…Ù†Ű§ŰłŰš ۹ۧۮۯ۟ ŰąÛŒŰ§ ŰŻÙ†ÛŒŰ§ÛŒ ŰȘŰłŰȘ Ù†ŰšŰ§ÛŒŰŻ ۧŰČ ŰȘکنیک Ù‡Ű§ÛŒ ŰȘŰłŰȘ ŰŹŰŻÛŒŰŻ ۧ۳ŰȘÙ‚ŰšŰ§Ù„ Ú©Ù†ŰŻŰŸ + +ۧێŰȘŰšŰ§Ù‡ Ù†Ú©Ù†ÛŒŰŻŰŒ ۯ۱ ŰłŰ§Ù„ 2019 ŰȘŰłŰȘ Ù‡Ű±Ù…ŰŒ ŰȘŰłŰȘ TDD و ÙˆŰ§Ű­ŰŻ هنوŰČ ÛŒÚ© ŰȘکنیک Ù‚ŰŻŰ±ŰȘÙ…Ù†ŰŻ Ù‡ŰłŰȘÙ†ŰŻ و ۭۧŰȘÙ…Ű§Ù„Ű§Ù‹ ŰšÙ‡ŰȘŰ±ÛŒÙ† ŰȘŰ·Ű§ŰšÙ‚ ŰšŰ±Ű§ÛŒ ŰšŰłÛŒŰ§Ű±ÛŒ ۧŰČ ŰšŰ±Ù†Ű§Ù…Ù‡ Ù‡Ű§ Ù‡ŰłŰȘÙ†ŰŻ. ÙÙ‚Ű· Ù…Ű§Ù†Ù†ŰŻ Ù‡Ű± Ù…ŰŻÙ„ ŰŻÛŒÚŻŰ±ÛŒ ۚۧ ÙˆŰŹÙˆŰŻ Ù…ÙÛŒŰŻ ŰšÙˆŰŻÙ†, [ÚŻŰ§Ù‡ÛŒ Ű§ÙˆÙ‚Ű§ŰȘ ŰšŰ§ÛŒŰŻ ۧێŰȘŰšŰ§Ù‡ ۚۧێۯ](https://en.wikipedia.org/wiki/All_models_are_wrong). ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† Ù…Ű«Ű§Ù„ŰŒ یک ŰšŰ±Ù†Ű§Ù…Ù‡ IoT ۱ۧ ۯ۱ Ù†ŰžŰ± ŰšÚŻÛŒŰ±ÛŒŰŻ که ŰšŰłÛŒŰ§Ű±ÛŒ ۧŰČ Ű±ÙˆÛŒŰŻŰ§ŰŻÙ‡Ű§ ۱ۧ ۯ۱ یک ÚŻŰ°Ű±ÚŻŰ§Ù‡ ÙŸÛŒŰ§Ù… Ù…Ű§Ù†Ù†ŰŻ Kafka/RabbitMQ ÙˆŰ§Ű±ŰŻ Ù…ÛŒâ€ŒÚ©Ù†ŰŻŰŒ که ŰłÙŸŰł ŰšÙ‡ Ű§Ù†ŰšŰ§Ű± ŰŻŰ§ŰŻÙ‡ ۳۱ۧŰČÛŒŰ± Ù…ÛŒâ€ŒŰŽÙˆŰŻ و ۯ۱ Ù†Ù‡Ű§ÛŒŰȘ ŰȘÙˆŰłŰ· ŰšŰ±ŰźÛŒ ۧŰČ Ű±Ű§ŰšŰ·â€ŒÙ‡Ű§ÛŒ ŰȘŰ­Ù„ÛŒÙ„ÛŒ ÙŸŰ±Űł و ŰŹÙˆ Ù…ÛŒâ€ŒŰŽÙˆŰŻ. ŰąÛŒŰ§ ÙˆŰ§Ù‚ŰčŰ§Ù‹ ŰšŰ§ÛŒŰŻ 50 ۯ۱۔ۯ ۧŰČ ŰšÙˆŰŻŰŹÙ‡ ŰȘŰłŰȘ ŰźÙˆŰŻ ۱ۧ Ű”Ű±Ù Ù†ÙˆŰŽŰȘن ŰąŰČمون Ù‡Ű§ÛŒ ÙˆŰ§Ű­ŰŻ ŰšŰ±Ű§ÛŒ ŰšŰ±Ù†Ű§Ù…Ù‡ Ű§ÛŒ کنیم که ÛŒÚ©ÙŸŰ§Ű±Ú†Ù‡ Ù…Ű­ÙˆŰ± ۧ۳ŰȘ و ŰȘÙ‚Ű±ÛŒŰšŰ§Ù‹ هیچ Ù…Ù†Ű·Ù‚ÛŒ Ù†ŰŻŰ§Ű±ŰŻŰŸ ۚۧ Ű§ÙŰČŰ§ÛŒŰŽ ŰȘنوŰč Ű§Ù†ÙˆŰ§Űč ŰšŰ±Ù†Ű§Ù…Ù‡ Ù‡Ű§ (۱ۚۧŰȘ Ù‡Ű§ŰŒ Ú©Ű±ÛŒÙŸŰȘÙˆŰŒ Ù…Ù‡Ű§Ű±ŰȘ Ù‡Ű§ÛŒ Ű§Ù„Ú©ŰłŰ§) ŰŽŰ§Ù†Űł ŰšÛŒŰŽŰȘŰ±ÛŒ ŰšŰ±Ű§ÛŒ ÛŒŰ§ÙŰȘن ŰłÙ†Ű§Ű±ÛŒÙˆÙ‡Ű§ÛŒÛŒ ÙˆŰŹÙˆŰŻ ۯۧ۱ۯ که ۯ۱ ŰąÙ† Ù‡Ű±Ù… ŰąŰČÙ…Ű§ÛŒŰŽÛŒ ŰšÙ‡ŰȘŰ±ÛŒÙ† ŰȘŰ·Ű§ŰšÙ‚ Ù†ÛŒŰłŰȘ. + +وقŰȘ ŰąÙ† Ű±ŰłÛŒŰŻÙ‡ ۧ۳ŰȘ که Ù…ŰŹÙ…ÙˆŰčه ŰąŰČÙ…Ű§ÛŒŰŽÛŒ ŰźÙˆŰŻ ۱ۧ Űșنی Ú©Ù†ÛŒŰŻ و ۚۧ Ű§Ù†ÙˆŰ§Űč ŰȘŰłŰȘ Ù‡Ű§ÛŒ ŰšÛŒŰŽŰȘŰ±ÛŒ ŰąŰŽÙ†Ű§ ŰŽÙˆÛŒŰŻ (ÚŻÙ„ÙˆÙ„Ù‡ Ù‡Ű§ÛŒ ŰšŰčŰŻÛŒ Ű§ÛŒŰŻÙ‡ Ù‡Ű§ÛŒ کمی ۱ۧ ÙŸÛŒŰŽÙ†Ù‡Ű§ŰŻ می Ú©Ù†Ù†ŰŻ)ی Ù…ŰŻÙ„ Ù‡Ű§ÛŒ Ű°Ù‡Ù†ÛŒ Ù…Ű§Ù†Ù†ŰŻ Ù‡Ű±Ù… ŰąŰČÙ…Ű§ÛŒŰŽŰŒ Ű§Ù…Ű§ همچنین Ű§Ù†ÙˆŰ§Űč ŰąŰČÙ…Ű§ÛŒŰŽ ۱ۧ ۚۧ Ù…ŰŽÚ©Ù„Ű§ŰȘ ŰŻÙ†ÛŒŰ§ÛŒ ÙˆŰ§Ù‚Űčی که ۚۧ ŰąÙ† Ù…ÙˆŰ§ŰŹÙ‡ Ù‡ŰłŰȘÛŒŰŻ Ù…Ű·Ű§ŰšÙ‚ŰȘ ŰŻÙ‡ÛŒŰŻ ('Hey, API Ù…Ű§ ۟۱ۧۚ ۧ۳ŰȘی ŰšÛŒŰ§ÛŒÛŒŰŻ ŰȘŰłŰȘ Ù‚Ű±Ű§Ű±ŰŻŰ§ŰŻ Ù…ŰšŰȘنی ۚ۱ Ù…Ű”Ű±Ùâ€ŒÚ©Ù†Ù†ŰŻÙ‡ ŰšÙ†ÙˆÛŒŰłÛŒÙ…!»)ی ŰąŰČÙ…Ű§ÛŒŰŽâ€ŒÙ‡Ű§ÛŒ ŰźÙˆŰŻ ۱ۧ Ù…Ű§Ù†Ù†ŰŻ ŰłŰ±Ù…Ű§ÛŒÙ‡â€ŒÚŻŰ°Ű§Ű±ÛŒ که ۳ۚۯ ŰłÙ‡Ű§Ù…ÛŒ ۱ۧ ۚ۱ ۧ۳ۧ۳ ŰȘŰŹŰČیه و ŰȘŰ­Ù„ÛŒÙ„ Ű±ÛŒŰłÚ© Ù…ÛŒâ€ŒŰłŰ§ŰČۯی مŰȘنوŰč Ú©Ù†ÛŒŰŻâ€” ۧ۱ŰČÛŒŰ§ŰšÛŒ Ú©Ù†ÛŒŰŻ که ۯ۱ کۏۧ ممکن ۧ۳ŰȘ Ù…ŰŽÚ©Ù„Ű§ŰȘ Ű§ÛŒŰŹŰ§ŰŻ ŰŽÙˆÙ†ŰŻ و ۚۧ ŰšŰ±ŰźÛŒ ۧŰČ Ű§Ù‚ŰŻŰ§Ù…Ű§ŰȘ ÙŸÛŒŰŽÚŻÛŒŰ±Ű§Ù†Ù‡ ŰšŰ±Ű§ÛŒ Ú©Ű§Ù‡ŰŽ ۟۷۱ۧŰȘ ۭۧŰȘÙ…Ű§Ù„ÛŒ Ù…Ű·Ű§ŰšÙ‚ŰȘ ŰŻÙ‡ÛŒŰŻ. + +یک کلمه ۭۧŰȘÛŒŰ§Ű·: ۧ۳ŰȘŰŻÙ„Ű§Ù„ TDD ۯ۱ ŰŻÙ†ÛŒŰ§ÛŒ Ù†Ű±Ù… Ű§ÙŰČۧ۱ یک Ú†Ù‡Ű±Ù‡ ŰŻÙˆÚŻŰ§Ù†ÚŻÛŒ کۧ۰ۚ مŰčمولی ŰŻŰ§Ű±ŰŻŰŒ ŰšŰ±ŰźÛŒ موŰčŰžÙ‡ می Ú©Ù†Ù†ŰŻ که ۧŰČ ŰąÙ† ۯ۱ همه ۏۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ú©Ù†Ù†ŰŻŰŒ ŰšŰ±ŰźÛŒ ŰŻÛŒÚŻŰ± ÙÚ©Ű± می Ú©Ù†Ù†ŰŻ که Ű§ÛŒÙ† ŰŽÛŒŰ·Ű§Ù† ۧ۳ŰȘ. Ù‡Ű± Ú©Űł ŰšÙ‡ Ű·ÙˆŰ± Ù…Ű·Ù„Ù‚ ۔ۭۚŰȘ می Ú©Ù†ŰŻ ۧێŰȘŰšŰ§Ù‡ می Ú©Ù†ŰŻ:] + +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** ŰšŰ±ŰźÛŒ ۧŰČ Ű§ŰšŰČŰ§Ű±Ù‡Ű§ ۚۧ ROI ŰŽÚŻÙŰȘ Ű§Ù†ÚŻÛŒŰČ Ű±Ű§ ۧŰČ ŰŻŰłŰȘ ŰźÙˆŰ§Ù‡ÛŒŰŻ ۯۧۯی ŰšŰ±ŰźÛŒ Ù…Ű§Ù†Ù†ŰŻ Fuzzی lint و ŰŹÙ‡ŰŽ می ŰȘÙˆŰ§Ù†Ù†ŰŻ ۯ۱ 10 ŰŻÙ‚ÛŒÙ‚Ù‡ ۧ۱ŰČŰŽ Ű§Ű±Ű§ŰŠÙ‡ ŰŻÙ‡Ù†ŰŻ. + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :clap: Ù…Ű«Ű§Ù„ ۯ۱۳ŰȘ: ŰłÛŒÙ†ŰŻÛŒ ŰłŰ±ÛŒŰŻÙ‡Ű§Ù† یک نمونه کۧ۱ ŰąŰČÙ…Ű§ÛŒŰŽÛŒ Űșنی ۱ۧ ۯ۱ ÙŸŰłŰȘ ŰŽÚŻÙŰȘâ€ŒŰ§Ù†ÚŻÛŒŰČ ŰźÙˆŰŻ «ŰȘŰłŰȘ Ú©Ű±ŰŻÙ† Ù…ÛŒÚ©Ű±ÙˆŰłŰ±ÙˆÛŒŰłâ€ŒÙ‡Ű§Â» ÙŸÛŒŰŽÙ†Ù‡Ű§ŰŻ Ù…ÛŒâ€ŒÚ©Ù†ŰŻ - ŰšÙ‡ همین Ű±ÙˆŰŽ. + +![alt text](assets/bp-12-rich-testing.jpeg "Cindy Sridharan suggests a rich testing portfolio in her amazing post ‘Testing Microservices — the sane way’") + +â˜șExample: [YouTube: “Beyond Unit Tests: 5 Shiny Node.JS Test Types (2018)” (Yoni Goldberg)](https://www.youtube.com/watch?v=-2zP494wdUY&feature=youtu.be) + +
    + +![alt text](assets/bp-12-Yoni-Goldberg-Testing.jpeg "A test name that constitutes 3 parts") + +
    + +

    + +## âšȘ 2.2 ŰȘŰłŰȘ Ú©Ű§Ù…ÙŸÙˆÙ†Ù†ŰȘ ممکن ۧ۳ŰȘ ŰšÙ‡ŰȘŰ±ÛŒÙ† کۧ۱ ŰŽÙ…Ű§ ۚۧێۯ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** Ù‡Ű± Unit test ۚ۟ێ کوچکی ۧŰČ ŰšŰ±Ù†Ű§Ù…Ù‡ ۱ۧ ÙŸÙˆŰŽŰŽ Ù…ÛŒâ€ŒŰŻÙ‡ŰŻ و ÙŸÙˆŰŽŰŽ کل ŰąÙ† ÚŻŰ±Ű§Ù† ۧ۳ŰȘی ۯ۱ Ű­Ű§Ù„ÛŒ که ŰȘŰłŰȘ E2E ŰšÙ‡ ۱ۭۧŰȘی ŰČمین ŰČÛŒŰ§ŰŻÛŒ ۱ۧ ÙŸÙˆŰŽŰŽ Ù…ÛŒâ€ŒŰŻÙ‡ŰŻŰŒ Ű§Ù…Ű§ ÙŸÙˆŰłŰȘه ÙŸÙˆŰłŰȘه و Ú©Ù†ŰŻŰȘ۱ ۧ۳ŰȘی چ۱ۧ یک Ű±ÙˆÛŒÚ©Ű±ŰŻ مŰȘŰčŰ§ŰŻÙ„ ۱ۧ ۧŰčÙ…Ű§Ù„ نکنیم و ŰȘŰłŰȘâ€ŒÙ‡Ű§ÛŒÛŒ ŰšŰČŰ±ÚŻâ€ŒŰȘ۱ ۧŰČ ŰąÙ† ŰšÙ†ÙˆÛŒŰłÛŒÙ…. Unit tests Ű§Ù…Ű§ کوچکŰȘ۱ ۧŰČ ŰȘŰłŰȘ E2E۟ ŰȘŰłŰȘ Ú©Ű§Ù…ÙŸÙˆÙ†Ù†ŰȘ ŰąÙ‡Ù†ÚŻ Ù†Ű§ŰźÙˆŰ§Ù†ŰŻÙ‡ ŰŻÙ†ÛŒŰ§ÛŒ ŰąŰČÙ…Ű§ÛŒŰŽ ۧ۳ŰȘâ€”â€ŠŰąÙ†Ù‡Ű§ ŰšÙ‡ŰȘŰ±ÛŒÙ† Ù‡Ű§ ۱ۧ ۧŰČ Ù‡Ű± ŰŻÙˆ ŰŻÙ†ÛŒŰ§ Ű§Ű±Ű§ŰŠÙ‡ می ŰŻÙ‡Ù†ŰŻ: ŰčÙ…Ù„Ú©Ű±ŰŻ مŰčقول و Ű§Ù…Ú©Ű§Ù† ۧŰčÙ…Ű§Ù„ Ű§Ù„ÚŻÙˆÙ‡Ű§ÛŒ TDD + ÙŸÙˆŰŽŰŽ ÙˆŰ§Ù‚Űčی و ŰčŰ§Ù„ÛŒ. + +ŰȘŰłŰȘâ€ŒÙ‡Ű§ÛŒ Ú©Ű§Ù…ÙŸÙˆÙ†Ù†ŰȘ Ű±ÙˆÛŒ Â«ÙˆŰ§Ű­ŰŻÂ» Ù…ÛŒÚ©Ű±ÙˆŰłŰ±ÙˆÛŒŰł ŰȘÙ…Ű±Ú©ŰČ Ù…ÛŒâ€ŒÚ©Ù†Ù†ŰŻŰŒ ŰąÙ†â€ŒÙ‡Ű§ ۚ۱ ŰźÙ„Ű§Ù API کۧ۱ Ù…ÛŒâ€ŒÚ©Ù†Ù†ŰŻŰŒ Ù‡Ű± چیŰČی ۱ۧ که مŰȘŰčلق ŰšÙ‡ ŰźÙˆŰŻ Ù…ÛŒÚ©Ű±ÙˆŰłŰ±ÙˆÛŒŰł ۧ۳ŰȘ (Ù…Ű«Ù„Ű§Ù‹ DB ÙˆŰ§Ù‚Űčی ÛŒŰ§ Ű­ŰŻŰ§Ù‚Ù„ Ù†ŰłŰźÙ‡ ŰŻŰ±ÙˆÙ† Ű­Ű§ÙŰžÙ‡ ŰąÙ† DB) ۱ۧ Ù…ŰłŰźŰ±Ù‡ Ù†Ù…ÛŒâ€ŒÚ©Ù†Ù†ŰŻŰŒ Ű§Ù…Ű§ Ù‡Ű± چیŰČی ۱ۧ که ŰźŰ§Ű±ŰŹÛŒ ۧ۳ŰȘی ۟۱ۯ Ù†Ù…ÛŒâ€ŒÚ©Ù†Ù†ŰŻ. Ù…Ű§Ù†Ù†ŰŻ ŰȘÙ…Ű§Űł ۚۧ ŰłŰ§ÛŒŰ± Ù…ÛŒÚ©Ű±ÙˆŰłŰ±ÙˆÛŒŰł Ù‡Ű§. ۚۧ Ű§Ù†ŰŹŰ§Ù… Ű§ÛŒÙ† Ú©Ű§Ű±ŰŒ ŰąÙ†Ú†Ù‡ ۱ۧ که ŰšÙ‡ کۧ۱ Ù…ÛŒâ€ŒÚŻÛŒŰ±ÛŒÙ… ŰąŰČÙ…Ű§ÛŒŰŽ Ù…ÛŒâ€ŒÚ©Ù†ÛŒÙ…ŰŒ ŰšŰ±Ù†Ű§Ù…Ù‡ ۱ۧ ۧŰČ ŰšÛŒŰ±ÙˆÙ† ŰšÙ‡ ŰŻŰ§ŰźÙ„ نŰČŰŻÛŒÚ© می‌کنیم و ۯ۱ Ù…ŰŻŰȘ ŰČÙ…Ű§Ù† مŰčقولی ۧŰčŰȘÙ…Ű§ŰŻ ŰšÙ‡ Ù†ÙŰł ŰšŰ§Ù„Ű§ÛŒÛŒ ŰšÙ‡ ŰŻŰłŰȘ Ù…ÛŒâ€ŒŰąÙˆŰ±ÛŒÙ…. + +[Ù…Ű§ یک Ű±Ű§Ù‡Ù†Ù…Ű§ÛŒ Ú©Ű§Ù…Ù„ ŰŻŰ§Ű±ÛŒÙ… که Ű”Ű±ÙŰ§Ù‹ ŰšÙ‡ Ù†ÙˆŰŽŰȘن ŰȘŰłŰȘ Ù‡Ű§ÛŒ Ù…Ű€Ù„ÙÙ‡ ŰšÙ‡ Ű±ÙˆŰŽ Ű”Ű­ÛŒŰ­ ۧ۟ŰȘ۔ۧ۔ ۯۧ۱ۯ](https://github.com/testjavascript/nodejs-integration-tests-best-practices) + +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** ممکن ۧ۳ŰȘ Ű±ÙˆŰČÙ‡Ű§ÛŒ Ű·ÙˆÙ„Ű§Ù†ÛŒ ۱ۧ Ű”Ű±Ù Ù†ÙˆŰŽŰȘن Unit test Ú©Ù†ÛŒŰŻ ŰȘۧ مŰȘÙˆŰŹÙ‡ ŰŽÙˆÛŒŰŻ که ÙÙ‚Ű· 20 ۯ۱۔ۯ ÙŸÙˆŰŽŰŽ ŰłÛŒŰłŰȘم ۱ۧ ŰŻŰ§Ű±ÛŒŰŻ + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :clap: Ù…Ù‚Ű§Ù„ ۯ۱۳ŰȘ: Supertest ۧۏۧŰČه می ŰŻÙ‡ŰŻ ŰȘۧ نŰČŰŻÛŒÚ© ŰŽŰŻÙ† ŰšÙ‡ Express API ۯ۱ ÙŰ±ŰąÛŒÙ†ŰŻ (ŰłŰ±ÛŒŰč و ÙŸÙˆŰŽŰŽ Ú†Ù†ŰŻÛŒÙ† Ù„Ű§ÛŒÙ‡) + +![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "Examples with Mocha") + +![alt text](assets/bp-13-component-test-yoni-goldberg.png " [Supertest](https://www.npmjs.com/package/supertest) allows approaching Express API in-process (fast and cover many layers)") + +
    + +

    + +## âšȘ 2.3 Ű§Ű·Ù…ÛŒÙ†Ű§Ù† Ű­Ű§Ű”Ù„ Ú©Ù†ÛŒŰŻ که Ù†ŰłŰźÙ‡â€ŒÙ‡Ű§ÛŒ ŰŹŰŻÛŒŰŻ Ù†Ű±Ù… Ű§ÙŰČۧ۱ , ŰȘŰłŰȘ Ù‡Ű§ÛŒÛŒ که ŰšŰ±Ű§ÛŒ api Ù‡Ű§ Ù†ÙˆŰŽŰȘه ŰŽŰŻÙ‡ ۧ۳ŰȘ ۱ۧ ۟۱ۧۚ Ù†Ù…ÛŒâ€ŒÚ©Ù†Ù†ŰŻ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** ŰšÙ†Ű§ŰšŰ±Ű§ÛŒÙ† Microservice ŰŽÙ…Ű§ Ú†Ù†ŰŻÛŒÙ† Ù…ŰŽŰȘŰ±ÛŒ ۯۧ۱ۯ و ŰŽÙ…Ű§ Ú†Ù†ŰŻÛŒÙ† Ù†ŰłŰźÙ‡ ۧŰČ Ű§ÛŒÙ† ŰłŰ±ÙˆÛŒŰł ۱ۧ ŰšÙ‡ ŰŻÙ„Ű§ÛŒÙ„ ۳ۧŰČÚŻŰ§Ű±ÛŒ ۧۏ۱ۧ می Ú©Ù†ÛŒŰŻ (Ű±Ű§Ű¶ÛŒ Ù†ÚŻÙ‡ ۯۧێŰȘن همه). ŰłÙŸŰł Ù…Ù‚ŰŻŰ§Ű±ÛŒ ÙÛŒÙ„ŰŻ ۱ۧ ŰȘŰșÛŒÛŒŰ± Ù…ÛŒâ€ŒŰŻÙ‡ÛŒŰŻ و Â«ŰšÙˆÙ…!Â»ŰŒ Ù…ŰŽŰȘŰ±ÛŒ مهمی که ŰšÙ‡ Ű§ÛŒÙ† ŰČمینه مŰȘکی ۧ۳ŰȘ ŰčŰ”ŰšŰ§Ù†ÛŒ ۧ۳ŰȘ. Ű§ÛŒÙ† Catch-22 ŰŻÙ†ÛŒŰ§ÛŒ ÛŒÚ©ÙŸŰ§Ű±Ú†Ù‡ ۳ۧŰČی ۧ۳ŰȘ: ŰšŰ±Ű§ÛŒ Ű·Ű±Ù ŰłŰ±ÙˆŰ± ŰšŰłÛŒŰ§Ű± Ú†Ű§Ù„ŰŽ ŰšŰ±Ű§Ù†ÚŻÛŒŰČ Ű§ŰłŰȘ که ŰȘÙ…Ű§Ù… Ű§Ù†ŰȘ۞ۧ۱ۧŰȘ Ù…ŰŽŰȘŰ±ÛŒ Ú†Ù†ŰŻÚŻŰ§Ù†Ù‡ ۱ۧ ۯ۱ Ù†ŰžŰ± ŰšÚŻÛŒŰ±ŰŻâ€”â€ŠŰ§ŰČ ŰłÙˆÛŒ ŰŻÛŒÚŻŰ±ŰŒ Ù…ŰŽŰȘŰ±ÛŒŰ§Ù† نمی ŰȘÙˆŰ§Ù†Ù†ŰŻ هیچ ŰąŰČÙ…Ű§ÛŒŰŽÛŒ ۱ۧ Ű§Ù†ŰŹŰ§Ù… ŰŻÙ‡Ù†ŰŻ ŰČÛŒŰ±Ű§ ŰłŰ±ÙˆŰ± ŰȘŰ§Ű±ÛŒŰź Ű§Ù†ŰȘێۧ۱ ۱ۧ کنŰȘŰ±Ù„ می Ú©Ù†ŰŻ. Ű·ÛŒÙÛŒ ۧŰČ ŰȘÚ©Ù†ÛŒÚ©â€ŒÙ‡Ű§ ÙˆŰŹÙˆŰŻ ŰŻŰ§Ű±Ù†ŰŻ که می‌ŰȘÙˆŰ§Ù†Ù†ŰŻ Ù…ŰŽÚ©Ù„ Ù‚Ű±Ű§Ű±ŰŻŰ§ŰŻ ۱ۧ Ú©Ű§Ù‡ŰŽ ŰŻÙ‡Ù†ŰŻŰŒ ŰšŰ±ŰźÛŒ ŰłŰ§ŰŻÙ‡ Ù‡ŰłŰȘÙ†ŰŻŰŒ ŰšŰ±ŰźÛŒ ŰŻÛŒÚŻŰ± ŰŻŰ§Ű±Ű§ÛŒ ÙˆÛŒÚ˜ÚŻÛŒâ€ŒÙ‡Ű§ÛŒ Űșنی‌ŰȘ۱ Ù‡ŰłŰȘÙ†ŰŻ و Ù…Ù†Ű­Ù†ÛŒ ÛŒŰ§ŰŻÚŻÛŒŰ±ÛŒ ŰȘÙ†ŰŻŰȘŰ±ÛŒ ۱ۧ Ű·Ù„Űš Ù…ÛŒâ€ŒÚ©Ù†Ù†ŰŻ. ۯ۱ یک Ű±ÙˆÛŒÚ©Ű±ŰŻ ŰłŰ§ŰŻÙ‡ و ŰȘÙˆŰ”ÛŒÙ‡â€ŒŰŽŰŻÙ‡ŰŒ Ű§Ű±Ű§ŰŠÙ‡â€ŒŰŻÙ‡Ù†ŰŻÙ‡ API ۚ۳ŰȘه npm ۱ۧ ۚۧ ŰȘŰ§ÛŒÙŸ API منŰȘێ۱ Ù…ÛŒâ€ŒÚ©Ù†ŰŻ (Ù…Ű§Ù†Ù†ŰŻ JSDocی TypeScript). ŰłÙŸŰł Ù…Ű”Ű±Ù Ú©Ù†Ù†ŰŻÚŻŰ§Ù† می ŰȘÙˆŰ§Ù†Ù†ŰŻ Ű§ÛŒÙ† Ú©ŰȘŰ§ŰšŰźŰ§Ù†Ù‡ ۱ۧ ŰŻŰ±ÛŒŰ§ÙŰȘ Ú©Ù†Ù†ŰŻ و ۧŰČ ŰČÙ…Ű§Ù† Ù‡ÙˆŰŽÙ…Ù†ŰŻŰ§Ù†Ù‡ و ۧŰčŰȘŰšŰ§Ű±ŰłÙ†ŰŹÛŒ ŰšÙ‡Ű±Ù‡ Ù…Ù†ŰŻ ŰŽÙˆÙ†ŰŻ. یک Ű±ÙˆÛŒÚ©Ű±ŰŻ ŰŽÛŒÚ© ŰȘ۱ ۧŰČ ŰąÙ† ŰšŰ±Ű§ÛŒ ۧ۳ŰȘÙŰ§ŰŻÙ‡ [PACT](https://docs.pact.io/) که ŰšŰ±Ű§ÛŒ Ű±ŰłÙ…ÛŒ Ú©Ű±ŰŻÙ† Ű§ÛŒÙ† ÙŰ±ŰąÛŒÙ†ŰŻ ۚۧ Ű±ÙˆÛŒÚ©Ű±ŰŻÛŒ ŰšŰłÛŒŰ§Ű± Ù…ŰźŰ±Űš مŰȘÙˆÙ„ŰŻ ŰŽŰŻÙ‡â€ŒŰ§Ù†ŰŻâ€”â€”â€ŠÙ†Ù‡ ŰłŰ±ÙˆŰ± ŰšŰ±Ù†Ű§Ù…Ù‡ ŰąŰČÙ…Ű§ÛŒŰŽÛŒ ۱ۧ ۧŰČ ŰźÙˆŰŻ ŰȘŰčŰ±ÛŒÙ Ù…ÛŒâ€ŒÚ©Ù†ŰŻŰŒ ŰšÙ„Ú©Ù‡ Ù…ŰŽŰȘŰ±ÛŒ ŰąŰČÙ…Ű§ÛŒŰŽâ€ŒÙ‡Ű§ÛŒ ŰłŰ±ÙˆŰ± ۱ۧ ŰȘŰčŰ±ÛŒÙ Ù…ÛŒâ€ŒÚ©Ù†ŰŻ! PACT می‌ŰȘÙˆŰ§Ù†ŰŻ Ű§Ù†ŰȘ۞ۧ۱ۧŰȘ Ù…ŰŽŰȘŰ±ÛŒ ۱ۧ ۫ۚŰȘ Ú©Ù†ŰŻ و ۯ۱ یک Ù…Ú©Ű§Ù† Ù…ŰŽŰȘŰ±Ú©ŰŒ «کۧ۱گŰČۧ۱» Ù‚Ű±Ű§Ű± ŰŻÙ‡ŰŻŰŒ ŰšÙ†Ű§ŰšŰ±Ű§ÛŒÙ† ŰłŰ±ÙˆŰ± می‌ŰȘÙˆŰ§Ù†ŰŻ Ű§Ù†ŰȘ۞ۧ۱ۧŰȘ ۱ۧ ŰŹÙ…Űčâ€ŒŰąÙˆŰ±ÛŒ Ú©Ù†ŰŻ و ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ Ú©ŰȘŰ§ŰšŰźŰ§Ù†Ù‡ PACT Ű±ÙˆÛŒ Ù‡Ű± ۳ۧ۟ŰȘنی ۧۏ۱ۧ Ú©Ù†ŰŻ ŰȘۧ Ù‚Ű±Ű§Ű±ŰŻŰ§ŰŻÙ‡Ű§ÛŒ ŰŽÚ©ŰłŰȘه ۱ۧ ŰŽÙ†Ű§ŰłŰ§ÛŒÛŒ Ú©Ù†ŰŻ - یک Ű§Ù†ŰȘ۞ۧ۱ Ù…ŰŽŰȘŰ±ÛŒ که ŰšŰ±ŰąÙˆŰ±ŰŻÙ‡ Ù†ŰŽŰŻÙ‡ ۧ۳ŰȘ. ۚۧ Ű§Ù†ŰŹŰ§Ù… Ű§ÛŒÙ† Ú©Ű§Ű±ŰŒ همه Ù†Ű§Ù‡Ù…Ű§Ù‡Ù†ÚŻÛŒ Ù‡Ű§ÛŒ API ŰłŰ±ÙˆŰ±-کلینŰȘ ۯ۱ Ù…Ű±Ű§Ű­Ù„ Ű§ÙˆÙ„ÛŒÙ‡ ۳ۧ۟ŰȘ/CI ŰŽÙ†Ű§ŰłŰ§ÛŒÛŒ می ŰŽÙˆÙ†ŰŻ و ممکن ۧ۳ŰȘ ŰŽÙ…Ű§ ۱ۧ ۧŰČ Ù†Ű§Ű§Ù…ÛŒŰŻÛŒ ŰČÛŒŰ§ŰŻÛŒ Ù†ŰŹŰ§ŰȘ ŰŻÙ‡ŰŻ. +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** ÚŻŰČینه Ù‡Ű§ÛŒ ŰŹŰ§ÛŒÚŻŰČین ŰȘŰłŰȘ ŰŻŰłŰȘی ۟۳ŰȘه Ú©Ù†Ù†ŰŻÙ‡ ÛŒŰ§ ŰȘ۱۳ ۧŰČ Ű§ŰłŰȘÙ‚Ű±Ű§Ű± Ù‡ŰłŰȘÙ†ŰŻ + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :clap: Ù…Ù‚Ű§Ù„ ۯ۱۳ŰȘ: + +![](https://img.shields.io/badge/🔧%20Example%20using%20PACT-blue.svg "Examples with PACT") + +![alt text](assets/bp-14-testing-best-practices-contract-flow.png) + +
    + +

    + +## âšȘ  2.4 middleware ŰźÙˆŰŻ ۱ۧ ŰŹŰŻŰ§ÚŻŰ§Ù†Ù‡ ŰȘŰłŰȘ Ú©Ù†ÛŒŰŻ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** ŰšŰłÛŒŰ§Ű±ÛŒ ۧŰČ ŰȘŰłŰȘ Middleware ۧۏŰȘÙ†Ű§Űš می Ú©Ù†Ù†ŰŻ ŰČÛŒŰ±Ű§ ŰąÙ†Ù‡Ű§ ۚ۟ێ کوچکی ۧŰČ ŰłÛŒŰłŰȘم ۱ۧ Ù†ŰŽŰ§Ù† می ŰŻÙ‡Ù†ŰŻ و ŰšÙ‡ یک ŰłŰ±ÙˆŰ± Express ŰČÙ†ŰŻÙ‡ Ù†ÛŒŰ§ŰČ ŰŻŰ§Ű±Ù†ŰŻ. Ù‡Ű± ŰŻÙˆ ŰŻÙ„ÛŒÙ„ ۧێŰȘŰšŰ§Ù‡ Ù‡ŰłŰȘÙ†ŰŻâ€”â€”Middleware Ù‡Ű§ کوچک Ù‡ŰłŰȘÙ†ŰŻ Ű§Ù…Ű§ ۚ۱ همه ÛŒŰ§ ŰšÛŒŰŽŰȘ۱ ŰŻŰ±ŰźÙˆŰ§ŰłŰȘ Ù‡Ű§ ŰȘŰŁŰ«ÛŒŰ± می ÚŻŰ°Ű§Ű±Ù†ŰŻ و می ŰȘÙˆŰ§Ù†Ù†ŰŻ ŰšÙ‡ ۱ۭۧŰȘی ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† ŰȘÙˆŰ§ŰšŰč ŰźŰ§Ù„Ű”ÛŒ که Ű§ŰŽÛŒŰ§ŰĄ JS {req,res} ۱ۧ ŰŻŰ±ÛŒŰ§ÙŰȘ می Ú©Ù†Ù†ŰŻ ŰąŰČÙ…Ű§ÛŒŰŽ ŰŽÙˆÙ†ŰŻ. ŰšŰ±Ű§ÛŒ ŰąŰČÙ…Ű§ÛŒŰŽ ŰčÙ…Ù„Ú©Ű±ŰŻ Ù…ÛŒŰ§Ù†â€ŒŰ§ÙŰČŰ§Ű±ŰŒ ÙÙ‚Ű· ŰšŰ§ÛŒŰŻ ŰąÙ† ۱ۧ ÙŰ±Ű§ŰźÙˆŰ§Ù†ÛŒ و ŰŹŰ§ŰłÙˆŰłÛŒ ک۱ۯ ([ŰšŰ±Ű§ÛŒ Ù…Ű«Ű§Ù„ ۧŰČ Sinon ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ú©Ù†ÛŒŰŻ](https://www.npmjs.com/package/sinon)) ۯ۱ ŰȘŰčŰ§Ù…Ù„ ۚۧ ŰŻÙˆ object {req,res} ŰšŰ±Ű§ÛŒ Ű§Ű·Ù…ÛŒÙ†Ű§Ù† ۧŰČ Ű§Ù†ŰŹŰ§Ù… ŰčÙ…Ù„Ú©Ű±ŰŻ Ű”Ű­ÛŒŰ­ ŰčÙ…Ù„Ú©Ű±ŰŻ. Ú©ŰȘŰ§ŰšŰźŰ§Ù†Ù‡ [node-mock-http](https://www.npmjs.com/package/node-mocks-http) ŰąÙ† ۱ۧ Ű­ŰȘی ÙŰ±Ű§ŰȘ۱ می ۚ۱ۯ و ŰŻÙˆ object {req,res} ۱ۧ Ù‡Ù…Ű±Ű§Ù‡ ۚۧ ŰŹŰ§ŰłÙˆŰłÛŒ ۧŰČ Ű±ÙŰȘۧ۱ ŰąÙ†Ù‡Ű§ ÙŰ§Ú©ŰȘÙˆŰ± می Ú©Ù†ŰŻ. ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† Ù…Ű«Ű§Ù„ŰŒ می ŰȘÙˆŰ§Ù†ŰŻ ۧۯŰčۧ Ú©Ù†ŰŻ که ŰąÛŒŰ§ ÙˆŰ¶ŰčیŰȘ http که Ű±ÙˆÛŒ ŰŽÛŒ res ŰȘÙ†ŰžÛŒÙ… ŰŽŰŻÙ‡ ۧ۳ŰȘ ۚۧ Ű§Ù†ŰȘ۞ۧ۱ۧŰȘ Ù…Ű·Ű§ŰšÙ‚ŰȘ ۯۧ۱ۯ ÛŒŰ§ ŰźÛŒŰ± (ŰšÙ‡ Ù…Ű«Ű§Ù„ ŰČÛŒŰ± Ù…Ű±Ű§ŰŹŰčه Ú©Ù†ÛŒŰŻ). +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** یک Ű§ŰŽÚ©Ű§Ù„ ۯ۱ Ù…ÛŒŰ§Ù† Ű§ÙŰČۧ۱ Express === یک Ű§ŰŽÚ©Ű§Ù„ ۯ۱ همه ÛŒŰ§ ۧک۫۱ request Ù‡Ű§ + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :clap: Ù…Ù‚Ű§Ù„ ۯ۱۳ŰȘ: ŰȘŰłŰȘ middleware ŰšÙ‡â€ŒŰ”ÙˆŰ±ŰȘ Ù…ŰŹŰČۧ ŰšŰŻÙˆÙ† ŰšŰ±Ù‚Ű±Ű§Ű±ÛŒ ŰȘÙ…Ű§Űłâ€ŒÙ‡Ű§ÛŒ ŰŽŰšÚ©Ù‡ و ŰšÛŒŰŻŰ§Ű± Ú©Ű±ŰŻÙ† کل ŰŻŰłŰȘÚŻŰ§Ù‡ Express + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Jest") + +```javascript +//middleware که می ŰźÙˆŰ§Ù‡ÛŒÙ… ŰȘŰłŰȘ کنیم +const unitUnderTest = require("./middleware"); +const httpMocks = require("node-mocks-http"); +//Jest syntax, equivelant to describe() & it() in Mocha +test("ŰŻŰ±ŰźÙˆŰ§ŰłŰȘ ŰšŰŻÙˆÙ† Ù‡ŰŻŰ± ۭۧ۱ۧŰČ Ù‡ÙˆÛŒŰȘی ŰšŰ§ÛŒŰŻ ÙˆŰ¶ŰčیŰȘ http 403 ۱ۧ ŰšŰ±ÚŻŰ±ŰŻŰ§Ù†ŰŻ", () => { + const request = httpMocks.createRequest({ + method: "GET", + url: "/user/42", + headers: { + authentication: "" + } + }); + const response = httpMocks.createResponse(); + unitUnderTest(request, response); + expect(response.statusCode).toBe(403); +}); +``` + +
    + +

    + +## âšȘ 2.5 Ű§Ù†ŰŻŰ§ŰČه ÚŻÛŒŰ±ÛŒ و ۚۧŰČ۳ۧŰČی ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ Ű§ŰšŰČŰ§Ű±Ù‡Ű§ÛŒ ŰȘŰ­Ù„ÛŒÙ„ ۧ۳ŰȘۧŰȘیکی + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ Ű§ŰšŰČŰ§Ű±Ù‡Ű§ÛŒ ŰȘŰŹŰČیه و ŰȘŰ­Ù„ÛŒÙ„ ۧ۳ŰȘۧŰȘیک ۚۧ Ű§Ű±Ű§ŰŠÙ‡ Ű±ÙˆŰŽ Ù‡Ű§ÛŒ Űčینی ŰšŰ±Ű§ÛŒ ŰšÙ‡ŰšÙˆŰŻ کیفیŰȘ Ú©ŰŻ و Ű­ÙŰž Ú©ŰŻ ŰŽÙ…Ű§ کمک می Ú©Ù†ŰŻ. می‌ŰȘÙˆŰ§Ù†ÛŒŰŻ ۧۚŰČŰ§Ű±Ù‡Ű§ÛŒ ŰȘŰŹŰČیه و ŰȘŰ­Ù„ÛŒÙ„ ۧ۳ŰȘۧŰȘیک ۱ۧ ŰšÙ‡ ۳ۧ۟ŰȘ CI ŰźÙˆŰŻ Ű§Ű¶Ű§ÙÙ‡ Ú©Ù†ÛŒŰŻ ŰȘۧ ŰČÙ…Ű§Ù†ÛŒ که ŰšÙˆÛŒ Ú©ŰŻ ÙŸÛŒŰŻŰ§ Ù…ÛŒâ€ŒŰŽÙˆŰŻŰŒ لŰșو ŰŽÙˆŰŻ. Ù†Ù‚Ű§Ű· Ű§Ű”Ù„ÛŒ ÙŰ±ÙˆŰŽ ŰąÙ† Ù†ŰłŰšŰȘ ŰšÙ‡ ÙŸŰ±ŰŻÙ‡â€ŒŰšÙ†ŰŻÛŒ ŰłŰ§ŰŻÙ‡ŰŒ ŰȘÙˆŰ§Ù†Ű§ÛŒÛŒ ۚۧŰČŰ±ŰłÛŒ کیفیŰȘ ۯ۱ ŰČمینه ÙŰ§ÛŒÙ„â€ŒÙ‡Ű§ÛŒ مŰȘŰčŰŻŰŻ (ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† Ù…Ű«Ű§Ù„ ŰŽÙ†Ű§ŰłŰ§ÛŒÛŒ Ù…ÙˆŰ§Ű±ŰŻ ŰȘÚ©Ű±Ű§Ű±ÛŒ)ی Ű§Ù†ŰŹŰ§Ù… ŰȘŰŹŰČیه و ŰȘŰ­Ù„ÛŒÙ„ ÙŸÛŒŰŽŰ±ÙŰȘه (Ù…Ű§Ù†Ù†ŰŻ ÙŸÛŒÚ†ÛŒŰŻÚŻÛŒ Ú©ŰŻ) و ÙŸÛŒÚŻÛŒŰ±ÛŒ ŰȘŰ§Ű±ÛŒŰźÚ†Ù‡ و ÙŸÛŒŰŽŰ±ÙŰȘ Ù…ŰŽÚ©Ù„Ű§ŰȘ Ú©ŰŻ ۧ۳ŰȘ. ŰŻÙˆ نمونه ۧŰČ Ű§ŰšŰČŰ§Ű±Ù‡Ű§ÛŒÛŒ که می ŰȘÙˆŰ§Ù†ÛŒŰŻ ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ú©Ù†ÛŒŰŻ Űčۚۧ۱ŰȘÙ†ŰŻ ۧŰČ [SonarQube](https://www.sonarqube.org/) (4,900+ [ŰłŰȘŰ§Ű±Ù‡](https://github.com/SonarSource/sonarqube)) و [Ú©ŰŻ ۹ۚ و Ù‡ÙˆŰ§](https://codeclimate.com/) (2,000+ [ŰłŰȘŰ§Ű±Ù‡](https://github.com/codeclimate/codeclimate)) + +۳ۧŰČÙ†ŰŻÙ‡: [Keith Holliday](https://github.com/TheHollidayInn) + +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** ۚۧ کیفیŰȘ ÙŸŰ§ÛŒÛŒÙ† Ú©ŰŻŰŒ Ű§ŰŽÚ©Ű§Ù„Ű§ŰȘ و ŰčÙ…Ù„Ú©Ű±ŰŻ Ù‡Ù…ÛŒŰŽÙ‡ Ù…ŰŽÚ©Ù„ÛŒ ۧ۳ŰȘ که هیچ Ú©ŰȘŰ§ŰšŰźŰ§Ù†Ù‡ ŰŹŰŻÛŒŰŻ و ŰšŰ±Ű§Ù‚ ŰŹŰŻÛŒŰŻÛŒ نمی ŰȘÙˆŰ§Ù†ŰŻ ŰąÙ† ۱ۧ ŰšŰ±Ű·Ű±Ù Ú©Ù†ŰŻ. + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :clap: Ù…Ù‚Ű§Ù„ ۯ۱۳ŰȘ: CodeClimateی یک ۧۚŰČۧ۱ ŰȘŰŹŰ§Ű±ÛŒ ۧ۳ŰȘ که می ŰȘÙˆŰ§Ù†ŰŻ مŰȘŰŻ Ù‡Ű§ÛŒ ÙŸÛŒÚ†ÛŒŰŻÙ‡ ۱ۧ ŰŽÙ†Ű§ŰłŰ§ÛŒÛŒ Ú©Ù†ŰŻ: + +![](https://img.shields.io/badge/🔧%20Example%20using%20Code%20Climate-blue.svg "Examples with CodeClimate") + +![alt text](assets/bp-16-yoni-goldberg-quality.png "CodeClimate, a commercial tool that can identify complex methods:") + +
    + +

    + +## âšȘ  2.6 ŰąÙ…Ű§ŰŻÚŻÛŒ ŰźÙˆŰŻ ۱ۧ ŰšŰ±Ű§ÛŒ Ù‡Ű±ŰŹ و Ù…Ű±ŰŹ Ù…Ű±ŰȘۚ۷ ۚۧ Node ŰšŰ±Ű±ŰłÛŒ Ú©Ù†ÛŒŰŻ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** ŰšÙ‡ Ű·ÙˆŰ± ŰčŰŹÛŒŰšÛŒŰŒ ۧک۫۱ ŰȘŰłŰȘâ€ŒÙ‡Ű§ÛŒ Ù†Ű±Ù…â€ŒŰ§ÙŰČۧ۱ ÙÙ‚Ű· ۯ۱ Ù…ÙˆŰ±ŰŻ Ù…Ù†Ű·Ù‚ و ŰŻŰ§ŰŻÙ‡â€ŒÙ‡Ű§ Ù‡ŰłŰȘÙ†ŰŻŰŒ Ű§Ù…Ű§ ŰšŰ±ŰźÛŒ ۧŰČ ŰšŰŻŰȘŰ±ÛŒÙ† چیŰČÙ‡Ű§ÛŒÛŒ که ۧŰȘÙŰ§Ù‚ Ù…ÛŒâ€ŒŰ§ÙŰȘŰŻ (و ÙˆŰ§Ù‚ŰčŰ§Ù‹ Ú©Ű§Ù‡ŰŽ ŰąÙ† ۳۟ŰȘ ۧ۳ŰȘ) Ù…ŰłŰ§ŰŠÙ„ ŰČÛŒŰ±ŰłŰ§ŰźŰȘی ۧ۳ŰȘ. ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† Ù…Ű«Ű§Ù„ŰŒ ŰąÛŒŰ§ ŰȘۧ ŰšÙ‡ Ű­Ű§Ù„ ŰąŰČÙ…Ű§ÛŒŰŽ Ú©Ű±ŰŻÙ‡â€ŒŰ§ÛŒŰŻ که چه ۧŰȘÙŰ§Ù‚ÛŒ Ù…ÛŒâ€ŒŰ§ÙŰȘŰŻ وقŰȘی Ű­Ű§ÙŰžÙ‡ ÙŸŰ±ŰŻŰ§ŰČŰŽ ŰŽÙ…Ű§ ŰšÛŒŰŽ ۧŰČ Ű­ŰŻ ŰšŰ§Ű±ÚŻÛŒŰ±ÛŒ Ù…ÛŒâ€ŒŰŽÙˆŰŻŰŒ ÛŒŰ§ ŰČÙ…Ű§Ù†ÛŒ که ŰłŰ±ÙˆŰ±/ÙŰ±ŰąÛŒÙ†ŰŻ ۧŰČ ŰšÛŒÙ† Ù…ÛŒâ€ŒŰ±ÙˆŰŻŰŒ ÛŒŰ§ ŰąÛŒŰ§ ŰłÛŒŰłŰȘم Ù†ŰžŰ§Ű±ŰȘ ŰŽÙ…Ű§ مŰȘÙˆŰŹÙ‡ Ù…ÛŒâ€ŒŰŽÙˆŰŻ که API 50ÙȘ Ú©Ù†ŰŻŰȘ۱ Ù…ÛŒâ€ŒŰŽÙˆŰŻŰŸ ŰšŰ±Ű§ÛŒ ŰąŰČÙ…Ű§ÛŒŰŽ و Ú©Ű§Ù‡ŰŽ Ű§ÛŒÙ† نوŰč چیŰČÙ‡Ű§ÛŒ ۹ۯ——[Ù…Ù‡Ù†ŰŻŰłÛŒ ŰąŰŽÙˆŰš](https://principlesofchaos.org/) ŰȘÙˆŰłŰ· نŰȘÙÙ„ÛŒÚ©Űł مŰȘÙˆÙ„ŰŻ ŰŽŰŻ. Ù‡ŰŻÙ ŰąÙ† Ű§Ű±Ű§ŰŠÙ‡ ŰąÚŻŰ§Ù‡ÛŒŰŒ Ú†Ű§Ű±Ú†ÙˆŰš Ù‡Ű§ و ۧۚŰČŰ§Ű±Ù‡Ű§ÛŒÛŒ ŰšŰ±Ű§ÛŒ ŰąŰČÙ…Ű§ÛŒŰŽ Ű§Ù†ŰčŰ·Ű§Ù ÙŸŰ°ÛŒŰ±ÛŒ ŰšŰ±Ù†Ű§Ù…Ù‡ Ù…Ű§ ۯ۱ ۚ۱ۧۚ۱ Ù…ŰłŰ§ŰŠÙ„ ŰąŰŽÙŰȘه ۧ۳ŰȘ. ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† Ù…Ű«Ű§Ù„ یکی ۧŰČ Ű§ŰšŰČŰ§Ű±Ù‡Ű§ÛŒ مŰčŰ±ÙˆÙ ŰąÙ†ŰŒ [میمون ŰąŰŽÙˆŰš](https://github.com/Netflix/chaosmonkey), ŰšÙ‡ Ű·ÙˆŰ± ŰȘŰ”Ű§ŰŻÙÛŒ ŰłŰ±ÙˆŰ±Ù‡Ű§ ۱ۧ می Ú©ŰŽŰŻ ŰȘۧ Ű§Ű·Ù…ÛŒÙ†Ű§Ù† Ű­Ű§Ű”Ù„ ŰŽÙˆŰŻ که ŰłŰ±ÙˆÛŒŰł Ù…Ű§ Ù‡Ù…Ú†Ù†Ű§Ù† می ŰȘÙˆŰ§Ù†ŰŻ ŰšÙ‡ Ú©Ű§Ű±ŰšŰ±Ű§Ù† ŰłŰ±ÙˆÛŒŰł ŰŻÙ‡ŰŻ و ŰšÙ‡ یک ŰłŰ±ÙˆŰ± ŰȘکیه نمی Ú©Ù†ŰŻ (Ù†ŰłŰźÙ‡ Kubernetes نیŰČ ÙˆŰŹÙˆŰŻ ŰŻŰ§Ű±ŰŻŰŒ [میمون Ú©ÙˆŰšÙ‡](https://github.com/asobti/kube-monkey), که ŰșÙ„Ű§Ù Ù‡Ű§ ۱ۧ می Ú©ŰŽŰŻ). همه Ű§ÛŒÙ† ۧۚŰČŰ§Ű±Ù‡Ű§ ۯ۱ ۳۷ۭ میŰČŰšŰ§Ù†ÛŒ/ٟلŰȘÙŰ±Ù… کۧ۱ Ù…ÛŒâ€ŒÚ©Ù†Ù†ŰŻŰŒ Ű§Ù…Ű§ ۧگ۱ Ù…ÛŒâ€ŒŰźÙˆŰ§Ù‡ÛŒŰŻ ŰąŰŽÙŰȘÚŻÛŒ Node ŰźŰ§Ù„Ű” ۱ۧ ŰąŰČÙ…Ű§ÛŒŰŽ و Ű§ÛŒŰŹŰ§ŰŻ Ú©Ù†ÛŒŰŻŰŒ چه Ù…ÛŒâ€ŒÚ©Ù†ÛŒŰŻŰŒ Ù…Ű§Ù†Ù†ŰŻ ŰšŰ±Ű±ŰłÛŒ Ű§ÛŒÙ†Ú©Ù‡ Ú†ÚŻÙˆÙ†Ù‡ ÙŰ±ŰąÛŒÙ†ŰŻ Node ŰŽÙ…Ű§ ۚۧ ŰźŰ·Ű§Ù‡Ű§ÛŒ Ú©ŰŽÙ Ù†ŰŽŰŻÙ‡ŰŒ ۱ۯ Ú©Ű±ŰŻÙ† وŰčŰŻÙ‡â€ŒÙ‡Ű§ÛŒ کنŰȘŰ±Ù„ Ù†ŰŽŰŻÙ‡ŰŒ Ű­Ű§ÙŰžÙ‡ v8 ŰšŰ§Ű±ÚŻÛŒŰ±ÛŒ ŰŽŰŻÙ‡ ۚۧ ۭۯۧک۫۱ Ù…ŰŹŰ§ŰČ 1.7 ÚŻÛŒÚŻŰ§ŰšŰ§ÛŒŰȘ ÛŒŰ§ Ű§ÛŒÙ†Ú©Ù‡ ŰąÛŒŰ§ کنŰȘŰ±Ù„ Ù…ÛŒâ€ŒŰŽÙˆŰŻ چه Ù…ÛŒâ€ŒŰŽÙˆŰŻ. ŰČÙ…Ű§Ù†ÛŒ که Ű­Ù„Ù‚Ù‡ Ű±ÙˆÛŒŰŻŰ§ŰŻ ۧŰșÙ„Űš Ù…ŰłŰŻÙˆŰŻ می ŰŽÙˆŰŻŰŒ ŰȘŰŹŰ±ŰšÙ‡ Ú©Ű§Ű±ŰšŰ±ÛŒ ŰŽÙ…Ű§ Ű±Ű¶Ű§ÛŒŰȘ ۚ۟ێ ŰšŰ§Ù‚ÛŒ می Ù…Ű§Ù†ŰŻŰŸ ŰšŰ±Ű§ÛŒ ÙŸŰ±ŰŻŰ§ŰźŰȘن ŰšÙ‡ Ű§ÛŒÙ† که Ù†ÙˆŰŽŰȘه Ű§Ù…ŰŒ [node-chaos](https://github.com/i0natan/node-chaos-monkey) (alpha) که Ű§Ù†ÙˆŰ§Űč ۧŰčÙ…Ű§Ù„ ŰąŰŽÙŰȘه Ù…Ű±ŰȘۚ۷ ۚۧ Node ۱ۧ ÙŰ±Ű§Ù‡Ù… می Ú©Ù†ŰŻ +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** Ű§ÛŒÙ†ŰŹŰ§ Ű±Ű§Ù‡ ÚŻŰ±ÛŒŰČی Ù†ÛŒŰłŰȘی Ù‚Ű§Ù†ÙˆÙ† Ù…ÙˆŰ±ÙÛŒ ŰšŰŻÙˆÙ† Ű±Ű­Ù… ŰšÙ‡ ŰȘÙˆÙ„ÛŒŰŻ ŰŽÙ…Ű§ Ű¶Ű±ŰšÙ‡ می ŰČÙ†ŰŻ + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :clap: Ù…Ù‚Ű§Ù„ ۯ۱۳ŰȘ: : Node-chaos می‌ŰȘÙˆŰ§Ù†ŰŻ Ű§Ù†ÙˆŰ§Űč ŰŽÙˆŰźÛŒâ€ŒÙ‡Ű§ÛŒ Node.js ۱ۧ Ű§ÛŒŰŹŰ§ŰŻ Ú©Ù†ŰŻŰŒ ŰšÙ†Ű§ŰšŰ±Ű§ÛŒÙ† می‌ŰȘÙˆŰ§Ù†ÛŒŰŻ میŰČŰ§Ù† Ű§Ù†ŰčŰ·Ű§Ùâ€ŒÙŸŰ°ÛŒŰ±ÛŒ ŰšŰ±Ù†Ű§Ù…Ù‡ ŰŽÙ…Ű§ ۯ۱ ۚ۱ۧۚ۱ Ù‡Ű±ŰŹ و Ù…Ű±ŰŹ ۱ۧ ŰąŰČÙ…Ű§ÛŒŰŽ Ú©Ù†ÛŒŰŻ. + +![alt text](assets/bp-17-yoni-goldberg-chaos-monkey-nodejs.png "Node-chaos can generate all sort of Node.js pranks so you can test how resilience is your app to chaos") + +
    + +
    + +## âšȘ 2.7 ۧۏŰȘÙ†Ű§Űš ۧŰČ ŰȘŰŹÙ‡ÛŒŰČۧŰȘ ŰȘŰłŰȘ ŰŹÙ‡Ű§Ù†ÛŒ و ŰŻŰ§Ù†Ù‡ŰŒ Ű§Ű¶Ű§ÙÙ‡ Ú©Ű±ŰŻÙ† ŰŻŰ§ŰŻÙ‡ Ù‡Ű§ ۯ۱ Ù‡Ű± ŰąŰČمون + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** ۚۧ ŰȘÙˆŰŹÙ‡ ŰšÙ‡ Ù‚Ű§Ù†ÙˆÙ† Ű·Ù„Ű§ÛŒÛŒ (ÚŻÙ„ÙˆÙ„Ù‡ 0)ی Ù‡Ű± ŰȘŰłŰȘ ŰšŰ§ÛŒŰŻ Ù…ŰŹÙ…ÙˆŰčه Ű§ÛŒ ۧŰČ Ű±ŰŻÛŒÙ Ù‡Ű§ÛŒ DB ŰźÙˆŰŻ ۱ۧ Ű§Ű¶Ű§ÙÙ‡ Ú©Ű±ŰŻÙ‡ و Ű±ÙˆÛŒ ŰąÙ† Űčمل Ú©Ù†ŰŻ ŰȘۧ ۧŰČ ŰŹÙŰȘ ŰŽŰŻÙ† ŰŹÙ„ÙˆÚŻÛŒŰ±ÛŒ Ú©Ù†ŰŻ و ŰšÙ‡ ۱ۭۧŰȘی ۯ۱ Ù…ÙˆŰ±ŰŻ ŰŹŰ±ÛŒŰ§Ù† ŰȘŰłŰȘ ۧ۳ŰȘŰŻÙ„Ű§Ù„ Ú©Ù†ŰŻ. ۯ۱ ÙˆŰ§Ù‚Űčی Ű§ÛŒÙ† Ű§Ù…Ű± ۧŰșÙ„Űš ŰȘÙˆŰłŰ· ŰąŰČÙ…Ű§ÛŒŰŽâ€ŒÚ©Ù†Ù†ŰŻÚŻŰ§Ù†ÛŒ که Ù‚ŰšÙ„ ۧŰČ Ű§ŰŹŰ±Ű§ÛŒ ŰąŰČÙ…Ű§ÛŒŰŽâ€ŒÙ‡Ű§ (که ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† «ŰȘŰłŰȘ ÙÛŒÚ©ŰłÚ†Ű±Â» نیŰČ ŰŽÙ†Ű§ŰźŰȘه Ù…ÛŒâ€ŒŰŽÙˆŰŻ) ۱ۧ ۚۧ ŰŻŰ§ŰŻÙ‡â€ŒÙ‡Ű§ ŰšÙ‡ Ù…Ù†ŰžÙˆŰ± ŰšÙ‡ŰšÙˆŰŻ ŰčÙ…Ù„Ú©Ű±ŰŻ Ù…ÛŒâ€ŒŰŻŰ§Ù†Ù†ŰŻŰŒ Ù†Ù‚Ű¶ Ù…ÛŒâ€ŒŰŽÙˆŰŻ. ۯ۱ Ű­Ű§Ù„ÛŒ که ŰčÙ…Ù„Ú©Ű±ŰŻ ۯ۱ ÙˆŰ§Ù‚Űč یک Ù†ÚŻŰ±Ű§Ù†ÛŒ مŰčŰȘۚ۱ ۧ۳ŰȘ. ŰčÙ…Ù„Ű§Ù‹ŰŒ Ù‡Ű± Ù…ÙˆŰ±ŰŻ ŰąŰČÙ…Ű§ÛŒŰŽÛŒ ۱ۧ ŰšÙ‡ ۔۱ۭۧŰȘ ŰłÙˆŰ§ŰšÙ‚ DB Ù…ÙˆŰ±ŰŻ Ù†ÛŒŰ§ŰČ ŰźÙˆŰŻ ۱ۧ Ű§Ű¶Ű§ÙÙ‡ Ú©Ù†ÛŒŰŻ و ÙÙ‚Ű· Ű±ÙˆÛŒ ŰąÙ† Ű±Ú©ÙˆŰ±ŰŻÙ‡Ű§ Űčمل Ú©Ù†ÛŒŰŻ. ۧگ۱ ŰčÙ…Ù„Ú©Ű±ŰŻ ŰšÙ‡ یک Ù†ÚŻŰ±Ű§Ù†ÛŒ Ű­ÛŒŰ§ŰȘی ŰȘŰšŰŻÛŒÙ„ ŰŽÙˆŰŻ - یک Ù…Ű”Ű§Ù„Ű­Ù‡ مŰȘÙˆŰ§ŰČن ممکن ۧ۳ŰȘ ŰšÙ‡ ŰŽÚ©Ù„ کۧێŰȘ ŰȘÙ†Ù‡Ű§ Ù…ŰŹÙ…ÙˆŰčه ŰąŰČÙ…Ű§ÛŒŰŽâ€ŒÙ‡Ű§ÛŒÛŒ ۚۧێۯ که ŰŻŰ§ŰŻÙ‡â€ŒÙ‡Ű§ ۱ۧ ŰȘŰșÛŒÛŒŰ± Ù†Ù…ÛŒâ€ŒŰŻÙ‡Ù†ŰŻ (Ù…Ű«Ù„Ű§Ù‹ ŰŻŰ±ŰźÙˆŰ§ŰłŰȘâ€ŒÙ‡Ű§) +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** ŰȘŰčۯۧۯ کمی ۧŰČ ŰȘŰłŰȘ Ù‡Ű§ ŰŽÚ©ŰłŰȘ می ŰźÙˆŰ±Ù†ŰŻŰŒ ۧ۳ŰȘÙ‚Ű±Ű§Ű± مŰȘوقف می ŰŽÙˆŰŻŰŒ ŰȘیم Ù…Ű§ Ű§Ú©Ù†ÙˆÙ† ŰČÙ…Ű§Ù† ÚŻŰ±Ű§Ù†ŰšÙ‡Ű§ÛŒÛŒ ۱ۧ Ű”Ű±Ù می Ú©Ù†ŰŻŰŒ ŰąÛŒŰ§ Ù…Ű§ ۚۧگ ŰŻŰ§Ű±ÛŒÙ…ŰŸ ŰšÛŒŰ§ÛŒÛŒŰŻ ŰšŰ±Ű±ŰłÛŒ Ú©Ù†ÛŒÙ…ŰŒ Ű§ÙˆÙ‡ Ù†Ù‡â€”ŰšÙ‡ Ù†ŰžŰ± Ù…ÛŒâ€ŒŰ±ŰłŰŻ ŰŻÙˆ ŰȘŰłŰȘ ŰŻŰ§ŰŻÙ‡â€ŒÙ‡Ű§ÛŒ Ű§ÙˆÙ„ÛŒÙ‡ Ù…ŰŽŰ§ŰšÙ‡ÛŒ ۱ۧ ŰŹÙ‡ŰŽ Ù…ÛŒâ€ŒŰŻŰ§ŰŻÙ†ŰŻ + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :thumbsdown: Ù…Ű«Ű§Ù„ ۶ۯ Ű§Ù„ÚŻÙˆ: ŰȘŰłŰȘâ€ŒÙ‡Ű§ Ù…ŰłŰȘقل Ù†ÛŒŰłŰȘÙ†ŰŻ و ŰšŰ±Ű§ÛŒ ŰȘŰșŰ°ÛŒÙ‡ ŰŻŰ§ŰŻÙ‡â€ŒÙ‡Ű§ÛŒ DB ŰŹÙ‡Ű§Ù†ÛŒ ŰšÙ‡ Ú†Ù†ŰŻ Ù‚Ù„Ű§Űš ŰŹÙ‡Ű§Ù†ÛŒ مŰȘکی Ù‡ŰłŰȘÙ†ŰŻ. + +![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "Examples with Mocha") + +```javascript +before(async () => { + //Ű§ÙŰČÙˆŰŻÙ† ŰłŰ§ÛŒŰȘ Ù‡Ű§ و ŰŻŰ§ŰŻÙ‡ Ù‡Ű§ÛŒ Ù…ŰŻÛŒŰ±Ű§Ù† ŰšÙ‡ DB Ù…Ű§. ŰŻŰ§ŰŻÙ‡ Ù‡Ű§ کۏۧ۳ŰȘ۟ ۟ۧ۱ۏ ۧŰČ. ۯ۱ ŰšŰ±ŰźÛŒ framework Ù‡Ű§ÛŒ json ÛŒŰ§ Ù…Ù‡Ű§ŰŹŰ±ŰȘ ŰźŰ§Ű±ŰŹÛŒ + await DB.AddSeedDataFromJson('seed.json'); +}); +it("Ù‡Ù†ÚŻŰ§Ù… ŰšÙ‡ Ű±ÙˆŰČ Ű±ŰłŰ§Ù†ÛŒ Ù†Ű§Ù… ŰłŰ§ÛŒŰȘی ŰȘŰŁÛŒÛŒŰŻ موفقیŰȘ ŰąÙ…ÛŒŰČ ŰŻŰ±ÛŒŰ§ÙŰȘ Ú©Ù†ÛŒŰŻ", async () => { + //من می ŰŻŰ§Ù†Ù… که Ù†Ű§Ù… ŰłŰ§ÛŒŰȘ "portal" ÙˆŰŹÙˆŰŻ ۯۧ۱ۯ - ŰąÙ† ۱ۧ ۯ۱ ÙŰ§ÛŒÙ„ Ù‡Ű§ÛŒ seed ŰŻÛŒŰŻÙ… + const siteToUpdate = await SiteService.getSiteByName("Portal"); + const updateNameResult = await SiteService.changeName(siteToUpdate, "newName"); + expect(updateNameResult).to.be(true); +}); +it("Ù‡Ù†ÚŻŰ§Ù… ÙŸŰ±Űł و ŰŹÙˆ ۚ۱ ۧ۳ۧ۳ Ù†Ű§Ù… ŰłŰ§ÛŒŰȘی ŰłŰ§ÛŒŰȘ Ù…Ù†Ű§ŰłŰš ۱ۧ ŰŻŰ±ÛŒŰ§ÙŰȘ Ú©Ù†ÛŒŰŻ", async () => { + //من می ŰŻŰ§Ù†Ù… که Ù†Ű§Ù… ŰłŰ§ÛŒŰȘ "portal" ÙˆŰŹÙˆŰŻ ۯۧ۱ۯ - ŰąÙ† ۱ۧ ۯ۱ ÙŰ§ÛŒÙ„ Ù‡Ű§ÛŒ seed ŰŻÛŒŰŻÙ… + const siteToCheck = await SiteService.getSiteByName("Portal"); + expect(siteToCheck.name).to.be.equal("Portal"); //ŰŽÚ©ŰłŰȘ! ŰȘŰłŰȘ Ù‚ŰšÙ„ÛŒ Ù†Ű§Ù… ۱ۧ ŰȘŰșÛŒÛŒŰ± ŰŻÙ‡ÛŒŰŻ:[ +}); + +``` + +
    + +### :clap: Ù…Ù‚Ű§Ù„ ۯ۱۳ŰȘ: Ù…Ű§ می ŰȘÙˆŰ§Ù†ÛŒÙ… ۯ۱ ŰąŰČمون ŰšÙ…Ű§Ù†ÛŒÙ…ŰŒ Ù‡Ű± ŰąŰČمون ۚ۱ ۧ۳ۧ۳ Ù…ŰŹÙ…ÙˆŰčه ŰŻŰ§ŰŻÙ‡ Ù‡Ű§ÛŒ ŰźÙˆŰŻ Űčمل می Ú©Ù†ŰŻ + +```javascript +it("Ù‡Ù†ÚŻŰ§Ù… ŰšÙ‡ Ű±ÙˆŰČ Ű±ŰłŰ§Ù†ÛŒ Ù†Ű§Ù… ŰłŰ§ÛŒŰȘی ŰȘŰŁÛŒÛŒŰŻ موفقیŰȘ ŰąÙ…ÛŒŰČ ŰŻŰ±ÛŒŰ§ÙŰȘ Ú©Ù†ÛŒŰŻ", async () => { + //ŰȘŰłŰȘ Ű§Ű¶Ű§ÙÙ‡ Ú©Ű±ŰŻÙ† یک Ű±Ú©ÙˆŰ±ŰŻ ŰŹŰŻÛŒŰŻ ŰŹŰŻÛŒŰŻ و Űčمل Ú©Ű±ŰŻÙ† ÙÙ‚Ű· ۚ۱ Ű±ÙˆÛŒ Ű±Ú©ÙˆŰ±ŰŻÙ‡Ű§ ۧ۳ŰȘ + const siteUnderTest = await SiteService.addSite({ + name: "siteForUpdateTest" + }); + const updateNameResult = await SiteService.changeName(siteUnderTest, "newName"); + expect(updateNameResult).to.be(true); +}); +``` + +
    + +
    + +## âšȘ 2.8 یک ۧ۳ŰȘ۱ۧŰȘژی ÙŸŰ§Ú©ŰłŰ§ŰČی ŰŻŰ§ŰŻÙ‡ ÙˆŰ§Ű¶Ű­ Ű§Ù†ŰȘ۟ۧۚ Ú©Ù†ÛŒŰŻ: ŰšŰčŰŻ ۧŰČ Ù‡Ù…Ù‡ (ŰȘÙˆŰ”ÛŒÙ‡ می ŰŽÙˆŰŻ) ÛŒŰ§ ŰšŰčŰŻ ۧŰČ Ù‡Ű± Ú©ŰŻŰ§Ù… + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** ŰČÙ…Ű§Ù†ÛŒ که ŰȘŰłŰȘ Ù‡Ű§ ÙŸŰ§ÛŒÚŻŰ§Ù‡ ŰŻŰ§ŰŻÙ‡ ۱ۧ ŰȘمیŰČ Ù…ÛŒ Ú©Ù†Ù†ŰŻŰŒ Ù†Ű­ÙˆÙ‡ Ù†ÚŻŰ§Ű±ŰŽ ŰȘŰłŰȘ Ù‡Ű§ ۱ۧ ŰȘŰčیین می Ú©Ù†ŰŻ. ŰŻÙˆ ÚŻŰČینه Ù…Ù†Ű§ŰłŰšŰŒ ŰȘمیŰČ Ú©Ű±ŰŻÙ† ŰšŰčŰŻ ۧŰČ Ù‡Ù…Ù‡ ŰąŰČÙ…Ű§ÛŒŰŽ Ù‡Ű§ ۯ۱ Ù…Ù‚Ű§ŰšÙ„ ŰȘمیŰČ Ú©Ű±ŰŻÙ† ŰšŰčŰŻ ۧŰČ Ù‡Ű± ŰąŰČÙ…Ű§ÛŒŰŽ ۧ۳ŰȘ. ۚۧ Ű§Ù†ŰȘ۟ۧۚ ÚŻŰČینه ŰŻÙˆÙ…ŰŒ ŰȘمیŰČ Ú©Ű±ŰŻÙ† ÙŸŰł ۧŰČ Ù‡Ű± ŰąŰČÙ…Ű§ÛŒŰŽ ŰŹŰŻŰ§ÙˆÙ„ ŰȘمیŰČ Ű±Ű§ ŰȘŰ¶Ù…ÛŒÙ† می Ú©Ù†ŰŻ و مŰČŰ§ÛŒŰ§ÛŒ ŰąŰČÙ…Ű§ÛŒŰŽÛŒ ۱ۭۧŰȘ ۱ۧ ŰšŰ±Ű§ÛŒ ŰȘÙˆŰłŰčه ŰŻÙ‡Ù†ŰŻÙ‡ Ű§ÛŒŰŹŰ§ŰŻ می Ú©Ù†ŰŻ. هیچ Ű±Ú©ÙˆŰ±ŰŻ ŰŻÛŒÚŻŰ±ÛŒ Ù‡Ù†ÚŻŰ§Ù… ŰŽŰ±ÙˆŰč ŰąŰČÙ…Ű§ÛŒŰŽ ÙˆŰŹÙˆŰŻ Ù†ŰŻŰ§Ű±ŰŻŰŒ می ŰȘÙˆŰ§Ù† Ù…Ű·Ù…ŰŠÙ† ŰšÙˆŰŻ که Ú©ŰŻŰ§Ù… ŰŻŰ§ŰŻÙ‡ Ù…ÙˆŰ±ŰŻ ÙŸŰ±ŰłŰŽ Ù‚Ű±Ű§Ű± می ÚŻÛŒŰ±ŰŻ و Ű­ŰȘی ممکن ۧ۳ŰȘ ÙˆŰłÙˆŰłÙ‡ ŰŽÙˆŰŻ Ű±ŰŻÛŒÙ Ù‡Ű§ ۱ۧ ۯ۱ Ű·ÙˆÙ„ ۧۯŰčŰ§Ù‡Ű§ ŰšŰŽÙ…Ű§Ű±ŰŻ. Ű§ÛŒÙ† Ű§Ù…Ű± ۚۧ مŰčŰ§ÛŒŰš ŰŽŰŻÛŒŰŻÛŒ Ù‡Ù…Ű±Ű§Ù‡ ۧ۳ŰȘ: Ù‡Ù†ÚŻŰ§Ù… ۧۏ۱ۧ ۯ۱ Ű­Ű§Ù„ŰȘ Ú†Ù†ŰŻ ÙŰ±ŰąÛŒÙ†ŰŻÛŒŰŒ ŰąŰČÙ…Ű§ÛŒŰŽâ€ŒÙ‡Ű§ ۭۧŰȘÙ…Ű§Ù„Ű§Ù‹ ۚۧ ÛŒÚ©ŰŻÛŒÚŻŰ± ŰȘŰŻŰ§ŰźÙ„ ŰŻŰ§Ű±Ù†ŰŻ. ۯ۱ Ű­Ű§Ù„ÛŒ که process-1 ŰŹŰŻŰ§ÙˆÙ„ ۱ۧ ÙŸŰ§Ú© می Ú©Ù†ŰŻŰŒ ۯ۱ Ù‡Ù…Ű§Ù† Ù„Ű­ŰžÙ‡ ÙŸŰ±ŰŻŰ§ŰČŰŽ-2 ŰšŰ±Ű§ÛŒ ŰŻŰ§ŰŻÙ‡ Ù‡Ű§ ŰŹŰłŰȘŰŹÙˆ می Ú©Ù†ŰŻ و ۚۧ ŰŽÚ©ŰłŰȘ Ù…ÙˆŰ§ŰŹÙ‡ می ŰŽÙˆŰŻ (ŰČÛŒŰ±Ű§ DB ŰšÙ‡ Ű·ÙˆŰ± Ù†Ű§ÚŻÙ‡Ű§Ù†ÛŒ ŰȘÙˆŰłŰ· ÙŰ±ŰąÛŒÙ†ŰŻ-1 Ű­Ű°Ù ŰŽŰŻÙ‡ ۧ۳ŰȘ). ŰčÙ„Ű§ÙˆÙ‡ ۚ۱ Ű§ÛŒÙ†ŰŒ ŰčÛŒŰšâ€ŒÛŒŰ§ŰšÛŒ ŰȘŰłŰȘâ€ŒÙ‡Ű§ÛŒ ŰŽÚ©ŰłŰȘ ŰźÙˆŰ±ŰŻÙ‡ ۳۟ŰȘ‌ŰȘ۱ ۧ۳ŰȘ - ۚۧŰČŰŻÛŒŰŻ ۧŰČ DB هیچ Ű±Ú©ÙˆŰ±ŰŻÛŒ ۱ۧ Ù†ŰŽŰ§Ù† Ù†Ù…ÛŒâ€ŒŰŻÙ‡ŰŻ. + +ÚŻŰČینه ŰŻÙˆÙ… Ű§ÛŒÙ† ۧ۳ŰȘ که ÙŸŰł ۧŰČ Ű§ŰȘÙ…Ű§Ù… ŰȘÙ…Ű§Ù… ÙŰ§ÛŒÙ„ Ù‡Ű§ÛŒ ŰȘŰłŰȘ (ÛŒŰ§ Ű­ŰȘی Ű±ÙˆŰČŰ§Ù†Ù‡!) ÙŸŰ§Ú©ŰłŰ§ŰČی Ú©Ù†ÛŒŰŻ. Ű§ÛŒÙ† Ű±ÙˆÛŒÚ©Ű±ŰŻ ŰšÙ‡ Ű§ÛŒÙ† مŰčنی ۧ۳ŰȘ که Ù‡Ù…Ű§Ù† DB ۚۧ Ű±Ú©ÙˆŰ±ŰŻÙ‡Ű§ÛŒ Ù…ÙˆŰŹÙˆŰŻŰŒ ŰȘÙ…Ű§Ù… ŰȘŰłŰȘ Ù‡Ű§ و ÙŰ±ŰąÛŒÙ†ŰŻÙ‡Ű§ ۱ۧ Ű§Ű±Ű§ŰŠÙ‡ می ŰŻÙ‡ŰŻ. ŰšŰ±Ű§ÛŒ ŰŹÙ„ÙˆÚŻÛŒŰ±ÛŒ ۧŰČ ÙŸŰ§ گ۰ۧێŰȘن Ű±ÙˆÛŒ Ű§Ù†ÚŻŰŽŰȘŰ§Ù† ÛŒÚ©ŰŻÛŒÚŻŰ±ŰŒ ŰąŰČمون Ù‡Ű§ ŰšŰ§ÛŒŰŻ ŰłÙˆŰ§ŰšÙ‚ ŰźŰ§Ű”ÛŒ ۱ۧ که Ű§Ű¶Ű§ÙÙ‡ Ú©Ű±ŰŻÙ‡ Ű§Ù†ŰŻ Ű§Ű¶Ű§ÙÙ‡ Ú©Ű±ŰŻÙ‡ و ۚ۱ ۧ۳ۧ۳ ŰąÙ†Ù‡Ű§ Űčمل Ú©Ù†Ù†ŰŻ. ŰąÛŒŰ§ ŰšŰ§ÛŒŰŻ ŰšŰ±Ű±ŰłÛŒ Ú©Ù†ÛŒŰŻ که Ű±Ú©ÙˆŰ±ŰŻÛŒ Ű§Ű¶Ű§ÙÙ‡ ŰŽŰŻÙ‡ ۧ۳ŰȘ۟ ÙŰ±Ű¶ Ú©Ù†ÛŒŰŻ هŰČŰ§Ű±Ű§Ù† Ű±Ú©ÙˆŰ±ŰŻ و ÙŸŰ±Űł و ŰŹÙˆÛŒ ŰŻÛŒÚŻŰ± ŰšŰ±Ű§ÛŒ Ű±Ú©ÙˆŰ±ŰŻÙ‡Ű§ÛŒÛŒ ÙˆŰŹÙˆŰŻ ۯۧ۱ۯ که ŰšÙ‡ ۔۱ۭۧŰȘ Ű§Ű¶Ű§ÙÙ‡ ŰŽŰŻÙ‡ Ű§Ù†ŰŻ. ŰąÛŒŰ§ ŰšŰ§ÛŒŰŻ ŰšŰ±Ű±ŰłÛŒ Ú©Ù†ÛŒŰŻ که یک Ű±Ú©ÙˆŰ±ŰŻ Ű­Ű°Ù ŰŽŰŻÙ‡ ۧ۳ŰȘ۟ نمی ŰȘÙˆŰ§Ù† ŰŹŰŻÙˆÙ„ ŰźŰ§Ù„ÛŒ ۱ۧ ÙŰ±Ű¶ Ú©Ű±ŰŻŰŒ ŰšŰ±Ű±ŰłÛŒ Ú©Ù†ÛŒŰŻ که Ű§ÛŒÙ† Ű±Ú©ÙˆŰ±ŰŻ ۟ۧ۔ ÙˆŰŹÙˆŰŻ Ù†ŰŻŰ§Ű±ŰŻ. Ű§ÛŒÙ† ŰȘکنیک ŰŻŰłŰȘŰ§ÙˆŰ±ŰŻÙ‡Ű§ÛŒ Ù‚ŰŻŰ±ŰȘÙ…Ù†ŰŻ کمی ۱ۧ ŰšÙ‡ Ù‡Ù…Ű±Ű§Ù‡ ۯۧ۱ۯ: ŰČÙ…Ű§Ù†ÛŒ که یک ŰȘÙˆŰłŰčÙ‡â€ŒŰŻÙ‡Ù†ŰŻÙ‡ Ù…ÛŒâ€ŒŰźÙˆŰ§Ù‡ŰŻ ۧŰȘÙŰ§Ù‚ÛŒ ۱ۧ که ۱۟ ŰŻŰ§ŰŻÙ‡ ۧ۳ŰȘ ŰšÙÙ‡Ù…ŰŻ - ŰšÙ‡ Ű·ÙˆŰ± ŰšÙˆÙ…ÛŒ ۯ۱ Ű­Ű§Ù„ŰȘ Ú†Ù†ŰŻ ÙŰ±ŰąÛŒÙ†ŰŻÛŒ کۧ۱ Ù…ÛŒâ€ŒÚ©Ù†ŰŻ - ŰŻŰ§ŰŻÙ‡â€ŒÙ‡Ű§ ŰąÙ†ŰŹŰ§ Ù‡ŰłŰȘÙ†ŰŻ و Ű­Ű°Ù Ù†Ù…ÛŒâ€ŒŰŽÙˆÙ†ŰŻ. همچنین ŰŽŰ§Ù†Űł ÛŒŰ§ÙŰȘن ۚۧگ Ù‡Ű§ ۱ۧ Ű§ÙŰČŰ§ÛŒŰŽ می ŰŻÙ‡ŰŻ ŰČÛŒŰ±Ű§ DB ÙŸŰ± ۧŰČ Ű±Ú©ÙˆŰ±ŰŻ ۧ۳ŰȘ و ŰšÙ‡ Ű·ÙˆŰ± Ù…Ű”Ù†ÙˆŰčی ŰźŰ§Ù„ÛŒ Ù†ÛŒŰłŰȘ. [ŰŹŰŻÙˆÙ„ Ù…Ù‚Ű§ÛŒŰłÙ‡ Ú©Ű§Ù…Ù„ ۱ۧ Ű§ÛŒÙ†ŰŹŰ§ ŰšŰšÛŒÙ†ÛŒŰŻ](https://github.com/testjavascript/nodejs-integration-tests-best-practices/blob/master/graphics/db-clean-options.png). +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** ŰšŰŻÙˆÙ† ۧ۳ŰȘ۱ۧŰȘژی ŰšŰ±Ű§ÛŒ ۏۯۧ Ú©Ű±ŰŻÙ† Ű±Ú©ÙˆŰ±ŰŻÙ‡Ű§ ÛŒŰ§ ŰȘمیŰČ Ú©Ű±ŰŻÙ† - ŰȘŰłŰȘ Ù‡Ű§ Ű±ÙˆÛŒ Ű§Ù†ÚŻŰŽŰȘŰ§Ù† ÛŒÚ©ŰŻÛŒÚŻŰ± Ù‚Ű±Ű§Ű± می ÚŻÛŒŰ±Ù†ŰŻ. ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ ŰȘŰ±Ű§Ú©Ù†ŰŽâ€ŒÙ‡Ű§ ÙÙ‚Ű· ŰšŰ±Ű§ÛŒ DB Ű±Ű§ŰšŰ·Ù‡â€ŒŰ§ÛŒ کۧ۱ Ù…ÛŒâ€ŒÚ©Ù†ŰŻ و ۭۧŰȘÙ…Ű§Ù„Ű§Ù‹ ŰČÙ…Ű§Ù†ÛŒ که ŰȘŰ±Ű§Ú©Ù†ŰŽâ€ŒÙ‡Ű§ÛŒ ŰŻŰ±ÙˆÙ†ÛŒ ÙˆŰŹÙˆŰŻ ŰŻŰ§Ű±ŰŻŰŒ ÙŸÛŒÚ†ÛŒŰŻÙ‡ Ù…ÛŒâ€ŒŰŽÙˆŰŻ + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :clap: ŰȘمیŰČ Ú©Ű±ŰŻÙ† ŰšŰčŰŻ ۧŰČ ŰȘÙ…Ű§Ù… ŰąŰČÙ…Ű§ÛŒŰŽŰ§ŰȘ نه لŰČÙˆÙ…Ű§ ŰšŰčŰŻ ۧŰČ Ù‡Ű± ŰŻÙˆÛŒŰŻÙ†. Ù‡Ű±Ú†Ù‡ ŰŻŰ§ŰŻÙ‡â€ŒÙ‡Ű§ÛŒ ŰšÛŒŰŽŰȘŰ±ÛŒ ۯ۱ Ű­ÛŒÙ† Ű§Ù†ŰŹŰ§Ù… ŰąŰČÙ…Ű§ÛŒŰŽâ€ŒÙ‡Ű§ ۯۧێŰȘه ŰšŰ§ŰŽÛŒÙ… - ŰšÛŒŰŽŰȘ۱ ŰŽŰšÛŒÙ‡ Ű§Ù…ŰȘÛŒŰ§ŰČۧŰȘ ŰȘÙˆÙ„ÛŒŰŻ ۧ۳ŰȘ + +```javascript + //ŰšŰčŰŻ ۧŰČ Ù‡Ù…Ù‡ ÙŸŰ§Ú©ŰłŰ§ŰČی (ŰȘÙˆŰ”ÛŒÙ‡ می ŰŽÙˆŰŻ) +// global-teardown.js +module.exports = async () => { + // ... + if (Math.ceil(Math.random() * 10) === 10) { + await new OrderRepository().cleanup(); + } +}; +``` + +
    + +
    + +## âšȘ 2.9 ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ Ű±Ù‡ÚŻÛŒŰ± HTTPی ŰŹŰČŰĄ ۱ۧ ۧŰČ ŰŹÙ‡Ű§Ù† ۏۯۧ Ú©Ù†ÛŒŰŻ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** Ù…Ű€Ù„ÙÙ‡ ŰȘŰ­ŰȘ ŰąŰČÙ…Ű§ÛŒŰŽ ۱ۧ ۚۧ Ű±Ù‡ÚŻÛŒŰ±ÛŒ Ù‡Ű± ŰŻŰ±ŰźÙˆŰ§ŰłŰȘ HTTP ŰźŰ±ÙˆŰŹÛŒ و Ű§Ű±Ű§ŰŠÙ‡ ÙŸŰ§ŰłŰź Ù…ÙˆŰ±ŰŻ Ù†ŰžŰ± ۏۯۧ Ú©Ù†ÛŒŰŻ ŰȘۧ HTTP API Ù‡Ù…Ú©Ű§Ű± ŰąŰłÛŒŰš Ù†ŰšÛŒÙ†ŰŻ. Nock یک ۧۚŰČۧ۱ ŰčŰ§Ù„ÛŒ ŰšŰ±Ű§ÛŒ Ű§ÛŒÙ† Ù…Ű§Ù…ÙˆŰ±ÛŒŰȘ ۧ۳ŰȘ ŰČÛŒŰ±Ű§ یک Ù†Ű­Ùˆ Ù…Ù†Ű§ŰłŰš ŰšŰ±Ű§ÛŒ ŰȘŰčŰ±ÛŒÙ Ű±ÙŰȘۧ۱ ŰźŰŻÙ…Ű§ŰȘ ŰźŰ§Ű±ŰŹÛŒ Ű§Ű±Ű§ŰŠÙ‡ می ŰŻÙ‡ŰŻ. ۏۯۧ۳ۧŰČی ŰšŰ±Ű§ÛŒ ŰŹÙ„ÙˆÚŻÛŒŰ±ÛŒ ۧŰČ Ù†ÙˆÛŒŰČ Ùˆ ŰčÙ…Ù„Ú©Ű±ŰŻ ŰąÙ‡ŰłŰȘه Ű¶Ű±ÙˆŰ±ÛŒ ۧ۳ŰȘی Ű§Ù…Ű§ ŰšÛŒŰŽŰȘ۱ ŰšŰ±Ű§ÛŒ ŰŽŰšÛŒÙ‡â€ŒŰłŰ§ŰČی ŰłÙ†Ű§Ű±ÛŒÙˆÙ‡Ű§ و ÙŸŰ§ŰłŰźâ€ŒÙ‡Ű§ÛŒ Ù…ŰźŰȘلف - یک ŰŽŰšÛŒÙ‡â€ŒŰłŰ§ŰČ ÙŸŰ±ÙˆŰ§ŰČ ŰźÙˆŰš ŰšÙ‡ مŰčÙ†Ű§ÛŒ Ű±Ù†ÚŻâ€ŒŰąÙ…ÛŒŰČی ŰąŰłÙ…Ű§Ù† ŰąŰšÛŒ ŰŽÙŰ§Ù Ù†ÛŒŰłŰȘی ŰšÙ„Ú©Ù‡ Ű§ÛŒŰŹŰ§ŰŻ Ű·ÙˆÙŰ§Ù† و Ù‡Ű±ŰŹâ€ŒÙˆÙ…Ű±ŰŹ Ű§Ù…Ù† ۧ۳ŰȘ. Ű§ÛŒÙ† ۯ۱ مŰčÙ…Ű§Ű±ÛŒ Microservice که ۯ۱ ŰąÙ† ŰȘÙ…Ű±Ú©ŰČ Ù‡Ù…ÛŒŰŽÙ‡ ŰšŰ§ÛŒŰŻ ۚ۱ Ű±ÙˆÛŒ یک ŰŹŰČŰĄ ÙˆŰ§Ű­ŰŻ ŰšŰŻÙˆÙ† ŰŻŰ±ÚŻÛŒŰ± Ú©Ű±ŰŻÙ† ŰšÙ‚ÛŒÙ‡ ŰŹÙ‡Ű§Ù† ۹ۧۮۯی ŰȘقویŰȘ ŰŽŰŻÙ‡ ۧ۳ŰȘ. Ű§ÚŻŰ±Ú†Ù‡ می ŰȘÙˆŰ§Ù† Ű±ÙŰȘۧ۱ ŰłŰ±ÙˆÛŒŰł ŰźŰ§Ű±ŰŹÛŒ ۱ۧ ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ ŰȘŰłŰȘ ŰŻÙˆŰšÙ„ (Ù…ŰłŰźŰ±Ù‡) ŰŽŰšÛŒÙ‡ ۳ۧŰČی Ú©Ű±ŰŻŰŒ Ű§Ù…Ű§ ŰȘŰ±ŰŹÛŒŰ­Ű§Ù‹ Ú©ŰŻ Ù…ŰłŰȘÙ‚Ű± ŰŽŰŻÙ‡ ۱ۧ Ù„Ù…Űł Ù†Ú©Ù†ÛŒŰŻ و ۯ۱ ۳۷ۭ ŰŽŰšÚ©Ù‡ Űčمل Ú©Ù†ÛŒŰŻ ŰȘۧ ŰȘŰłŰȘ Ù‡Ű§ ۱ۧ ŰŹŰčŰšÙ‡ ŰłÛŒŰ§Ù‡ ŰźŰ§Ù„Ű” Ù†ÚŻÙ‡ ŰŻŰ§Ű±ÛŒŰŻ. Ù†Ù‚Ű·Ù‡ ۶Űčف ۏۯۧ۳ۧŰČی ŰčŰŻÙ… ŰȘŰŽŰźÛŒŰ” ŰČÙ…Ű§Ù†ÛŒ که Ù…Ű€Ù„ÙÙ‡ Ù‡Ù…Ú©Ű§Ű± ŰȘŰșÛŒÛŒŰ± می Ú©Ù†ŰŻ و مŰȘÙˆŰŹÙ‡ Ù†ŰŽŰŻÙ† ŰłÙˆŰĄ ŰȘÙŰ§Ù‡Ù… ŰšÛŒÙ† ŰŻÙˆ ŰłŰ±ÙˆÛŒŰł ۧ۳ŰȘ - Ù…Ű·Ù…ŰŠÙ† ŰŽÙˆÛŒŰŻ که ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ Ú†Ù†ŰŻ ŰąŰČÙ…Ű§ÛŒŰŽ Ù‚Ű±Ű§Ű±ŰŻŰ§ŰŻ ÛŒŰ§ E2E Ű§ÛŒÙ† ۱ۧ ŰŹŰšŰ±Ű§Ù† Ú©Ù†ÛŒŰŻ. +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** ŰšŰ±ŰźÛŒ ۧŰČ ŰłŰ±ÙˆÛŒŰłâ€ŒÙ‡Ű§ یک Ù†ŰłŰźÙ‡ ŰŹŰčلی ۱ۧ Ű§Ű±Ű§ŰŠÙ‡ Ù…ÛŒâ€ŒÚ©Ù†Ù†ŰŻ که می‌ŰȘÙˆŰ§Ù†ŰŻ ŰȘÙˆŰłŰ· ŰȘÙ…Ű§Űłâ€ŒÚŻÛŒŰ±Ù†ŰŻÙ‡ ŰšÙ‡ Ű”ÙˆŰ±ŰȘ Ù…Ű­Ù„ÛŒŰŒ مŰčÙ…ÙˆÙ„Ű§Ù‹ ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ Docker ۧۏ۱ۧ ŰŽÙˆŰŻ. ŰšŰ±ŰźÛŒ ۧŰČ ŰłŰ±ÙˆÛŒŰłâ€ŒÙ‡Ű§ Ù…Ű­ÛŒŰ· «sandbox» ۱ۧ Ű§Ű±Ű§ŰŠÙ‡ Ù…ÛŒâ€ŒÚ©Ù†Ù†ŰŻŰŒ ŰšÙ†Ű§ŰšŰ±Ű§ÛŒÙ† ŰłŰ±ÙˆÛŒŰł ÙˆŰ§Ù‚Űčی Ű¶Ű±ŰšÙ‡ Ù…ÛŒâ€ŒŰźÙˆŰ±ŰŻ Ű§Ù…Ű§ هیچ هŰČینه ÛŒŰ§ ŰčÙˆŰ§Ű±Ű¶ ŰŹŰ§Ù†ŰšÛŒ Ű§ÛŒŰŹŰ§ŰŻ Ù†Ù…ÛŒâ€ŒŰŽÙˆŰŻ - Ű§ÛŒÙ† کۧ۱ Ű”ŰŻŰ§ÛŒ Ű±Ű§Ù‡â€ŒŰ§Ù†ŰŻŰ§ŰČی ŰłŰ±ÙˆÛŒŰł ێ۟۔ Ű«Ű§Ù„Ű« ۱ۧ Ú©Ű§Ù‡ŰŽ Ù…ÛŒâ€ŒŰŻÙ‡ŰŻŰŒ Ű§Ù…Ű§ Ű§Ù…Ú©Ű§Ù† ŰŽŰšÛŒÙ‡â€ŒŰłŰ§ŰČی ŰłÙ†Ű§Ű±ÛŒÙˆÙ‡Ű§ ۱ۧ نیŰČ ÙŰ±Ű§Ù‡Ù… Ù†Ù…ÛŒâ€ŒÚ©Ù†ŰŻ. + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :clap: ŰŹÙ„ÙˆÚŻÛŒŰ±ÛŒ ۧŰČ ŰȘÙ…Ű§Űł ŰŽŰšÚ©Ù‡ ۚۧ ۧۏŰČŰ§ÛŒ ŰźŰ§Ű±ŰŹÛŒ Ű§Ù…Ú©Ű§Ù† ŰŽŰšÛŒÙ‡ ۳ۧŰČی ŰłÙ†Ű§Ű±ÛŒÙˆÙ‡Ű§ و ŰšÙ‡ Ű­ŰŻŰ§Ù‚Ù„ Ű±ŰłŰ§Ù†ŰŻÙ† نویŰČ Ű±Ű§ ÙŰ±Ű§Ù‡Ù… می Ú©Ù†ŰŻ + +```javascript +// ŰŻŰ±ŰźÙˆŰ§ŰłŰȘ Ù‡Ű§ÛŒ API Ù‡Ű§ÛŒ ێ۟۔ Ű«Ű§Ù„Ű« ۱ۧ Ű±Ù‡ÚŻÛŒŰ±ÛŒ Ú©Ù†ÛŒŰŻ و یک ÙŸŰ§ŰłŰź ۧŰČ ÙŸÛŒŰŽ ŰȘŰčŰ±ÛŒÙ ŰŽŰŻÙ‡ ۱ۧ ŰšŰ±ÚŻŰ±ŰŻŰ§Ù†ÛŒŰŻ +beforeEach(() => { + nock('http://localhost/user/').get(`/1`).reply(200, { + id: 1, + name: 'John', + }); +}); +``` + +
    + +## âšȘ 2.10 ۷۱ۭ ÙŸŰ§ŰłŰź ۱ۧ ŰšÛŒŰŽŰȘ۱ ۯ۱ Ù…ÙˆŰ§Ù‚Űčی که ÙÛŒÙ„ŰŻÙ‡Ű§ÛŒ ŰȘÙˆÙ„ÛŒŰŻ ŰźÙˆŰŻÚ©Ű§Ű± ÙˆŰŹÙˆŰŻ ŰŻŰ§Ű±ŰŻŰŒ ŰąŰČÙ…Ű§ÛŒŰŽ Ú©Ù†ÛŒŰŻ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** Ù‡Ù†ÚŻŰ§Ù…ÛŒ که ۧۯŰčۧ Ú©Ű±ŰŻÙ† ŰšŰ±Ű§ÛŒ ŰŻŰ§ŰŻÙ‡ Ù‡Ű§ÛŒ ۟ۧ۔ ŰșÛŒŰ±Ù…Ù…Ú©Ù† ۧ۳ŰȘی ÙˆŰŹÙˆŰŻ و Ű§Ù†ÙˆŰ§Űč ÙÛŒÙ„ŰŻ Ű§ŰŹŰšŰ§Ű±ÛŒ ۱ۧ ŰšŰ±Ű±ŰłÛŒ Ú©Ù†ÛŒŰŻ. ÚŻŰ§Ù‡ÛŒ Ű§ÙˆÙ‚Ű§ŰȘی ÙŸŰ§ŰłŰź Ű­Ű§ÙˆÛŒ ÙÛŒÙ„ŰŻÙ‡Ű§ÛŒ مهم ۚۧ ŰŻŰ§ŰŻÙ‡ Ù‡Ű§ÛŒ ÙŸÙˆÛŒŰ§ ۧ۳ŰȘ که نمی ŰȘÙˆŰ§Ù† ŰąÙ†Ù‡Ű§ ۱ۧ Ù‡Ù†ÚŻŰ§Ù… Ù†ÙˆŰŽŰȘن ŰąŰČمون ÙŸÛŒŰŽ ŰšÛŒÙ†ÛŒ Ú©Ű±ŰŻŰŒ Ù…Ű§Ù†Ù†ŰŻ ŰȘŰ§Ű±ÛŒŰź Ù‡Ű§ و Ű§ÙŰČŰ§ÛŒŰŽ ۧŰčۯۧۯ. ۧگ۱ Ù‚Ű±Ű§Ű±ŰŻŰ§ŰŻ API وŰčŰŻÙ‡ می ŰŻÙ‡ŰŻ که Ű§ÛŒÙ† ÙÛŒÙ„ŰŻÙ‡Ű§ ٟوچ Ù†ŰźÙˆŰ§Ù‡Ù†ŰŻ ŰšÙˆŰŻ و Ű§Ù†ÙˆŰ§Űč Ù…Ù†Ű§ŰłŰš ۱ۧ ۯ۱ ŰźÙˆŰŻ ŰŻŰ§Ű±Ù†ŰŻŰŒ ŰąŰČÙ…Ű§ÛŒŰŽ ŰąÙ† Ű¶Ű±ÙˆŰ±ÛŒ ۧ۳ŰȘ. ۧک۫۱ Ú©ŰȘŰ§ŰšŰźŰ§Ù†Ù‡ Ù‡Ű§ÛŒ ۧۯŰčۧ ۧŰČ Ű§Ù†ÙˆŰ§Űč ŰšŰ±Ű±ŰłÛŒ ÙŸŰŽŰȘÛŒŰšŰ§Ù†ÛŒ می Ú©Ù†Ù†ŰŻ. ۧگ۱ ÙŸŰ§ŰłŰź کوچک ۧ۳ŰȘی ŰŻŰ§ŰŻÙ‡ Ù‡Ű§ÛŒ ۚ۱گێŰȘی ۱ۧ ŰšŰ±Ű±ŰłÛŒ Ú©Ù†ÛŒŰŻ و ۚۧ هم ۯ۱ Ù‡Ù…Ű§Ù† ۧۯŰčۧ ŰȘŰ§ÛŒÙŸ Ú©Ù†ÛŒŰŻ (ŰšÙ‡ Ù…Ű«Ű§Ù„ Ú©ŰŻ Ù…Ű±Ű§ŰŹŰčه Ú©Ù†ÛŒŰŻ). یک ÚŻŰČینه ŰŻÛŒÚŻŰ± Ű§ÛŒÙ† ۧ۳ŰȘ که کل ÙŸŰ§ŰłŰź ۱ۧ ۯ۱ ۚ۱ۧۚ۱ یک ŰłÙ†ŰŻ OpenAPI (Swagger) ŰȘŰŁÛŒÛŒŰŻ Ú©Ù†ÛŒŰŻ. ۧک۫۱ Ű§ŰŹŰ±Ű§Ú©Ù†Ù†ŰŻÚŻŰ§Ù† ŰąŰČÙ…Ű§ÛŒŰŽ ŰŻŰ§Ű±Ű§ÛŒ ÙŸŰłÙˆÙ†ŰŻÙ‡Ű§ÛŒ ŰŹŰ§Ù…Űčه Ù‡ŰłŰȘÙ†ŰŻ که ÙŸŰ§ŰłŰź Ù‡Ű§ÛŒ API ۱ۧ ۯ۱ ۚ۱ۧۚ۱ Ű§ŰłÙ†Ű§ŰŻ ŰąÙ†Ù‡Ű§ ŰȘŰŁÛŒÛŒŰŻ می Ú©Ù†ŰŻ. + + +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** Ű§ÚŻŰ±Ú†Ù‡ ŰȘÙ…Ű§Űłâ€ŒÚŻÛŒŰ±Ù†ŰŻÙ‡ Ú©ŰŻ/API ŰšÙ‡ ÙÛŒÙ„ŰŻÙ‡Ű§ÛŒÛŒ ۚۧ ŰŻŰ§ŰŻÙ‡â€ŒÙ‡Ű§ÛŒ ÙŸÙˆÛŒŰ§ (Ù…Ű«Ù„Ű§Ù‹ ŰŽÙ†Ű§ŰłÙ‡ŰŒ ŰȘŰ§Ű±ÛŒŰź) مŰȘکی ۧ۳ŰȘی Ű§Ù…Ű§ ۯ۱ ŰčÙˆŰ¶ Ù†Ù…ÛŒâ€ŒŰąÛŒŰŻ و Ù‚Ű±Ű§Ű±ŰŻŰ§ŰŻ ۱ۧ ŰČÛŒŰ± ÙŸŰ§ Ù†Ù…ÛŒâ€ŒÚŻŰ°Ű§Ű±ŰŻ. + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :clap: ۚۧ ŰšÛŒŰ§Ù† Ű§ÛŒÙ†Ú©Ù‡ ÙÛŒÙ„ŰŻÙ‡Ű§ÛŒÛŒ ۚۧ Ù…Ù‚ŰŻŰ§Ű± ÙŸÙˆÛŒŰ§ ÙˆŰŹÙˆŰŻ ŰŻŰ§Ű±Ù†ŰŻ و نوŰč Ù…Ù†Ű§ŰłŰšÛŒ ŰŻŰ§Ű±Ù†ŰŻ + +```javascript + test('Ù‡Ù†ÚŻŰ§Ù… Ű§Ű¶Ű§ÙÙ‡ Ú©Ű±ŰŻÙ† یک ŰłÙŰ§Ű±ŰŽ مŰčŰȘۚ۱ ŰŹŰŻÛŒŰŻŰŒ ŰłÙŸŰł ŰšŰ§ÛŒŰŻ ۚۧ 200 ÙŸŰ§ŰłŰź ŰȘŰŁÛŒÛŒŰŻ Ù…ŰŹŰŻŰŻ ŰŻŰ±ÛŒŰ§ÙŰȘ Ú©Ù†ÛŒŰŻ', async () => { + // ... + //Ù…Ù‚Ű§ÛŒŰłÙ‡ Ú©Ű±ŰŻÙ† + expect(receivedAPIResponse).toMatchObject({ + status: 200, + data: { + id: expect.any(Number), // Ù‡Ű± ŰčŰŻŰŻÛŒ Ű§ÛŒÙ† ŰȘŰłŰȘ ۱ۧ ŰšŰ±ŰąÙˆŰ±ŰŻÙ‡ می Ú©Ù†ŰŻ + mode: 'approved', + }, + }); +}); +``` + +
    + +
    + +## âšȘ 2.12 Ù…ÙˆŰ§Ű±ŰŻ ÚŻÙˆŰŽÙ‡ و ŰąŰŽÙˆŰš ۧۯŰșŰ§Ù… Ù‡Ű§ ۱ۧ ŰšŰ±Ű±ŰłÛŒ Ú©Ù†ÛŒŰŻ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** Ù‡Ù†ÚŻŰ§Ù… ŰšŰ±Ű±ŰłÛŒ ۧۯŰșŰ§Ù… Ù‡Ű§ŰŒ ۧŰČ Ù…ŰłÛŒŰ±Ù‡Ű§ÛŒ ێۧۯ و Űșم Ű§Ù†ÚŻÛŒŰČ ÙŰ±Ű§ŰȘ۱ ŰšŰ±ÙˆÛŒŰŻ. نه ŰȘÙ†Ù‡Ű§ ÙŸŰ§ŰłŰź Ù‡Ű§ÛŒ ۟۷ۧ (ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† Ù…Ű«Ű§Ù„ŰŒ ŰźŰ·Ű§ÛŒ HTTP 500) ŰšÙ„Ú©Ù‡ Ù†Ű§Ù‡Ù†ŰŹŰ§Ű±ÛŒ Ù‡Ű§ÛŒ ۳۷ۭ ŰŽŰšÚ©Ù‡ Ù…Ű§Ù†Ù†ŰŻ ÙŸŰ§ŰłŰź Ù‡Ű§ÛŒ ŰąÙ‡ŰłŰȘه و ŰšÙ‡ ÙŸŰ§ÛŒŰ§Ù† Ű±ŰłÛŒŰŻÙ‡ ۱ۧ ŰšŰ±Ű±ŰłÛŒ Ú©Ù†ÛŒŰŻ. Ű§ÛŒÙ† ۫ۧۚŰȘ Ù…ÛŒâ€ŒÚ©Ù†ŰŻ که Ú©ŰŻ Ű§Ù†ŰčŰ·Ű§Ùâ€ŒÙŸŰ°ÛŒŰ± ۧ۳ŰȘ و می‌ŰȘÙˆŰ§Ù†ŰŻ ŰłÙ†Ű§Ű±ÛŒÙˆÙ‡Ű§ÛŒ Ù…ŰźŰȘلف ŰŽŰšÚ©Ù‡ ۱ۧ Ù…ŰŻÛŒŰ±ÛŒŰȘ Ú©Ù†ŰŻŰŒ Ù…Ű§Ù†Ù†ŰŻ Ű§Ù†ŰȘ۟ۧۚ Ù…ŰłÛŒŰ± ۯ۱۳ŰȘ ÙŸŰł ۧŰČ ÛŒÚ© ۚۧŰČه ŰČÙ…Ű§Ù†ÛŒŰŒ هیچ ŰŽŰ±Ű§ÛŒŰ· Ù…ŰłŰ§ŰšÙ‚Ù‡ ŰŽÚ©Ù†Ù†ŰŻÙ‡â€ŒŰ§ÛŒ Ù†ŰŻŰ§Ű±ŰŻŰŒ و Ű­Ű§ÙˆÛŒ یک Ù‚Ű·Űč Ú©Ù†Ù†ŰŻÙ‡ Ù…ŰŻŰ§Ű± ŰšŰ±Ű§ÛŒ ŰȘÙ„Ű§ŰŽâ€ŒÙ‡Ű§ÛŒ Ù…ŰŹŰŻŰŻ ۧ۳ŰȘ. ۧۚŰČŰ§Ű±Ù‡Ű§ÛŒ Ű±Ù‡ÚŻÛŒŰ± مŰčŰȘۚ۱ ŰšÙ‡ ۱ۭۧŰȘی می ŰȘÙˆŰ§Ù†Ù†ŰŻ Ű±ÙŰȘŰ§Ű±Ù‡Ű§ÛŒ Ù…ŰźŰȘلف ŰŽŰšÚ©Ù‡ Ù…Ű§Ù†Ù†ŰŻ ŰłŰ±ÙˆÛŒŰł Ù‡Ű§ÛŒ ÚŻÛŒŰŹ Ú©Ù†Ù†ŰŻÙ‡ ۱ۧ که ÚŻÙ‡ÚŻŰ§Ù‡ ۚۧ ŰŽÚ©ŰłŰȘ Ù…ÙˆŰ§ŰŹÙ‡ می ŰŽÙˆÙ†ŰŻ ŰŽŰšÛŒÙ‡ ۳ۧŰČی Ú©Ù†Ù†ŰŻ. Ű­ŰȘی می‌ŰȘÙˆŰ§Ù†ŰŻ مŰȘÙˆŰŹÙ‡ ŰŽÙˆŰŻ که چه ŰČÙ…Ű§Ù†ÛŒ Ù…Ù‚ŰŻŰ§Ű± ŰČÙ…Ű§Ù†â€ŒŰšÙ†ŰŻÛŒ ÙŸÛŒŰŽâ€ŒÙŰ±Ű¶ Ù…ŰŽŰȘŰ±ÛŒ HTTP ۧŰČ ŰČÙ…Ű§Ù† ÙŸŰ§ŰłŰź ŰŽŰšÛŒÙ‡â€ŒŰłŰ§ŰČÛŒâ€ŒŰŽŰŻÙ‡ ŰšÛŒŰŽŰȘ۱ ۧ۳ŰȘ و ÙÙˆŰ±Ű§Ù‹ ŰšŰŻÙˆÙ† Ű§Ù†ŰȘŰžŰ§Ű±ŰŒ یک ۧ۳ŰȘŰ«Ù†Ű§ÛŒ مهلŰȘ ŰČÙ…Ű§Ù†ÛŒ Ű§ÛŒŰŹŰ§ŰŻ Ú©Ù†ŰŻ. + + +
    + +❌ **ۯ۱ ŰčÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** ŰȘÙ…Ű§Ù… ŰȘŰłŰȘâ€ŒÙ‡Ű§ÛŒ ŰŽÙ…Ű§ ۚۧ موفقیŰȘ Ű§Ù†ŰŹŰ§Ù… Ù…ÛŒâ€ŒŰŽÙˆŰŻŰŒ Ű§ÛŒÙ† ÙÙ‚Ű· ŰȘÙˆÙ„ÛŒŰŻ ۧ۳ŰȘ که ۧŰČ Ú©Ű§Ű± Ù…ÛŒâ€ŒŰ§ÙŰȘŰŻ ÛŒŰ§ وقŰȘی ێ۟۔ Ű«Ű§Ù„Ű« ÙŸŰ§ŰłŰźâ€ŒÙ‡Ű§ÛŒ ۧ۳ŰȘŰ«Ù†Ű§ÛŒÛŒ Ű§Ű±ŰłŰ§Ù„ Ù…ÛŒâ€ŒÚ©Ù†ŰŻŰŒ ŰźŰ·Ű§Ù‡Ű§ ۱ۧ ŰšÙ‡ ۯ۱۳ŰȘی ÚŻŰČۧ۱ێ Ù†Ù…ÛŒâ€ŒÚ©Ù†ŰŻ. + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :clap: Ű§Ű·Ù…ÛŒÙ†Ű§Ù† ۧŰČ Ű§ÛŒÙ†Ú©Ù‡ ۯ۱ ŰźŰ±Ű§ŰšÛŒ ŰŽŰšÚ©Ù‡ŰŒ Ù‚Ű·Űč Ú©Ù†Ù†ŰŻÙ‡ Ù…ŰŻŰ§Ű± می ŰȘÙˆŰ§Ù†ŰŻ Ű±ÙˆŰČ Ű±Ű§ Ù†ŰŹŰ§ŰȘ ŰŻÙ‡ŰŻ + +```javascript + test('Ù‡Ù†ÚŻŰ§Ù…ÛŒ که ŰłŰ±ÙˆÛŒŰł Ú©Ű§Ű±ŰšŰ±Ű§Ù† یک ۚۧ۱ ۚۧ 503 ÙŸŰ§ŰłŰź می ŰŻÙ‡ŰŻ و Ù…Ú©Ű§Ù†ÛŒŰłÙ… Ű§Ù…ŰȘŰ­Ű§Ù† Ù…ŰŹŰŻŰŻ ۧŰčÙ…Ű§Ù„ می ŰŽÙˆŰŻŰŒ ŰłÙŰ§Ű±ŰŽ ۚۧ موفقیŰȘ Ű§Ű¶Ű§ÙÙ‡ می ŰŽÙˆŰŻ', async () => { + //Ù…Ù‚ŰŻŰ§Ű± ŰŻÙ‡ÛŒ Ú©Ű±ŰŻÙ† + nock.removeInterceptor(userServiceNock.interceptors[0]) + nock('http://localhost/user/') + .get('/1') + .reply(503, undefined, { 'Retry-After': 100 }); + nock('http://localhost/user/') + .get('/1') + .reply(200); + const orderToAdd = { + userId: 1, + productId: 2, + mode: 'approved', + }; + + //ۧۏ۱ۧ Ú©Ű±ŰŻÙ† + const response = await axiosAPIClient.post('/order', orderToAdd); + + //Ù…Ù‚Ű§ÛŒŰłÙ‡ Ú©Ű±ŰŻÙ† + expect(response.status).toBe(200); +}); +``` + +
    + +
    + + +## âšȘ 2.13 ÙŸÙ†ŰŹ نŰȘÛŒŰŹÙ‡ ŰšŰ§Ù„Ù‚ÙˆÙ‡ ۱ۧ ŰȘŰłŰȘ Ú©Ù†ÛŒŰŻ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** Ù‡Ù†ÚŻŰ§Ù… ŰšŰ±Ù†Ű§Ù…Ù‡ Ű±ÛŒŰČی ŰąŰČÙ…Ű§ÛŒŰŽŰ§ŰȘ ŰźÙˆŰŻŰŒ ÙŸÙ†ŰŹ ŰźŰ±ÙˆŰŹÛŒ ŰŹŰ±ÛŒŰ§Ù† مŰčمولی ۱ۧ ÙŸÙˆŰŽŰŽ ŰŻÙ‡ÛŒŰŻ. Ù‡Ù†ÚŻŰ§Ù…ÛŒ که ŰąŰČÙ…Ű§ÛŒŰŽ ŰŽÙ…Ű§ ۯ۱ Ű­Ű§Ù„ Ű§Ù†ŰŹŰ§Ù… ŰšŰ±ŰźÛŒ Ű§Ù‚ŰŻŰ§Ù…Ű§ŰȘ ۧ۳ŰȘ (ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† Ù…Ű«Ű§Ù„ŰŒ ÙŰ±Ű§ŰźÙˆŰ§Ù†ÛŒ API)ی ÙˆŰ§Ú©Ù†ŰŽÛŒ ۯ۱ Ű­Ű§Ù„ ۱۟ ŰŻŰ§ŰŻÙ† ۧ۳ŰȘی چیŰČی مŰčÙ†Ű§ŰŻŰ§Ű± ۱۟ می ŰŻÙ‡ŰŻ و Ù†ÛŒŰ§ŰČ ŰšÙ‡ ŰąŰČÙ…Ű§ÛŒŰŽ ۯۧ۱ۯ. ŰȘÙˆŰŹÙ‡ ۯۧێŰȘه ŰšŰ§ŰŽÛŒŰŻ که Ù…Ű§ ŰšÙ‡ Ù†Ű­ÙˆÙ‡ کۧ۱ Ú©Ű±ŰŻÙ† Ű§Ù‡Ù…ÛŒŰȘ نمی ŰŻÙ‡ÛŒÙ…. ŰȘÙ…Ű±Ú©ŰČ Ù…Ű§ Ű±ÙˆÛŒ نŰȘŰ§ÛŒŰŹ ۧ۳ŰȘی چیŰČÙ‡Ű§ÛŒÛŒ که ۧŰČ ŰšÛŒŰ±ÙˆÙ† Ù‚Ű§ŰšÙ„ ŰȘÙˆŰŹÙ‡ Ù‡ŰłŰȘÙ†ŰŻ و ممکن ۧ۳ŰȘ ۚ۱ کۧ۱ۚ۱ ŰȘŰŁŰ«ÛŒŰ± ŰšÚŻŰ°Ű§Ű±Ù†ŰŻ. Ű§ÛŒÙ† ÙŸÛŒŰ§Ù…ŰŻÙ‡Ű§/ÙˆŰ§Ú©Ù†ŰŽ Ù‡Ű§ ۱ۧ می ŰȘÙˆŰ§Ù† ۯ۱ 5 ŰŻŰłŰȘه Ù‚Ű±Ű§Ű± ۯۧۯ: + +‱ نŰȘÛŒŰ­Ù‡ - ŰȘŰłŰȘ یک Űčمل ۱ۧ ÙŰ±Ű§ŰźÙˆŰ§Ù†ÛŒ می Ú©Ù†ŰŻ (ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† Ù…Ű«Ű§Ù„ŰŒ ۧŰČ Ű·Ű±ÛŒÙ‚ API) و یک ÙŸŰ§ŰłŰź ŰŻŰ±ÛŒŰ§ÙŰȘ می Ú©Ù†ŰŻ. Ű§Ú©Ù†ÙˆÙ† ŰšÙ‡ ŰšŰ±Ű±ŰłÛŒ ۔ۭŰȘ ŰŻŰ§ŰŻÙ‡ Ù‡Ű§ÛŒ ÙŸŰ§ŰłŰźŰŒ ۷۱ۭ و ÙˆŰ¶ŰčیŰȘ HTTP می ÙŸŰ±ŰŻŰ§ŰČŰŻ + +‱ state ŰŹŰŻÛŒŰŻ - ÙŸŰł ۧŰČ ÙŰ±Ű§ŰźÙˆŰ§Ù†ÛŒ یک ŰčÙ…Ù„ŰŒ ŰšŰ±ŰźÛŒ ۧŰČ ŰŻŰ§ŰŻÙ‡â€ŒÙ‡Ű§ÛŒ **ۯ۱ ŰŻŰłŰȘ۱۳ Űčموم** ۭۧŰȘÙ…Ű§Ù„Ű§Ù‹ Ű§Ű”Ù„Ű§Ű­ Ù…ÛŒâ€ŒŰŽÙˆÙ†ŰŻ + +‱ ŰȘÙ…Ű§Űł Ù‡Ű§ÛŒ ŰŻŰ§ŰźÙ„ÛŒ - ÙŸŰł ۧŰČ ÙŰ±Ű§ŰźÙˆŰ§Ù†ÛŒ یک ŰčÙ…Ù„ŰŒ ŰšŰ±Ù†Ű§Ù…Ù‡ ممکن ۧ۳ŰȘ یک Ù…Ű€Ù„ÙÙ‡ ŰźŰ§Ű±ŰŹÛŒ ۱ۧ ۧŰČ Ű·Ű±ÛŒÙ‚ HTTP ÛŒŰ§ Ù‡Ű± Ű§Ù†ŰȘÙ‚Ű§Ù„ ŰŻÛŒÚŻŰ± ÙŰ±Ű§ŰźÙˆŰ§Ù†ÛŒ Ú©Ù†ŰŻ. ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† Ù…Ű«Ű§Ù„ŰŒ ŰȘÙ…Ű§Űł ŰšŰ±Ű§ÛŒ Ű§Ű±ŰłŰ§Ù„ ÙŸÛŒŰ§Ù…Ú©ŰŒ Ű§ÛŒÙ…ÛŒÙ„ ÛŒŰ§ ێۧ۱ژ کۧ۱ŰȘ ۧŰčŰȘŰšŰ§Ű±ÛŒ + +‱ Ű”ÙÛŒ ۧŰČ ÙŸÛŒŰ§Ù… Ù‡Ű§ - نŰȘÛŒŰŹÙ‡ یک ŰŹŰ±ÛŒŰ§Ù† ممکن ۧ۳ŰȘ یک ÙŸÛŒŰ§Ù… ۯ۱ یک Ű”Ù ۚۧێۯ + +‱ Ù‚Ű§ŰšÙ„ÛŒŰȘ Ù…ŰŽŰ§Ù‡ŰŻÙ‡ - ŰšŰ±ŰźÛŒ ۧŰČ Ú†ÛŒŰČÙ‡Ű§ Ù…Ű§Ù†Ù†ŰŻ ŰźŰ·Ű§Ù‡Ű§ ÛŒŰ§ Ű±ÙˆÛŒŰŻŰ§ŰŻÙ‡Ű§ÛŒ ŰȘŰŹŰ§Ű±ÛŒ Ù‚Ű§ŰšÙ„ ŰȘÙˆŰŹÙ‡ ŰšŰ§ÛŒŰŻ Ù†ŰžŰ§Ű±ŰȘ ŰŽÙˆÙ†ŰŻ. Ù‡Ù†ÚŻŰ§Ù…ÛŒ که یک ŰȘŰ±Ű§Ú©Ù†ŰŽ ۚۧ ŰŽÚ©ŰłŰȘ Ù…ÙˆŰ§ŰŹÙ‡ می ŰŽÙˆŰŻŰŒ نه ŰȘÙ†Ù‡Ű§ Ù…Ű§ Ű§Ù†ŰȘ۞ۧ۱ ÙŸŰ§ŰłŰź Ù…Ù†Ű§ŰłŰš ۱ۧ ŰŻŰ§Ű±ÛŒÙ…ŰŒ ŰšÙ„Ú©Ù‡ Ù…ŰŻÛŒŰ±ÛŒŰȘ Ű”Ű­ÛŒŰ­ ۟۷ۧ و ۫ۚŰȘ/ŰłÙ†ŰŹÙ‡ Ù‡Ű§ÛŒ Ù…Ù†Ű§ŰłŰš ۱ۧ نیŰČ Ű§Ù†ŰȘ۞ۧ۱ ŰŻŰ§Ű±ÛŒÙ…. Ű§ÛŒÙ† Ű§Ű·Ù„Ű§ŰčۧŰȘ Ù…ŰłŰȘÙ‚ÛŒÙ…Ű§Ù‹ ŰšÙ‡ یک کۧ۱ۚ۱ ŰšŰłÛŒŰ§Ű± مهم می Ű±ÙˆŰŻ - کۧ۱ۚ۱ ops (یŰčنی ŰȘÙˆÙ„ÛŒŰŻ SRE/admin) + + +

    + +# ۚ۟ێ 3ïžâƒŁ: Frontend ŰȘŰłŰȘ Ú©Ű±ŰŻÙ† + +## âšȘ  3.1 UI ۱ۧ ۧŰČ Ù„Ű§ŰŹÛŒÚ© ŰšŰ±Ù†Ű§Ù…Ù‡ ۏۯۧ Ú©Ù†ÛŒŰŻ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** Ù‡Ù†ÚŻŰ§Ù… ŰȘÙ…Ű±Ú©ŰČ ŰšŰ± Ű±ÙˆÛŒ ŰȘŰłŰȘ Ù…Ù†Ű·Ù‚ Ù…Ű€Ù„ÙÙ‡ŰŒ ŰŹŰČŰŠÛŒŰ§ŰȘ UI ŰȘŰšŰŻÛŒÙ„ ŰšÙ‡ نویŰČ Ù…ÛŒ ŰŽÙˆŰŻ که ŰšŰ§ÛŒŰŻ ۧ۳ŰȘ۟۱ۧۏ ŰŽÙˆŰŻŰŒ ŰšÙ†Ű§ŰšŰ±Ű§ÛŒÙ† ŰąŰČÙ…Ű§ÛŒŰŽŰ§ŰȘ ŰŽÙ…Ű§ می ŰȘÙˆŰ§Ù†Ù†ŰŻ ۚ۱ Ű±ÙˆÛŒ ŰŻŰ§ŰŻÙ‡ Ù‡Ű§ÛŒ ŰźŰ§Ù„Ű” ŰȘÙ…Ű±Ú©ŰČ Ú©Ù†Ù†ŰŻ. ŰčÙ…Ù„Ű§Ù‹ ŰŻŰ§ŰŻÙ‡â€ŒÙ‡Ű§ÛŒ Ù…ÙˆŰ±ŰŻ Ù†ŰžŰ± ۱ۧ ۧŰČ Ù†ŰŽŰ§Ù†Ù‡â€ŒÚŻŰ°Ű§Ű±ÛŒ ŰšÙ‡ Ű±ÙˆŰŽÛŒ Ű§Ù†ŰȘŰČۧŰčی ۧ۳ŰȘ۟۱ۧۏ Ú©Ù†ÛŒŰŻ که ŰźÛŒÙ„ÛŒ ۚۧ ÙŸÛŒŰ§ŰŻÙ‡â€ŒŰłŰ§ŰČی ÚŻŰ±Ű§ÙÛŒÚ©ÛŒ Ù‡Ù…Ű±Ű§Ù‡ Ù†ŰšŰ§ŰŽŰŻŰŒ ÙÙ‚Ű· Ű±ÙˆÛŒ ŰŻŰ§ŰŻÙ‡â€ŒÙ‡Ű§ÛŒ ŰźŰ§Ù„Ű” (ۯ۱ Ù…Ù‚Ű§ŰšÙ„ ŰŹŰČŰŠÛŒŰ§ŰȘ ÚŻŰ±Ű§ÙÛŒÚ©ÛŒ HTML/CSS) ۧۯŰčۧ Ú©Ù†ÛŒŰŻ و Ű§Ù†ÛŒÙ…ÛŒŰŽÙ†â€ŒÙ‡Ű§ÛŒÛŒ ۱ۧ که Ú©Ù†ŰŻ Ù…ÛŒâ€ŒŰŽÙˆÙ†ŰŻ ŰșÛŒŰ±ÙŰčŰ§Ù„ Ú©Ù†ÛŒŰŻ. ممکن ۧ۳ŰȘ ÙˆŰłÙˆŰłÙ‡ ŰŽÙˆÛŒŰŻ که ۧŰČ Ű±Ù†ŰŻŰ± Ú©Ű±ŰŻÙ† ŰźÙˆŰŻŰŻŰ§Ű±ÛŒ Ú©Ù†ÛŒŰŻ و ÙÙ‚Ű· Ù‚ŰłÙ…ŰȘ ÙŸŰŽŰȘی ۱ۧۚ۷ Ú©Ű§Ű±ŰšŰ±ÛŒ (Ù…Ű§Ù†Ù†ŰŻ ŰłŰ±ÙˆÛŒŰłâ€ŒÙ‡Ű§ŰŒ Ű§Ù‚ŰŻŰ§Ù…Ű§ŰȘی ÙŰ±ÙˆŰŽÚŻŰ§Ù‡) ۱ۧ ŰąŰČÙ…Ű§ÛŒŰŽ Ú©Ù†ÛŒŰŻŰŒ Ű§Ù…Ű§ Ű§ÛŒÙ† Ù…Ù†ŰŹŰ± ŰšÙ‡ ŰąŰČÙ…Ű§ÛŒŰŽâ€ŒÙ‡Ű§ÛŒ ŰȘŰźÛŒÙ„ÛŒ Ù…ÛŒâ€ŒŰŽÙˆŰŻ که ŰšÙ‡ ÙˆŰ§Ù‚ŰčیŰȘ ŰŽŰšŰ§Ù‡ŰȘ Ù†ŰŻŰ§Ű±Ù†ŰŻ و Ù…ÙˆŰ§Ű±ŰŻÛŒ ۱ۧ که ŰŻŰ§ŰŻÙ‡â€ŒÙ‡Ű§ÛŒ Ù…Ù†Ű§ŰłŰš ÙˆŰŹÙˆŰŻ Ù†ŰŻŰ§Ű±ŰŻ ۱ۧ Ù†ŰŽŰ§Ù† Ù†Ù…ÛŒâ€ŒŰŻÙ‡ŰŻ. Ű­ŰȘی ÙˆŰ§Ű±ŰŻ UI ŰŽÙˆÛŒŰŻ + +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** ŰŻŰ§ŰŻÙ‡â€ŒÙ‡Ű§ÛŒ Ù…Ű­Ű§ŰłŰšŰ§ŰȘی ŰźŰ§Ù„Ű” ŰąŰČمون ŰŽÙ…Ű§ ممکن ۧ۳ŰȘ ۯ۱ 10 Ù…ÛŒÙ„ÛŒâ€ŒŰ«Ű§Ù†ÛŒÙ‡ ŰąÙ…Ű§ŰŻÙ‡ ۹ۧۮۯی Ű§Ù…Ű§ ÙŸŰł ۧŰČ ŰąÙ† کل ŰąŰČمون ŰšÙ‡ ŰŻÙ„ÛŒÙ„ ŰšŰ±ŰźÛŒ Ű§Ù†ÛŒÙ…ÛŒŰŽÙ†â€ŒÙ‡Ű§ÛŒ ÙŰ§Ù†ŰȘŰČی و Ù†Ű§Ù…Ű±ŰšÙˆŰ·ŰŒ 500 Ù…ÛŒÙ„ÛŒâ€ŒŰ«Ű§Ù†ÛŒÙ‡ (100 ŰȘŰłŰȘ = 1 ŰŻÙ‚ÛŒÙ‚Ù‡) Ű·ÙˆÙ„ ŰźÙˆŰ§Ù‡ŰŻ Ú©ŰŽÛŒŰŻ. + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :clap: مŰȘŰ§Ù„ ۯ۱۳ŰȘ: ۏۯۧ Ú©Ű±ŰŻÙ† ŰŹŰČŰŠÛŒŰ§ŰȘ UI + +![](https://img.shields.io/badge/🔧%20Example%20using%20React-blue.svg "Examples with React") ![](https://img.shields.io/badge/🔧%20Example%20using%20React%20Testing%20Library-blue.svg "Examples with react-testing-library") + +```javascript +test("Ù‡Ù†ÚŻŰ§Ù…ÛŒ که Ù„ÛŒŰłŰȘ Ú©Ű§Ű±ŰšŰ±Ű§Ù† ŰšŰ±Ű§ÛŒ Ù†ŰŽŰ§Ù† ŰŻŰ§ŰŻÙ† ÙÙ‚Ű· VIP ÙŸŰ±Ú†Ù… ÚŻŰ°Ű§Ű±ÛŒ می ŰŽÙˆŰŻŰŒ ŰšŰ§ÛŒŰŻ ÙÙ‚Ű· ۧŰčŰ¶Ű§ÛŒ VIP Ù†Ù…Ű§ÛŒŰŽ ŰŻŰ§ŰŻÙ‡ ŰŽÙˆŰŻ", () => { + // Ù…Ù‚ŰŻŰ§Ű± ŰŻÙ‡ÛŒ Ú©Ű±ŰŻÙ† + const allUsers = [{ id: 1, name: "Yoni Goldberg", vip: false }, { id: 2, name: "John Doe", vip: true }]; + + // ۧۏ۱ۧ Ú©Ű±ŰŻÙ† + const { getAllByTestId } = render(); + + // Ù…ÙŰ§ÛŒŰłÙ‡ Ú©Ű±ŰŻÙ† - ۧۚŰȘۯۧ ŰŻŰ§ŰŻÙ‡ Ù‡Ű§ ۱ۧ ۧŰČ UI ۧ۳ŰȘ۟۱ۧۏ Ú©Ù†ÛŒŰŻ + const allRenderedUsers = getAllByTestId("user").map(uiElement => uiElement.textContent); + const allRealVIPUsers = allUsers.filter(user => user.vip).map(user => user.name); + expect(allRenderedUsers).toEqual(allRealVIPUsers); //ŰŻŰ§ŰŻÙ‡ Ù‡Ű§ ۱ۧ ۚۧ ŰŻŰ§ŰŻÙ‡ Ù‡Ű§ Ù…Ù‚Ű§ÛŒŰłÙ‡ Ú©Ù†ÛŒŰŻŰŒ Ű§ÛŒÙ†ŰŹŰ§ ۱ۧۚ۷ Ú©Ű§Ű±ŰšŰ±ÛŒ ÙˆŰŹÙˆŰŻ Ù†ŰŻŰ§Ű±ŰŻ +}); +``` + +
    + +### :thumbsdown: Ù…Ű«Ű§Ù„ ۶ۯ Ű§Ù„ÚŻÙˆ: ŰŹŰČŰŠÛŒŰ§ŰȘ و ŰŻŰ§ŰŻÙ‡ Ù‡Ű§ÛŒ ۱ۧۚ۷ Ú©Ű§Ű±ŰšŰ±ÛŒ ŰȘŰ±Ú©ÛŒŰšÛŒ ۧۯŰčۧ + +```javascript +test("Ù‡Ù†ÚŻŰ§Ù… ÙŸŰ±Ú†Ù… ÚŻŰ°Ű§Ű±ÛŒ ŰšŰ±Ű§ÛŒ Ù†Ù…Ű§ÛŒŰŽ ÙÙ‚Ű· VIPی ŰšŰ§ÛŒŰŻ ÙÙ‚Ű· ۧŰčŰ¶Ű§ÛŒ VIP Ù†Ù…Ű§ÛŒŰŽ ŰŻŰ§ŰŻÙ‡ ŰŽÙˆŰŻ", () => { + // Ù…Ù‚ŰŻŰ§Ű±ÛŒ ŰŻÙ‡ÛŒ Ú©Ű±ŰŻÙ† + const allUsers = [{ id: 1, name: "Yoni Goldberg", vip: false }, { id: 2, name: "John Doe", vip: true }]; + + // ۧۏ۱ۧ Ú©Ű±ŰŻÙ† + const { getAllByTestId } = render(); + + // Ù…Ù‚Ű§ŰšŰłÙ‡ Ú©Ű±ŰŻÙ† - ŰȘŰ±Ú©ÛŒŰš ui و data ۯ۱ Ű§ÛŒÙ† Ù‚ŰłÙ…ŰȘ + expect(getAllByTestId("user")).toEqual('[
  • John Doe
  • ]'); +}); +``` + +
    + +

    + +## âšȘ  3.2 ÙŸŰ±Űł و ŰŹÙˆ ۧŰČ ŰčÙ†Ű§Ű”Ű± HTML ۚ۱ ۧ۳ۧ۳ ÙˆÛŒÚ˜ÚŻÛŒ Ù‡Ű§ÛŒÛŒ که ŰšŰčÛŒŰŻ ۧ۳ŰȘ ŰȘŰșÛŒÛŒŰ± Ú©Ù†Ù†ŰŻ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** ŰčÙ†Ű§Ű”Ű± HTML ۱ۧ ۚ۱ ۧ۳ۧ۳ ÙˆÛŒÚ˜ÚŻÛŒ Ù‡Ű§ÛŒÛŒ که ۭۧŰȘÙ…Ű§Ù„Ű§Ù‹ ۚ۱ ŰźÙ„Ű§Ù Ű§Ù†ŰȘŰźŰ§ŰšÚŻŰ±Ù‡Ű§ÛŒ CSS و Ù…Ű§Ù†Ù†ŰŻ ۚ۱چ۳ۚ Ù‡Ű§ÛŒ ÙŰ±Ù…ŰŒ ۯ۱ ŰȘŰșÛŒÛŒŰ±Ű§ŰȘ ÚŻŰ±Ű§ÙÛŒÚ©ÛŒ ŰšŰ§Ù‚ÛŒ می Ù…Ű§Ù†Ù†ŰŻŰŒ ÙŸŰ±Űł و ŰŹÙˆ Ú©Ù†ÛŒŰŻ. ۧگ۱ ŰčÙ†Ű”Ű± ŰȘŰčÛŒÛŒÙ†â€ŒŰŽŰŻÙ‡ چنین ÙˆÛŒÚ˜ÚŻÛŒâ€ŒÙ‡Ű§ÛŒÛŒ ۱ۧ Ù†ŰŻŰ§Ű±ŰŻŰŒ یک ÙˆÛŒÚ˜ÚŻÛŒ ŰȘŰłŰȘ ۧ۟ŰȘŰ”Ű§Ű”ÛŒ Ù…Ű§Ù†Ù†ŰŻ «test-id-submit-button» Ű§ÛŒŰŹŰ§ŰŻ Ú©Ù†ÛŒŰŻ. ÙŸÛŒÙ…ÙˆŰŻÙ† Ű§ÛŒÙ† Ù…ŰłÛŒŰ± نه ŰȘÙ†Ù‡Ű§ ŰȘŰ¶Ù…ÛŒÙ† Ù…ÛŒâ€ŒÚ©Ù†ŰŻ که ŰȘŰłŰȘâ€ŒÙ‡Ű§ÛŒ ŰčÙ…Ù„Ú©Ű±ŰŻÛŒ/Ù…Ù†Ű·Ù‚ÛŒ ŰŽÙ…Ű§ Ù‡Ű±ÚŻŰČ ŰšÙ‡ ŰŻÙ„ÛŒÙ„ ŰȘŰșÛŒÛŒŰ±Ű§ŰȘ ŰžŰ§Ù‡Ű±ÛŒ و Ű§Ű­ŰłŰ§ŰłÛŒ ŰŽÚ©ŰłŰȘه Ù†Ù…ÛŒâ€ŒŰŽÙˆÙ†ŰŻŰŒ ŰšÙ„Ú©Ù‡ ŰšŰ±Ű§ÛŒ کل ŰȘیم Ù…ŰŽŰźŰ” Ù…ÛŒâ€ŒŰŽÙˆŰŻ که Ű§ÛŒÙ† ŰčÙ†Ű”Ű± و ÙˆÛŒÚ˜ÚŻÛŒ ŰȘÙˆŰłŰ· ŰȘŰłŰȘ â€ŒÙ‡Ű§ ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ù…ÛŒâ€ŒŰŽÙˆÙ†ŰŻ و Ù†ŰšŰ§ÛŒŰŻ Ű­Ű°Ù ŰŽÙˆÙ†ŰŻ. + +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** ŰŽÙ…Ű§ Ù…ÛŒâ€ŒŰźÙˆŰ§Ù‡ÛŒŰŻ ŰčÙ…Ù„Ú©Ű±ŰŻ ÙˆŰ±ÙˆŰŻ ŰšÙ‡ ŰłÛŒŰłŰȘم ۱ۧ که ŰŽŰ§Ù…Ù„ ŰšŰłÛŒŰ§Ű±ÛŒ ۧŰČ Ù…Ű€Ù„ÙÙ‡â€ŒÙ‡Ű§ŰŒ Ù…Ù†Ű·Ù‚ و ŰłŰ±ÙˆÛŒŰłâ€ŒÙ‡Ű§ Ù…ÛŒâ€ŒŰŽÙˆŰŻŰŒ ŰąŰČÙ…Ű§ÛŒŰŽ Ú©Ù†ÛŒŰŻŰŒ همه چیŰČ ŰšÙ‡â€ŒŰźÙˆŰšÛŒ ŰȘÙ†ŰžÛŒÙ… ŰŽŰŻÙ‡ ۧ۳ŰȘ - Ù…ÙˆŰ§Ű±ŰŻ ŰźŰ±ŰŻŰŒ ŰŹŰ§ŰłÙˆŰłâ€ŒÙ‡Ű§ŰŒ ŰȘÙ…Ű§Űłâ€ŒÙ‡Ű§ÛŒ Ajax ۏۯۧ ŰŽŰŻÙ‡â€ŒŰ§Ù†ŰŻ. همه ŰčŰ§Ù„ÛŒ ŰšÙ‡ Ù†ŰžŰ± می Ű±ŰłÙ†ŰŻ ŰłÙŸŰł ŰȘŰłŰȘ ŰŽÚ©ŰłŰȘ می ŰźÙˆŰ±ŰŻ ŰČÛŒŰ±Ű§ ۷۱ۭۧ Ú©Ù„Ű§Űł div CSS ۱ۧ ۧŰČ "thick-border" ŰšÙ‡ "thin-border" ŰȘŰșÛŒÛŒŰ± ŰŻŰ§ŰŻÙ‡ ۧ۳ŰȘ.' + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :clap: Ű§Ù†ŰŹŰ§Ù… ۯ۱۳ŰȘ Ù…Ű«Ű§Ù„: ÙŸŰ±Űł و ŰŹÙˆ ۧŰČ ÛŒÚ© ŰčÙ†Ű”Ű± ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ ÛŒÚ© ÙˆÛŒÚ˜ÚŻÛŒ ۧ۟ŰȘŰ”Ű§Ű”ÛŒ ŰšŰ±Ű§ÛŒ ŰȘŰłŰȘ + +![](https://img.shields.io/badge/🔧%20Example%20using%20React-blue.svg "Examples with React") + +```html +// Ú©ŰŻ Ù†ŰŽŰ§Ù†Ù‡ ÚŻŰ°Ű§Ű±ÛŒ (ŰšŰźŰŽÛŒ ۧŰČ Ù…Ű€Ù„ÙÙ‡ React) +

    + + {value} + + +

    +``` + +```javascript +// Ű§ÛŒÙ† Ù…Ű«Ű§Ù„ ۧŰČ react-testing-library ۧ۳ŰȘÙŰ§ŰŻÙ‡ می Ú©Ù†ŰŻ +test("Ù‡Ű± ŰČÙ…Ű§Ù† که هیچ ŰŻŰ§ŰŻÙ‡ Ű§ÛŒ ŰšÙ‡ مŰȘŰ±ÛŒÚ© منŰȘقل نمی ŰŽÙˆŰŻŰŒ 0 ۱ۧ ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† ÙŸÛŒŰŽ ÙŰ±Ű¶ Ù†ŰŽŰ§Ù† ŰŻÙ‡ÛŒŰŻ", () => { + // Ù…Ù‚ŰŻŰ§Ű± ŰŻÙ‡ÛŒ Ú©Ű±ŰŻÙ† + const metricValue = undefined; + + // ۧۏ۱ۧ Ú©Ű±ŰŻÙ† + const { getByTestId } = render(); + + expect(getByTestId("errorsLabel").text()).toBe("0"); +}); +``` + +
    + +### :thumbsdown: Ù…Ű«Ű§Ù„ ۶ۯ Ű§Ù„ÚŻÙˆ: ŰȘکیه ۚ۱ ÙˆÛŒÚ˜ÚŻÛŒ Ù‡Ű§ÛŒ CSS + +```html + +{value} + +``` + +```javascript +// Ű§ÛŒÙ† Ù…Ű«Ű§Ù„ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ ŰąÙ†ŰČیم ۧ۳ŰȘ +test("Ù‡Ű± ŰČÙ…Ű§Ù† که هیچ ŰŻŰ§ŰŻÙ‡ Ű§ÛŒ Ű§Ű±ŰłŰ§Ù„ Ù†ŰŽÙˆŰŻŰŒ مŰčÛŒŰ§Ű± ۟۷ۧ Ű”ÙŰ± ۱ۧ Ù†ŰŽŰ§Ù† می ŰŻÙ‡ŰŻ", () => { + // ... + + expect(wrapper.find("[className='d-flex-column']").text()).toBe("0"); +}); +``` + +
    + +
    + +## âšȘ  3.3 ۯ۱ Ű”ÙˆŰ±ŰȘ Ű§Ù…Ú©Ű§Ù†ŰŒ ۚۧ یک ŰŹŰČŰĄ ÙˆŰ§Ù‚Űčی و Ú©Ű§Ù…Ù„Ű§ Ű±Ù†ŰŻŰ± ŰŽŰŻÙ‡ ŰȘŰłŰȘ Ú©Ù†ÛŒŰŻ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** Ù‡Ű± ŰČÙ…Ű§Ù† که Ű§Ù†ŰŻŰ§ŰČه مŰčقولی ۯۧێŰȘÛŒŰŻŰŒ Ù…Ű€Ù„ÙÙ‡ ŰźÙˆŰŻ ۱ۧ ۧŰČ ŰźŰ§Ű±ŰŹ Ù…Ű§Ù†Ù†ŰŻ Ú©Ű§Ű±ŰšŰ±Ű§Ù†ŰȘŰ§Ù† ŰąŰČÙ…Ű§ÛŒŰŽ Ú©Ù†ÛŒŰŻŰŒ UI ۱ۧ ŰšÙ‡ Ű·ÙˆŰ± Ú©Ű§Ù…Ù„ Ű±Ù†ŰŻŰ± Ú©Ù†ÛŒŰŻŰŒ ۚ۱ ۧ۳ۧ۳ ŰąÙ† Űčمل Ú©Ù†ÛŒŰŻ و ۧۯŰčۧ Ú©Ù†ÛŒŰŻ که ۱ۧۚ۷ Ú©Ű§Ű±ŰšŰ±ÛŒ Ű±Ù†ŰŻŰ± ŰŽŰŻÙ‡ Ù…Ű·Ű§ŰšÙ‚ Ű§Ù†ŰȘ۞ۧ۱ Űčمل می Ú©Ù†ŰŻ. ۧŰČ Ù‡Ű± ÚŻÙˆÙ†Ù‡ Ű±Ù†ŰŻŰ± ŰȘÙ…ŰłŰźŰ± ŰąÙ…ÛŒŰČی ŰŹŰČŰŠÛŒ و کم Űčمق ŰźÙˆŰŻŰŻŰ§Ű±ÛŒ Ú©Ù†ÛŒŰŻ - Ű§ÛŒÙ† Ű±ÙˆŰŽ ممکن ۧ۳ŰȘ ŰšÙ‡ ŰŻÙ„ÛŒÙ„ Ú©Ù…ŰšÙˆŰŻ ŰŹŰČŰŠÛŒŰ§ŰȘ Ù…Ù†ŰŹŰ± ŰšÙ‡ Ű§ŰŽÚ©Ű§Ù„Ű§ŰȘ Ù…Ű­ÙÙˆŰž Ù†ŰŽŰŻÙ‡ و ŰȘŰčÙ…ÛŒŰ± و Ù†ÚŻÙ‡ŰŻŰ§Ű±ÛŒ ۳۟ŰȘ ŰȘ۱ ŰŽÙˆŰŻ ŰČÛŒŰ±Ű§ ŰąŰČÙ…Ű§ÛŒŰŽ Ù‡Ű§ ۚۧ ۧۏŰČŰ§ÛŒ ŰŻŰ§ŰźÙ„ÛŒ ŰšÙ‡ هم می ŰźÙˆŰ±Ù†ŰŻ (ŰšÙ‡ ÚŻÙ„ÙˆÙ„Ù‡ Ù…Ű±Ű§ŰŹŰčه Ú©Ù†ÛŒŰŻ ['ۧŰČ ŰȘŰłŰȘ ŰŹŰčŰšÙ‡ ŰłÛŒŰ§Ù‡ Ű­Ù…Ű§ÛŒŰȘ Ú©Ù†ÛŒŰŻ'](https://github.com/goldbergyoni/javascript-testing-best-practices#-%EF%B8%8F-14-stick-to-black-box-testing-test-only-public-methods)). ۧگ۱ یکی ۧŰČ Ű§ŰŹŰČŰ§ÛŒ Ú©ÙˆŰŻÚ© ŰšÙ‡ Ű·ÙˆŰ± Ù‚Ű§ŰšÙ„ ŰȘÙˆŰŹÙ‡ÛŒ Ú©Ù†ŰŻ می ŰŽÙˆŰŻ (Ù…Ű«Ù„Ű§Ù‹ Ű§Ù†ÛŒÙ…ÛŒŰŽÙ†) ÛŒŰ§ ŰȘÙ†ŰžÛŒÙ…Ű§ŰȘ ۱ۧ ÙŸÛŒÚ†ÛŒŰŻÙ‡ می Ú©Ù†ŰŻ - ŰšÙ‡ Ű·ÙˆŰ± ÙˆŰ§Ű¶Ű­ ŰąÙ† ۱ۧ ۚۧ یک ŰŹŰčلی ŰŹŰ§ÛŒÚŻŰČین Ú©Ù†ÛŒŰŻ. + +ۚۧ ŰȘÙ…Ű§Ù… ŰąÙ†Ú†Ù‡ ÚŻÙŰȘه ۮۯی یک کلمه ۭۧŰȘÛŒŰ§Ű· Ù„Ű§ŰČم ۧ۳ŰȘ: Ű§ÛŒÙ† ŰȘکنیک ŰšŰ±Ű§ÛŒ ۧۏŰČŰ§ÛŒ کوچک/مŰȘÙˆŰłŰ· ​​که Ű§Ù†ŰŻŰ§ŰČه مŰčقولی ۧŰČ Ű§ŰŹŰČŰ§ÛŒ Ú©ÙˆŰŻÚ© ۱ۧ ۯ۱ ŰźÙˆŰŻ ŰŹŰ§ÛŒ می ŰŻÙ‡Ù†ŰŻŰŒ کۧ۱ می Ú©Ù†ŰŻ. Ű±Ù†ŰŻŰ± Ú©Ű±ŰŻÙ† Ú©Ű§Ù…Ù„ یک Ù…Ű€Ù„ÙÙ‡ ۚۧ ŰȘŰčۯۧۯ ŰČÛŒŰ§ŰŻ ÙŰ±ŰČÙ†ŰŻŰ§Ù†ŰŒ ۧ۳ŰȘŰŻÙ„Ű§Ù„ ۯ۱ Ù…ÙˆŰ±ŰŻ ŰŽÚ©ŰłŰȘ ŰȘŰłŰȘ (ŰȘŰ­Ù„ÛŒÙ„ ŰčلŰȘ Ű±ÛŒŰŽÙ‡) ۱ۧ ŰŻŰŽÙˆŰ§Ű± می Ú©Ù†ŰŻ و ممکن ۧ۳ŰȘ ŰšŰłÛŒŰ§Ű± Ú©Ù†ŰŻ ŰŽÙˆŰŻ. ۯ۱ چنین Ù…ÙˆŰ§Ű±ŰŻÛŒŰŒ ÙÙ‚Ű· Ú†Ù†ŰŻ ŰąŰČÙ…Ű§ÛŒŰŽ ۚ۱ Ű±ÙˆÛŒ ŰąÙ† ŰŹŰČŰĄ ÙˆŰ§Ù„ŰŻ Ú†Ű§Ù‚ ŰšÙ†ÙˆÛŒŰłÛŒŰŻ و ŰąŰČÙ…Ű§ÛŒŰŽ Ù‡Ű§ÛŒ ŰšÛŒŰŽŰȘŰ±ÛŒ ۱ۧ ۚ۱ Ű±ÙˆÛŒ ÙŰ±ŰČÙ†ŰŻŰ§Ù† ŰąÙ† ŰšÙ†ÙˆÛŒŰłÛŒŰŻ + +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** Ù‡Ù†ÚŻŰ§Ù…ÛŒ که ۚۧ ÙŰ±Ű§ŰźÙˆŰ§Ù†ÛŒ Ű±ÙˆŰŽâ€ŒÙ‡Ű§ÛŒ ŰźŰ”ÙˆŰ”ÛŒ و ŰšŰ±Ű±ŰłÛŒ ÙˆŰ¶ŰčیŰȘ ŰŻŰ±ÙˆÙ†ÛŒ یک Ú©Ű§Ù…ÙŸÙˆÙ†Ù†ŰȘ ŰšÙ‡ ŰŻŰ±ÙˆÙ†ÛŒ ŰąÙ† ÙˆŰ§Ű±ŰŻ Ù…ÛŒâ€ŒŰŽÙˆÛŒŰŻ - ŰšŰ§ÛŒŰŻ همه ŰąŰČÙ…Ű§ÛŒŰŽâ€ŒÙ‡Ű§ ۱ۧ Ù‡Ù†ÚŻŰ§Ù… ۚۧŰČ۳ۧŰČی Ű§ŰŹŰ±Ű§ÛŒ Ù…Ű€Ù„ÙÙ‡â€ŒÙ‡Ű§ Ù…ŰŹŰŻŰŻŰ§Ù‹ ŰȘŰșÛŒÛŒŰ± ŰŻÙ‡ÛŒŰŻ. ŰąÛŒŰ§ ÙˆŰ§Ù‚ŰčŰ§Ù‹ ŰšŰ±Ű§ÛŒ Ű§ÛŒÙ† ۳۷ۭ ۧŰČ Ù†ÚŻÙ‡ŰŻŰ§Ű±ÛŒ ŰžŰ±ÙÛŒŰȘ ŰŻŰ§Ű±ÛŒŰŻŰŸ? + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :clap: Ű§Ù†ŰŹŰ§Ù… ۯ۱۳ŰȘ ŰąÙ† Ù…Ű«Ű§Ù„: کۧ۱ ŰšÙ‡ Ű”ÙˆŰ±ŰȘ ÙˆŰ§Ù‚Űč ŰšÛŒÙ†Ű§Ù†Ù‡ ۚۧ یک ŰŹŰČŰĄ Ú©Ű§Ù…Ù„Ű§Ù‹ Ű±Ù†ŰŻŰ± ŰŽŰŻÙ‡ + +![](https://img.shields.io/badge/🔧%20Example%20using%20React-blue.svg "Examples with React") ![](https://img.shields.io/badge/🔧%20Example%20using%20Enzyme-blue.svg "Examples with Enzyme") + +```javascript +class Calendar extends React.Component { + static defaultProps = { showFilters: false }; + + render() { + return ( +
    + A filters panel with a button to hide/show filters + +
    + ); + } +} + +//ŰšŰ±Ű§ÛŒ Ù…Ű«Ű§Ù„ ۧŰČ React & Enzyme ۧ۳ŰȘÙŰ§ŰŻÙ‡ می ŰŽÙˆŰŻ +test("Ű±ÙˆÛŒÚ©Ű±ŰŻ ÙˆŰ§Ù‚Űč ÚŻŰ±Ű§ÛŒŰ§Ù†Ù‡: Ù‡Ù†ÚŻŰ§Ù…ÛŒ که ŰšŰ±Ű§ÛŒ Ù†Ù…Ű§ÛŒŰŽ فیلŰȘŰ±Ù‡Ű§ کلیک Ú©Ù†ÛŒŰŻŰŒ فیلŰȘŰ±Ù‡Ű§ Ù†Ù…Ű§ÛŒŰŽ ŰŻŰ§ŰŻÙ‡ می ŰŽÙˆÙ†ŰŻ", () => { + // Ù…Ù‚ŰŻŰ§Ű± ŰŻÙ‡ÛŒ Ú©Ű±ŰŻÙ† + const wrapper = mount(); + + // ۧۏ۱ۧ Ú©Ű±ŰŻÙ† + wrapper.find("button").simulate("click"); + + // Ù…Ù‚Ű§ŰšŰłÙ‡ Ú©Ű±ŰŻÙ† + expect(wrapper.text().includes("Choose Filter")); + // ŰšÙ‡ Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ ۧ۳ŰȘ که کۧ۱ۚ۱ ŰšÙ‡ Ű§ÛŒÙ† ŰčÙ†Ű”Ű± نŰČŰŻÛŒÚ© می ŰŽÙˆŰŻ: ŰȘÙˆŰłŰ· مŰȘن +}); +``` + +### :thumbsdown: Ù…Ű«Ű§Ù„ ۶ۯ Ű§Ù„ÚŻÙˆ: ŰȘÙ…ŰłŰźŰ± ÙˆŰ§Ù‚ŰčیŰȘ ۚۧ Ű±Ù†ŰŻŰ± کم Űčمق + +```javascript +test("Ű±ÙˆÛŒÚ©Ű±ŰŻ کم Űčمق/ Ù…ŰłŰźŰ±Ù‡ ŰŽŰŻÙ‡: وقŰȘی ŰšŰ±Ű§ÛŒ Ù†Ù…Ű§ÛŒŰŽ فیلŰȘŰ±Ù‡Ű§ کلیک Ú©Ù†ÛŒŰŻŰŒ فیلŰȘŰ±Ù‡Ű§ Ù†Ù…Ű§ÛŒŰŽ ŰŻŰ§ŰŻÙ‡ می ŰŽÙˆÙ†ŰŻ", () => { + // Ù…Ù‚ŰŻŰ§Ű± ŰŻÙ‡ÛŒ Ú©Ű±ŰŻÙ† + const wrapper = shallow(); + + // ۧۏ۱ۧ Ú©Ű±ŰŻÙ† + wrapper + .find("filtersPanel") + .instance() + .showFilters(); + // Tap into the internals, bypass the UI and invoke a method. White-box approach + + // Ù…Ù‚Ű§ÛŒŰłÙ‡ Ú©Ű±ŰŻÙ† + expect(wrapper.find("Filter").props()).toEqual({ title: "Choose Filter" }); + // ۧگ۱ Ù†Ű§Ù… ÙŸŰ±ÙˆÙŸÙˆŰČŰ§Ù„ ۱ۧ ŰȘŰșÛŒÛŒŰ± ŰŻÙ‡ÛŒÙ… ÛŒŰ§ چیŰČی Ù…Ű±ŰšÙˆŰ·Ù‡ ۱ۧ ÙŸŰ§Űł نکنیم Ú†Ù‡ŰŸ +}); +``` + +
    + +
    + +## âšȘ  3.4 Ù†ŰźÙˆŰ§ŰšÛŒŰŻŰŒ ۧŰČ frameworkâ€ŒÙ‡Ű§ÛŒ ŰŻŰ§ŰźÙ„ÛŒ ÙŸŰŽŰȘÛŒŰšŰ§Ù†ÛŒ ŰšŰ±Ű§ÛŒ Ű±ÙˆÛŒŰŻŰ§ŰŻÙ‡Ű§ÛŒ Ù‡Ù…ÚŻŰ§Ù… ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ú©Ù†ÛŒŰŻ. همچنین ŰłŰčی Ú©Ù†ÛŒŰŻ Ú©Ű§Ű±Ù‡Ű§ ۱ۧ ŰȘŰłŰ±ÛŒŰč Ú©Ù†ÛŒŰŻ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** ۯ۱ ŰšŰłÛŒŰ§Ű±ÛŒ ۧŰČ Ù…ÙˆŰ§Ű±ŰŻŰŒ ÙˆŰ§Ű­ŰŻ ŰȘŰ­ŰȘ ŰąŰČÙ…Ű§ÛŒŰŽ ŰČÙ…Ű§Ù† Ú©Ű§Ù…Ù„ Ù†Ű§ŰŽÙ†Ű§ŰźŰȘه ۧ۳ŰȘ (Ù…Ű«Ù„Ű§Ù‹ Ű§Ù†ÛŒÙ…ÛŒŰŽÙ† ŰžŰ§Ù‡Ű± ŰčÙ†Ű”Ű± ۱ۧ ŰšÙ‡ Ű­Ű§Ù„ŰȘ ŰȘŰčلیق ۯ۱ Ù…ÛŒâ€ŒŰąÙˆŰ±ŰŻ) - ۯ۱ Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘی ۧŰČ ŰźÙˆŰ§ŰšÛŒŰŻÙ† ŰźÙˆŰŻŰŻŰ§Ű±ÛŒ Ú©Ù†ÛŒŰŻ (Ù…Ű«Ù„Ű§Ù‹ setTimeOut) و Ű±ÙˆŰŽâ€ŒÙ‡Ű§ÛŒ Ù‚Ű·Űčی‌ŰȘŰ±ÛŒ ۱ۧ که ۧک۫۱ ٟلŰȘÙŰ±Ù…â€ŒÙ‡Ű§ Ű§Ű±Ű§ŰŠÙ‡ Ù…ÛŒâ€ŒŰŻÙ‡Ù†ŰŻ ŰȘŰ±ŰŹÛŒŰ­ ŰŻÙ‡ÛŒŰŻ. ŰšŰ±ŰźÛŒ ۧŰČ Ú©ŰȘŰ§ŰšŰźŰ§Ù†Ù‡ Ù‡Ű§ Ű§Ù…Ú©Ű§Ù† Ű§Ù†ŰȘ۞ۧ۱ ŰšŰ±Ű§ÛŒ ŰčÙ…Ù„ÛŒŰ§ŰȘ ۱ۧ ÙŰ±Ű§Ù‡Ù… می Ú©Ù†Ù†ŰŻ (e.g. [Cypress cy.request('url')](https://docs.cypress.io/guides/references/best-practices.html#Unnecessary-Waiting)), ŰłŰ§ÛŒŰ± API ŰšŰ±Ű§ÛŒ Ű§Ù†ŰȘ۞ۧ۱ Ù…Ű§Ù†Ù†ŰŻ [@testing-library/dom method wait(expect(element))](https://testing-library.com/docs/guide-disappearance). ÚŻŰ§Ù‡ÛŒ Ű§ÙˆÙ‚Ű§ŰȘ یک Ű±Ű§Ù‡ ŰžŰ±ÛŒÙ ŰȘŰ±ŰŒ ۟۱ۯ Ú©Ű±ŰŻÙ† Ù…Ù†ŰšŰč Ú©Ù†ŰŻ ۧ۳ŰȘی Ù…Ű§Ù†Ù†ŰŻ APIی و ŰłÙŸŰł ŰČÙ…Ű§Ù†ÛŒ که Ù„Ű­ŰžÙ‡ ÙŸŰ§ŰłŰź Ù‚Ű·Űčی ۮۯی مولفه می ŰȘÙˆŰ§Ù†ŰŻ ŰšÙ‡ ۔۱ۭۧŰȘ ŰŻÙˆŰšŰ§Ű±Ù‡ Ű§Ű±Ű§ŰŠÙ‡ ŰŽÙˆŰŻ. Ù‡Ù†ÚŻŰ§Ù…ÛŒ که ŰšÙ‡ ŰšŰ±ŰźÛŒ ۧŰČ Ű§ŰŹŰČŰ§ÛŒ ŰźŰ§Ű±ŰŹÛŒ که می ŰźÙˆŰ§ŰšÙ†ŰŻ ÙˆŰ§ŰšŰłŰȘه می ŰŽÙˆÙ†ŰŻŰŒ ممکن ۧ۳ŰȘ Ù…ÙÛŒŰŻ ۚۧێۯ [hurry-up the clock](https://jestjs.io/docs/en/timer-mocks). ŰźÙˆŰ§ŰšÛŒŰŻÙ† Ű§Ù„ÚŻÙˆÛŒÛŒ ۧ۳ŰȘ که ŰšŰ§ÛŒŰŻ ۧŰČ ŰąÙ† ۧۏŰȘÙ†Ű§Űš Ú©Ù†ÛŒŰŻ ŰČÛŒŰ±Ű§ ۚۧŰčŰ« می ŰŽÙˆŰŻ ŰąŰČمون ŰŽÙ…Ű§ ŰąÙ‡ŰłŰȘه ÛŒŰ§ ÙŸŰ±ŰźŰ·Ű± ۚۧێۯ (ŰČÙ…Ű§Ù†ÛŒ که ŰšŰ±Ű§ÛŒ Ù…ŰŻŰȘ کوŰȘŰ§Ù‡ÛŒ منŰȘ۞۱ می Ù…Ű§Ù†ÛŒŰŻ). Ù‡Ű± ŰČÙ…Ű§Ù† که ŰźÙˆŰ§Űš و Ù†ŰžŰ±ŰłÙ†ŰŹÛŒ ۧۏŰȘÙ†Ű§Űš Ù†Ű§ÙŸŰ°ÛŒŰ± ۧ۳ŰȘ و هیچ ÙŸŰŽŰȘÛŒŰšŰ§Ù†ÛŒ ۧŰČ Ú†Ű§Ű±Ú†ÙˆŰš ŰȘŰłŰȘ ÙˆŰŹÙˆŰŻ Ù†ŰŻŰ§Ű±ŰŻŰŒ ŰšŰ±ŰźÛŒ ۧŰČ Ú©ŰȘŰ§ŰšŰźŰ§Ù†Ù‡ Ù‡Ű§ÛŒ npm Ù…Ű§Ù†Ù†ŰŻ [wait-for-expect](https://www.npmjs.com/package/wait-for-expect) می ŰȘÙˆŰ§Ù†ŰŻ ۚۧ یک Ű±Ű§Ù‡ Ű­Ù„ نیمه Ù‚Ű·Űčی کمک Ú©Ù†ŰŻ +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** Ù‡Ù†ÚŻŰ§Ù… ŰźÙˆŰ§Űš Ű·ÙˆÙ„Ű§Ù†ÛŒ Ù…ŰŻŰȘی ŰąŰČÙ…Ű§ÛŒŰŽ Ù‡Ű§ یک Ù…Ű±ŰȘŰšÙ‡ Ú©Ù†ŰŻŰȘ۱ ŰźÙˆŰ§Ù‡Ù†ŰŻ ŰšÙˆŰŻ. Ù‡Ù†ÚŻŰ§Ù…ÛŒ که ŰłŰčی می Ú©Ù†ÛŒŰŻ ŰšŰ±Ű§ÛŒ ŰȘŰčۯۧۯ کمی ŰšŰźÙˆŰ§ŰšÛŒŰŻŰŒ ŰČÙ…Ű§Ù†ÛŒ که ÙˆŰ§Ű­ŰŻ ŰȘŰ­ŰȘ ŰąŰČÙ…Ű§ÛŒŰŽ ŰšÙ‡ موقŰč ÙŸŰ§ŰłŰź Ù†ŰŻÙ‡ŰŻŰŒ ŰąŰČÙ…Ű§ÛŒŰŽ ۚۧ ŰŽÚ©ŰłŰȘ Ù…ÙˆŰ§ŰŹÙ‡ می ŰŽÙˆŰŻ. ŰšÙ†Ű§ŰšŰ±Ű§ÛŒÙ† ŰšÙ‡ یک Ù…ŰšŰ§ŰŻÙ„Ù‡ ŰšÛŒÙ† ÙŸÙˆŰłŰȘه ÙŸÙˆŰłŰȘه ŰŽŰŻÙ† و ŰčÙ…Ù„Ú©Ű±ŰŻ ۚۯ ŰźÙ„Ű§Ű”Ù‡ می ŰŽÙˆŰŻ + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :clap: Ű§Ù†ŰŹŰ§Ù… ۯ۱۳ŰȘ Ù…Ű«Ű§Ù„: API E2E که ÙÙ‚Ű· ŰČÙ…Ű§Ù†ÛŒ که ŰčÙ…Ù„ÛŒŰ§ŰȘ Ù‡Ù…ÚŻŰ§Ù…â€ŒŰłŰ§ŰČی Ű§Ù†ŰŹŰ§Ù… ŰŽÙˆŰŻ ŰšŰ±Ű·Ű±Ù Ù…ÛŒâ€ŒŰŽÙˆŰŻ (Cypress) + +![](https://img.shields.io/badge/🔹%20Example%20using%20Cypress-blue.svg "Using Cypress to illustrate the idea") +![](https://img.shields.io/badge/🔧%20Example%20using%20React%20Testing%20Library-blue.svg "Examples with react-testing-library") + +```javascript +// ۧ۳ŰȘÙŰ§ŰŻÙ‡ ÙƒŰ±ŰŻÙ† ۧŰČ Cypress +cy.get("#show-products").click(); // navigate +cy.wait("@products"); // ۔ۚ۱ Ú©Ù†ÛŒŰŻ ŰȘۧ Ù…ŰłÛŒŰ± ŰžŰ§Ù‡Ű± ŰŽÙˆŰŻ +// Ű§ÛŒÙ† ۟۷ ŰȘÙ†Ù‡Ű§ ŰČÙ…Ű§Ù†ÛŒ ۧۏ۱ۧ می ŰŽÙˆŰŻ که Ù…ŰłÛŒŰ± ŰąÙ…Ű§ŰŻÙ‡ ۚۧێۯ +``` + +### :clap: Ű§Ù†ŰŹŰ§Ù… ۯ۱۳ŰȘ Ù…Ű«Ű§Ù„: ŰąŰČÙ…Ű§ÛŒŰŽ Ú©ŰȘŰ§ŰšŰźŰ§Ù†Ù‡ Ű§ÛŒ که منŰȘ۞۱ ŰčÙ†Ű§Ű”Ű± DOM ۧ۳ŰȘ + +```javascript +// @testing-library/dom +test("movie title appears", async () => { + // ŰčÙ†Ű”Ű± ۯ۱ ۧۚŰȘۯۧ ÙˆŰŹÙˆŰŻ Ù†ŰŻŰ§Ű±ŰŻ ... + + //منŰȘ۞۱ ŰžÙ‡ÙˆŰ± ŰšŰ§ŰŽÛŒŰŻ + await wait(() => { + expect(getByText("the lion king")).toBeInTheDocument(); + }); + + // منŰȘ۞۱ ŰžŰ§Ù‡Ű± ŰŽŰŻÙ† ŰšŰ§ŰŽÛŒŰŻ و ŰčÙ†Ű”Ű± ۱ۧ ŰšŰ±ÚŻŰ±ŰŻŰ§Ù†ÛŒŰŻ + const movie = await waitForElement(() => getByText("the lion king")); +}); +``` + +### :thumbsdown: Ù…Ű«Ű§Ù„ ۶ۯ Ű§Ù„ÚŻÙˆ: Ú©ŰŻ ŰźÙˆŰ§Űš ŰłÙŰ§Ű±ŰŽÛŒ + +```javascript +test("movie title appears", async () => { + // ŰčÙ†Ű”Ű± ۯ۱ ۧۚŰȘۯۧ ÙˆŰŹÙˆŰŻ Ù†ŰŻŰ§Ű±ŰŻ ... + + // Ù…Ù†Ű·Ù‚ Ű§Ù†ŰȘ۞ۧ۱ ŰłÙŰ§Ű±ŰŽÛŒ (ۭۧŰȘÛŒŰ§Ű·: ŰłŰ§ŰŻÙ‡ŰŒ ŰšŰŻÙˆÙ† مهلŰȘ ŰČÙ…Ű§Ù†ÛŒ) + const interval = setInterval(() => { + const found = getByText("the lion king"); + if (found) { + clearInterval(interval); + expect(getByText("the lion king")).toBeInTheDocument(); + } + }, 100); + + // منŰȘ۞۱ ŰžŰ§Ù‡Ű± ŰŽŰŻÙ† ŰšŰ§ŰŽÛŒŰŻ و ŰčÙ†Ű”Ű± ۱ۧ ŰšŰ±ÚŻŰ±ŰŻŰ§Ù†ÛŒŰŻ + const movie = await waitForElement(() => getByText("the lion king")); +}); +``` + +
    + +
    + +## âšȘ  3.5 Ù…Ű±Ű§Ù‚Űš Ù†Ű­ÙˆÙ‡ Ű§Ű±Ű§ŰŠÙ‡ Ù…Ű­ŰȘÙˆŰ§ ۧŰČ Ű·Ű±ÛŒÙ‚ ŰŽŰšÚ©Ù‡ ŰšŰ§ŰŽÛŒŰŻ + +![](https://img.shields.io/badge/🔧%20Example%20using%20Google%20LightHouse-blue.svg "Examples with Lighthouse") + +✅ **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** ŰšŰ±ŰźÛŒ ۧŰČ Ù…Ű§Ù†ÛŒŰȘÙˆŰ±Ù‡Ű§ÛŒ فŰčŰ§Ù„ ۱ۧ ۧŰčÙ…Ű§Ù„ Ú©Ù†ÛŒŰŻ که ŰȘŰ¶Ù…ÛŒÙ† Ù…ÛŒâ€ŒÚ©Ù†ŰŻ ŰšŰ§Ű±ÚŻŰ°Ű§Ű±ÛŒ Ű”ÙŰ­Ù‡ ۯ۱ ŰŽŰšÚ©Ù‡ ÙˆŰ§Ù‚Űčی ŰšÙ‡ÛŒÙ†Ù‡ ŰŽŰŻÙ‡ ۧ۳ŰȘ - Ű§ÛŒÙ† ŰŽŰ§Ù…Ù„ Ù‡Ű± ÚŻÙˆÙ†Ù‡ Ù†ÚŻŰ±Ű§Ù†ÛŒ UX Ù…Ű§Ù†Ù†ŰŻ ŰšŰ§Ű±ÚŻŰ°Ű§Ű±ÛŒ Ű”ÙŰ­Ù‡ ŰąÙ‡ŰłŰȘه ÛŒŰ§ ۚ۳ŰȘÙ‡â€ŒŰ§ÛŒ کوچک Ù†ŰŽŰŻÙ‡ Ù…ÛŒâ€ŒŰŽÙˆŰŻ. ۚۧŰČۧ۱ ۧۚŰČۧ۱ ۚۧŰČŰ±ŰłÛŒ کوŰȘŰ§Ù‡ Ù†ÛŒŰłŰȘ: ۧۚŰČŰ§Ű±Ù‡Ű§ÛŒ Ű§ŰłŰ§ŰłÛŒ Ù…Ű§Ù†Ù†ŰŻ [pingdom](https://www.pingdom.com/), AWS CloudWatch, [gcp StackDriver](https://cloud.google.com/monitoring/uptime-checks/) can be easily configured to watch whether the server is alive and response under a reasonable SLA. This only scratches the surface of what might get wrong, hence it's preferable to opt for tools that specialize in frontend (e.g. [lighthouse](https://developers.google.com/web/tools/lighthouse/), [pagespeed](https://developers.google.com/speed/pagespeed/insights/)) and perform richer analysis. The focus should be on symptoms, metrics that directly affect the UX, like page load time, [meaningful paint](https://scotch.io/courses/10-web-performance-audit-tips-for-your-next-billion-users-in-2018/fmp-first-meaningful-paint), [time until the page gets interactive (TTI)](https://calibreapp.com/blog/time-to-interactive/). On top of that, one may also watch for technical causes like ensuring the content is compressed, time to the first byte, optimize images, ensuring reasonable DOM size, SSL and many others. It's advisable to have these rich monitors both during development, as part of the CI and most important - 24x7 over the production's servers/CDN + +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** ŰšŰ§ÛŒŰŻ Ù†Ű§Ű§Ù…ÛŒŰŻ Ú©Ù†Ù†ŰŻÙ‡ ۚۧێۯ که مŰȘÙˆŰŹÙ‡ ŰŽÙˆÛŒŰŻ ÙŸŰł ۧŰČ Ú†Ù†ÛŒÙ† ŰŻÙ‚ŰȘ ŰČÛŒŰ§ŰŻÛŒ ŰšŰ±Ű§ÛŒ Ű§ÛŒŰŹŰ§ŰŻ یک UIی ŰȘŰłŰȘ Ù‡Ű§ÛŒ ŰčÙ…Ù„Ú©Ű±ŰŻÛŒ 100% ÚŻŰ°Ű±Ű§Ù†ŰŻÙ‡ ŰŽŰŻÙ‡ و ۚ۳ŰȘه ŰšÙ†ŰŻÛŒ ÙŸÛŒÚ†ÛŒŰŻÙ‡ - UX ŰšÙ‡ ŰŻÙ„ÛŒÙ„ ÙŸÛŒÚ©Ű±ŰšÙ†ŰŻÛŒ ۧێŰȘŰšŰ§Ù‡ CDN ÙˆŰ­ŰŽŰȘÙ†Ű§Ú© و Ú©Ù†ŰŻ ۧ۳ŰȘ. + +
    + +
    ✏ نمونه Ú©ŰŻ + +### :clap: Ű§Ù†ŰŹŰ§Ù… ۯ۱۳ŰȘ Ù…Ű«Ű§Ù„: ÚŻŰČۧ۱ێ ۚۧŰČŰ±ŰłÛŒ ŰšŰ§Ű±ÚŻŰ°Ű§Ű±ÛŒ Ű”ÙŰ­Ù‡ ÙŰ§Ù†ÙˆŰł ŰŻŰ±ÛŒŰ§ÛŒÛŒ + +![](/assets/lighthouse2.png "Lighthouse page load inspection report") + +
    + +
    + +## âšȘ  3.6 Ù…Ù†Ű§ŰšŰč ۶Űčیف و Ú©Ù†ŰŻÛŒ Ù…Ű§Ù†Ù†ŰŻ APIÙ‡Ű§ÛŒ Backend ۱ۧ ŰŹÙ…Űč ŰąÙˆŰ±ÛŒ Ú©Ù†ÛŒŰŻ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** Ù‡Ù†ÚŻŰ§Ù… Ú©ŰŻÙ†ÙˆÛŒŰłÛŒ ŰȘŰłŰȘâ€ŒÙ‡Ű§ÛŒ Ű§Ű”Ù„ÛŒ ŰźÙˆŰŻ (نه ŰȘŰłŰȘâ€ŒÙ‡Ű§ÛŒ E2E)ی ۧŰČ ŰŻŰ±ÚŻÛŒŰ± Ú©Ű±ŰŻÙ† Ù‡Ű± Ù…Ù†ŰšŰčی که ۟ۧ۱ۏ ۧŰČ Ù…ŰłŰŠÙˆÙ„ÛŒŰȘ و کنŰȘŰ±Ù„ ŰŽÙ…Ű§ŰłŰȘ Ù…Ű§Ù†Ù†ŰŻ API ÙŸŰŽŰȘÛŒŰšŰ§Ù† ŰźÙˆŰŻŰŻŰ§Ű±ÛŒ Ú©Ù†ÛŒŰŻ و ŰšÙ‡ ŰŹŰ§ÛŒ ŰąÙ† ۧŰČ ŰźŰ±ŰŻÙ‡â€ŒÙ‡Ű§ ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ú©Ù†ÛŒŰŻ (یŰčنی ŰȘŰłŰȘ ŰŻÙˆŰšŰ§Ű±). ŰčÙ…Ù„Ű§ŰŒ ŰšÙ‡ ŰŹŰ§ÛŒ ŰȘÙ…Ű§Űł Ù‡Ű§ÛŒ ŰŽŰšÚ©Ù‡ ÙˆŰ§Ù‚Űčی ۚۧ API Ù‡Ű§ŰŒ ۧŰČ Ú†Ù†ŰŻ Ú©ŰȘŰ§ŰšŰźŰ§Ù†Ù‡ ŰŻÙˆŰȘŰ§ÛŒÛŒ (Ù…Ű§Ù†Ù†ŰŻ [Sinon](https://sinonjs.org/), [Test doubles](https://www.npmjs.com/package/testdouble), etc) ŰšŰ±Ű§ÛŒ کلفŰȘ Ú©Ű±ŰŻÙ† ÙŸŰ§ŰłŰź API. مŰČیŰȘ Ű§Ű”Ù„ÛŒ ŰŹÙ„ÙˆÚŻÛŒŰ±ÛŒ ۧŰČ ÙŸÙˆŰłŰȘه ÙŸÙˆŰłŰȘه ŰŽŰŻÙ† ۧ۳ŰȘ - ŰąŰČÙ…Ű§ÛŒŰŽ ÛŒŰ§ Ù…Ű±Ű­Ù„Ù‡â€ŒŰšÙ†ŰŻÛŒ APIÙ‡Ű§ Ű·ŰšÙ‚ ŰȘŰčŰ±ÛŒÙ Ú†Ù†ŰŻŰ§Ù† ÙŸŰ§ÛŒŰŻŰ§Ű± Ù†ÛŒŰłŰȘÙ†ŰŻ و Ù‡Ű± ۧŰČ Ú†Ù†ŰŻ ÚŻŰ§Ù‡ÛŒ ۯ۱ ŰȘŰłŰȘâ€ŒÙ‡Ű§ÛŒ ŰŽÙ…Ű§ ŰŽÚ©ŰłŰȘ Ù…ÛŒâ€ŒŰźÙˆŰ±Ù†ŰŻŰŒ Ű§ÚŻŰ±Ú†Ù‡ Ù…Ű€Ù„ÙÙ‡ ŰŽÙ…Ű§ ŰšÙ‡ ŰźÙˆŰšÛŒ Ű±ÙŰȘۧ۱ Ù…ÛŒâ€ŒÚ©Ù†ŰŻ (Ű§Ù†ÙˆŰ§Űč ŰȘÙˆÙ„ÛŒŰŻ ŰšŰ±Ű§ÛŒ ŰąŰČÙ…Ű§ÛŒŰŽ ۯ۱ Ù†ŰžŰ± ÚŻŰ±ÙŰȘه Ù†ŰŽŰŻÙ‡ ۧ۳ŰȘ و مŰčÙ…ÙˆÙ„Ű§Ù‹ ŰŻŰ±ŰźÙˆŰ§ŰłŰȘâ€ŒÙ‡Ű§ ۱ۧ Ú©Ű§Ù‡ŰŽ Ù…ÛŒâ€ŒŰŻÙ‡ŰŻ). Ű§Ù†ŰŹŰ§Ù… Ű§ÛŒÙ† کۧ۱ ŰšÙ‡ ŰŽŰšÛŒÙ‡â€ŒŰłŰ§ŰČی Ű±ÙŰȘŰ§Ű±Ù‡Ű§ÛŒ API Ù…ŰźŰȘلف ۧۏۧŰČه Ù…ÛŒâ€ŒŰŻÙ‡ŰŻ که Ű±ÙŰȘۧ۱ Ù…Ű€Ù„ÙÙ‡ ŰŽÙ…Ű§ ۱ۧ Ù…Ű§Ù†Ù†ŰŻ ŰČÙ…Ű§Ù†ÛŒ که هیچ ŰŻŰ§ŰŻÙ‡â€ŒŰ§ÛŒ ÙŸÛŒŰŻŰ§ Ù†Ù…ÛŒâ€ŒŰŽÙˆŰŻ ÛŒŰ§ ŰČÙ…Ű§Ù†ÛŒ که API ۟۷ۧ Ù…ÛŒâ€ŒŰŻÙ‡ŰŻŰŒ Ù‡ŰŻŰ§ÛŒŰȘ Ú©Ù†ŰŻ. ŰąŰźŰ±ÛŒÙ† Ű§Ù…Ű§ نه Ú©Ù…â€ŒŰ§Ù‡Ù…ÛŒŰȘی ŰȘÙ…Ű§Űłâ€ŒÙ‡Ű§ÛŒ ŰŽŰšÚ©Ù‡ ۳۱ŰčŰȘ ŰąŰČÙ…Ű§ÛŒŰŽâ€ŒÙ‡Ű§ ۱ۧ ŰšŰłÛŒŰ§Ű± Ú©Ù†ŰŻ Ù…ÛŒâ€ŒÚ©Ù†ŰŻ + +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** Ù…ÛŒŰ§Ù†ÚŻÛŒÙ† ŰȘŰłŰȘ ŰšÛŒŰŽŰȘ۱ ۧŰČ Ú†Ù†ŰŻ میلی Ű«Ű§Ù†ÛŒÙ‡ ۧۏ۱ۧ نمی ŰŽÙˆŰŻŰŒ یک ŰȘÙ…Ű§Űł API مŰčمولی 100 میلی Ű«Ű§Ù†ÛŒÙ‡ Ű·ÙˆÙ„ می Ú©ŰŽŰŻ>ی Ű§ÛŒÙ† ۚۧŰčŰ« می ŰŽÙˆŰŻ Ù‡Ű± ŰȘŰłŰȘ 20 ۚ۱ۧۚ۱ Ú©Ù†ŰŻŰȘ۱ ŰŽÙˆŰŻ. + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :clap: Ű§Ù†ŰŹŰ§Ù… ۯ۱۳ŰȘ Ù…Ű«Ű§Ù„: Ù‚Ű·Űč Ú©Ű±ŰŻÙ† ÛŒŰ§ Ű±Ù‡ÚŻÛŒŰ±ÛŒ ŰȘÙ…Ű§Űłâ€ŒÙ‡Ű§ÛŒ API + +![](https://img.shields.io/badge/🔧%20Example%20using%20React-blue.svg "Examples with React") ![](https://img.shields.io/badge/🔧%20Example%20using%20React%20Testing%20Library-blue.svg "Examples with react-testing-library") + +```javascript +// ÙˆŰ§Ű­ŰŻ ۯ۱ Ű­Ű§Ù„ ŰąŰČÙ…Ű§ÛŒŰŽ +export default function ProductsList() { + const [products, setProducts] = useState(false); + + const fetchProducts = async () => { + const products = await axios.get("api/products"); + setProducts(products); + }; + + useEffect(() => { + fetchProducts(); + }, []); + + return products ?
    {products}
    :
    No products
    ; +} + +// ŰȘŰłŰȘ Ú©Ű±ŰŻÙ† +test("When no products exist, show the appropriate message", () => { + // Ù…Ù‚ŰŻŰ§Ű± ŰŻÙ‡ÛŒ Ú©Ű±ŰŻÙ† + nock("api") + .get(`/products`) + .reply(404); + + // ۧۏ۱ۧ Ú©Ű±ŰŻÙ† + const { getByTestId } = render(); + + // Ù…Ù‚Ű§ÛŒŰłÙ‡ Ú©Ű±ŰŻÙ† + expect(getByTestId("no-products-message")).toBeTruthy(); +}); +``` + +
    + +
    + +## âšȘ  3.7 ŰȘŰčۯۧۯ ŰšŰłÛŒŰ§Ű± کمی ۧŰČ ŰȘŰłŰȘ Ù‡Ű§ÛŒ ۳۱ŰȘŰ§ŰłŰ±ÛŒ که کل ŰłÛŒŰłŰȘم ۱ۧ ۯ۱ ۚ۱ می ÚŻÛŒŰ±ŰŻ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** Ű§ÚŻŰ±Ú†Ù‡ E2E (ÙŸŰ§ÛŒŰ§Ù† ŰšÙ‡ Ű§Ù†ŰȘÙ‡Ű§) مŰčÙ…ÙˆÙ„Ű§Ù‹ ŰšÙ‡ مŰčÙ†Ű§ÛŒ ŰąŰČÙ…Ű§ÛŒŰŽ ÙÙ‚Ű· UI ۚۧ یک Ù…Ű±ÙˆŰ±ÚŻŰ± ÙˆŰ§Ù‚Űčی ۧ۳ŰȘ (Ù†ÚŻŰ§Ù‡ Ú©Ù†ÛŒŰŻ ŰšÙ‡ [bullet 3.6](https://github.com/goldbergyoni/javascript-testing-best-practices#-%EF%B8%8F-36-stub-flaky-and-slow-resources-like-backend-apis)), ŰšŰ±Ű§ÛŒ ŰŻÛŒÚŻŰ± ŰąÙ†Ù‡Ű§ ŰšÙ‡ مŰčÙ†Ű§ÛŒ ŰąŰČÙ…Ű§ÛŒŰŽ Ù‡Ű§ÛŒÛŒ Ù‡ŰłŰȘÙ†ŰŻ که کل ŰłÛŒŰłŰȘم ۧŰČ ŰŹÙ…Ù„Ù‡ ŰšŰ§Ű·Ù† ÙˆŰ§Ù‚Űčی ۱ۧ ÚŻŰłŰȘ۱ێ می ŰŻÙ‡Ù†ŰŻ. ŰȘŰłŰȘâ€ŒÙ‡Ű§ÛŒ نوŰč ŰŻÙˆÙ… ŰšŰłÛŒŰ§Ű± ۧ۱ŰČŰŽÙ…Ù†ŰŻ Ù‡ŰłŰȘÙ†ŰŻŰŒ ŰČÛŒŰ±Ű§ Ű§ŰŽÚ©Ű§Ù„Ű§ŰȘ ÛŒÚ©ÙŸŰ§Ű±Ú†Ù‡â€ŒŰłŰ§ŰČی ŰšÛŒÙ† frontend و backend ۱ۧ ÙŸÙˆŰŽŰŽ Ù…ÛŒâ€ŒŰŻÙ‡Ù†ŰŻ که ممکن ۧ۳ŰȘ ŰšÙ‡ ŰŻÙ„ÛŒÙ„ ۯ۱ک ۧێŰȘŰšŰ§Ù‡ ۧŰČ Ű·Ű±Ű­ Ù…ŰšŰ§ŰŻÙ„Ù‡ ۱۟ ŰŻÙ‡ŰŻ. ŰąÙ†Ù‡Ű§ همچنین یک Ű±ÙˆŰŽ Ú©Ű§Ű±ŰąÙ…ŰŻ ŰšŰ±Ű§ÛŒ Ú©ŰŽÙ Ù…ŰłŰ§ŰŠÙ„ ÛŒÚ©ÙŸŰ§Ű±Ú†Ù‡ ۳ۧŰČی backend-to-backend Ù‡ŰłŰȘÙ†ŰŻ (Ù…Ű«Ù„Ű§Ù‹ Microservice A ÙŸÛŒŰ§Ù… ۧێŰȘŰšŰ§Ù‡ÛŒ ۱ۧ ŰšÙ‡ Microservice B Ű§Ű±ŰłŰ§Ù„ می Ú©Ù†ŰŻ) و Ű­ŰȘی ŰšŰ±Ű§ÛŒ ŰȘŰŽŰźÛŒŰ” ŰźŰ±Ű§ŰšÛŒ Ù‡Ű§ÛŒ ۧ۳ŰȘÙ‚Ű±Ű§Ű± - هیچ Ú†Ű§Ű±Ú†ÙˆŰš Backend ŰšŰ±Ű§ÛŒ ŰąŰČÙ…Ű§ÛŒŰŽ E2E ÙˆŰŹÙˆŰŻ Ù†ŰŻŰ§Ű±ŰŻ که ŰšÙ‡ Ű§Ù†ŰŻŰ§ŰČه UI ŰŻÙˆŰłŰȘŰ§Ù†Ù‡ و ŰšŰ§Ù„Űș ۚۧێۯ. Ú†Ű§Ű±Ú†ÙˆŰš Ù‡Ű§ÛŒÛŒ Ù…Ű§Ù†Ù†ŰŻ[Cypress](https://www.cypress.io/) و [Puppeteer](https://github.com/GoogleChrome/puppeteer). Ù†Ù‚Ű·Ù‡ ۶Űčف چنین ŰąŰČÙ…Ű§ÛŒŰŽâ€ŒÙ‡Ű§ÛŒÛŒ هŰČینه ŰšŰ§Ù„Ű§ÛŒ ÙŸÛŒÚ©Ű±ŰšÙ†ŰŻÛŒ یک Ù…Ű­ÛŒŰ· ۚۧ ۧۏŰČŰ§ÛŒ ŰšŰłÛŒŰ§Ű± ŰČÛŒŰ§ŰŻ و ŰšÛŒŰŽŰȘ۱ ŰŽÚ©Ù†Ù†ŰŻÚŻÛŒ ŰąÙ†Ù‡Ű§ ۧ۳ŰȘ - ۚۧ ŰȘÙˆŰŹÙ‡ ŰšÙ‡ 50 Ù…ÛŒÚ©Ű±ÙˆŰłŰ±ÙˆÛŒŰłŰŒ Ű­ŰȘی ۧگ۱ یکی ۧŰČ ŰąÙ†Ù‡Ű§ Ù†Ű§Ù…ÙˆÙÙ‚ ۹ۧۮۯی کل E2E ÙÙ‚Ű· ۧŰČ Ú©Ű§Ű± Ű§ÙŰȘŰ§ŰŻÙ‡ ۧ۳ŰȘ. ŰšÙ‡ همین ŰŻÙ„ÛŒÙ„ŰŒ Ù…Ű§ ŰšŰ§ÛŒŰŻ ۧŰČ Ű§ÛŒÙ† ŰȘکنیک کم ۧ۳ŰȘÙŰ§ŰŻÙ‡ کنیم و ۭۧŰȘÙ…Ű§Ù„Ű§Ù‹ 1-10 Ù…ÙˆŰ±ŰŻ ۧŰČ ŰąÙ†â€ŒÙ‡Ű§ ۱ۧ ۯۧێŰȘه ŰšŰ§ŰŽÛŒÙ… و نه ŰšÛŒŰŽŰȘ۱. ÚŻÙŰȘه Ù…ÛŒâ€ŒŰŽÙˆŰŻŰŒ Ű­ŰȘی ŰȘŰčۯۧۯ کمی ۧŰČ ŰȘŰłŰȘâ€ŒÙ‡Ű§ÛŒ E2E ۭۧŰȘÙ…Ű§Ù„Ű§Ù‹ نوŰč Ù…ŰŽÚ©Ù„Ű§ŰȘی ۱ۧ که ŰšŰ±Ű§ÛŒ ŰąÙ†Ù‡Ű§ Ù‡ŰŻÙ Ù‚Ű±Ű§Ű± ÚŻŰ±ÙŰȘÙ‡â€ŒŰ§Ù†ŰŻ - ŰźŰ·Ű§Ù‡Ű§ÛŒ ۧ۳ŰȘÙ‚Ű±Ű§Ű± و ÛŒÚ©ÙŸŰ§Ű±Ú†Ù‡â€ŒŰłŰ§ŰČی ۱ۧ ŰŽÙ†Ű§ŰłŰ§ÛŒÛŒ Ù…ÛŒâ€ŒÚ©Ù†Ù†ŰŻ. ŰȘÙˆŰ”ÛŒÙ‡ Ù…ÛŒâ€ŒŰŽÙˆŰŻ ŰąÙ†â€ŒÙ‡Ű§ ۱ۧ Ű±ÙˆÛŒ یک Ù…Ű­ÛŒŰ· Ű”Ű­Ù†Ù‡â€ŒŰłŰ§ŰČی ŰŽŰšÛŒÙ‡ ŰȘÙˆÙ„ÛŒŰŻ ۧۏ۱ۧ Ú©Ù†ÛŒŰŻ + +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** UI ممکن ۧ۳ŰȘ ŰšŰ±Ű§ÛŒ ŰąŰČÙ…Ű§ÛŒŰŽ ŰčÙ…Ù„Ú©Ű±ŰŻ ŰźÙˆŰŻ ŰłŰ±Ù…Ű§ÛŒÙ‡ ÚŻŰ°Ű§Ű±ÛŒ ŰČÛŒŰ§ŰŻÛŒ Ú©Ù†ŰŻ ŰȘۧ ŰŻÛŒŰ±ŰȘ۱ مŰȘÙˆŰŹÙ‡ ŰŽÙˆŰŻ که ۚۧ۱ ۚۧŰČÚŻŰŽŰȘی ۚۧŰČÚŻŰŽŰȘی (ŰŽÙ…Ű§ÛŒÙ‡ ŰŻŰ§ŰŻÙ‡ Ű§ÛŒ که UI ŰšŰ§ÛŒŰŻ ۚۧ ŰąÙ† کۧ۱ Ú©Ù†ŰŻ) ŰšŰłÛŒŰ§Ű± مŰȘÙŰ§ÙˆŰȘ ۧŰČ ŰąÙ†Ú†Ù‡ Ű§Ù†ŰȘ۞ۧ۱ می Ű±ÙˆŰŻ ۧ۳ŰȘ. + +
    + +## âšȘ  3.8 ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ù…ŰŹŰŻŰŻ ۧŰČ Ű§ŰčŰȘŰšŰ§Ű±Ù†Ű§Ù…Ù‡ ÙˆŰ±ÙˆŰŻ ŰšÙ‡ ŰłÛŒŰłŰȘÙ…ŰŒ ŰȘŰłŰȘ Ù‡Ű§ÛŒ E2E ۱ۧ Ű§ÙŰČŰ§ÛŒŰŽ ŰŻÙ‡ÛŒŰŻ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** ۯ۱ ŰȘŰłŰȘâ€ŒÙ‡Ű§ÛŒ E2E که ŰŽŰ§Ù…Ù„ یک ŰšŰ§Ű·Ù† ÙˆŰ§Ù‚Űčی ۧ۳ŰȘ و ŰšŰ±Ű§ÛŒ ÙŰ±Ű§ŰźÙˆŰ§Ù†ÛŒâ€ŒÙ‡Ű§ÛŒ API ŰšÙ‡ یک ŰȘوکن کۧ۱ۚ۱ مŰčŰȘۚ۱ مŰȘکی ۧ۳ŰȘی ۏۯۧ۳ۧŰČی ŰąŰČمون ۯ۱ ŰłŰ·Ű­ÛŒ که کۧ۱ۚ۱ Ű§ÛŒŰŹŰ§ŰŻ ŰŽŰŻÙ‡ و ۯ۱ Ù‡Ű± ŰŻŰ±ŰźÙˆŰ§ŰłŰȘی ŰšÙ‡ ŰłÛŒŰłŰȘم ÙˆŰ§Ű±ŰŻ ŰŽÙˆŰŻŰŒ ŰłÙˆŰŻÛŒ Ù†ŰŻŰ§Ű±ŰŻ. ۯ۱ ŰčÙˆŰ¶ŰŒ Ù‚ŰšÙ„ ۧŰČ ŰŽŰ±ÙˆŰč Ű§ŰŹŰ±Ű§ÛŒ ŰąŰČÙ…Ű§ÛŒŰŽâ€ŒÙ‡Ű§ ÙÙ‚Ű· یک ۚۧ۱ ÙˆŰ§Ű±ŰŻ ŰŽÙˆÛŒŰŻ (یŰčنی Ù‚ŰšÙ„ ۧŰČ Ù‡Ù…Ù‡ Ù‚Ù„Ű§Űš)ی ŰȘوکن ۱ۧ ۯ۱ ŰšŰ±ŰźÛŒ ۧŰČ Ű­Ű§ÙŰžÙ‡â€ŒÙ‡Ű§ÛŒ Ù…Ű­Ù„ÛŒ Ű°ŰźÛŒŰ±Ù‡ Ú©Ù†ÛŒŰŻ و ۯ۱ ŰŻŰ±ŰźÙˆŰ§ŰłŰȘâ€ŒÙ‡Ű§ Ù…ŰŹŰŻŰŻŰ§Ù‹ ۧŰČ ŰąÙ† ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ú©Ù†ÛŒŰŻ. ŰšÙ‡ Ù†ŰžŰ± می ۱۳ۯ که Ű§ÛŒÙ† یکی ۧŰČ Ű§Ű”ÙˆÙ„ Ű§Ű”Ù„ÛŒ ŰąŰČÙ…Ű§ÛŒŰŽ ۱ۧ Ù†Ù‚Ű¶ می Ú©Ù†ŰŻ - ŰȘŰłŰȘ ۱ۧ ŰšŰŻÙˆÙ† ŰŹÙŰȘ ŰŽŰŻÙ† Ù…Ù†Ű§ŰšŰč Ù…ŰłŰȘقل Ù†ÚŻÙ‡ ŰŻŰ§Ű±ÛŒŰŻ. ۯ۱ Ű­Ű§Ù„ÛŒ که Ű§ÛŒÙ† یک Ù†ÚŻŰ±Ű§Ù†ÛŒ مŰčŰȘۚ۱ ۧ۳ŰȘی ŰčÙ…Ù„Ú©Ű±ŰŻ ۯ۱ ŰȘŰłŰȘâ€ŒÙ‡Ű§ÛŒ E2E یک Ù†ÚŻŰ±Ű§Ù†ÛŒ Ú©Ù„ÛŒŰŻÛŒ ۧ۳ŰȘ و Ű§ÛŒŰŹŰ§ŰŻ 1-3 ŰŻŰ±ŰźÙˆŰ§ŰłŰȘ API Ù‚ŰšÙ„ ۧŰČ ŰŽŰ±ÙˆŰč Ù‡Ű± ŰąŰČÙ…Ű§ÛŒŰŽ ممکن ۧ۳ŰȘ Ù…Ù†ŰŹŰ± ŰšÙ‡ ŰČÙ…Ű§Ù† Ű§ŰŹŰ±Ű§ÛŒ ÙˆŰ­ŰŽŰȘÙ†Ű§Ú© ŰŽÙˆŰŻ. ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ù…ŰŹŰŻŰŻ ۧŰČ Ű§ŰčŰȘŰšŰ§Ű±Ù†Ű§Ù…Ù‡â€ŒÙ‡Ű§ ŰšÙ‡ Ű§ÛŒÙ† مŰčنی Ù†ÛŒŰłŰȘ که ŰąŰČÙ…Ű§ÛŒŰŽâ€ŒÙ‡Ű§ ŰšŰ§ÛŒŰŻ Ű±ÙˆÛŒ ŰłÙˆŰ§ŰšÙ‚ کۧ۱ۚ۱ ÛŒÚ©ŰłŰ§Ù†ÛŒ Űčمل Ú©Ù†Ù†ŰŻ - ۧگ۱ ŰšÙ‡ ŰłÙˆŰ§ŰšÙ‚ کۧ۱ۚ۱ (Ù…Ű«Ù„Ű§Ù‹ ŰłŰ§ŰšÙ‚Ù‡ ÙŸŰ±ŰŻŰ§ŰźŰȘâ€ŒÙ‡Ű§ÛŒ کۧ۱ۚ۱ ŰąŰČÙ…Ű§ÛŒŰŽÛŒ) ŰȘکیه Ù…ÛŒâ€ŒÚ©Ù†Ù†ŰŻŰŒ Ù…Ű·Ù…ŰŠÙ† ŰŽÙˆÛŒŰŻ که ŰąÙ† ŰłÙˆŰ§ŰšÙ‚ ۱ۧ ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† ŰšŰźŰŽÛŒ ۧŰČ ŰąŰČÙ…Ű§ÛŒŰŽ Ű§ÛŒŰŹŰ§ŰŻ Ú©Ű±ŰŻÙ‡â€ŒŰ§ÛŒŰŻ و ۧŰČ Ű§ŰŽŰȘŰ±Ű§Ú©â€ŒÚŻŰ°Ű§Ű±ÛŒ ÙˆŰŹÙˆŰŻ ŰąÙ†Ù‡Ű§ ۚۧ ŰłŰ§ÛŒŰ± ŰąŰČÙ…Ű§ÛŒŰŽâ€ŒÙ‡Ű§ ۧۏŰȘÙ†Ű§Űš Ú©Ù†ÛŒŰŻ. همچنین ŰšÙ‡ ÛŒŰ§ŰŻ ۯۧێŰȘه ŰšŰ§ŰŽÛŒŰŻ که Backend می‌ŰȘÙˆŰ§Ù†ŰŻ ŰŹŰčلی ۚۧێۯ - ۧگ۱ ŰąŰČÙ…Ű§ÛŒŰŽâ€ŒÙ‡Ű§ÛŒ ŰŽÙ…Ű§ ۚ۱ Ű±ÙˆÛŒ frontend مŰȘÙ…Ű±Ú©ŰČ ŰŽŰŻÙ‡â€ŒŰ§Ù†ŰŻŰŒ ŰšÙ‡ŰȘ۱ ۧ۳ŰȘ ŰąÙ† ۱ۧ Ű§ÛŒŰČوله Ú©Ù†ÛŒŰŻ و API ŰšŰ§Ű·Ù† ۱ۧ ۟۱ۯ Ú©Ù†ÛŒŰŻ (Ù†ÚŻŰ§Ù‡ Ú©Ù†ÛŒŰŻ ŰšÙ‡ [bullet 3.6](https://github.com/goldbergyoni/javascript-testing-best-practices#-%EF%B8%8F-36-stub-flaky-and-slow-resources-like-backend-apis)). + +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** ۚۧ ŰȘÙˆŰŹÙ‡ ŰšÙ‡ 200 Ù…ÙˆŰ±ŰŻ ŰȘŰłŰȘ و ۚۧ ÙŰ±Ű¶ ÙˆŰ±ÙˆŰŻ = 100ms = 20 Ű«Ű§Ù†ÛŒÙ‡ ÙÙ‚Ű· ŰšŰ±Ű§ÛŒ ÙˆŰ±ÙˆŰŻ Ù…ŰŹŰŻŰŻ و ŰŻÙˆŰšŰ§Ű±Ù‡ + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :clap: Ű§Ù†ŰŹŰ§Ù… ۯ۱۳ŰȘ ŰąÙ† Ù…Ű«Ű§Ù„: Ù‚ŰšÙ„ ۧŰČ Ù‡Ù…Ù‡ ÙˆŰ§Ű±ŰŻ ŰŽÙˆÛŒŰŻ و نه Ù‚ŰšÙ„ ۧŰČ Ù‡Ű± Ú©ŰŻŰ§Ù… + +![](https://img.shields.io/badge/🔹%20Example%20using%20Cypress-blue.svg "Using Cypress to illustrate the idea") + +```javascript +let authenticationToken; + +// Ù‚ŰšÙ„ ۧŰČ Ű§ŰŹŰ±Ű§ÛŒ همه ŰąŰČÙ…Ű§ÛŒŰŽŰ§ŰȘ ۧŰȘÙŰ§Ù‚ می Ű§ÙŰȘŰŻ +before(() => { + cy.request('POST', 'http://localhost:3000/login', { + username: Cypress.env('username'), + password: Cypress.env('password'), + }) + .its('body') + .then((responseFromLogin) => { + authenticationToken = responseFromLogin.token; + }) +}) + +// Ù‚ŰšÙ„ ۧŰČ Ù‡Ű± ŰąŰČمون ۧŰȘÙŰ§Ù‚ می Ű§ÙŰȘŰŻ +beforeEach(setUser => { + cy.visit('/home', () => { + onBeforeLoad (win => { + win.localStorage.setItem('token', JSON.stringify(authenticationToken)) + }) + }) +}) +``` + +
    + +
    + +## âšȘ  3.9 یک ŰȘŰłŰȘ ŰŻÙˆŰŻ E2E ۯۧێŰȘه ŰšŰ§ŰŽÛŒŰŻ که ÙÙ‚Ű· ۯ۱ ۳۱ۧ۳۱ Ù†Ù‚ŰŽÙ‡ ŰłŰ§ÛŒŰȘ ۭ۱کŰȘ می Ú©Ù†ŰŻ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** ŰšŰ±Ű§ÛŒ Ù†ŰžŰ§Ű±ŰȘ ۚ۱ ŰȘÙˆÙ„ÛŒŰŻ و ŰšŰ±Ű±ŰłÛŒ ŰłÙ„Ű§Ù…ŰȘ ŰČÙ…Ű§Ù† ŰȘÙˆŰłŰčÙ‡ŰŒ یک ŰȘŰłŰȘ E2E ۱ۧ ۧۏ۱ۧ Ú©Ù†ÛŒŰŻ که ۧŰČ Ù‡Ù…Ù‡/ŰšÛŒŰŽŰȘ۱ Ű”ÙŰ­Ű§ŰȘ ŰłŰ§ÛŒŰȘ ۚۧŰČŰŻÛŒŰŻ می Ú©Ù†ŰŻ و Ù…Ű·Ù…ŰŠÙ† می ŰŽÙˆŰŻ که هیچ Ú©Űł ۟۱ۧۚ نمی ŰŽÙˆŰŻ. Ű§ÛŒÙ† نوŰč ŰȘŰłŰȘ ۚۧŰČÚŻŰŽŰȘ ŰłŰ±Ù…Ű§ÛŒÙ‡ ŰČÛŒŰ§ŰŻÛŒ ۱ۧ ŰšÙ‡ Ű§Ű±Ù…ŰșŰ§Ù† می ŰąÙˆŰ±ŰŻ ŰČÛŒŰ±Ű§ Ù†ÙˆŰŽŰȘن و Ù†ÚŻÙ‡ŰŻŰ§Ű±ÛŒ ŰąÙ† ŰšŰłÛŒŰ§Ű± ŰąŰłŰ§Ù† ۧ۳ŰȘی Ű§Ù…Ű§ می ŰȘÙˆŰ§Ù†ŰŻ Ù‡Ű± نوŰč ŰźŰ±Ű§ŰšÛŒ ۧŰČ ŰŹÙ…Ù„Ù‡ Ù…ŰŽÚ©Ù„Ű§ŰȘ ŰčÙ…Ù„Ú©Ű±ŰŻÛŒŰŒ ŰŽŰšÚ©Ù‡ و ۧ۳ŰȘÙ‚Ű±Ű§Ű± ۱ۧ ŰȘŰŽŰźÛŒŰ” ŰŻÙ‡ŰŻ. ŰłŰ§ÛŒŰ± ŰłŰšÚ©â€ŒÙ‡Ű§ÛŒ ŰšŰ±Ű±ŰłÛŒ ŰŻÙˆŰŻ و ŰłÙ„Ű§Ù…ŰȘ Űčقل Ú†Ù†ŰŻŰ§Ù† Ù‚Ű§ŰšÙ„ ۧŰčŰȘÙ…Ű§ŰŻ و ŰŹŰ§Ù…Űč Ù†ÛŒŰłŰȘÙ†ŰŻ - ŰšŰ±ŰźÛŒ ۧŰČ ŰȘÛŒÙ…â€ŒÙ‡Ű§ÛŒ ŰčÙ…Ù„ÛŒŰ§ŰȘی ÙÙ‚Ű· Ű”ÙŰ­Ù‡ Ű§Ű”Ù„ÛŒ (ŰȘÙˆÙ„ÛŒŰŻ) ۱ۧ ÙŸÛŒÙ†ÚŻ Ù…ÛŒâ€ŒÚ©Ù†Ù†ŰŻ ÛŒŰ§ ŰȘÙˆŰłŰčÙ‡â€ŒŰŻÙ‡Ù†ŰŻÚŻŰ§Ù†ÛŒ که ŰȘŰłŰȘâ€ŒÙ‡Ű§ÛŒ ۧۯŰșŰ§Ù… ŰČÛŒŰ§ŰŻÛŒ ۱ۧ Ű§Ù†ŰŹŰ§Ù… Ù…ÛŒâ€ŒŰŻÙ‡Ù†ŰŻ که Ù…ŰŽÚ©Ù„Ű§ŰȘ ۚ۳ŰȘÙ‡â€ŒŰšÙ†ŰŻÛŒ و Ù…Ű±ÙˆŰ±ÚŻŰ± ۱ۧ Ú©ŰŽÙ Ù†Ù…ÛŒâ€ŒÚ©Ù†Ù†ŰŻ. Ù†Ű§ÚŻÙŰȘه Ù†Ù…Ű§Ù†ŰŻ که ŰȘŰłŰȘ ŰŻÙˆŰŻ ŰŹŰ§ÛŒÚŻŰČین ŰȘŰłŰȘ Ù‡Ű§ÛŒ ŰčÙ…Ù„Ú©Ű±ŰŻÛŒ نمی ŰŽÙˆŰŻŰŒ ŰšÙ„Ú©Ù‡ ÙÙ‚Ű· ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† یک ۹ێکۧ۱۳ۧŰČ ŰłŰ±ÛŒŰč ŰŻÙˆŰŻ Űčمل می Ú©Ù†ŰŻ. + +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** ممکن ۧ۳ŰȘ همه چیŰČ ŰčŰ§Ù„ÛŒ ŰšÙ‡ Ù†ŰžŰ± ŰšŰ±ŰłŰŻŰŒ همه ŰąŰČÙ…Ű§ÛŒŰŽâ€ŒÙ‡Ű§ ۚۧ موفقیŰȘ Ű§Ù†ŰŹŰ§Ù… Ù…ÛŒâ€ŒŰŽÙˆÙ†ŰŻŰŒ ŰšŰ±Ű±ŰłÛŒ ŰłÙ„Ű§Ù…ŰȘ ŰȘÙˆÙ„ÛŒŰŻ نیŰČ Ù…Ű«ŰšŰȘ ۧ۳ŰȘی Ű§Ù…Ű§ Ù…Ű€Ù„ÙÙ‡ ÙŸŰ±ŰŻŰ§ŰźŰȘ Ù…ŰŽÚ©Ù„ÛŒ ۯ۱ ۚ۳ŰȘÙ‡â€ŒŰšÙ†ŰŻÛŒ ۯۧێŰȘ و ÙÙ‚Ű· Ù…ŰłÛŒŰ± ÙŸŰ±ŰŻŰ§ŰźŰȘ / Ű±Ù†ŰŻŰ± Ù†Ù…ÛŒâ€ŒŰŽÙˆŰŻ. + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :clap: Ű§Ù†ŰŹŰ§Ù… ۯ۱۳ŰȘ Ù…Ű«Ű§Ù„: smoke ۯ۱ ŰȘÙ…Ű§Ù… Ű”ÙŰ­Ű§ŰȘ ۭ۱کŰȘ می Ú©Ù†ŰŻ + +![](https://img.shields.io/badge/🔹%20Example%20using%20Cypress-blue.svg "Using Cypress to illustrate the idea") + +```javascript +it("Ù‡Ù†ÚŻŰ§Ù… Ű§Ù†ŰŹŰ§Ù… ŰȘŰłŰȘ smoke ۯ۱ ŰȘÙ…Ű§Ù… Ű”ÙŰ­Ű§ŰȘی ŰšŰ§ÛŒŰŻ همه ŰąÙ†Ù‡Ű§ ۱ۧ ۚۧ موفقیŰȘ ŰšŰ§Ű±ÚŻÛŒŰ±ÛŒ Ú©Ù†ÛŒŰŻ", () => { + // نمونه Ű§ÛŒ ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ Cypress ۧ۳ŰȘ Ű§Ù…Ű§ می ŰȘÙˆŰ§Ù† ŰąÙ† ۱ۧ ŰšÙ‡ ۱ۭۧŰȘی ۧۏ۱ۧ ک۱ۯ + // ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ Ù‡Ű± Ù…ŰŹÙ…ÙˆŰčه E2E + cy.visit("https://mysite.com/home"); + cy.contains("Home"); + cy.visit("https://mysite.com/Login"); + cy.contains("Login"); + cy.visit("https://mysite.com/About"); + cy.contains("About"); +}); +``` + +
    + +
    + +## âšȘ  3.10 ŰąŰČÙ…Ű§ÛŒŰŽ Ù‡Ű§ ۱ۧ ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† یک ŰłÙ†ŰŻ Ù…ŰŽŰ§Ű±Ú©ŰȘی ŰČÙ†ŰŻÙ‡ ۯ۱ مŰč۱۶ ŰŻÛŒŰŻ Ù‚Ű±Ű§Ű± ŰŻÙ‡ÛŒŰŻ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** ŰčÙ„Ű§ÙˆÙ‡ ۚ۱ Ű§ÙŰČŰ§ÛŒŰŽ Ù‚Ű§ŰšÙ„ÛŒŰȘ Ű§Ű·Ù…ÛŒÙ†Ű§Ù† ŰšŰ±Ù†Ű§Ù…Ù‡ŰŒ ŰąŰČÙ…Ű§ÛŒŰŽâ€ŒÙ‡Ű§ ÙŰ±Ű”ŰȘ ۏ۰ۧۚ ŰŻÛŒÚŻŰ±ÛŒ ۱ۧ ŰšÙ‡ Ű±ÙˆÛŒ میŰČ Ù…ÛŒâ€ŒŰąÙˆŰ±Ù†ŰŻ - ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† Ù…ŰłŰȘÙ†ŰŻŰ§ŰȘ ŰšŰ±Ù†Ű§Ù…Ù‡ ŰČÙ†ŰŻÙ‡ Ű§Ű±Ű§ŰŠÙ‡ Ù…ÛŒâ€ŒŰŽÙˆÙ†ŰŻ. ۧŰČ ŰąÙ†ŰŹŰ§ÛŒÛŒ که ŰȘŰłŰȘâ€ŒÙ‡Ű§ ۰ۧŰȘŰ§Ù‹ ۚۧ ŰČŰšŰ§Ù†ÛŒ کمŰȘ۱ فنی و Ù…Ű­Ű”ÙˆÙ„/UX ۔ۭۚŰȘ Ù…ÛŒâ€ŒÚ©Ù†Ù†ŰŻŰŒ ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ Ű§ŰšŰČŰ§Ű±Ù‡Ű§ÛŒ Ù…Ù†Ű§ŰłŰš می‌ŰȘÙˆŰ§Ù†Ù†ŰŻ ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† یک Ù…Ű”Ù†ÙˆŰč ۧ۱ŰȘŰšŰ§Ű·ÛŒ Űčمل Ú©Ù†Ù†ŰŻ که ŰȘۧ Ű­ŰŻ ŰČÛŒŰ§ŰŻÛŒ همه همŰȘŰ§ÛŒŰ§Ù† - ŰȘÙˆŰłŰčÙ‡â€ŒŰŻÙ‡Ù†ŰŻÚŻŰ§Ù† و Ù…ŰŽŰȘŰ±ÛŒŰ§Ù†ŰŽŰ§Ù† ۱ۧ Ù‡Ù…ŰłÙˆ Ù…ÛŒâ€ŒÚ©Ù†ŰŻ. ŰšŰ±Ű§ÛŒ Ù…Ű«Ű§Ù„ŰŒ ŰšŰ±ŰźÛŒ ۧŰČ Ú†Ű§Ű±Ú†ÙˆŰšâ€ŒÙ‡Ű§ Ű§Ù…Ú©Ű§Ù† ŰšÛŒŰ§Ù† ŰŹŰ±ÛŒŰ§Ù† و Ű§Ù†ŰȘ۞ۧ۱ۧŰȘ (یŰčنی ۷۱ۭ ŰąŰČÙ…Ű§ÛŒŰŽâ€ŒÙ‡Ű§) ۱ۧ ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ ŰČŰšŰ§Ù†ÛŒ Ù‚Ű§ŰšÙ„ ŰźÙˆŰ§Ù†ŰŻÙ† ŰšŰ±Ű§ÛŒ Ű§Ù†ŰłŰ§Ù† ÙŰ±Ű§Ù‡Ù… Ù…ÛŒâ€ŒÚ©Ù†Ù†ŰŻ ŰȘۧ Ù‡Ű± Ű°ÛŒÙ†ÙŰčی ۧŰČ ŰŹÙ…Ù„Ù‡ Ù…ŰŻÛŒŰ±Ű§Ù† Ù…Ű­Ű”ÙˆÙ„ŰŒ ŰšŰȘÙˆŰ§Ù†Ù†ŰŻ ŰąŰČÙ…Ű§ÛŒŰŽâ€ŒÙ‡Ű§ÛŒÛŒ ۱ۧ که ŰšÙ‡â€ŒŰȘۧŰČÚŻÛŒ ŰšÙ‡ ŰłÙ†ŰŻ Ù†ÛŒŰ§ŰČÙ…Ù†ŰŻÛŒâ€ŒÙ‡Ű§ÛŒ ŰČÙ†ŰŻÙ‡ ŰȘŰšŰŻÛŒÙ„ ŰŽŰŻÙ‡â€ŒŰ§Ù†ŰŻŰŒ ŰšŰźÙˆŰ§Ù†Ù†ŰŻŰŒ ŰȘŰŁÛŒÛŒŰŻ Ú©Ù†Ù†ŰŻ و ۯ۱ ŰąÙ† Ù‡Ù…Ú©Ű§Ű±ÛŒ Ú©Ù†Ù†ŰŻ. Ű§ÛŒÙ† ŰȘکنیک همچنین ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† "ŰąŰČمون ÙŸŰ°ÛŒŰ±ŰŽ" Ù†Ű§Ù…ÛŒŰŻÙ‡ می ŰŽÙˆŰŻ ŰČÛŒŰ±Ű§ ŰšÙ‡ Ù…ŰŽŰȘŰ±ÛŒ ۧۏۧŰČه می ŰŻÙ‡ŰŻ مŰčÛŒŰ§Ű±Ù‡Ű§ÛŒ ÙŸŰ°ÛŒŰ±ŰŽ ŰźÙˆŰŻ ۱ۧ ŰšÙ‡ ŰČŰšŰ§Ù† ŰłŰ§ŰŻÙ‡ ŰȘŰčŰ±ÛŒÙ Ú©Ù†ŰŻ. Ű§ÛŒÙ† Ù‡ŰłŰȘ [BDD (behavior-driven testing)](https://en.wikipedia.org/wiki/Behavior-driven_development) ۯ۱ ŰźŰ§Ù„Ű” ŰȘŰ±ÛŒÙ† ŰŽÚ©Ù„ ŰźÙˆŰŻ یکی ۧŰČ ÙŰ±ÛŒÙ…ÙˆŰ±Ú© Ù‡Ű§ÛŒ Ù…Ű­ŰšÙˆŰšÛŒ که Ű§ÛŒÙ† Ű§Ù…Ú©Ű§Ù† ۱ۧ ÙŰ±Ű§Ù‡Ù… می Ú©Ù†ŰŻ Ű§ÛŒÙ† ۧ۳ŰȘ [Cucumber which has a JavaScript flavor](https://github.com/cucumber/cucumber-js), Ù…Ű«Ű§Ù„ ŰČÛŒŰ± ۱ۧ ŰšŰšÛŒÙ†ÛŒŰŻ یک ÙŰ±Ű”ŰȘ Ù…ŰŽŰ§ŰšÙ‡ Ű§Ù…Ű§ مŰȘÙŰ§ÙˆŰȘ ŰŻÛŒÚŻŰ±, [StoryBook](https://storybook.js.org/), ۧۏۧŰČه می ŰŻÙ‡ŰŻ ŰȘۧ Ù…Ű€Ù„ÙÙ‡ Ù‡Ű§ÛŒ ۱ۧۚ۷ Ú©Ű§Ű±ŰšŰ±ÛŒ ۱ۧ ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† یک کۧŰȘŰ§Ù„ÙˆÚŻ ÚŻŰ±Ű§ÙÛŒÚ©ÛŒ ۯ۱ مŰč۱۶ ŰŻÛŒŰŻ Ù‚Ű±Ű§Ű± ŰŻÙ‡ÛŒŰŻŰŒ ŰŹŰ§ÛŒÛŒ که می ŰȘÙˆŰ§Ù†ÛŒŰŻ ۯ۱ Ű­Ű§Ù„ŰȘ Ù‡Ű§ÛŒ Ù…ŰźŰȘلف Ù‡Ű± Ù…Ű€Ù„ÙÙ‡ Ù‚ŰŻÙ… ŰšŰČÙ†ÛŒŰŻ (Ù…Ű«Ù„Ű§Ù‹ یک ŰŽŰšÚ©Ù‡ ŰšŰŻÙˆÙ† فیلŰȘ۱ Ű§Ű±Ű§ŰŠÙ‡ ŰŻÙ‡ÛŒŰŻŰŒ ŰąÙ† ŰŽŰšÚ©Ù‡ ۱ۧ ۚۧ Ú†Ù†ŰŻÛŒÙ† Ű±ŰŻÛŒÙ ÛŒŰ§ ۚۧ هیچ Ú©ŰŻŰ§Ù… و ŰșÛŒŰ±Ù‡ Ű§Ű±Ű§ŰŠÙ‡ Ú©Ù†ÛŒŰŻ)ی ŰšŰšÛŒÙ†ÛŒŰŻ Ú†ÚŻÙˆÙ†Ù‡ ŰšÙ‡ Ù†ŰžŰ± می ۱۳ۯ و Ú†ÚŻÙˆÙ†Ù‡ ۧ۳ŰȘ. ŰšŰ±Ű§ÛŒ Ű±Ű§Ù‡ Ű§Ù†ŰŻŰ§ŰČی ŰąÙ† Ű­Ű§Ù„ŰȘ - Ű§ÛŒÙ† می ŰȘÙˆŰ§Ù†ŰŻ ŰšŰ±Ű§ÛŒ Ű§ÙŰ±Ű§ŰŻ Ù…Ű­Ű”ÙˆÙ„ نیŰČ ŰŹŰ°Ű§Űš ۹ۧۮۯی Ű§Ù…Ű§ ŰšÛŒŰŽŰȘ۱ ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† ŰłÙ†ŰŻ ŰČÙ†ŰŻÙ‡ ŰšŰ±Ű§ÛŒ ŰȘÙˆŰłŰčه ŰŻÙ‡Ù†ŰŻÚŻŰ§Ù†ÛŒ که ŰąÙ† ۧۏŰČۧ ۱ۧ Ù…Ű”Ű±Ù می Ú©Ù†Ù†ŰŻŰŒ Űčمل می Ú©Ù†ŰŻ. + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** ÙŸŰł ۧŰČ ŰłŰ±Ù…Ű§ÛŒÙ‡ ÚŻŰ°Ű§Ű±ÛŒ Ù…Ù†Ű§ŰšŰč ۚ۱ŰȘ۱ ۯ۱ ŰąŰČÙ…Ű§ÛŒŰŽŰŒ ÙÙ‚Ű· Ű­ÛŒÙ ۧ۳ŰȘ که ۧŰČ Ű§ÛŒÙ† ŰłŰ±Ù…Ű§ÛŒÙ‡ ÚŻŰ°Ű§Ű±ÛŒ ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ù†Ú©Ù†ÛŒŰŻ و ۧ۱ŰČŰŽ ŰČÛŒŰ§ŰŻÛŒ ک۳ۚ Ù†Ú©Ù†ÛŒŰŻ + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :clap: Ű§Ù†ŰŹŰ§Ù… ۯ۱۳ŰȘ ŰąÙ† Ù…Ű«Ű§Ù„: ŰȘÙˆŰ”ÛŒÙ ŰȘŰłŰȘ Ù‡Ű§ ŰšÙ‡ ŰČŰšŰ§Ù† Ű§Ù†ŰłŰ§Ù† ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ cucumber-js + +![](https://img.shields.io/badge/🔹%20Example%20using%20Cucumber-blue.svg "Examples using Cucumber") + +```text +Ű§ÛŒÙ†ÚŻÙˆÙ†Ù‡ می ŰȘÙˆŰ§Ù† ŰȘŰłŰȘ Ù‡Ű§ ۱ۧ ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ cucumber ŰȘÙˆŰ”ÛŒÙ ک۱ۯ: ŰČŰšŰ§Ù†ÛŒ ŰłŰ§ŰŻÙ‡ که ŰšÙ‡ Ù‡Ű± Ú©ŰłÛŒ ۧۏۧŰČه می ŰŻÙ‡ŰŻ ۯ۱ک Ú©Ù†ŰŻ و Ù‡Ù…Ú©Ű§Ű±ÛŒ Ú©Ù†ŰŻ. + +Feature: Twitter new tweet + + I want to tweet something in Twitter + + @focus + Scenario: Tweeting from the home page + Given I open Twitter home + Given I click on "New tweet" button + Given I type "Hello followers!" in the textbox + Given I click on "Submit" button + Then I see message "Tweet saved" +``` + +### :clap: Ű§Ù†ŰŹŰ§Ù… ۯ۱۳ŰȘ Ù…Ű«Ű§Ù„: ŰȘŰŹŰłÙ… ۧۏŰČŰ§ÛŒ Ù…Ű§ŰŒ Ű­Ű§Ù„ŰȘâ€ŒÙ‡Ű§ÛŒ Ù…ŰźŰȘلف و ÙˆŰ±ÙˆŰŻÛŒâ€ŒÙ‡Ű§ÛŒ ŰąÙ†Ù‡Ű§ ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ Storybook + +![](https://img.shields.io/badge/🔹%20Example%20using%20StoryBook-blue.svg "Using StoryBook") + +![alt text](assets/story-book.jpg "Storybook") + +
    + +

    + +## âšȘ  3.11 Ù…ŰŽÚ©Ù„Ű§ŰȘ ŰšŰ”Ű±ÛŒ ۱ۧ ۚۧ ۧۚŰČŰ§Ű±Ù‡Ű§ÛŒ ŰźÙˆŰŻÚ©Ű§Ű± ŰȘŰŽŰźÛŒŰ” ŰŻÙ‡ÛŒŰŻ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** ۧۚŰČŰ§Ű±Ù‡Ű§ÛŒ ŰźÙˆŰŻÚ©Ű§Ű± ۱ۧ ŰšŰ±Ű§ÛŒ ÚŻŰ±ÙŰȘن Ű§ŰłÚ©Ű±ÛŒÙ† ێۧŰȘ Ù‡Ű§ÛŒ ۱ۧۚ۷ Ú©Ű§Ű±ŰšŰ±ÛŒ ۯ۱ Ù‡Ù†ÚŻŰ§Ù… Ű§Ű±Ű§ŰŠÙ‡ ŰȘŰșÛŒÛŒŰ±Ű§ŰȘ ŰȘÙ†ŰžÛŒÙ… Ú©Ù†ÛŒŰŻ و Ù…ŰŽÚ©Ù„Ű§ŰȘ ŰšŰ”Ű±ÛŒ Ù…Ű§Ù†Ù†ŰŻ Ù‡Ù…ÙŸÙˆŰŽŰ§Ù†ÛŒ ÛŒŰ§ ŰŽÚ©ŰłŰȘن Ù…Ű­ŰȘÙˆŰ§ ۱ۧ ŰŽÙ†Ű§ŰłŰ§ÛŒÛŒ Ú©Ù†ÛŒŰŻ. Ű§ÛŒÙ† ŰȘŰ¶Ù…ÛŒÙ† Ù…ÛŒâ€ŒÚ©Ù†ŰŻ که نه ŰȘÙ†Ù‡Ű§ ŰŻŰ§ŰŻÙ‡â€ŒÙ‡Ű§ÛŒ Ù…Ù†Ű§ŰłŰš ŰąÙ…Ű§ŰŻÙ‡ Ù…ÛŒâ€ŒŰŽÙˆÙ†ŰŻŰŒ ŰšÙ„Ú©Ù‡ کۧ۱ۚ۱ نیŰČ Ù…ÛŒâ€ŒŰȘÙˆŰ§Ù†ŰŻ ŰšÙ‡ ۱ۭۧŰȘی ŰąÙ†â€ŒÙ‡Ű§ ۱ۧ ŰšŰšÛŒÙ†ŰŻ. Ű§ÛŒÙ† ŰȘکنیک ŰšÙ‡ Ű·ÙˆŰ± ÚŻŰłŰȘŰ±ŰŻÙ‡ Ù…ÙˆŰ±ŰŻ ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ù‚Ű±Ű§Ű± Ù†ÚŻŰ±ÙŰȘه ۧ۳ŰȘی ۷۱ŰČ ÙÚ©Ű± ŰȘŰłŰȘ Ù…Ű§ ŰšÙ‡ ŰłÙ…ŰȘ ŰȘŰłŰȘâ€ŒÙ‡Ű§ÛŒ ŰčÙ…Ù„Ú©Ű±ŰŻÛŒ ÚŻŰ±Ű§ÛŒŰŽ ŰŻŰ§Ű±ŰŻŰŒ Ű§Ù…Ű§ ŰȘŰ”Ű§ÙˆÛŒŰ± ŰąÙ† چیŰČی ۧ۳ŰȘ که کۧ۱ۚ۱ ŰȘŰŹŰ±ŰšÙ‡ Ù…ÛŒâ€ŒÚ©Ù†ŰŻ و ۚۧ Ű§Ù†ÙˆŰ§Űč ŰŻŰłŰȘÚŻŰ§Ù‡â€ŒÙ‡Ű§ÛŒ ŰšŰłÛŒŰ§Ű±ŰŒ Ù†Ű§ŰŻÛŒŰŻÙ‡ ÚŻŰ±ÙŰȘن ŰšŰ±ŰźÛŒ ۧŰČ ŰšŰ§ÚŻâ€ŒÙ‡Ű§ÛŒ Ù†Ű§ŰźÙˆŰŽŰ§ÛŒÙ†ŰŻ UI ŰšŰłÛŒŰ§Ű± ŰąŰłŰ§Ù† ۧ۳ŰȘ. ŰšŰ±ŰźÛŒ ۧŰČ Ű§ŰšŰČŰ§Ű±Ù‡Ű§ÛŒ Ű±Ű§ÛŒÚŻŰ§Ù† می ŰȘÙˆŰ§Ù†Ù†ŰŻ Ű§Ű”ÙˆÙ„ Ű§ÙˆÙ„ÛŒÙ‡ ۱ۧ ÙŰ±Ű§Ù‡Ù… Ú©Ù†Ù†ŰŻ - Ű§ŰłÚ©Ű±ÛŒÙ† ێۧŰȘ Ù‡Ű§ ۱ۧ ŰšŰ±Ű§ÛŒ ۚۧŰČŰ±ŰłÛŒ Ú†ŰŽÙ… Ű§Ù†ŰłŰ§Ù† ŰȘÙˆÙ„ÛŒŰŻ و Ű°ŰźÛŒŰ±Ù‡ Ú©Ù†Ù†ŰŻ. Ű§ÚŻŰ±Ú†Ù‡ Ű§ÛŒÙ† Ű±ÙˆÛŒÚ©Ű±ŰŻ ممکن ۧ۳ŰȘ ŰšŰ±Ű§ÛŒ ŰšŰ±Ù†Ű§Ù…Ù‡â€ŒÙ‡Ű§ÛŒ کوچک Ú©Ű§ÙÛŒ ۹ۧۮۯی Ű§Ù…Ű§ Ù…Ű§Ù†Ù†ŰŻ Ù‡Ű± ŰąŰČÙ…Ű§ÛŒŰŽ ŰŻŰłŰȘی ŰŻÛŒÚŻŰ±ÛŒ که Ù‡Ű± ŰČÙ…Ű§Ù† که چیŰČی ŰȘŰșÛŒÛŒŰ± Ù…ÛŒâ€ŒÚ©Ù†ŰŻ ŰšÙ‡ Ù†ÛŒŰ±ÙˆÛŒ Ű§Ù†ŰłŰ§Ù†ÛŒ Ù†ÛŒŰ§ŰČ ŰŻŰ§Ű±ŰŻŰŒ Ù†Ű§Ù‚Ű” ۧ۳ŰȘ. ۧŰČ ŰłÙˆÛŒ ŰŻÛŒÚŻŰ±ŰŒ ŰšÙ‡ ŰŻÙ„ÛŒÙ„ Ù†ŰŻŰ§ŰŽŰȘن ŰȘŰčŰ±ÛŒÙ ÙˆŰ§Ű¶Ű­ŰŒ ŰȘŰŽŰźÛŒŰ” ŰźÙˆŰŻÚ©Ű§Ű± Ù…ŰłŰ§ŰŠÙ„ ۱ۧۚ۷ Ú©Ű§Ű±ŰšŰ±ÛŒ ŰšŰłÛŒŰ§Ű± Ú†Ű§Ù„ŰŽ ŰšŰ±Ű§Ù†ÚŻÛŒŰČ Ű§ŰłŰȘ - Ű§ÛŒÙ†ŰŹŰ§ŰłŰȘ که Ù…ÛŒŰŻŰ§Ù† "Ű±ÚŻŰ±ŰłÛŒÙˆÙ† ŰšŰ”Ű±ÛŒ" ŰšÙ‡ ۔ۯۧ ۯ۱ می ŰąÛŒŰŻ و ۚۧ Ù…Ù‚Ű§ÛŒŰłÙ‡ ۱ۧۚ۷ Ú©Ű§Ű±ŰšŰ±ÛŒ Ù‚ŰŻÛŒÙ…ÛŒ ۚۧ ŰąŰźŰ±ÛŒÙ† ŰȘŰșÛŒÛŒŰ±Ű§ŰȘ و ŰȘŰŽŰźÛŒŰ” ŰȘÙŰ§ÙˆŰȘ Ù‡Ű§ŰŒ Ű§ÛŒÙ† مŰčÙ…Ű§ ۱ۧ Ű­Ù„ می Ú©Ù†ŰŻ. ŰšŰ±ŰźÛŒ ۧŰČ Ű§ŰšŰČŰ§Ű±Ù‡Ű§ÛŒ OSS/Ű±Ű§ÛŒÚŻŰ§Ù† می ŰȘÙˆŰ§Ù†Ù†ŰŻ ŰšŰ±ŰźÛŒ ۧŰČ Ű§ÛŒÙ† Ù‚Ű§ŰšÙ„ÛŒŰȘ Ù‡Ű§ ۱ۧ Ű§Ű±Ű§ŰŠÙ‡ ŰŻÙ‡Ù†ŰŻ (e.g. [wraith](https://github.com/BBC-News/wraith), [PhantomCSS](<[https://github.com/HuddleEng/PhantomCSS](https://github.com/HuddleEng/PhantomCSS)>) Ű§Ù…Ű§ ممکن ۧ۳ŰȘ ŰČÙ…Ű§Ù† Ű±Ű§Ù‡ Ű§Ù†ŰŻŰ§ŰČی Ù‚Ű§ŰšÙ„ ŰȘÙˆŰŹÙ‡ÛŒ ۱ۧ ێۧ۱ژ Ú©Ù†ŰŻ. ۟۷ ŰȘŰŹŰ§Ű±ÛŒ ۧۚŰČۧ۱ (e.g. [Applitools](https://applitools.com/), [Percy.io](https://percy.io/)) ۚۧ Ù‡Ù…ÙˆŰ§Ű±ŰłŰ§ŰČی Ù†Ű”Űš و ۚ۳ŰȘÙ‡â€ŒŰšÙ†ŰŻÛŒ ÙˆÛŒÚ˜ÚŻÛŒâ€ŒÙ‡Ű§ÛŒ ÙŸÛŒŰŽŰ±ÙŰȘه Ù…Ű§Ù†Ù†ŰŻ ۱ۧۚ۷ Ú©Ű§Ű±ŰšŰ±ÛŒ Ù…ŰŻÛŒŰ±ÛŒŰȘی Ù‡ŰŽŰŻŰ§Ű±ŰŒ ۶ۚ۷ Ù‡ÙˆŰŽÙ…Ù†ŰŻ ۚۧ Ű­Ű°Ù نویŰČ ŰšŰ”Ű±ÛŒ (Ù…Ű§Ù†Ù†ŰŻ ŰȘŰšÙ„ÛŒŰșۧŰȘی Ű§Ù†ÛŒÙ…ÛŒŰŽÙ†â€ŒÙ‡Ű§) و Ű­ŰȘی ŰȘŰŹŰČیه و ŰȘŰ­Ù„ÛŒÙ„ ŰčلŰȘ Ű§Ű”Ù„ÛŒ ŰȘŰșÛŒÛŒŰ±Ű§ŰȘ DOM/CSS که Ù…Ù†ŰŹŰ± ŰšÙ‡ Ű§ÛŒÙ† Ù…ŰŽÚ©Ù„ ŰŽŰŻÙ‡ ۧ۳ŰȘی یک Ù‚ŰŻÙ… ŰšÛŒŰŽŰȘ۱ ۧ۳ŰȘ. + +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** Ű”ÙŰ­Ù‡ Ù…Ű­ŰȘÙˆŰ§ÛŒÛŒ که Ù…Ű­ŰȘÙˆŰ§ÛŒ ŰčŰ§Ù„ÛŒ ۱ۧ Ù†Ù…Ű§ÛŒŰŽ Ù…ÛŒâ€ŒŰŻÙ‡ŰŻ Ú†Ù‚ŰŻŰ± ŰźÙˆŰš ۧ۳ŰȘ (100ÙȘ ŰȘŰłŰȘâ€ŒÙ‡Ű§ ۚۧ موفقیŰȘ Ű§Ù†ŰŹŰ§Ù… ŰŽŰŻ)ی ÙÙˆŰ±Ű§ ŰšŰ§Ű±ÚŻÛŒŰ±ÛŒ Ù…ÛŒâ€ŒŰŽÙˆŰŻ Ű§Ù…Ű§ نیمی ۧŰČ Ù…Ù†Ű·Ù‚Ù‡ Ù…Ű­ŰȘÙˆŰ§ ÙŸÙ†Ù‡Ű§Ù† ۧ۳ŰȘ.? + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :thumbsdown: Ù…Ű«Ű§Ù„ ۶ۯ Ű§Ù„ÚŻÙˆ: یک Ű±ÚŻŰ±ŰłÛŒÙˆÙ† ŰšŰ”Ű±ÛŒ مŰčمولی - Ù…Ű­ŰȘÙˆŰ§ÛŒ ۯ۱۳ŰȘی که ۚۯ Ű§Ű±Ű§ŰŠÙ‡ می ŰŽÙˆŰŻ + +![alt text](assets/amazon-visual-regression.jpeg "Amazon page breaks") + +
    + +### :clap: Ű§Ù†ŰŹŰ§Ù… ۯ۱۳ŰȘ Ù…Ű«Ű§Ù„: ÙŸÛŒÚ©Ű±ŰšÙ†ŰŻÛŒ wraith ŰšŰ±Ű§ÛŒ ÚŻŰ±ÙŰȘن و Ù…Ù‚Ű§ÛŒŰłÙ‡ ŰčÚ©Űł Ù‡Ű§ÛŒ ÙÙˆŰ±ÛŒ UI + +![](https://img.shields.io/badge/🔹%20Example%20using%20Wraith-blue.svg "Using Wraith") + +``` +​# Add as many domains as necessary. Key will act as a label​ + +domains: + english: "http://www.mysite.com"​ + +​# Type screen widths below, here are a couple of examples​ + +screen_widths: + + - 600​ + - 768​ + - 1024​ + - 1280​ + +​# Type page URL paths below, here are a couple of examples​ +paths: + about: + path: /about + selector: '.about'​ + subscribe: + selector: '.subscribe'​ + path: /subscribe +``` + +### :clap: Ű§Ù†ŰŹŰ§Ù… ۯ۱۳ŰȘ Ù…Ű«Ű§Ù„: ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ Applitools ŰšŰ±Ű§ÛŒ Ù…Ù‚Ű§ÛŒŰłÙ‡ ŰčÚ©Űł ÙÙˆŰ±ÛŒ و ŰłŰ§ÛŒŰ± ÙˆÛŒÚ˜ÚŻÛŒ Ù‡Ű§ÛŒ ÙŸÛŒŰŽŰ±ÙŰȘه + +![](https://img.shields.io/badge/🔹%20Example%20using%20AppliTools-blue.svg "Using Applitools") ![](https://img.shields.io/badge/🔹%20Example%20using%20Cypress-blue.svg "Using Cypress to illustrate the idea") + +```javascript +import * as todoPage from "../page-objects/todo-page"; + +describe("visual validation", () => { + before(() => todoPage.navigate()); + beforeEach(() => cy.eyesOpen({ appName: "TAU TodoMVC" })); + afterEach(() => cy.eyesClose()); + + it("should look good", () => { + cy.eyesCheckWindow("empty todo list"); + todoPage.addTodo("Clean room"); + todoPage.addTodo("Learn javascript"); + cy.eyesCheckWindow("two todos"); + todoPage.toggleTodo(0); + cy.eyesCheckWindow("mark as completed"); + }); +}); +``` + +
    + +

    + +# ۚ۟ێ 4ïžâƒŁ: Ű§Ù†ŰŻŰ§ŰČه ÚŻÛŒŰ±ÛŒ Ű§Ű«Ű±ŰšŰźŰŽÛŒ ŰąŰČمون + +

    + +## âšȘ  4.1 ÙŸÙˆŰŽŰŽ Ú©Ű§ÙÛŒ ŰšŰ±Ű§ÛŒ ۯۧێŰȘن ۧŰčŰȘÙ…Ű§ŰŻ ŰšÙ‡ Ù†ÙŰł ۯۧێŰȘه ŰšŰ§ŰŽÛŒŰŻŰŒ ŰšÙ‡ Ù†ŰžŰ± می ۱۳ۯ 80ÙȘ ŰčŰŻŰŻ ŰźÙˆŰŽ ŰŽŰ§Ù†Űł ۚۧێۯ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** Ù‡ŰŻÙ ۧŰČ ŰȘŰłŰȘ ÚŻŰ±ÙŰȘن ۧŰčŰȘÙ…Ű§ŰŻ ŰšÙ‡ Ù†ÙŰł Ú©Ű§ÙÛŒ ŰšŰ±Ű§ÛŒ ۭ۱کŰȘ ŰłŰ±ÛŒŰč ۧ۳ŰȘی ŰšŰŻÛŒÙ‡ÛŒ ۧ۳ŰȘ که Ù‡Ű± چه Ú©ŰŻ ŰšÛŒŰŽŰȘ۱ ŰȘŰłŰȘ ŰŽÙˆŰŻŰŒ ŰȘیم می ŰȘÙˆŰ§Ù†ŰŻ ۧŰčŰȘÙ…Ű§ŰŻ ŰšÛŒŰŽŰȘŰ±ÛŒ ۯۧێŰȘه ۚۧێۯ. ÙŸÙˆŰŽŰŽ مŰčÛŒŰ§Ű±ÛŒ ۧ۳ŰȘ ۧŰČ ŰȘŰčۯۧۯ ŰźŰ·ÙˆŰ· Ú©ŰŻ (و ŰŽŰ§ŰźÙ‡ Ù‡Ű§ŰŒ ŰŻŰłŰȘÙˆŰ±Ű§ŰȘ و ŰșÛŒŰ±Ù‡) که ŰȘÙˆŰłŰ· ŰąŰČÙ…Ű§ÛŒŰŽ Ù‡Ű§ ŰšÙ‡ ŰŻŰłŰȘ می ŰąÛŒÙ†ŰŻ. ÙŸŰł Ú†Ù‚ŰŻŰ± Ú©Ű§ÙÛŒ ۧ۳ŰȘ۟ 10-30ÙȘ ÙˆŰ§Ű¶Ű­ ۧ۳ŰȘ که ŰšŰ±Ű§ÛŒ ۯ۱ک ۯ۱۳ŰȘی ۳ۧ۟ŰȘ ŰšŰłÛŒŰ§Ű± کم ۧ۳ŰȘی ۧŰČ Ű·Ű±Ù ŰŻÛŒÚŻŰ± 100ÙȘ ŰšŰłÛŒŰ§Ű± ÚŻŰ±Ű§Ù† ۧ۳ŰȘ و ممکن ۧ۳ŰȘ ŰȘÙ…Ű±Ú©ŰČ ŰŽÙ…Ű§ ۱ۧ ۧŰČ Ù…ŰłÛŒŰ±Ù‡Ű§ÛŒ مهم ŰšÙ‡ ÚŻÙˆŰŽÙ‡ Ù‡Ű§ÛŒ ŰčŰŹÛŒŰš و ŰșŰ±ÛŒŰš Ú©ŰŻ ŰȘŰșÛŒÛŒŰ± ŰŻÙ‡ŰŻ. ÙŸŰ§ŰłŰź Ű·ÙˆÙ„Ű§Ù†ÛŒ Ű§ÛŒÙ† ۧ۳ŰȘ که ŰšÙ‡ ŰčÙˆŰ§Ù…Ù„ ŰČÛŒŰ§ŰŻÛŒ Ù…Ű§Ù†Ù†ŰŻ نوŰč ŰšŰ±Ù†Ű§Ù…Ù‡ ۚ۳ŰȘÚŻÛŒ ۯۧ۱ۯ - ۧگ۱ ŰŽÙ…Ű§ ۯ۱ Ű­Ű§Ù„ ۳ۧ۟ŰȘ Ù†ŰłÙ„ ŰšŰčŰŻÛŒ Ű§ÛŒŰ±ŰšŰ§Űł A380 Ù‡ŰłŰȘÛŒŰŻŰŒ 100ÙȘ Ű¶Ű±ÙˆŰ±ÛŒ ۧ۳ŰȘی ŰšŰ±Ű§ÛŒ یک ÙˆŰš ŰłŰ§ÛŒŰȘ ŰȘŰ”Ű§ÙˆÛŒŰ± کۧ۱ŰȘونی 50ÙȘ ممکن ۧ۳ŰȘ ŰźÛŒÙ„ÛŒ ŰČÛŒŰ§ŰŻ ۚۧێۯ. Ű§ÚŻŰ±Ú†Ù‡ ۧک۫۱ ŰčÙ„Ű§Ù‚Ù‡ Ù…Ù†ŰŻŰ§Ù† ŰšÙ‡ ŰȘŰłŰȘ ۧۯŰčۧ می Ú©Ù†Ù†ŰŻ که ۹۳ŰȘŰ§Ù†Ù‡ ÙŸÙˆŰŽŰŽ Ù…Ù†Ű§ŰłŰš مŰȘنی ۧ۳ŰȘی ۧک۫۱ ŰąÙ†Ù‡Ű§ ŰčŰŻŰŻ 80ÙȘ ۱ۧ نیŰČ ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† یک Ù‚Ű§Ù†ÙˆÙ† ۰ک۱ می Ú©Ù†Ù†ŰŻ. ([Fowler: “in the upper 80s or 90s”](https://martinfowler.com/bliki/TestCoverage.html)) که ۭۧŰȘÙ…Ű§Ù„Ű§Ù‹ ŰšŰ§ÛŒŰŻ ۧک۫۱ ŰšŰ±Ù†Ű§Ù…Ù‡ Ù‡Ű§ ۱ۧ ŰšŰ±ŰąÙˆŰ±ŰŻÙ‡ Ú©Ù†ŰŻ. + +Ù†Ú©Ű§ŰȘ ÙŸÛŒŰ§ŰŻÙ‡ ۳ۧŰČی: ممکن ۧ۳ŰȘ ŰšŰźÙˆŰ§Ù‡ÛŒŰŻ ۧۯŰșŰ§Ù… ÙŸÛŒÙˆŰłŰȘه (CI) ŰźÙˆŰŻ ۱ۧ ŰšŰ±Ű§ÛŒ ۯۧێŰȘن ۹۳ŰȘŰ§Ù†Ù‡ ÙŸÙˆŰŽŰŽ ÙŸÛŒÚ©Ű±ŰšÙ†ŰŻÛŒ Ú©Ù†ÛŒŰŻ. ([Jest link](https://jestjs.io/docs/en/configuration.html#collectcoverage-boolean)) و ۳ۧ۟ŰȘنی ۱ۧ که Ù…Ű·Ű§ŰšÙ‚ ۚۧ Ű§ÛŒÙ† ۧ۳ŰȘŰ§Ù†ŰŻŰ§Ű±ŰŻ Ù†ÛŒŰłŰȘ مŰȘوقف Ú©Ù†ÛŒŰŻ (همچنین می ŰȘÙˆŰ§Ù† ۹۳ŰȘŰ§Ù†Ù‡ ۱ۧ ŰšŰ±Ű§ÛŒ Ù‡Ű± ŰŹŰČŰĄ ÙŸÛŒÚ©Ű±ŰšÙ†ŰŻÛŒ Ú©Ű±ŰŻŰŒ ŰšÙ‡ Ù…Ű«Ű§Ù„ Ú©ŰŻ ŰČÛŒŰ± Ù…Ű±Ű§ŰŹŰčه Ú©Ù†ÛŒŰŻ). ŰčÙ„Ű§ÙˆÙ‡ ۚ۱ Ű§ÛŒÙ†ŰŒ ŰȘŰŽŰźÛŒŰ” Ú©Ű§Ù‡ŰŽ ÙŸÙˆŰŽŰŽ ۳ۧ۟ŰȘ ۱ۧ ۯ۱ Ù†ŰžŰ± ŰšÚŻÛŒŰ±ÛŒŰŻ (ŰČÙ…Ű§Ù†ÛŒ که یک Ú©ŰŻ ŰŹŰŻÛŒŰŻ مŰȘŰčÙ‡ŰŻ ŰŽŰŻÙ‡ ŰŻŰ§Ű±Ű§ÛŒ ÙŸÙˆŰŽŰŽ کمŰȘŰ±ÛŒ ۧ۳ŰȘ)â€”â€ŠŰ§ÛŒÙ† ۚۧŰčŰ« Ù…ÛŒâ€ŒŰŽÙˆŰŻ ŰȘÙˆŰłŰčÙ‡â€ŒŰŻÙ‡Ù†ŰŻÚŻŰ§Ù† Ù…Ù‚ŰŻŰ§Ű± Ú©ŰŻ ŰȘŰłŰȘ ŰŽŰŻÙ‡ ۱ۧ Ű§ÙŰČŰ§ÛŒŰŽ ŰŻÙ‡Ù†ŰŻ ÛŒŰ§ Ű­ŰŻŰ§Ù‚Ù„ Ű­ÙŰž Ú©Ù†Ù†ŰŻ. همه ŰąÙ†Ú†Ù‡ ÚŻÙŰȘه ۮۯی ÙŸÙˆŰŽŰŽ ŰȘÙ†Ù‡Ű§ یک مŰčÛŒŰ§Ű± ۧ۳ŰȘی یک مŰčÛŒŰ§Ű± Ù…ŰšŰȘنی ۚ۱ Ú©Ù…ÛŒŰŒ که ŰšŰ±Ű§ÛŒ Ù†ŰŽŰ§Ù† ŰŻŰ§ŰŻÙ† ۧ۳ŰȘŰ­Ú©Ű§Ù… ŰąŰČÙ…Ű§ÛŒŰŽ ŰŽÙ…Ű§ Ú©Ű§ÙÛŒ Ù†ÛŒŰłŰȘ. و همچنین Ù‡Ù…Ű§Ù†Ű·ÙˆŰ± که ۯ۱ ÚŻÙ„ÙˆÙ„Ù‡ Ù‡Ű§ÛŒ ŰšŰčŰŻÛŒ Ù†ŰŽŰ§Ù† ŰŻŰ§ŰŻÙ‡ ŰŽŰŻÙ‡ ۧ۳ŰȘ می ŰȘÙˆŰ§Ù† ŰąÙ† ۱ۧ ÙŰ±ÛŒŰš ۯۧۯ + +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** ۧŰčŰȘÙ…Ű§ŰŻ ŰšÙ‡ Ù†ÙŰł و ۧŰčۯۧۯ ŰŻŰłŰȘ ŰšÙ‡ ŰŻŰłŰȘ هم می ŰŻÙ‡Ù†ŰŻŰŒ ŰšŰŻÙˆÙ† Ű§ÛŒÙ†Ú©Ù‡ ÙˆŰ§Ù‚ŰčŰ§Ù‹ ŰšŰŻŰ§Ù†ÛŒŰŻ که ŰšÛŒŰŽŰȘ۱ ŰłÛŒŰłŰȘم ۱ۧ ŰąŰČÙ…Ű§ÛŒŰŽ Ú©Ű±ŰŻÙ‡ Ű§ÛŒŰŻ - همچنین Ù…Ù‚ŰŻŰ§Ű±ÛŒ ŰȘ۱۳ ÙˆŰŹÙˆŰŻ ŰźÙˆŰ§Ù‡ŰŻ ۯۧێŰȘ و ŰȘ۱۳ ۳۱ŰčŰȘ ŰŽÙ…Ű§ ۱ۧ Ú©Ű§Ù‡ŰŽ می ŰŻÙ‡ŰŻ. + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :clap: Ù…Ű«Ű§Ù„: یک ÚŻŰČۧ۱ێ ÙŸÙˆŰŽŰŽ مŰčمولی + +![alt text](assets/bp-18-yoni-goldberg-code-coverage.png "A typical coverage report") + +
    + +### :clap: Ű§Ù†ŰŹŰ§Ù… ۯ۱۳ŰȘ ŰąÙ† Ù…Ű«Ű§Ù„: ŰȘÙ†ŰžÛŒÙ… ÙŸÙˆŰŽŰŽ ŰšŰ±Ű§ÛŒ Ù‡Ű± ŰŹŰČŰĄ (ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ Jest) + +![](https://img.shields.io/badge/🔹%20Example%20using%20Jest-blue.svg "Using Jest") + +![alt text](assets/bp-18-code-coverage2.jpeg "Setting up coverage per component (using Jest)") + +
    + +

    + +## âšȘ  4.2 ÚŻŰČۧ۱ێ Ù‡Ű§ÛŒ ÙŸÙˆŰŽŰŽ ۱ۧ ŰšŰ±Ű§ÛŒ ŰŽÙ†Ű§ŰłŰ§ÛŒÛŒ Ù…Ù†Ű§Ű·Ù‚ ŰąŰČÙ…Ű§ÛŒŰŽ Ù†ŰŽŰŻÙ‡ و ŰłŰ§ÛŒŰ± Ù…ÙˆŰ§Ű±ŰŻ ŰčŰŹÛŒŰš ۚۧŰČŰ±ŰłÛŒ Ú©Ù†ÛŒŰŻ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** ŰšŰ±ŰźÛŒ ۧŰČ Ù…ŰłŰ§ŰŠÙ„ ŰŻÙ‚ÛŒÙ‚Ű§Ù‹ ŰČÛŒŰ± ۱ۧۯۧ۱ ÙŸÙ†Ù‡Ű§Ù† می ŰŽÙˆÙ†ŰŻ و ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ Ű§ŰšŰČŰ§Ű±Ù‡Ű§ÛŒ ŰłÙ†ŰȘی ÙŸÛŒŰŻŰ§ Ú©Ű±ŰŻÙ† ŰąÙ†Ù‡Ű§ ÙˆŰ§Ù‚ŰčŰ§Ù‹ ۳۟ŰȘ ۧ۳ŰȘ. Ű§ÛŒÙ†Ù‡Ű§ ÙˆŰ§Ù‚ŰčŰ§Ù‹ ۚۧگ Ù†ÛŒŰłŰȘÙ†ŰŻŰŒ ŰšÙ„Ú©Ù‡ ŰšÛŒŰŽŰȘ۱ Ű±ÙŰȘŰ§Ű±Ù‡Ű§ÛŒ ŰŽÚŻÙŰȘ Ű§Ù†ÚŻÛŒŰČ ŰšŰ±Ù†Ű§Ù…Ù‡ Ù‡ŰłŰȘÙ†ŰŻ که ممکن ۧ۳ŰȘ ŰȘŰŁŰ«ÛŒŰ± ŰŽŰŻÛŒŰŻÛŒ ۯۧێŰȘه ŰšŰ§ŰŽÙ†ŰŻ. ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† Ù…Ű«Ű§Ù„ŰŒ ۧŰșÙ„Űš ŰšŰ±ŰźÛŒ ۧŰČ Ù…Ù†Ű§Ű·Ù‚ Ú©ŰŻ Ù‡Ű±ÚŻŰČ ÛŒŰ§ ŰšÙ‡ Ù†ŰŻŰ±ŰȘ ÙŰ±Ű§ŰźÙˆŰ§Ù†ÛŒ نمی ŰŽÙˆÙ†ŰŻâ€Š-â€ŠŰŽÙ…Ű§ ÙÚ©Ű± می Ú©Ù†ÛŒŰŻ که Ú©Ù„Ű§Űł "PricingCalculator" Ù‡Ù…ÛŒŰŽÙ‡ قیمŰȘ Ù…Ű­Ű”ÙˆÙ„ ۱ۧ ŰȘŰčیین می Ú©Ù†ŰŻŰŒ Ű§Ù…Ű§ ŰšÙ‡ Ù†ŰžŰ± می ۱۳ۯ که ۯ۱ ÙˆŰ§Ù‚Űč Ù‡Ű±ÚŻŰČ ÙŰ±Ű§ŰźÙˆŰ§Ù†ÛŒ نمی ŰŽÙˆŰŻŰŒ Ű§ÚŻŰ±Ú†Ù‡ Ù…Ű§ 10000 Ù…Ű­Ű”ÙˆÙ„ ۯ۱ DB ŰŻŰ§Ű±ÛŒÙ… و ŰȘŰčۯۧۯ ŰČÛŒŰ§ŰŻÛŒ ÙŰ±ÙˆŰŽ... ÙŸÙˆŰŽŰŽ Ú©ŰŻ ÚŻŰČŰ§Ű±ŰŽâ€ŒÙ‡Ű§ ŰšÙ‡ ŰŽÙ…Ű§ کمک Ù…ÛŒâ€ŒÚ©Ù†Ù†ŰŻ مŰȘÙˆŰŹÙ‡ ŰŽÙˆÛŒŰŻ که ŰąÛŒŰ§ ŰšŰ±Ù†Ű§Ù…Ù‡ ŰšÙ‡â€ŒÚŻÙˆÙ†Ù‡â€ŒŰ§ÛŒ که ŰŽÙ…Ű§ ÙÚ©Ű± Ù…ÛŒâ€ŒÚ©Ù†ÛŒŰŻ Ű±ÙŰȘۧ۱ Ù…ÛŒâ€ŒÚ©Ù†ŰŻ ÛŒŰ§ ŰźÛŒŰ±. ŰšÙ‡ ŰșÛŒŰ± ۧŰČ ŰąÙ†ŰŒ همچنین می‌ŰȘÙˆŰ§Ù†ŰŻ Ù…ŰŽŰźŰ” Ú©Ù†ŰŻ که Ú©ŰŻŰ§Ù… نوŰč Ú©ŰŻ ŰąŰČÙ…Ű§ÛŒŰŽ Ù†ŰŽŰŻÙ‡ ۧ۳ŰȘ - ۚۧ Ű§Ű·Ù„Ű§Űč ۧŰČ Ű§ÛŒÙ†Ú©Ù‡ 80 ۯ۱۔ۯ Ú©ŰŻ ŰąŰČÙ…Ű§ÛŒŰŽ ŰŽŰŻÙ‡ ۧ۳ŰȘی Ù†Ù…ÛŒâ€ŒÚŻÙˆÛŒŰŻ که ŰąÛŒŰ§ Ù‚ŰłÙ…ŰȘâ€ŒÙ‡Ű§ÛŒ Ű­ÛŒŰ§ŰȘی ÙŸÙˆŰŽŰŽ ŰŻŰ§ŰŻÙ‡ ŰŽŰŻÙ‡â€ŒŰ§Ù†ŰŻ ÛŒŰ§ ŰźÛŒŰ±. Ű§ÛŒŰŹŰ§ŰŻ ÚŻŰČۧ۱ێ ŰąŰłŰ§Ù† ۧ۳ŰȘ - ÙÙ‚Ű· ŰšŰ±Ù†Ű§Ù…Ù‡ ŰźÙˆŰŻ ۱ۧ ۯ۱ Ù…Ű±Ű­Ù„Ù‡ ŰȘÙˆÙ„ÛŒŰŻ ÛŒŰ§ ۯ۱ Ű­ÛŒÙ† ŰąŰČÙ…Ű§ÛŒŰŽ ۚۧ Ű±ŰŻÛŒŰ§ŰšÛŒ ÙŸÙˆŰŽŰŽ ۧۏ۱ۧ Ú©Ù†ÛŒŰŻ و ŰłÙŸŰł ÚŻŰČŰ§Ű±ŰŽâ€ŒÙ‡Ű§ÛŒ Ű±Ù†ÚŻŰ§Ű±Ù†ÚŻÛŒ ۱ۧ Ù…ŰŽŰ§Ù‡ŰŻÙ‡ Ú©Ù†ÛŒŰŻ که Ù†ŰŽŰ§Ù† Ù…ÛŒâ€ŒŰŻÙ‡ŰŻ ŰȘŰčۯۧۯ ŰŻÙŰčۧŰȘ ÙŰ±Ű§ŰźÙˆŰ§Ù†ÛŒ Ù‡Ű± Ù†Ű§Ű­ÛŒÙ‡ Ú©ŰŻ Ù…ŰŽŰźŰ” Ù…ÛŒâ€ŒŰŽÙˆŰŻ. ۧگ۱ وقŰȘ ŰźÙˆŰŻ ۱ۧ Ű”Ű±Ù Ù†ÚŻŰ§Ù‡ÛŒ Ű§ŰŹÙ…Ű§Ù„ÛŒ ŰšÙ‡ Ű§ÛŒÙ† ŰŻŰ§ŰŻÙ‡ Ù‡Ű§ Ú©Ù†ÛŒŰŻ - ممکن ۧ۳ŰȘ Ú†Ù†ŰŻ ۧێŰȘŰšŰ§Ù‡ ÙŸÛŒŰŻŰ§ Ú©Ù†ÛŒŰŻ +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** ۧگ۱ Ù†Ù…ÛŒâ€ŒŰŻŰ§Ù†ÛŒŰŻ Ú©ŰŻŰ§Ù… ۚ۟ێ ۧŰČ Ú©ŰŻŰȘŰ§Ù† ŰąŰČÙ…Ű§ÛŒŰŽ Ù†ŰŽŰŻÙ‡ ۧ۳ŰȘی Ù†Ù…ÛŒâ€ŒŰŻŰ§Ù†ÛŒŰŻ Ű§ÛŒÙ† Ù…ŰŽÚ©Ù„Ű§ŰȘ ۧŰČ Ú©ŰŹŰ§ Ù…ÛŒâ€ŒŰąÛŒÙ†ŰŻ. + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :thumbsdown: Ù…Ű«Ű§Ù„ ۶ۯ Ű§Ù„ÚŻÙˆ: Ű§ÛŒÙ† ÚŻŰČۧ۱ێ ÙŸÙˆŰŽŰŽ چه Ű§ŰŽÚ©Ű§Ù„ÛŒ ŰŻŰ§Ű±ŰŻŰŸ + +ۚ۱ ۧ۳ۧ۳ یک ŰłÙ†Ű§Ű±ÛŒÙˆÛŒ ÙˆŰ§Ù‚Űčی که ۯ۱ ŰąÙ† Ù…Ű§ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ ŰšŰ±Ù†Ű§Ù…Ù‡ ŰźÙˆŰŻ ۱ۧ ۯ۱ QA Ű±ŰŻÛŒŰ§ŰšÛŒ Ú©Ű±ŰŻÛŒÙ… و Ű§Ù„ÚŻÙˆÙ‡Ű§ÛŒ ÙˆŰ±ÙˆŰŻ ŰŹŰ§Ù„ŰšÛŒ ۱ۧ ÙŸÛŒŰŻŰ§ Ú©Ű±ŰŻÛŒÙ… (نکŰȘه: میŰČŰ§Ù† ŰźŰ±Ű§ŰšÛŒ Ù‡Ű§ÛŒ ÙˆŰ±ÙˆŰŻ ŰšÙ‡ ŰłÛŒŰłŰȘم Ù†Ű§Ù…ŰȘÙ†Ű§ŰłŰš ۧ۳ŰȘی چیŰČی ŰšÙ‡ ÙˆŰ¶ÙˆŰ­ ۧێŰȘŰšŰ§Ù‡ ۧ۳ŰȘ. ۯ۱ Ù†Ù‡Ű§ÛŒŰȘ Ù…ŰŽŰźŰ” ŰŽŰŻ که ŰšŰ±ŰźÛŒ ۧŰČ ŰšŰ§ÚŻ Ù‡Ű§ÛŒ frontend Ù…ŰŻŰ§Ù… ŰšÙ‡ ŰłÛŒŰłŰȘم ÙˆŰ§Ű±ŰŻ می ŰŽÙˆÙ†ŰŻ. API ÙˆŰ±ÙˆŰŻ ŰšŰ§Ű·Ù†) + +![alt text](assets/bp-19-coverage-yoni-goldberg-nodejs-consultant.png "What’s wrong with this coverage report?") + +
    + +

    + +## âšȘ  4.3 Ű§Ù†ŰŻŰ§ŰČه ÚŻÛŒŰ±ÛŒ ÙŸÙˆŰŽŰŽ Ù…Ù†Ű·Ù‚ÛŒ ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ ŰȘŰłŰȘ ŰŹÙ‡ŰŽ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** مŰčÛŒŰ§Ű± ÙŸÙˆŰŽŰŽ ŰłÙ†ŰȘی ۧŰșÙ„Űš ŰŻŰ±ÙˆŰș می ÚŻÙˆÛŒŰŻ: ممکن ۧ۳ŰȘ ÙŸÙˆŰŽŰŽ 100ÙȘ Ú©ŰŻ ۱ۧ ŰšÙ‡ ŰŽÙ…Ű§ Ù†ŰŽŰ§Ù† ŰŻÙ‡ŰŻŰŒ Ű§Ù…Ű§ هیچ یک ۧŰČ ŰȘÙˆŰ§ŰšŰč ŰŽÙ…Ű§ŰŒ Ű­ŰȘی یک Ù…ÙˆŰ±ŰŻŰŒ ÙŸŰ§ŰłŰź Ù…Ù†Ű§ŰłŰš ۱ۧ Ù†ŰŽŰ§Ù† نمی ŰŻÙ‡ŰŻ. Ú†Ű·ÙˆŰ±ŰŸ ŰąÙ† ۱ۧ ŰšÙ‡ ŰłŰ§ŰŻÚŻÛŒ Ű§Ù†ŰŻŰ§ŰČه ÚŻÛŒŰ±ÛŒ می Ú©Ù†ŰŻ که ۧŰČ Ú©ŰŻŰ§Ù… ŰźŰ·ÙˆŰ· Ú©ŰŻ ŰȘŰłŰȘ ۚۧŰČŰŻÛŒŰŻ Ú©Ű±ŰŻÙ‡ ۧ۳ŰȘی Ű§Ù…Ű§ ŰšŰ±Ű±ŰłÛŒ نمی Ú©Ù†ŰŻ که ŰąÛŒŰ§ ŰąŰČمون Ù‡Ű§ ÙˆŰ§Ù‚ŰčŰ§Ù‹ چیŰČی ۱ۧ ŰąŰČÙ…Ű§ÛŒŰŽ Ú©Ű±ŰŻÙ‡ Ű§Ù†ŰŻ - ŰšŰ±Ű§ÛŒ ÙŸŰ§ŰłŰź ۯ۱۳ŰȘ Ű§ŰžÙ‡Ű§Ű± ŰŽŰŻÙ‡ ۧ۳ŰȘ. Ù…Ű§Ù†Ù†ŰŻ Ú©ŰłÛŒ که ŰšŰ±Ű§ÛŒ ŰȘۏۧ۱ŰȘ ŰłÙŰ± می Ú©Ù†ŰŻ و Ù…Ù‡Ű± ÙŸŰ§ŰłÙŸÙˆŰ±ŰȘ ŰźÙˆŰŻ ۱ۧ Ù†ŰŽŰ§Ù† می ŰŻÙ‡ŰŻ - Ű§ÛŒÙ† Ù†ŰŽŰ§Ù† ŰŻÙ‡Ù†ŰŻÙ‡ Ű§Ù†ŰŹŰ§Ù… هیچ Ú©Ű§Ű±ÛŒ Ù†ÛŒŰłŰȘی ÙÙ‚Ű· Ű§ÛŒÙ†Ú©Ù‡ Ű§Ùˆ ۧŰČ ŰȘŰčۯۧۯ کمی ۧŰČ ÙŰ±ÙˆŰŻÚŻŰ§Ù‡ Ù‡Ű§ و هŰȘل Ù‡Ű§ ۚۧŰČŰŻÛŒŰŻ Ú©Ű±ŰŻÙ‡ ۧ۳ŰȘ. + +ŰąŰČÙ…Ű§ÛŒŰŽ Ù…ŰšŰȘنی ۚ۱ ŰŹÙ‡ŰŽ ۚۧ Ű§Ù†ŰŻŰ§ŰČÙ‡â€ŒÚŻÛŒŰ±ÛŒ Ù…Ù‚ŰŻŰ§Ű± Ú©ŰŻÛŒ که ÙˆŰ§Ù‚ŰčŰ§Ù‹ ŰȘŰłŰȘ ŰŽŰŻÙ‡ ۧ۳ŰȘ و نه ÙÙ‚Ű· ۚۧŰČŰŻÛŒŰŻ ŰŽŰŻÙ‡ŰŒ ŰšÙ‡ ŰŽÙ…Ű§ کمک Ù…ÛŒâ€ŒÚ©Ù†ŰŻ.. [Stryker](https://stryker-mutator.io/) یک Ú©ŰȘŰ§ŰšŰźŰ§Ù†Ù‡ ŰŹŰ§ÙˆŰ§ Ű§ŰłÚ©Ű±ÛŒÙŸŰȘ ŰšŰ±Ű§ÛŒ ŰąŰČÙ…Ű§ÛŒŰŽ ŰŹÙ‡ŰŽ ۧ۳ŰȘ و ÙŸÛŒŰ§ŰŻÙ‡ ۳ۧŰČی ŰąÙ† ÙˆŰ§Ù‚ŰčŰ§Ù‹ Ù…Ù†ŰžÙ… ۧ۳ŰȘ: + +(1) ŰšÙ‡ ŰčÙ…ŰŻ Ú©ŰŻ ۱ۧ ŰȘŰșÛŒÛŒŰ± می ŰŻÙ‡ŰŻ و "Ű§ŰŽÚ©Ű§Ù„Ű§ŰȘ ÚŻÛŒŰ§Ù‡ÛŒ" ۱ۧ Ű§ÛŒŰŹŰ§ŰŻ می Ú©Ù†ŰŻ. ŰšŰ±Ű§ÛŒ Ù…Ű«Ű§Ù„ Ú©ŰŻ newOrder.price===0 ŰšÙ‡ newOrder.price!=0 ŰȘŰšŰŻÛŒÙ„ می ŰŽÙˆŰŻ. Ű§ÛŒÙ† "Ű§ŰŽÚ©Ű§Ù„" ŰŹÙ‡ŰŽ Ù†Ű§Ù…ÛŒŰŻÙ‡ می ŰŽÙˆŰŻ + +(2) ŰȘŰłŰȘ Ù‡Ű§ ۱ۧ ۧۏ۱ۧ می Ú©Ù†ŰŻŰŒ ۧگ۱ همه موفق ŰŽÙˆÙ†ŰŻŰŒ Ù…ŰŽÚ©Ù„ ŰŻŰ§Ű±ÛŒÙ… - ŰąŰČÙ…Ű§ÛŒŰŽ Ù‡Ű§ ŰšÙ‡ Ù‡ŰŻÙ ŰźÙˆŰŻ یŰčنی Ú©ŰŽÙ ۚۧگ Ù‡Ű§ Űčمل Ù†Ú©Ű±ŰŻÙ†ŰŻŰŒ ŰŹÙ‡ŰŽ Ù‡Ű§ ŰšÙ‡ Ű§Ű”Ű·Ù„Ű§Ű­ ŰČÙ†ŰŻÙ‡ می Ù…Ű§Ù†Ù†ŰŻ. ۧگ۱ ŰąŰČÙ…Ű§ÛŒŰŽ Ù‡Ű§ ŰŽÚ©ŰłŰȘ ŰźÙˆŰ±ŰŻÙ†ŰŻŰŒ ŰčŰ§Ù„ÛŒ ۧ۳ŰȘی ŰŹÙ‡ŰŽ Ù‡Ű§ Ú©ŰŽŰȘه ŰŽŰŻÙ†ŰŻ. + +ŰŻŰ§Ù†ŰłŰȘن Ű§ÛŒÙ†Ú©Ù‡ همه ÛŒŰ§ ŰšÛŒŰŽŰȘ۱ ŰŹÙ‡ŰŽâ€ŒÙ‡Ű§ Ú©ŰŽŰȘه ŰŽŰŻÙ‡â€ŒŰ§Ù†ŰŻŰŒ Ű§Ű·Ù…ÛŒÙ†Ű§Ù† ŰšŰłÛŒŰ§Ű± ŰšÛŒŰŽŰȘŰ±ÛŒ Ù†ŰłŰšŰȘ ŰšÙ‡ ÙŸÙˆŰŽŰŽ ŰłÙ†ŰȘی Ù…ÛŒâ€ŒŰŻÙ‡ŰŻ و ŰČÙ…Ű§Ù† Ű±Ű§Ù‡â€ŒŰ§Ù†ŰŻŰ§ŰČی Ù…ŰŽŰ§ŰšÙ‡ ۧ۳ŰȘ. +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** ŰŽÙ…Ű§ ÙŰ±ÛŒŰš ŰźÙˆŰ§Ù‡ÛŒŰŻ ŰźÙˆŰ±ŰŻ که ÙÚ©Ű± Ú©Ù†ÛŒŰŻ ÙŸÙˆŰŽŰŽ 85ÙȘ ŰšÙ‡ Ű§ÛŒÙ† مŰčنی ۧ۳ŰȘ که ŰȘŰłŰȘ ŰŽÙ…Ű§ Ű§ŰŽÚ©Ű§Ù„Ű§ŰȘ ۱ۧ ۯ۱ 85ÙȘ ۧŰČ Ú©ŰŻ ŰŽÙ…Ű§ ŰȘŰŽŰźÛŒŰ” می ŰŻÙ‡ŰŻ. + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :thumbsdown: Ù…Ű«Ű§Ù„ ۶ۯ Ű§Ù„ÚŻÙˆ: 100% ÙŸÙˆŰŽŰŽŰŒ 0% ŰȘŰłŰȘ + +![](https://img.shields.io/badge/🔹%20Example%20using%20Stryker-blue.svg "Using Stryker") + +```javascript +function addNewOrder(newOrder) { + logger.log(`Adding new order ${newOrder}`); + DB.save(newOrder); + Mailer.sendMail(newOrder.assignee, `A new order was places ${newOrder}`); + + return { approved: true }; +} + +it("addNewOrder ۱ۧ ŰȘŰłŰȘ Ú©Ù†ÛŒŰŻŰŒ ۧŰČ Ú†Ù†ÛŒÙ† Ù†Ű§Ù… Ù‡Ű§ÛŒ ŰąŰČÙ…Ű§ÛŒŰŽÛŒ ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ù†Ú©Ù†ÛŒŰŻ", () => { + addNewOrder({ assignee: "John@mailer.com", price: 120 }); +}); //ÙŸÙˆŰŽŰŽ 100ÙȘ Ú©ŰŻ ۱ۧ فŰčŰ§Ù„ می Ú©Ù†ŰŻŰŒ Ű§Ù…Ű§ چیŰČی ۱ۧ ŰšŰ±Ű±ŰłÛŒ نمی Ú©Ù†ŰŻ +``` + +
    + +### :clap: Ű§Ù†ŰŹŰ§Ù… ۯ۱۳ŰȘ Ù…Ű«Ű§Ù„: ÚŻŰČۧ۱ێ Ù‡Ű§ÛŒ Strykerی ۧۚŰČŰ§Ű±ÛŒ ŰšŰ±Ű§ÛŒ ŰąŰČÙ…Ű§ÛŒŰŽ ŰŹÙ‡ŰŽŰŒ Ù…Ù‚ŰŻŰ§Ű± Ú©ŰŻÛŒ ۱ۧ که ŰąŰČÙ…Ű§ÛŒŰŽ Ù†ŰŽŰŻÙ‡ ۧ۳ŰȘ ŰŽÙ†Ű§ŰłŰ§ÛŒÛŒ و ŰŽÙ…Ű§Ű±ŰŽ می Ú©Ù†ŰŻ (ŰŹÙ‡ŰŽ) + +![alt text](assets/bp-20-yoni-goldberg-mutation-testing.jpeg "Stryker reports, a tool for mutation testing, detects and counts the amount of code that is not tested (Mutations)") + +
    + +

    + +## âšȘ 4.4 ŰŹÙ„ÙˆÚŻÛŒŰ±ÛŒ ۧŰČ Ù…ŰŽÚ©Ù„Ű§ŰȘ Ú©ŰŻ ŰȘŰłŰȘ ۚۧ لینŰȘŰ±Ù‡Ű§ÛŒ ŰȘŰłŰȘ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** Ù…ŰŹÙ…ÙˆŰčه Ű§ÛŒ ۧŰČ ÙŸÙ„Ű§ÚŻÛŒÙ† Ù‡Ű§ÛŒ ESLint ŰšÙ‡ Ű·ÙˆŰ± ۟ۧ۔ ŰšŰ±Ű§ÛŒ ۚۧŰČŰ±ŰłÛŒ Ű§Ù„ÚŻÙˆÙ‡Ű§ÛŒ Ú©ŰŻ ŰȘŰłŰȘ و Ú©ŰŽÙ Ù…ŰŽÚ©Ù„Ű§ŰȘ ۳ۧ۟ŰȘه ŰŽŰŻÙ‡ ۧ۳ŰȘ. Ù…Ű«Ù„Ű§, [eslint-plugin-mocha](https://www.npmjs.com/package/eslint-plugin-mocha) Ù‡Ù†ÚŻŰ§Ù…ÛŒ که یک ŰȘŰłŰȘ ۯ۱ ۳۷ۭ ŰŹÙ‡Ű§Ù†ÛŒ Ù†ÙˆŰŽŰȘه می ŰŽÙˆŰŻ (نه یک Űčۚۧ۱ŰȘ describe()) ÛŒŰ§ ŰČÙ…Ű§Ù†ÛŒ که ŰȘŰłŰȘ Ù‡Ű§ [ÙŸŰ±ŰŽ ک۱ۯ](https://mochajs.org/#inclusive-tests) که ممکن ۧ۳ŰȘ ŰšÙ‡ Ű§ÛŒÙ† ŰšŰ§ÙˆŰ± Ù†Ű§ŰŻŰ±ŰłŰȘ Ù…Ù†ŰŹŰ± ŰŽÙˆŰŻ که همه ŰąŰČمون Ù‡Ű§ Ù‚ŰšÙˆÙ„ ŰŽŰŻÙ‡ Ű§Ù†ŰŻ. ŰšÙ‡ همین ŰȘ۱ŰȘÛŒŰšŰŒ [eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest) ŰšŰ±Ű§ÛŒ Ù…Ű«Ű§Ù„ می ŰȘÙˆŰ§Ù†ŰŻ Ù‡ŰŽŰŻŰ§Ű± ŰŻÙ‡ŰŻ ŰČÙ…Ű§Ù†ÛŒ که یک ŰąŰČمون Ű§Ű”Ù„Ű§Ù‹ ۧۯŰčŰ§ÛŒÛŒ Ù†ŰŻŰ§Ű±ŰŻ (چک Ù†Ú©Ű±ŰŻÙ† چیŰČی) + +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** Ù…ŰŽŰ§Ù‡ŰŻÙ‡ 90% ÙŸÙˆŰŽŰŽ Ú©ŰŻ و 100% ŰȘŰłŰȘâ€ŒÙ‡Ű§ÛŒ ۳ۚŰČ ŰšŰ§ŰčŰ« Ù…ÛŒâ€ŒŰŽÙˆŰŻ Ú†Ù‡Ű±Ù‡ ŰŽÙ…Ű§ Ù„ŰšŰźÙ†ŰŻ ŰšŰČŰ±ÚŻÛŒ ŰšŰČÙ†ŰŻ ÙÙ‚Ű· ŰȘۧ ŰČÙ…Ű§Ù†ÛŒ که مŰȘÙˆŰŹÙ‡ ŰŽÙˆÛŒŰŻ که ŰšŰłÛŒŰ§Ű±ÛŒ ۧŰČ ŰȘŰłŰȘâ€ŒÙ‡Ű§ ŰšŰ±Ű§ÛŒ هیچ چیŰČی ۧۯŰčۧ Ù†Ù…ÛŒâ€ŒÚ©Ù†Ù†ŰŻ و ŰšŰłÛŒŰ§Ű±ÛŒ ۧŰČ Ù…ŰŹÙ…ÙˆŰčÙ‡â€ŒÙ‡Ű§ÛŒ ŰąŰČÙ…Ű§ÛŒŰŽÛŒ Ű”Ű±ÙŰ§Ù‹ Ù†Ű§ŰŻÛŒŰŻÙ‡ ÚŻŰ±ÙŰȘه ŰŽŰŻÙ‡â€ŒŰ§Ù†ŰŻ. Ű§Ù…ÛŒŰŻÙˆŰ§Ű±Ù… ۚ۱ ۧ۳ۧ۳ Ű§ÛŒÙ† Ù…ŰŽŰ§Ù‡ŰŻÙ‡ Ù†Ű§ŰŻŰ±ŰłŰȘ چیŰČی ۱ۧ Ù…ŰłŰȘÙ‚Ű± Ù†Ú©Ű±ŰŻÙ‡ ŰšŰ§ŰŽÛŒŰŻ + +
    +
    ✏ نمونه Ú©ŰŻ + +
    + +### :thumbsdown:Ù…Ű«Ű§Ù„ ۶ۯ Ű§Ù„ÚŻÙˆ: یک Ù…ÙˆŰ±ŰŻ ŰąŰČÙ…Ű§ÛŒŰŽÛŒ ÙŸŰ± ۧŰČ ŰźŰ·Ű§ŰŒ ŰźÙˆŰŽŰšŰźŰȘŰ§Ù†Ù‡ همه ŰȘÙˆŰłŰ· Linters ŰŻŰłŰȘÚŻÛŒŰ± ŰŽŰŻÙ†ŰŻ + +```javascript +describe("ŰȘÙˆŰ¶ÛŒŰ­Ű§ŰȘ ŰźÛŒÙ„ÛŒ کوŰȘŰ§Ù‡", () => { + const userToken = userService.getDefaultToken() // *error:no-setup-in-describeی ŰšÙ‡ ŰŹŰ§ÛŒ ŰąÙ† ۧŰČ Ù‡ÙˆÚ© Ù‡Ű§ (ŰšÙ‡ Ù…Ù‚ŰŻŰ§Ű± کم) ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ú©Ù†ÛŒŰŻ + it("کمی ŰȘÙˆŰ¶ÛŒŰ­Ű§ŰȘ", () => {});//* error: valid-test-description. ŰšŰ§ÛŒŰŻ ŰŽŰ§Ù…Ù„ کلمه "Should" + Ű­ŰŻŰ§Ù‚Ù„ 5 کلمه ۚۧێۯ +}); + +it.skip("Ù†Ű§Ù… ŰȘŰłŰȘ", () => {// *error:no-skipped-tests, error:error:no-global-tests. ŰȘŰłŰȘ Ù‡Ű§ ۱ۧ ÙÙ‚Ű· ۯ۱ Ù‚ŰłÙ…ŰȘ ŰȘÙˆŰ”ÛŒÙ ÛŒŰ§ Ù…ŰŹÙ…ÙˆŰčه Ù‚Ű±Ű§Ű± ŰŻÙ‡ÛŒŰŻ + expect("somevalue"); // error:no-assert +}); + +it("Ù†Ű§Ù… ŰȘŰłŰȘ", () => {// *error:no-identical-title. ŰčÙ†Ű§ÙˆÛŒÙ† Ù…Ù†Ű­Ű”Ű± ŰšÙ‡ ÙŰ±ŰŻ ۱ۧ ŰšÙ‡ ŰąŰČمون Ù‡Ű§ ۧ۟ŰȘ۔ۧ۔ ŰŻÙ‡ÛŒŰŻ +}); +``` + +
    + +

    + +# ۚ۟ێ 5ïžâƒŁ: CI و ŰłŰ§ÛŒŰ± مŰčÛŒŰ§Ű±Ù‡Ű§ÛŒ کیفیŰȘ + +

    + +## âšȘ  5.1 Enrich your linters and abort builds that have linting issues + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** لینŰȘŰ±Ù‡Ű§ یک Ù†Ű§Ù‡Ű§Ű± Ű±Ű§ÛŒÚŻŰ§Ù† Ù‡ŰłŰȘÙ†ŰŻŰŒ ۚۧ Ű±Ű§Ù‡ Ű§Ù†ŰŻŰ§ŰČی 5 ŰŻÙ‚ÛŒÙ‚Ù‡ Ű§ÛŒ ŰšÙ‡ Ű”ÙˆŰ±ŰȘ Ű±Ű§ÛŒÚŻŰ§Ù† یک ŰźÙ„ŰšŰ§Ù† ŰźÙˆŰŻÚ©Ű§Ű± ۧŰČ Ú©ŰŻ ŰŽÙ…Ű§ Ù…Ű­Ű§ÙŰžŰȘ می Ú©Ù†ŰŻ و Ù‡Ù†ÚŻŰ§Ù… ŰȘŰ§ÛŒÙŸ Ù…ŰŽÚ©Ù„ مهمی ۱ۧ مŰȘÙˆŰŹÙ‡ می ŰŽÙˆÛŒŰŻ. Ű±ÙˆŰČÙ‡Ű§ÛŒÛŒ که ÙŸŰ±ŰČÙ‡Ű§ ۯ۱ Ù…ÙˆŰ±ŰŻ Ù„ÙˆŰ§ŰČم ŰąŰ±Ű§ÛŒŰŽÛŒ ŰšÙˆŰŻÙ†ŰŻ (نه نیم کولن!) گ۰ێŰȘه ۧ۳ŰȘ. Ű§Ù…Ű±ÙˆŰČÙ‡ŰŒ Linters می ŰȘÙˆŰ§Ù†ŰŻ Ù…ŰŽÚ©Ù„Ű§ŰȘ ŰŽŰŻÛŒŰŻÛŒ Ù…Ű§Ù†Ù†ŰŻ ŰźŰ·Ű§Ù‡Ű§ÛŒÛŒ که ŰšÙ‡ ۯ۱۳ŰȘی ÙŸŰ±ŰȘۧۚ نمی ŰŽÙˆÙ†ŰŻ و Ű§Ű·Ù„Ű§ŰčۧŰȘ ۧŰČ ŰŻŰłŰȘ می ŰŻÙ‡Ù†ŰŻŰŒ ŰšÚŻÛŒŰ±Ù†ŰŻ. ŰčÙ„Ű§ÙˆÙ‡ ۚ۱ Ù…ŰŹÙ…ÙˆŰčه Ù‚ÙˆŰ§Ù†ÛŒÙ† Ű§ŰłŰ§ŰłÛŒ ŰŽÙ…Ű§ (Ù…Ű§Ù†Ù†ŰŻ [ESLint standard](https://www.npmjs.com/package/eslint-plugin-standard) ÛŒŰ§ [Airbnb style](https://www.npmjs.com/package/eslint-config-airbnb)), ŰŽŰ§Ù…Ù„ ŰšŰ±ŰźÛŒ ۧŰČ Linters ŰȘŰźŰ”Ű”ÛŒ Ù…Ű§Ù†Ù†ŰŻ [eslint-plugin-chai-expect](https://www.npmjs.com/package/eslint-plugin-chai-expect) که می ŰȘÙˆŰ§Ù†ŰŻ ŰȘŰłŰȘ Ù‡Ű§ ۱ۧ ŰšŰŻÙˆÙ† ۧۯŰčۧ Ú©ŰŽÙ Ú©Ù†ŰŻ, [eslint-plugin-promise](https://www.npmjs.com/package/eslint-plugin-promise?activeTab=readme) می ŰȘÙˆŰ§Ù†ŰŻ وŰčŰŻÙ‡ Ù‡Ű§ ۱ۧ ŰšŰŻÙˆÙ† هیچ Ù…ŰŽÚ©Ù„ÛŒ Ú©ŰŽÙ Ú©Ù†ŰŻ (Ú©ŰŻ ŰŽÙ…Ű§ Ù‡Ű±ÚŻŰČ Ű§ŰŻŰ§Ù…Ù‡ Ù†ŰźÙˆŰ§Ù‡ŰŻ ۯۧێŰȘ), [eslint-plugin-security](https://www.npmjs.com/package/eslint-plugin-security?activeTab=readme) که می ŰȘÙˆŰ§Ù†ŰŻ Űčۚۧ۱ۧŰȘ regex Ù…ŰŽŰȘŰ§Ù‚ ۱ۧ Ú©ŰŽÙ Ú©Ù†ŰŻ که ممکن ۧ۳ŰȘ ŰšŰ±Ű§ÛŒ Ű­Ù…Ù„Ű§ŰȘ DOS ۧ۳ŰȘÙŰ§ŰŻÙ‡ ŰŽÙˆŰŻŰŒ و [eslint-plugin-you-dont-need-lodash-underscore](https://www.npmjs.com/package/eslint-plugin-you-dont-need-lodash-underscore) ŰČÙ…Ű§Ù†ÛŒ که Ú©ŰŻ ۧŰČ Ű±ÙˆŰŽâ€ŒÙ‡Ű§ÛŒ Ú©ŰȘŰ§ŰšŰźŰ§Ù†Ù‡ ۧۚŰČŰ§Ű±ÛŒ ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ù…ÛŒâ€ŒÚ©Ù†ŰŻ که ŰšŰźŰŽÛŒ ۧŰČ Ű±ÙˆŰŽâ€ŒÙ‡Ű§ÛŒ Ù‡ŰłŰȘه V8 Ù…Ű§Ù†Ù†ŰŻ Lodash Ù‡ŰłŰȘÙ†ŰŻŰŒ می‌ŰȘÙˆŰ§Ù†ŰŻ Ù‡ŰŽŰŻŰ§Ű± ŰŻÙ‡ŰŻ.\_map(
) +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:**یک Ű±ÙˆŰČ ŰšŰ§Ű±Ű§Ù†ÛŒ ۱ۧ ۯ۱ Ù†ŰžŰ± ŰšÚŻÛŒŰ±ÛŒŰŻ که ۯ۱ ŰąÙ† ŰȘÙˆÙ„ÛŒŰŻ ŰŽÙ…Ű§ Ù…ŰŻŰ§Ù… ۟۱ۧۚ Ù…ÛŒâ€ŒŰŽÙˆŰŻ Ű§Ù…Ű§ ÚŻŰČŰ§Ű±ŰŽâ€ŒÙ‡Ű§ Ű±ŰŻÛŒŰ§ŰšÛŒ ÙŸŰŽŰȘه ۟۷ۧ ۱ۧ Ù†ŰŽŰ§Ù† Ù†Ù…ÛŒâ€ŒŰŻÙ‡Ù†ŰŻ. چی ۮۯ۟ Ú©ŰŻ ŰŽÙ…Ű§ ŰšÙ‡ ۧێŰȘŰšŰ§Ù‡ یک ŰŽÛŒ ŰšŰŻÙˆÙ† ۟۷ۧ ÙŸŰ±ŰȘۧۚ ک۱ۯ و ۱ۯ ÙŸŰŽŰȘه ۧŰČ ŰšÛŒÙ† Ű±ÙŰȘی ŰŻÙ„ÛŒÙ„ ŰźÙˆŰšÛŒ ŰšŰ±Ű§ÛŒ Ű¶Ű±ŰšÙ‡ ŰČŰŻÙ† ۳۱ ŰźÙˆŰŻ ŰšÙ‡ ŰŻÛŒÙˆŰ§Ű± ŰąŰŹŰ±ÛŒ ۧ۳ŰȘ. یک Ű±Ű§Ù‡â€ŒŰ§Ù†ŰŻŰ§ŰČی 5 ŰŻÙ‚ÛŒÙ‚Ù‡â€ŒŰ§ÛŒ لینŰȘ۱ می‌ŰȘÙˆŰ§Ù†ŰŻ Ű§ÛŒÙ† ۧێŰȘŰšŰ§Ù‡ ŰȘŰ§ÛŒÙŸÛŒ ۱ۧ ŰŽÙ†Ű§ŰłŰ§ÛŒÛŒ Ú©Ű±ŰŻÙ‡ و ۯ۱ Ű±ÙˆŰČ ŰŽÙ…Ű§ Ű”Ű±ÙÙ‡â€ŒŰŹÙˆÛŒÛŒ Ú©Ù†ŰŻ + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :thumbsdown: Ù…Ű«Ű§Ù„ Anti-Pattern: ŰŽÛŒ Error ۧێŰȘŰšŰ§Ù‡ ŰšÙ‡ ۧێŰȘŰšŰ§Ù‡ ÙŸŰ±ŰȘۧۚ ŰŽŰŻÙ‡ ۧ۳ŰȘی هیچ stack-trace ŰšŰ±Ű§ÛŒ Ű§ÛŒÙ† ۟۷ۧ ŰžŰ§Ù‡Ű± نمی ŰŽÙˆŰŻ. ŰźÙˆŰŽŰšŰźŰȘŰ§Ù†Ù‡ ESLint ۚۧگ ŰȘÙˆÙ„ÛŒŰŻ ŰšŰčŰŻÛŒ ۱ۧ ÙŸÛŒŰŻŰ§ می Ú©Ù†ŰŻ + +![alt text](assets/bp-21-yoni-goldberg-eslint.jpeg "The wrong Error object is thrown mistakenly, no stack-trace will appear for this error. Luckily, ESLint catches the next production bug") + +
    + +

    + +## âšȘ  5.2 Ű­Ù„Ù‚Ù‡ ۚۧŰČŰźÙˆŰ±ŰŻ ۱ۧ ۚۧ ŰȘÙˆŰłŰčه ŰŻÙ‡Ù†ŰŻÙ‡-CI Ù…Ű­Ù„ÛŒ کوŰȘŰ§Ù‡ Ú©Ù†ÛŒŰŻ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** ŰąÛŒŰ§ ۧŰČ ÛŒÚ© CI ۚۧ ۚۧŰČŰ±ŰłÛŒ Ù‡Ű§ÛŒ کیفیŰȘ ŰšŰ±Ű§Ù‚ Ù…Ű§Ù†Ù†ŰŻ ŰąŰČÙ…Ű§ÛŒŰŽŰŒ ÙŸŰ±ŰŻÙ‡ ŰČŰŻÙ†ŰŒ ŰšŰ±Ű±ŰłÛŒ ŰąŰłÛŒŰš ÙŸŰ°ÛŒŰ±ÛŒ Ù‡Ű§ و ŰșÛŒŰ±Ù‡ ۧ۳ŰȘÙŰ§ŰŻÙ‡ می Ú©Ù†ÛŒŰŻŰŸ ŰšÙ‡ ŰȘÙˆŰłŰčه ŰŻÙ‡Ù†ŰŻÚŻŰ§Ù† کمک Ú©Ù†ÛŒŰŻ ŰȘۧ Ű§ÛŒÙ† ۟۷ لوله ۱ۧ نیŰČ ŰšÙ‡ Ű”ÙˆŰ±ŰȘ Ù…Ű­Ù„ÛŒ ۧۏ۱ۧ Ú©Ù†Ù†ŰŻ ŰȘۧ ۚۧŰČŰźÙˆŰ±ŰŻ ÙÙˆŰ±ÛŒ ŰŻŰ±ÛŒŰ§ÙŰȘ Ú©Ù†Ù†ŰŻ و ŰČÙ…Ű§Ù† ۱ۧ کوŰȘŰ§Ù‡ Ú©Ù†Ù†ŰŻ [Ű­Ù„Ù‚Ù‡ ۚۧŰČŰźÙˆŰ±ŰŻ](https://www.gocd.org/2016/03/15/are-you-ready-for-continuous-delivery-part-2-feedback-loops/). Ú†Ű±Ű§ŰŸ یک ÙŰ±ŰąÛŒÙ†ŰŻ ŰȘŰłŰȘ Ú©Ű§Ű±ŰąÙ…ŰŻ Ű­Ù„Ù‚Ù‡ Ù‡Ű§ÛŒ ŰȘÚ©Ű±Ű§Ű±ÛŒ و مŰȘŰčŰŻŰŻÛŒ ۱ۧ ŰȘŰŽÚ©ÛŒÙ„ می ŰŻÙ‡ŰŻ: (1) ŰąŰČÙ…Ű§ÛŒŰŽŰ§ŰȘ -> (2) ۚۧŰČŰźÙˆŰ±ŰŻ -> (3) ۚۧŰČ۳ۧŰČ. Ù‡Ű±Ú†Ù‡ ۚۧŰČŰźÙˆŰ±ŰŻ ŰłŰ±ÛŒŰčŰȘ۱ ۹ۧۮۯی ŰȘÙˆŰłŰčه ŰŻÙ‡Ù†ŰŻÙ‡ می ŰȘÙˆŰ§Ù†ŰŻ ŰȘÚ©Ű±Ű§Ű±Ù‡Ű§ÛŒ ŰšÙ‡ŰšÙˆŰŻ ŰšÛŒŰŽŰȘŰ±ÛŒ ۱ۧ ۯ۱ Ù‡Ű± Ù…Ű§Ú˜ÙˆÙ„ Ű§Ù†ŰŹŰ§Ù… ŰŻÙ‡ŰŻ و نŰȘŰ§ÛŒŰŹ ۱ۧ Ú©Ű§Ù…Ù„ Ú©Ù†ŰŻ. ۯ۱ ŰȘÙ„Ù†ÚŻŰ±ŰŒ ŰČÙ…Ű§Ù†ÛŒ که ۚۧŰČŰźÙˆŰ±ŰŻ ŰŻÛŒŰ± می Ű±ŰłŰŻŰŒ ŰȘÚ©Ű±Ű§Ű±Ù‡Ű§ÛŒ ŰšÙ‡ŰšÙˆŰŻ کمŰȘŰ±ÛŒ می ŰȘÙˆŰ§Ù†ŰŻ ۯ۱ یک Ű±ÙˆŰČ ŰšŰłŰȘه ŰšÙ†ŰŻÛŒ ŰŽÙˆŰŻŰŒ ŰȘیم ممکن ۧ۳ŰȘ ۯ۱ Ű­Ű§Ù„ ۭۧ۶۱ ŰšÙ‡ ŰłÙ…ŰȘ Ù…ÙˆŰ¶ÙˆŰč/ÙˆŰžÛŒÙÙ‡/Ù…Ű§Ú˜ÙˆÙ„ ŰŻÛŒÚŻŰ±ÛŒ ۭ۱کŰȘ Ú©Ù†ŰŻ و ممکن ۧ۳ŰȘ ŰšŰ±Ű§ÛŒ Ű§Ű”Ù„Ű§Ű­ ŰąÙ† Ù…Ű§Ú˜ÙˆÙ„ ŰąÙ…Ű§ŰŻÙ‡ Ù†ŰšŰ§ŰŽŰŻ. + +ۯ۱ ŰčÙ…Ù„ŰŒ ŰšŰ±ŰźÛŒ ۧŰČ ÙŰ±ÙˆŰŽÙ†ŰŻÚŻŰ§Ù† CI (Ù…Ű«Ű§Ù„: [CLI Ù…Ű­Ù„ÛŒ CircleCI](https://circleci.com/docs/2.0/local-cli/)) ۧۏۧŰČه می ŰŻÙ‡ŰŻ ۟۷ لوله ۱ۧ ŰšÙ‡ Ű”ÙˆŰ±ŰȘ Ù…Ű­Ù„ÛŒ ۧۏ۱ۧ Ú©Ù†ŰŻ. ŰšŰ±ŰźÛŒ ۧŰČ Ű§ŰšŰČŰ§Ű±Ù‡Ű§ÛŒ ŰȘŰŹŰ§Ű±ÛŒ Ù…Ű§Ù†Ù†ŰŻ [wallaby ŰšÛŒÙ†ŰŽ Ù‡Ű§ÛŒ ŰšŰłÛŒŰ§Ű± ۧ۱ŰČŰŽÙ…Ù†ŰŻ و ŰąŰČÙ…Ű§ÛŒŰŽÛŒ ۱ۧ Ű§Ű±Ű§ŰŠÙ‡ می ŰŻÙ‡ŰŻ](https://wallabyjs.com/) ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† نمونه Ű§ÙˆÙ„ÛŒÙ‡ ŰȘÙˆŰłŰčه ŰŻÙ‡Ù†ŰŻÙ‡ (ŰšŰŻÙˆÙ† ÙˆŰ§ŰšŰłŰȘÚŻÛŒ). ۧŰČ Ű·Ű±Ù ŰŻÛŒÚŻŰ±ŰŒ می‌ŰȘÙˆŰ§Ù†ÛŒŰŻ Ű§ŰłÚ©Ű±ÛŒÙŸŰȘ npm ۱ۧ ŰšÙ‡ package.json Ű§Ű¶Ű§ÙÙ‡ Ú©Ù†ÛŒŰŻ که ŰȘÙ…Ű§Ù… ŰŻŰłŰȘÙˆŰ±Ű§ŰȘ کیفیŰȘ (Ù…Ű§Ù†Ù†ŰŻ ŰȘŰłŰȘی ÙŸŰ±ŰČی ŰąŰłÛŒŰšâ€ŒÙŸŰ°ÛŒŰ±ÛŒâ€ŒÙ‡Ű§) ۱ۧ ۧۏ۱ۧ Ù…ÛŒâ€ŒÚ©Ù†ŰŻ - ۧŰČ Ű§ŰšŰČŰ§Ű±Ù‡Ű§ÛŒÛŒ Ù…Ű§Ù†Ù†ŰŻ ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ú©Ù†ÛŒŰŻ. [همŰČÙ…Ű§Ù†](https://www.npmjs.com/package/concurrently) ŰšŰ±Ű§ÛŒ Ù…ÙˆŰ§ŰČی ۳ۧŰČی و Ú©ŰŻ ŰźŰ±ÙˆŰŹ ŰșÛŒŰ± Ű”ÙŰ± ۯ۱ Ű”ÙˆŰ±ŰȘ ŰźŰ±Ű§ŰšÛŒ یکی ۧŰČ Ű§ŰšŰČŰ§Ű±Ù‡Ű§. Ű§Ú©Ù†ÙˆÙ† ŰȘÙˆŰłŰčÙ‡â€ŒŰŻÙ‡Ù†ŰŻÙ‡ ŰšŰ§ÛŒŰŻ ÙÙ‚Ű· یک ŰŻŰłŰȘÙˆŰ± ۱ۧ ÙŰ±Ű§ŰźÙˆŰ§Ù†ÛŒ Ú©Ù†ŰŻ - ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† Ù…Ű«Ű§Ù„. "کیفیŰȘ Ű§ŰŹŰ±Ű§ÛŒ npm" -â€ŠŰšŰ±Ű§ÛŒ ŰŻŰ±ÛŒŰ§ÙŰȘ ۚۧŰČŰźÙˆŰ±ŰŻ ÙÙˆŰ±ÛŒ. همچنین ۯ۱ Ű”ÙˆŰ±ŰȘ ŰčŰŻÙ… موفقیŰȘ ŰšŰ±Ű±ŰłÛŒ کیفیŰȘ ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ githookی یک commit ۱ۧ لŰșو Ú©Ù†ÛŒŰŻ ([Ù‡Ű§ŰłÚ©ÛŒ می ŰȘÙˆŰ§Ù†ŰŻ کمک Ú©Ù†ŰŻ](https://github.com/typicode/husky)) +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** Ù‡Ù†ÚŻŰ§Ù…ÛŒ که نŰȘŰ§ÛŒŰŹ کیفی یک Ű±ÙˆŰČ ÙŸŰł ۧŰČ Ú©ŰŻ ŰšÙ‡ ŰŻŰłŰȘ Ù…ÛŒâ€ŒŰąÛŒÙ†ŰŻŰŒ ŰąŰČÙ…Ű§ÛŒŰŽ ŰšÙ‡ ŰšŰźŰŽÛŒ Ű±ÙˆŰ§Ù† ۧŰČ ŰȘÙˆŰłŰčه ŰȘŰšŰŻÛŒÙ„ Ù†Ù…ÛŒâ€ŒŰŽÙˆŰŻŰŒ ŰšÙ„Ú©Ù‡ ŰšÙ‡ یک Ù…Ű”Ù†ÙˆŰč Ű±ŰłÙ…ÛŒ ÙŸŰł ۧŰČ ÙˆŰ§Ù‚ŰčیŰȘ ŰȘŰšŰŻÛŒÙ„ Ù…ÛŒâ€ŒŰŽÙˆŰŻ. + +
    + +
    ✏ نمونه Ù‡Ű§ÛŒ Ú©ŰŻ + +
    + +### :clap: ۯ۱۳ŰȘ Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ† Ù…Ű«Ű§Ù„: Ű§ŰłÚ©Ű±ÛŒÙŸŰȘâ€ŒÙ‡Ű§ÛŒ npm که ۚۧŰČŰ±ŰłÛŒ کیفیŰȘ Ú©ŰŻ ۱ۧ Ű§Ù†ŰŹŰ§Ù… Ù…ÛŒâ€ŒŰŻÙ‡Ù†ŰŻŰŒ همه ŰšÙ‡â€ŒŰ”ÙˆŰ±ŰȘ Ù…ÙˆŰ§ŰČی ۧۏ۱ۧ Ù…ÛŒâ€ŒŰŽÙˆÙ†ŰŻ ÛŒŰ§ ŰČÙ…Ű§Ù†ÛŒ که یک ŰȘÙˆŰłŰčÙ‡â€ŒŰŻÙ‡Ù†ŰŻÙ‡ ŰȘÙ„Ű§ŰŽ Ù…ÛŒâ€ŒÚ©Ù†ŰŻ Ú©ŰŻ ŰŹŰŻÛŒŰŻÛŒ ۱ۧ ÙˆŰ§Ű±ŰŻ Ú©Ù†ŰŻ. + +```json +{ + "scripts": { + "inspect:sanity-testing": "mocha **/**--test.js --grep \"sanity\"", + "inspect:lint": "eslint .", + "inspect:vulnerabilities": "npm audit", + "inspect:license": "license-checker --failOn GPLv2", + "inspect:complexity": "plato .", + "inspect:all": "concurrently -c \"bgBlue.bold,bgMagenta.bold,yellow\" \"npm:inspect:quick-testing\" \"npm:inspect:lint\" \"npm:inspect:vulnerabilities\" \"npm:inspect:license\"" + }, + "husky": { + "hooks": { + "precommit": "npm run inspect:all", + "prepush": "npm run inspect:all" + } + } +} +``` + +
    + +

    + +## âšȘ 5.3 ŰąŰČÙ…Ű§ÛŒŰŽ e2e ۱ۧ Ű±ÙˆÛŒ یک ŰąÛŒÙ†Ù‡ ŰȘÙˆÙ„ÛŒŰŻ ÙˆŰ§Ù‚Űčی Ű§Ù†ŰŹŰ§Ù… ŰŻÙ‡ÛŒŰŻ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** ŰąŰČÙ…Ű§ÛŒŰŽ Ű§Ù†ŰȘÙ‡Ű§ ŰšÙ‡ Ű§Ù†ŰȘÙ‡Ű§ (e2e) Ú†Ű§Ù„ŰŽ Ű§Ű”Ù„ÛŒ Ù‡Ű± ۟۷ لوله CI ۧ۳ŰȘ - Ű§ÛŒŰŹŰ§ŰŻ یک ŰąÛŒÙ†Ù‡ ŰȘÙˆÙ„ÛŒŰŻ ŰČÙˆŰŻÚŻŰ°Ű± ÛŒÚ©ŰłŰ§Ù† ۯ۱ Ű­Ű§Ù„ ÙŸŰ±ÙˆŰ§ŰČ ŰšŰ§ ŰȘÙ…Ű§Ù… ŰźŰŻÙ…Ű§ŰȘ Ű§ŰšŰ±ÛŒ Ù…Ű±ŰȘۚ۷ می ŰȘÙˆŰ§Ù†ŰŻ ۟۳ŰȘه Ú©Ù†Ù†ŰŻÙ‡ و ÚŻŰ±Ű§Ù† ۚۧێۯ. ÛŒŰ§ÙŰȘن ŰšÙ‡ŰȘŰ±ÛŒÙ† ۳ۧŰČŰŽ ۚۧŰČی ŰŽÙ…Ű§ŰłŰȘ: [Docker-compose](https://serverless.com/) ۧۏۧŰČه می ŰŻÙ‡ŰŻ ŰȘۧ ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ ÛŒÚ© ÙŰ§ÛŒÙ„ مŰȘنی ŰłŰ§ŰŻÙ‡ŰŒ Ù…Ű­ÛŒŰ· Ű§ÛŒŰČوله ŰŽŰŻÙ‡ ۱ۧ ۚۧ Ú©Ű§Ù†ŰȘÛŒÙ†Ű±Ù‡Ű§ÛŒ ÛŒÚ©ŰłŰ§Ù† Ű§ÛŒŰŹŰ§ŰŻ Ú©Ù†ÛŒŰŻŰŒ Ű§Ù…Ű§ ÙÙ†Ű§ÙˆŰ±ÛŒ ÙŸŰŽŰȘÛŒŰšŰ§Ù† (ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† Ù…Ű«Ű§Ù„ ŰŽŰšÚ©Ù‡ŰŒ Ù…ŰŻÙ„ ۧ۳ŰȘÙ‚Ű±Ű§Ű±) ۚۧ ŰȘÙˆÙ„ÛŒŰŻŰ§ŰȘ ŰŻÙ†ÛŒŰ§ÛŒ ÙˆŰ§Ù‚Űčی مŰȘÙŰ§ÙˆŰȘ ۧ۳ŰȘ. می ŰȘÙˆŰ§Ù†ÛŒŰŻ ŰąÙ† ۱ۧ ۚۧ ŰąÙ† ŰȘŰ±Ú©ÛŒŰš Ú©Ù†ÛŒŰŻ [‘AWS Ù…Ű­Ù„ÛŒâ€™](https://github.com/localstack/localstack) ŰšŰ±Ű§ÛŒ کۧ۱ ۚۧ ŰȘŰčŰŻŰ§ŰŻÛŒ ۧŰČ ŰźŰŻÙ…Ű§ŰȘ ÙˆŰ§Ù‚Űčی AWS. Ű§ÚŻÙ‡ Ű±ÙŰȘی [serverless](https://serverless.com/) ÙŰ±ÛŒÙ…ÙˆŰ±Ú© Ù‡Ű§ÛŒ مŰȘŰčŰŻŰŻ Ù…Ű§Ù†Ù†ŰŻ ŰšŰŻÙˆÙ† ŰłŰ±ÙˆŰ± و [AWS SAM](https://docs.aws.amazon.com/lambda/latest/dg/serverless_app.html) ÙŰ±Ű§ŰźÙˆŰ§Ù†ÛŒ Ù…Ű­Ù„ÛŒ Ú©ŰŻ FaaS ۱ۧ Ű§Ù…Ú©Ű§Ù† ÙŸŰ°ÛŒŰ± می Ú©Ù†ŰŻ. + +Ű§Ú©ÙˆŰłÛŒŰłŰȘم ŰčŰžÛŒÙ… Kubernetes هنوŰČ ÛŒÚ© ۧۚŰČۧ۱ Ù…Ù†Ű§ŰłŰš ۧ۳ŰȘŰ§Ù†ŰŻŰ§Ű±ŰŻ ŰšŰ±Ű§ÛŒ ŰąÛŒÙ†Ù‡â€ŒÚ©Ű§Ű±ÛŒ Ù…Ű­Ù„ÛŒ و CI Ű±ŰłÙ…ÛŒ Ù†Ú©Ű±ŰŻÙ‡ ۧ۳ŰȘی Ű§ÚŻŰ±Ú†Ù‡ ۧۚŰČŰ§Ű±Ù‡Ű§ÛŒ ŰŹŰŻÛŒŰŻ ŰČÛŒŰ§ŰŻÛŒ ŰšÙ‡ Ű·ÙˆŰ± Ù…Ú©Ű±Ű± Ű±Ű§Ù‡â€ŒŰ§Ù†ŰŻŰ§ŰČی Ù…ÛŒâ€ŒŰŽÙˆÙ†ŰŻ. یک Ű±ÙˆÛŒÚ©Ű±ŰŻ Ű§ŰŹŰ±Ű§ÛŒ Â«Ú©ÙˆŰšŰ±Ù†ŰȘâ€ŒÙ‡Ű§ÛŒ Ú©ÙˆÚ†Ú©â€ŒŰŽŰŻÙ‡Â» ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ Ű§ŰšŰČŰ§Ű±Ù‡Ű§ÛŒÛŒ Ù…Ű§Ù†Ù†ŰŻ [Minikube](https://kubernetes.io/docs/setup/minikube/) and [MicroK8s](https://microk8s.io/) که ŰŽŰšÛŒÙ‡ چیŰČÙ‡Ű§ÛŒ ÙˆŰ§Ù‚Űčی Ù‡ŰłŰȘÙ†ŰŻ ÙÙ‚Ű· ۚۧ ۳۱ۚۧ۱ کمŰȘŰ±ÛŒ ŰčŰ±Ű¶Ù‡ می ŰŽÙˆÙ†ŰŻ. Ű±ÙˆŰŽ ŰŻÛŒÚŻŰ± ŰąŰČÙ…Ű§ÛŒŰŽ ۚ۱ Ű±ÙˆÛŒ یک "Kubernetes ÙˆŰ§Ù‚Űčی" ۧŰČ Ű±Ű§Ù‡ ŰŻÙˆŰ±ŰŒ ŰšŰ±ŰźÛŒ ۧŰČ Ű§Ű±Ű§ŰŠÙ‡ ŰŻÙ‡Ù†ŰŻÚŻŰ§Ù† CI ۧ۳ŰȘ (e.g. [Codefresh](https://codefresh.io/)) ŰŻŰ§Ű±Ű§ÛŒ ۧۯŰșŰ§Ù… ŰšÙˆÙ…ÛŒ ۚۧ Ù…Ű­ÛŒŰ· Kubernetes ۧ۳ŰȘ و Ű§ŰŹŰ±Ű§ÛŒ ۟۷ لوله CI ۱ۧ ۚ۱ Ű±ÙˆÛŒ چیŰČ ÙˆŰ§Ù‚Űčی ŰąŰłŰ§Ù† می Ú©Ù†ŰŻŰŒ ŰŻÛŒÚŻŰ±Ű§Ù† ۧۏۧŰČه می ŰŻÙ‡Ù†ŰŻ Ű§ŰłÚ©Ű±ÛŒÙŸŰȘ ŰłÙŰ§Ű±ŰŽÛŒ ۯ۱ ۚ۱ۧۚ۱ Kubernetes ۧŰČ Ű±Ű§Ù‡ ŰŻÙˆŰ± Ű§Ù†ŰŹŰ§Ù… ŰŽÙˆŰŻ. +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:**ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ ÙÙ†Ű§ÙˆŰ±ÛŒ Ù‡Ű§ÛŒ Ù…ŰźŰȘلف ŰšŰ±Ű§ÛŒ ŰȘÙˆÙ„ÛŒŰŻ و ŰąŰČÙ…Ű§ÛŒŰŽ Ù…ŰłŰȘلŰČم Ű­ÙŰž ŰŻÙˆ Ù…ŰŻÙ„ ۧ۳ŰȘÙ‚Ű±Ű§Ű± ۧ۳ŰȘ و ŰȘÙˆŰłŰčه ŰŻÙ‡Ù†ŰŻÚŻŰ§Ù† و ŰȘیم ŰčÙ…Ù„ÛŒŰ§ŰȘ ۱ۧ ۧŰČ Ù‡Ù… ۏۯۧ Ù†ÚŻÙ‡ می ۯۧ۱ۯ. + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :clap: Ù…Ű«Ű§Ù„: یک ۟۷ لوله CI که ŰźÙˆŰŽÙ‡ Kubernetes ۱ۧ ۯ۱ Ű­Ű§Ù„ ÙŸŰ±ÙˆŰ§ŰČ ŰȘÙˆÙ„ÛŒŰŻ می Ú©Ù†ŰŻ ([ۧŰčŰȘۚۧ۱: ÙŸÙˆÛŒŰ§-Ù…Ű­ÛŒŰ· Ù‡Ű§ÛŒ Kubernetes](https://container-solutions.com/dynamic-environments-kubernetes/)) + +
    deploy:
    stage: deploy
    image: registry.gitlab.com/gitlab-examples/kubernetes-deploy
    script:
    - ./configureCluster.sh $KUBE_CA_PEM_FILE $KUBE_URL $KUBE_TOKEN
    - kubectl create ns $NAMESPACE
    - kubectl create secret -n $NAMESPACE docker-registry gitlab-registry --docker-server="$CI_REGISTRY" --docker-username="$CI_REGISTRY_USER" --docker-password="$CI_REGISTRY_PASSWORD" --docker-email="$GITLAB_USER_EMAIL"
    - mkdir .generated
    - echo "$CI_BUILD_REF_NAME-$CI_BUILD_REF"
    - sed -e "s/TAG/$CI_BUILD_REF_NAME-$CI_BUILD_REF/g" templates/deals.yaml | tee ".generated/deals.yaml"
    - kubectl apply --namespace $NAMESPACE -f .generated/deals.yaml
    - kubectl apply --namespace $NAMESPACE -f templates/my-sock-shop.yaml
    environment:
    name: test-for-ci
    + +
    + +

    + +## âšȘ 5.4 Ù…ÙˆŰ§ŰČی Ú©Ű±ŰŻÙ† Ű§ŰŹŰ±Ű§ÛŒ ŰąŰČÙ…Ű§ÛŒŰŽ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** Ù‡Ù†ÚŻŰ§Ù…ÛŒ که ŰąŰČÙ…Ű§ÛŒŰŽ ŰšÙ‡ ۯ۱۳ŰȘی Ű§Ù†ŰŹŰ§Ù… ŰŽÙˆŰŻŰŒ ŰŻÙˆŰłŰȘ ŰŽÙ…Ű§ 24 ۳ۧŰčŰȘه و ŰȘÙ‚Ű±ÛŒŰšŰ§Ù‹ ÙÙˆŰ±ÛŒ ۚۧŰČŰźÙˆŰ±ŰŻ Ű§Ű±Ű§ŰŠÙ‡ می Ú©Ù†ŰŻ. ۯ۱ ŰčÙ…Ù„ŰŒ Ű§ŰŹŰ±Ű§ÛŒ ŰąŰČÙ…Ű§ÛŒŰŽ 500 ÙˆŰ§Ű­ŰŻ Ù…Ű­ŰŻÙˆŰŻ ŰšÙ‡ CPU ۚ۱ Ű±ÙˆÛŒ یک ۱ێŰȘه ممکن ۧ۳ŰȘ ŰźÛŒÙ„ÛŒ Ű·ÙˆÙ„ ŰšÚ©ŰŽŰŻ. ŰźÙˆŰŽŰšŰźŰȘŰ§Ù†Ù‡ŰŒ ŰŻÙˆÙ†ŰŻÙ‡ Ù‡Ű§ÛŒ ŰąŰČÙ…Ű§ÛŒŰŽÛŒ Ù…ŰŻŰ±Ù† و ٟلŰȘ ÙŰ±Ù… Ù‡Ű§ÛŒ CI (Ù…Ű§Ù†Ù†ŰŻ [Jest](https://github.com/facebook/jest), [AVA](https://github.com/avajs/ava) و [Mocha extensions](https://github.com/yandex/mocha-parallel-tests)) می ŰȘÙˆŰ§Ù†ŰŻ ŰąŰČمون ۱ۧ ۯ۱ Ú†Ù†ŰŻÛŒÙ† ÙŰ±ŰąÛŒÙ†ŰŻ Ù…ÙˆŰ§ŰČی Ú©Ù†ŰŻ و ŰšÙ‡ ŰšÙ‡ŰšÙˆŰŻ Ù‚Ű§ŰšÙ„ ŰȘÙˆŰŹÙ‡ÛŒ ۯ۱ ŰČÙ…Ű§Ù† ۚۧŰČŰźÙˆŰ±ŰŻ ŰŻŰłŰȘ ÛŒŰ§ŰšŰŻ. ŰšŰ±ŰźÛŒ ۧŰČ ÙŰ±ÙˆŰŽÙ†ŰŻÚŻŰ§Ù† CI نیŰČ ŰąŰČÙ…Ű§ÛŒŰŽŰ§ŰȘ ۱ۧ ۯ۱ Ú©Ű§Ù†ŰȘÛŒÙ†Ű±Ù‡Ű§ Ù…ÙˆŰ§ŰČی می Ú©Ù†Ù†ŰŻ (!) که Ű­Ù„Ù‚Ù‡ ۚۧŰČŰźÙˆŰ±ŰŻ ۱ۧ Ű­ŰȘی ŰšÛŒŰŽŰȘ۱ کوŰȘŰ§Ù‡ می Ú©Ù†ŰŻ. چه ŰšÙ‡ Ű”ÙˆŰ±ŰȘ Ù…Ű­Ù„ÛŒ ۚ۱ Ű±ÙˆÛŒ Ú†Ù†ŰŻÛŒÙ† ÙŰ±ŰąÛŒÙ†ŰŻŰŒ ÛŒŰ§ ۧŰČ Ű·Ű±ÛŒÙ‚ ŰšŰ±ŰźÛŒ CLI Ű§ŰšŰ±ÛŒ ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ Ú†Ù†ŰŻÛŒÙ† Ù…Ű§ŰŽÛŒÙ† - ŰȘÙ‚Ű§Ű¶Ű§ÛŒ Ù…ÙˆŰ§ŰČی Ú©Ű±ŰŻÙ† ŰȘŰłŰȘâ€ŒÙ‡Ű§ ۱ۧ Ù…ŰłŰȘقل Ù†ÚŻÙ‡ Ù…ÛŒâ€ŒŰŻŰ§Ű±ŰŻ ŰČÛŒŰ±Ű§ Ù‡Ű± Ú©ŰŻŰ§Ù… ممکن ۧ۳ŰȘ ۯ۱ ÙŰ±ŰąÛŒÙ†ŰŻÙ‡Ű§ÛŒ Ù…ŰźŰȘلف ۧۏ۱ۧ ŰŽÙˆÙ†ŰŻ. + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** ŰŻŰ±ÛŒŰ§ÙŰȘ نŰȘŰ§ÛŒŰŹ ŰąŰČمون 1 ۳ۧŰčŰȘ ÙŸŰł ۧŰČ ÙŰŽŰ§Ű± ŰŻŰ§ŰŻÙ† Ú©ŰŻ ŰŹŰŻÛŒŰŻŰŒ Ù‡Ù…Ű§Ù†Ű·ÙˆŰ± که Ù‚ŰšÙ„Ű§Ù‹ ÙˆÛŒÚ˜ÚŻÛŒâ€ŒÙ‡Ű§ÛŒ ŰšŰčŰŻÛŒ ۱ۧ Ú©ŰŻÙ†ÙˆÛŒŰłÛŒ Ú©Ű±ŰŻÙ‡â€ŒŰ§ÛŒŰŻŰŒ یک ŰŻŰłŰȘÙˆŰ± Ű§Ù„Űčمل ŰčŰ§Ù„ÛŒ ŰšŰ±Ű§ÛŒ Ú©Ű§Ù‡ŰŽ Ù…Ű±ŰȘۚ۷ Ú©Ű±ŰŻÙ† ŰȘŰłŰȘ ۧ۳ŰȘ. + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :clap: Ű§Ù†ŰŹŰ§Ù… ۯ۱۳ŰȘ Ù…Ű«Ű§Ù„: Ù…ÙˆŰ§ŰČی Ù…ÙˆÚ©Ű§ و ŰŹŰłŰȘ ŰšÙ‡ Ù„Ű·Ù ŰąŰČÙ…Ű§ÛŒŰŽ Ù…ÙˆŰ§ŰČی ۳ۧŰČی ŰšÙ‡ ۱ۭۧŰȘی ۧŰČ Ù…ÙˆÚ©Ű§ÛŒ ŰłÙ†ŰȘی ÙŸÛŒŰŽÛŒ می ÚŻÛŒŰ±Ù†ŰŻ. ([Credit: JavaScript Test-Runners Benchmark](https://medium.com/dailyjs/javascript-test-runners-benchmark-3a78d4117b4)) + +![alt text](assets/bp-24-yonigoldberg-jest-parallel.png "Mocha parallel & Jest easily outrun the traditional Mocha thanks to testing parallelization (Credit: JavaScript Test-Runners Benchmark)") + +
    + +

    + +## âšȘ 5.5 ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ Ú†Ú© Ù…ŰŹÙˆŰČ Ùˆ ŰłŰ±Ù‚ŰȘ Ű§ŰŻŰšÛŒ ۧŰČ Ù…ŰłŰ§ŰŠÙ„ Ù‚Ű§Ù†ÙˆÙ†ÛŒ ŰźÙˆŰŻŰŻŰ§Ű±ÛŒ Ú©Ù†ÛŒŰŻ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** Ù…ŰłŰ§ŰŠÙ„ Ù…Ű±ŰšÙˆŰ· ŰšÙ‡ Ù…ŰŹÙˆŰČ Ùˆ ŰłŰ±Ù‚ŰȘ Ű§ŰŻŰšÛŒ ۭۧŰȘÙ…Ű§Ù„Ű§Ù‹ ŰŻŰșŰŻŰșه Ű§Ű”Ù„ÛŒ ŰŽÙ…Ű§ ۯ۱ Ű­Ű§Ù„ ۭۧ۶۱ Ù†ÛŒŰłŰȘی Ű§Ù…Ű§ چ۱ۧ Ű§ÛŒÙ† کۧۯ۱ ۱ۧ ۯ۱ 10 ŰŻÙ‚ÛŒÙ‚Ù‡ ŰȘیک Ù†ŰŻÙ‡ÛŒŰŻŰŸ یک ŰŻŰłŰȘه ۧŰČ ŰšŰłŰȘه Ù‡Ű§ÛŒ npm Ù…Ű§Ù†Ù†ŰŻ [ŰšŰ±Ű±ŰłÛŒ Ù…ŰŹÙˆŰČ](https://www.npmjs.com/package/license-checker) و [چک ŰłŰ±Ù‚ŰȘ Ű§ŰŻŰšÛŒ](https://www.npmjs.com/package/plagiarism-checker) (ŰȘŰŹŰ§Ű±ÛŒ ۚۧ ۷۱ۭ Ű±Ű§ÛŒÚŻŰ§Ù†) ۱ۧ می ŰȘÙˆŰ§Ù† ŰšÙ‡ ۱ۭۧŰȘی ۯ۱ ۟۷ لوله CI ŰźÙˆŰŻ Ù‚Ű±Ű§Ű± ۯۧۯ و Űșم Ù‡Ű§ÛŒÛŒ Ù…Ű§Ù†Ù†ŰŻ ÙˆŰ§ŰšŰłŰȘÚŻÛŒ Ù‡Ű§ ۱ۧ ۚۧ Ù…ŰŹÙˆŰČÙ‡Ű§ÛŒ Ù…Ű­ŰŻÙˆŰŻ ÛŒŰ§ Ú©ŰŻÛŒ که ۧŰČ Stack Overflow Ú©ÙŸÛŒ ŰŽŰŻÙ‡ ۧ۳ŰȘ و ŰžŰ§Ù‡Ű±Ű§Ù‹ ŰšŰ±ŰźÛŒ ۧŰČ Ű­Ù‚ Ú†Ű§ÙŸ ۱ۧ Ù†Ù‚Ű¶ می Ú©Ù†ŰŻ ŰšŰ±Ű±ŰłÛŒ ک۱ۯ. + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** ŰšÙ‡ Ű·ÙˆŰ± Ù†Ű§ŰźÙˆŰ§ŰłŰȘÙ‡ŰŒ ŰȘÙˆŰłŰčÙ‡â€ŒŰŻÙ‡Ù†ŰŻÚŻŰ§Ù† ممکن ۧ۳ŰȘ ۧŰČ ŰšŰłŰȘÙ‡â€ŒÙ‡Ű§ÛŒÛŒ ۚۧ Ù…ŰŹÙˆŰČÙ‡Ű§ÛŒ Ù†Ű§Ù…Ù†Ű§ŰłŰš ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ú©Ù†Ù†ŰŻ ÛŒŰ§ Ú©ŰŻ ŰȘŰŹŰ§Ű±ÛŒ ۱ۧ Ú©ÙŸÛŒ ÙŸÛŒŰłŰȘ Ú©Ù†Ù†ŰŻ و ۚۧ Ù…ŰŽÚ©Ù„Ű§ŰȘ Ù‚Ű§Ù†ÙˆÙ†ÛŒ Ù…ÙˆŰ§ŰŹÙ‡ ŰŽÙˆÙ†ŰŻ. + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :clap: Ű§Ù†ŰŹŰ§Ù… ۯ۱۳ŰȘ ŰąÙ† Ù…Ű«Ű§Ù„: + +```shell +# Ù„Ű§ÛŒŰłÙ†Űł چک ۱ۧ ۯ۱ Ù…Ű­ÛŒŰ· CI ŰźÙˆŰŻ ÛŒŰ§ ŰšÙ‡ Ű”ÙˆŰ±ŰȘ Ù…Ű­Ù„ÛŒ Ù†Ű”Űš Ú©Ù†ÛŒŰŻ +npm install -g license-checker + +# ۧŰČ ŰąÙ† ŰšŰźÙˆŰ§Ù‡ÛŒŰŻ که ŰȘÙ…Ű§Ù… Ù…ŰŹÙˆŰČÙ‡Ű§ ۱ۧ Ű§ŰłÚ©Ù† Ú©Ù†ŰŻ و ۧگ۱ Ù…ŰŹÙˆŰČ ŰșÛŒŰ±Ù…ŰŹŰ§ŰČ ÙŸÛŒŰŻŰ§ ک۱ۯ ۚۧ Ú©ŰŻ ŰźŰ±ÙˆŰŹÛŒ ŰșÛŒŰ± ۧŰČ 0 ŰŽÚ©ŰłŰȘ ŰšŰźÙˆŰ±ŰŻ. ŰłÛŒŰłŰȘم CI ŰšŰ§ÛŒŰŻ Ű§ÛŒÙ† ŰŽÚ©ŰłŰȘ ۱ۧ ŰšÚŻÛŒŰ±ŰŻ و ۳ۧ۟ŰȘ ۱ۧ مŰȘوقف Ú©Ù†ŰŻ +license-checker --summary --failOn BSD +``` + +
    + +![alt text](assets/bp-25-nodejs-licsense.png) + +
    + +

    + +## âšȘ 5.6 ŰšÙ‡ Ű·ÙˆŰ± Ù…ŰŻŰ§ÙˆÙ… ÙˆŰ§ŰšŰłŰȘÚŻÛŒ Ù‡Ű§ÛŒ ŰąŰłÛŒŰš ÙŸŰ°ÛŒŰ± ۱ۧ ŰšŰ±Ű±ŰłÛŒ Ú©Ù†ÛŒŰŻ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** Ű­ŰȘی مŰčŰȘۚ۱ŰȘŰ±ÛŒÙ† ÙˆŰ§ŰšŰłŰȘÚŻÛŒ Ù‡Ű§ Ù…Ű§Ù†Ù†ŰŻ Express ŰąŰłÛŒŰš ÙŸŰ°ÛŒŰ±ÛŒ Ù‡Ű§ÛŒ ŰŽÙ†Ű§ŰźŰȘه ŰŽŰŻÙ‡ Ű§ÛŒ ŰŻŰ§Ű±Ù†ŰŻ. Ű§ÛŒÙ† می ŰȘÙˆŰ§Ù†ŰŻ ŰšÙ‡ ۱ۭۧŰȘی ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ Ű§ŰšŰČŰ§Ű±Ù‡Ű§ÛŒ ŰŹŰ§Ù…Űčه Ù…Ű§Ù†Ù†ŰŻ [npm audit](https://docs.npmjs.com/getting-started/running-a-security-audit), ÛŒŰ§ ۧۚŰČŰ§Ű±Ù‡Ű§ÛŒ ŰȘŰŹŰ§Ű±ÛŒ Ù…Ű§Ù†Ù†ŰŻ [snyk](https://snyk.io/) (Ù†ŰłŰźÙ‡ Ű§Ù†ŰŹÙ…Ù† Ű±Ű§ÛŒÚŻŰ§Ù† ۱ۧ نیŰČ Ű§Ű±Ű§ŰŠÙ‡ ŰŻÙ‡ÛŒŰŻ). Ù‡Ű± ŰŻÙˆ ۱ۧ می ŰȘÙˆŰ§Ù† ۧŰČ CI ŰŽÙ…Ű§ ۯ۱ Ù‡Ű± ۳ۧ۟ŰȘنی ÙŰ±Ű§ŰźÙˆŰ§Ù†ÛŒ ک۱ۯ + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** ÙŸŰ§Ú© Ù†ÚŻÙ‡ ۯۧێŰȘن Ú©ŰŻ ŰźÙˆŰŻ ۧŰČ ŰąŰłÛŒŰšâ€ŒÙŸŰ°ÛŒŰ±ÛŒâ€ŒÙ‡Ű§ ŰšŰŻÙˆÙ† ۧۚŰČۧ۱ ۧ۟ŰȘŰ”Ű§Ű”ÛŒŰŒ Ù…ŰłŰȘلŰČم Ű§ÛŒÙ† ۧ۳ŰȘ که ŰŻŰ§ŰŠÙ…Ű§Ù‹ Ű§Ù†ŰȘێۧ۱ۧŰȘ ŰąÙ†Ù„Ű§ÛŒÙ† ۯ۱ Ù…ÙˆŰ±ŰŻ ŰȘÙ‡ŰŻÛŒŰŻŰ§ŰȘ ŰŹŰŻÛŒŰŻ ۱ۧ ŰŻÙ†ŰšŰ§Ù„ Ú©Ù†ÛŒŰŻ. Ú©Ű§Ù…Ù„Ű§ ۟۳ŰȘه Ú©Ù†Ù†ŰŻÙ‡ + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :clap: Ù…Ù‚Ű§Ù„: NPM Audit نŰȘÛŒŰŹÙ‡ + +![alt text](assets/bp-26-npm-audit-snyk.png "NPM Audit result") + +
    + +

    + +## âšȘ 5.7 ŰšÙ‡â€ŒŰ±ÙˆŰČŰ±ŰłŰ§Ù†ÛŒâ€ŒÙ‡Ű§ÛŒ ÙˆŰ§ŰšŰłŰȘÚŻÛŒ ۱ۧ ŰźÙˆŰŻÚ©Ű§Ű± Ú©Ù†ÛŒŰŻ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** Yarn و npm ŰąŰźŰ±ÛŒÙ† مŰčŰ±ÙÛŒ package-lock.json یک Ú†Ű§Ù„ŰŽ ŰŹŰŻÛŒ ۱ۧ مŰčŰ±ÙÛŒ ک۱ۯ (ŰŹŰ§ŰŻÙ‡ ŰŹÙ‡Ù†Ù… ۚۧ نیŰȘ ŰźÙˆŰš Ù‡Ù…ÙˆŰ§Ű± ŰŽŰŻÙ‡ ۧ۳ŰȘ)â€”â€”ŰšÙ‡ Ű·ÙˆŰ± ÙŸÛŒŰŽ ÙŰ±Ű¶ Ű§Ú©Ù†ÙˆÙ† ۚ۳ŰȘه Ù‡Ű§ ŰŻÛŒÚŻŰ± ŰšÙ‡ Ű±ÙˆŰČ Ű±ŰłŰ§Ù†ÛŒ نمی ŰŽÙˆÙ†ŰŻ. Ű­ŰȘی ŰȘیمی که ŰšŰłÛŒŰ§Ű±ÛŒ ۧŰČ Ű§ŰłŰȘÙ‚Ű±Ű§Ű±Ù‡Ű§ÛŒ ŰŹŰŻÛŒŰŻ ۱ۧ ۚۧ "npm install" و "npm update" ۧۏ۱ۧ می Ú©Ù†ŰŻŰŒ هیچ ŰšÙ‡ Ű±ÙˆŰČ Ű±ŰłŰ§Ù†ÛŒ ŰŹŰŻÛŒŰŻÛŒ ŰŻŰ±ÛŒŰ§ÙŰȘ Ù†ŰźÙˆŰ§Ù‡ŰŻ ک۱ۯ. Ű§ÛŒÙ† Ù…Ù†ŰŹŰ± ŰšÙ‡ Ù†ŰłŰźÙ‡â€ŒÙ‡Ű§ÛŒ ۚ۳ŰȘÙ‡â€ŒÙ‡Ű§ÛŒ ÙˆŰ§ŰšŰłŰȘه ŰšÙ‡ ÙŸŰ§ÛŒÛŒÙ†â€ŒŰȘ۱ و ۯ۱ ۚۯŰȘŰ±ÛŒÙ† Ű­Ű§Ù„ŰȘ ŰšÙ‡ Ú©ŰŻÙ‡Ű§ÛŒ ŰąŰłÛŒŰšâ€ŒÙŸŰ°ÛŒŰ± Ù…ÛŒâ€ŒŰŽÙˆŰŻ. Ű§Ú©Ù†ÙˆÙ† ŰȘیم Ù‡Ű§ ŰšŰ±Ű§ÛŒ ŰšÙ‡ Ű±ÙˆŰČ Ű±ŰłŰ§Ù†ÛŒ ŰŻŰłŰȘی package.json ÛŒŰ§ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ Ű§ŰšŰČŰ§Ű±Ù‡Ű§ ŰšÙ‡ Ű­ŰłÙ† نیŰȘ و Ű­Ű§ÙŰžÙ‡ ŰȘÙˆŰłŰčه ŰŻÙ‡Ù†ŰŻÚŻŰ§Ù† مŰȘکی Ù‡ŰłŰȘÙ†ŰŻ [Ù…Ű§Ù†Ù†ŰŻ ncu](https://www.npmjs.com/package/npm-check-updates) ŰšÙ‡ Ű”ÙˆŰ±ŰȘ ŰŻŰłŰȘی یک Ű±Ű§Ù‡ Ù‚Ű§ŰšÙ„ Ű§Ű·Ù…ÛŒÙ†Ű§Ù† ŰȘ۱ می ŰȘÙˆŰ§Ù†ŰŻ ŰźÙˆŰŻÚ©Ű§Ű± Ú©Ű±ŰŻÙ† ÙŰ±ŰąÛŒÙ†ŰŻ ŰŻŰ±ÛŒŰ§ÙŰȘ Ù‚Ű§ŰšÙ„ ۧŰčŰȘÙ…Ű§ŰŻŰȘŰ±ÛŒÙ† Ù†ŰłŰźÙ‡ Ù‡Ű§ÛŒ ÙˆŰ§ŰšŰłŰȘÚŻÛŒ ۹ۧۮۯی Ű§ÚŻŰ±Ú†Ù‡ هیچ Ű±Ű§Ù‡ Ű­Ù„ ÚŻÙ„ÙˆÙ„Ù‡ Ù†Ù‚Ű±Ù‡ Ű§ÛŒ ÙˆŰŹÙˆŰŻ Ù†ŰŻŰ§Ű±ŰŻŰŒ Ű§Ù…Ű§ ŰŻÙˆ Ű±Ű§Ù‡ ۧŰȘÙˆÙ…Ű§ŰłÛŒÙˆÙ† ممکن ÙˆŰŹÙˆŰŻ ۯۧ۱ۯ: + +(1) CI می‌ŰȘÙˆŰ§Ù†ŰŻ ۯ۱ ۳ۧ۟ŰȘâ€ŒÙ‡Ű§ÛŒÛŒ که ÙˆŰ§ŰšŰłŰȘÚŻÛŒâ€ŒÙ‡Ű§ÛŒ Ù…Ù†ŰłÙˆŰź ŰŻŰ§Ű±Ù†ŰŻ ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ Ű§ŰšŰČŰ§Ű±Ù‡Ű§ÛŒÛŒ Ù…Ű§Ù†Ù†ŰŻ [‘npm outdated’](https://docs.npmjs.com/cli/outdated) ÛŒŰ§ ‘npm-check-updates (ncu)’ . Ű§Ù†ŰŹŰ§Ù… Ű§ÛŒÙ† کۧ۱ ŰȘÙˆŰłŰčه ŰŻÙ‡Ù†ŰŻÚŻŰ§Ù† ۱ۧ ÙˆŰ§ŰŻŰ§Ű± می Ú©Ù†ŰŻ ŰȘۧ ÙˆŰ§ŰšŰłŰȘÚŻÛŒ Ù‡Ű§ ۱ۧ ŰšÙ‡ Ű±ÙˆŰČ Ú©Ù†Ù†ŰŻ. + +(2) ۧŰČ Ű§ŰšŰČŰ§Ű±Ù‡Ű§ÛŒ ŰȘŰŹŰ§Ű±ÛŒ ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ú©Ù†ÛŒŰŻ که Ú©ŰŻ ۱ۧ Ű§ŰłÚ©Ù† می Ú©Ù†Ù†ŰŻ و ŰšÙ‡ Ű·ÙˆŰ± ŰźÙˆŰŻÚ©Ű§Ű± ŰŻŰ±ŰźÙˆŰ§ŰłŰȘ Ù‡Ű§ÛŒ Ú©ŰŽŰŽ ۱ۧ ۚۧ ÙˆŰ§ŰšŰłŰȘÚŻÛŒ Ù‡Ű§ÛŒ ŰšÙ‡ Ű±ÙˆŰČ Ű§Ű±ŰłŰ§Ù„ می Ú©Ù†Ù†ŰŻ. یک ŰłÙˆŰ§Ù„ ŰŹŰ§Ù„Űš ŰšŰ§Ù‚ÛŒ Ù…Ű§Ù†ŰŻÙ‡ Ű§ÛŒÙ† ۧ۳ŰȘ که ŰłÛŒŰ§ŰłŰȘ ŰšÙ‡â€ŒŰ±ÙˆŰČŰ±ŰłŰ§Ù†ÛŒ ÙˆŰ§ŰšŰłŰȘÚŻÛŒ Ú†ÚŻÙˆÙ†Ù‡ ŰšŰ§ÛŒŰŻ ŰšŰ§ŰŽŰŻâ€”â€ŠŰšÙ‡â€ŒŰ±ÙˆŰČŰ±ŰłŰ§Ù†ÛŒ ۯ۱ Ù‡Ű± ÙˆŰ”Ù„Ù‡ ۳۱ۚۧ۱ ŰČÛŒŰ§ŰŻÛŒ Ű§ÛŒŰŹŰ§ŰŻ Ù…ÛŒâ€ŒÚ©Ù†ŰŻŰŒ ŰšÙ‡â€ŒŰ±ÙˆŰČŰ±ŰłŰ§Ù†ÛŒ ۯ۱۳ŰȘ ŰČÙ…Ű§Ù†ÛŒ که یک Ű§Ű”Ù„ÛŒ منŰȘێ۱ Ù…ÛŒâ€ŒŰŽÙˆŰŻ ممکن ۧ۳ŰȘ ŰšÙ‡ یک Ù†ŰłŰźÙ‡ Ù†Ű§ÙŸŰ§ÛŒŰŻŰ§Ű± Ű§ŰŽŰ§Ű±Ù‡ Ú©Ù†ŰŻ (ŰšŰłÛŒŰ§Ű±ÛŒ ۧŰČ ŰšŰłŰȘÙ‡â€ŒÙ‡Ű§ ۯ۱ Ù‡Ù…Ű§Ù† Ű±ÙˆŰČÙ‡Ű§ÛŒ Ű§ÙˆÙ„ ÙŸŰł ۧŰČ Ű§Ù†ŰȘێۧ۱ ŰąŰłÛŒŰšâ€ŒÙŸŰ°ÛŒŰ± Ù‡ŰłŰȘÙ†ŰŻ., [۱ۧ ŰšŰšÛŒÙ†ÛŒŰŻ](https://nodesource.com/blog/a-high-level-post-mortem-of-the-eslint-scope-security-incident/) eslint-scope Ű­Ű§ŰŻŰ«Ù‡). + +یک ŰźŰ·â€ŒÙ…ŰŽÛŒ ŰšÙ‡â€ŒŰ±ÙˆŰČŰ±ŰłŰ§Ù†ÛŒ Ú©Ű§Ű±ŰąÙ…ŰŻ ممکن ۧ۳ŰȘ ۧۏۧŰČه ŰŻÙ‡ŰŻ ŰȘۧ Ù…Ù‚ŰŻŰ§Ű±ÛŒ Â«ŰŻÙˆŰ±Ù‡ ÙˆŰ§ÚŻŰ°Ű§Ű±ÛŒÂ» ÙˆŰŹÙˆŰŻ ۯۧێŰȘه ۚۧێۯ - ۧۏۧŰČه ŰŻÙ‡ÛŒŰŻ Ú©ŰŻ ŰšŰ±Ű§ÛŒ Ù…ŰŻŰȘی ۧŰČ ŰąŰźŰ±ÛŒÙ† @ و Ù†ŰłŰźÙ‡â€ŒÙ‡Ű§ ŰčÙ‚Űš ŰšÙ…Ű§Ù†ŰŻŰŒ Ù‚ŰšÙ„ ۧŰČ Ű§ÛŒÙ†Ú©Ù‡ Ù†ŰłŰźÙ‡ Ù…Ű­Ù„ÛŒ ۱ۧ Ù…Ù†ŰłÙˆŰź ۯ۱ Ù†ŰžŰ± ŰšÚŻÛŒŰ±ÛŒŰŻ (ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† Ù…Ű«Ű§Ù„ Ù†ŰłŰźÙ‡ Ù…Ű­Ù„ÛŒ 1.3.1 و Ù†ŰłŰźÙ‡ Ù…ŰźŰČن 1.3.8 ۧ۳ŰȘ). +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** ŰȘÙˆÙ„ÛŒŰŻ ŰŽÙ…Ű§ ۚ۳ŰȘه Ù‡Ű§ÛŒÛŒ ۱ۧ ۧۏ۱ۧ می Ú©Ù†ŰŻ که ŰšÙ‡ ۔۱ۭۧŰȘ ŰȘÙˆŰłŰ· Ù†ÙˆÛŒŰłÙ†ŰŻÙ‡ ŰąÙ†Ù‡Ű§ ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† ŰźŰ·Ű±Ù†Ű§Ú© ۚ۱چ۳ۚ ÚŻŰ°Ű§Ű±ÛŒ ŰŽŰŻÙ‡ ۧ۳ŰȘ + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :clap: Ù…Ù‚Ű§Ù„: [ncu](https://www.npmjs.com/package/npm-check-updates) می ŰȘÙˆŰ§Ù†ŰŻ ŰšÙ‡ Ű”ÙˆŰ±ŰȘ ŰŻŰłŰȘی ÛŒŰ§ ۯ۱ یک ۟۷ لوله CI ŰšŰ±Ű§ÛŒ ŰȘŰŽŰźÛŒŰ” میŰČŰ§Ù† ŰčÙ‚Űš Ù…Ű§Ù†ŰŻÚŻÛŒ Ú©ŰŻ ۧŰČ ŰąŰźŰ±ÛŒÙ† Ù†ŰłŰźÙ‡ Ù‡Ű§ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ŰŽÙˆŰŻ + +![alt text](assets/bp-27-yoni-goldberg-npm.png "ncu can be used manually or within a CI pipeline to detect to which extent the code lag behind the latest versions") + +
    + +

    + +## âšȘ  5.8 Ù†Ú©Ű§ŰȘ ŰŻÛŒÚŻŰ±ŰŒ ŰșÛŒŰ± Ù…Ű±ŰȘۚ۷ ۚۧ ÚŻŰ±Ù‡ŰŒ CI + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** Ű§ÛŒÙ† ÙŸŰłŰȘ ۚ۱ Ű±ÙˆÛŒ ŰȘÙˆŰ”ÛŒÙ‡â€ŒÙ‡Ű§ÛŒ ŰąŰČÙ…Ű§ÛŒŰŽÛŒ مŰȘÙ…Ű±Ú©ŰČ ŰŽŰŻÙ‡ ۧ۳ŰȘی ÛŒŰ§ Ű­ŰŻŰ§Ù‚Ù„ می‌ŰȘÙˆŰ§Ù† ŰąÙ† ۱ۧ ۚۧ Node JS Ù…Ű«Ű§Ù„ ŰČŰŻ. ۚۧ Ű§ÛŒÙ† Ű­Ű§Ù„ŰŒ Ű§ÛŒÙ† ÚŻÙ„ÙˆÙ„Ù‡ Ú†Ù†ŰŻ نکŰȘه ŰșÛŒŰ± Ù…Ű±ŰȘۚ۷ ۚۧ Node ۱ۧ که ŰšÙ‡ ŰźÙˆŰšÛŒ ŰŽÙ†Ű§ŰźŰȘه ŰŽŰŻÙ‡ Ù‡ŰłŰȘÙ†ŰŻŰŒ ÚŻŰ±ÙˆÙ‡ ŰšÙ†ŰŻÛŒ می Ú©Ù†ŰŻ + +
    1. ۧŰČ ÛŒÚ© Ù†Ű­Ùˆ ۧŰčÙ„Ű§Ù†ÛŒ ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ú©Ù†ÛŒŰŻ. Ű§ÛŒÙ† ŰȘÙ†Ù‡Ű§ ÚŻŰČینه ŰšŰ±Ű§ÛŒ ۧک۫۱ ÙŰ±ÙˆŰŽÙ†ŰŻÚŻŰ§Ù† ۧ۳ŰȘ Ű§Ù…Ű§ Ù†ŰłŰźÙ‡ Ù‡Ű§ÛŒ Ù‚ŰŻÛŒÙ…ÛŒ Jenkins Ű§Ù…Ú©Ű§Ù† ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ Ú©ŰŻ ÛŒŰ§ UI ۱ۧ می ŰŻÙ‡ŰŻ
    2. ÙŰ±ÙˆŰŽÙ†ŰŻÙ‡ Ű§ÛŒ ۱ۧ Ű§Ù†ŰȘ۟ۧۚ Ú©Ù†ÛŒŰŻ که ۧŰČ ÙŸŰŽŰȘÛŒŰšŰ§Ù†ÛŒ Docker ŰšÙˆÙ…ÛŒ ŰšŰ±ŰźÙˆŰ±ŰŻŰ§Ű± ۚۧێۯ
    3. ŰČÙˆŰŻ ŰŽÚ©ŰłŰȘ ŰšŰźÙˆŰ±ÛŒŰŻŰŒ ۧۚŰȘۯۧ ŰłŰ±ÛŒŰč ŰȘŰ±ÛŒÙ† ŰȘŰłŰȘ Ù‡Ű§ÛŒ ŰźÙˆŰŻ ۱ۧ ۧۏ۱ۧ Ú©Ù†ÛŒŰŻ. یک Ù…Ű±Ű­Ù„Ù‡ / Ù†Ù‚Ű·Ù‡ ŰčŰ·Ù "ŰȘŰłŰȘ ŰŻÙˆŰŻ" Ű§ÛŒŰŹŰ§ŰŻ Ú©Ù†ÛŒŰŻ که Ú†Ù†ŰŻÛŒÙ† ۚۧŰČŰ±ŰłÛŒ ŰłŰ±ÛŒŰč ۱ۧ ÚŻŰ±ÙˆÙ‡ ŰšÙ†ŰŻÛŒ می Ú©Ù†ŰŻ (Ù…Ű§Ù†Ù†ŰŻ ÙŸŰ±ŰŻÙ‡ ŰČŰŻÙ†ŰŒ ŰȘŰłŰȘ Ù‡Ű§ÛŒ ÙˆŰ§Ű­ŰŻ) و ۚۧŰČŰźÙˆŰ±ŰŻ ŰłŰ±ÛŒŰč ۱ۧ ŰšÙ‡ committer Ú©ŰŻ Ű§Ű±Ű§ŰŠÙ‡ می ŰŻÙ‡ŰŻ.
    4. ŰšŰ±Ű±ŰłÛŒ ŰȘÙ…Ű§Ù… Ù…Ű”Ù†ÙˆŰčۧŰȘ ۳ۧ۟ŰȘنی ۧŰČ ŰŹÙ…Ù„Ù‡ ÚŻŰČŰ§Ű±ŰŽâ€ŒÙ‡Ű§ÛŒ ŰąŰČÙ…Ű§ÛŒŰŽŰŒ ÚŻŰČŰ§Ű±ŰŽâ€ŒÙ‡Ű§ÛŒ ÙŸÙˆŰŽŰŽŰŒ ÚŻŰČŰ§Ű±ŰŽâ€ŒÙ‡Ű§ÛŒ ŰŹÙ‡ŰŽŰŒ ÚŻŰČŰ§Ű±ŰŽâ€ŒÙ‡Ű§ و ŰșÛŒŰ±Ù‡ ۱ۧ ŰąŰłŰ§Ù† Ú©Ù†ÛŒŰŻ.
    5. Ú†Ù†ŰŻÛŒÙ† ۟۷ لوله/ŰŽŰșل ŰšŰ±Ű§ÛŒ Ù‡Ű± Ű±ÙˆÛŒŰŻŰ§ŰŻ Ű§ÛŒŰŹŰ§ŰŻ Ú©Ù†ÛŒŰŻŰŒ ۧŰČ Ù…Ű±Ű§Ű­Ù„ ŰšÛŒÙ† ŰąÙ†Ù‡Ű§ ŰŻÙˆŰšŰ§Ű±Ù‡ ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ú©Ù†ÛŒŰŻ. ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† Ù…Ű«Ű§Ù„ŰŒ یک کۧ۱ ۱ۧ ŰšŰ±Ű§ÛŒ commit Ù‡Ű§ÛŒ ŰŽŰ§ŰźÙ‡ ÙˆÛŒÚ˜ÚŻÛŒ و یک کۧ۱ ŰŻÛŒÚŻŰ± ۱ۧ ŰšŰ±Ű§ÛŒ PR Ű§Ű”Ù„ÛŒ ÙŸÛŒÚ©Ű±ŰšÙ†ŰŻÛŒ Ú©Ù†ÛŒŰŻ. ŰšÙ‡ Ù‡Ű± Ù…Ù†Ű·Ù‚ ۧۏۧŰČه ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ù…ŰŹŰŻŰŻ ۱ۧ ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ Ù…Ű±Ű§Ű­Ù„ Ù…ŰŽŰȘ۱ک ŰšŰŻÙ‡ÛŒŰŻ (ۧک۫۱ ÙŰ±ÙˆŰŽÙ†ŰŻÚŻŰ§Ù† Ù…Ú©Ű§Ù†ÛŒŰČمی ŰšŰ±Ű§ÛŒ ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ù…ŰŹŰŻŰŻ ۧŰČ Ú©ŰŻ Ű§Ű±Ű§ŰŠÙ‡ می ŰŻÙ‡Ù†ŰŻ)
    6. Ù‡Ű±ÚŻŰČ Ű§ŰłŰ±Ű§Ű± ۱ۧ ۯ۱ یک ۧŰčÙ„Ű§Ù…ÛŒÙ‡ ŰŽŰșلی Ù‚Ű±Ű§Ű± Ù†ŰŻÙ‡ÛŒŰŻŰŒ ŰąÙ†Ù‡Ű§ ۱ۧ ۧŰČ ÛŒÚ© ÙŰ±ÙˆŰŽÚŻŰ§Ù‡ Ù…ŰźÙÛŒ ÛŒŰ§ ۧŰČ ÙŸÛŒÚ©Ű±ŰšÙ†ŰŻÛŒ ŰŽŰșل ŰšÚŻÛŒŰ±ÛŒŰŻ.
    7. ŰšÙ‡ ۔۱ۭۧŰȘ Ù†ŰłŰźÙ‡ ۱ۧ ۯ۱ یک Ù†ŰłŰźÙ‡ منŰȘێ۱ Ú©Ù†ÛŒŰŻ ÛŒŰ§ Ű­ŰŻŰ§Ù‚Ù„ Ű§Ű·Ù…ÛŒÙ†Ű§Ù† Ű­Ű§Ű”Ù„ Ú©Ù†ÛŒŰŻ که ŰȘÙˆŰłŰčه ŰŻÙ‡Ù†ŰŻÙ‡ Ű§ÛŒÙ† کۧ۱ ۱ۧ Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ‡ ۧ۳ŰȘ
    8. ÙÙ‚Ű· یک ۚۧ۱ ۚ۳ۧŰČÛŒŰŻ و ŰȘÙ…Ű§Ù… ۚۧŰČŰ±ŰłÛŒâ€ŒÙ‡Ű§ ۱ۧ Ű±ÙˆÛŒ یک Ù…Ű”Ù†ÙˆŰč ۳ۧ۟ŰȘ (Ù…Ű«Ù„Ű§Ù‹ ŰȘŰ”ÙˆÛŒŰ± ۯۧک۱) Ű§Ù†ŰŹŰ§Ù… ŰŻÙ‡ÛŒŰŻ.
    9. ۯ۱ یک Ù…Ű­ÛŒŰ· ŰČÙˆŰŻÚŻŰ°Ű± که Ű­Ű§Ù„ŰȘ Ű±Ű§Ù†ŰŽ ŰšÛŒÙ† ۳ۧ۟ŰȘâ€ŒÙ‡Ű§ ۱ۧ Ù†ŰŻŰ§Ű±ŰŻŰŒ ŰȘŰłŰȘ Ú©Ù†ÛŒŰŻ. Ű°ŰźÛŒŰ±Ù‡ node_modules ممکن ۧ۳ŰȘ ŰȘÙ†Ù‡Ű§ ۧ۳ŰȘŰ«Ù†Ű§ ۚۧێۯ
    +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** ŰŽÙ…Ű§ ŰłŰ§Ù„Ù‡Ű§ÛŒ ۟۱ۯ ۱ۧ ۧŰČ ŰŻŰłŰȘ ŰźÙˆŰ§Ù‡ÛŒŰŻ ۯۧۯ + +

    + +## âšȘ  5.9 ۳ۧ۟ŰȘ Ù…Ű§ŰȘŰ±ÛŒŰł: Ù‡Ù…Ű§Ù† Ù…Ű±Ű§Ű­Ù„ CI ۱ۧ ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ Ú†Ù†ŰŻÛŒÙ† Ù†ŰłŰźÙ‡ Node ۧۏ۱ۧ Ú©Ù†ÛŒŰŻ + +:white_check_mark: **Ű§Ù†ŰŹŰ§Ù… ŰŻŰ§ŰŻÙ†:** ŰšŰ±Ű±ŰłÛŒ کیفیŰȘ ۯ۱ Ù…ÙˆŰ±ŰŻ ŰłŰ±Ù†ŰŻÛŒÙŸÛŒŰȘی ۧ۳ŰȘی Ù‡Ű±Ú†Ù‡ ŰČمینه ŰšÛŒŰŽŰȘŰ±ÛŒ ۱ۧ ÙŸÙˆŰŽŰŽ ŰŻÙ‡ÛŒŰŻŰŒ ۯ۱ ŰȘŰŽŰźÛŒŰ” ŰČÙˆŰŻÙ‡Ù†ÚŻŰ§Ù… Ù…ŰŽÚ©Ù„Ű§ŰȘ ŰŽŰ§Ù†Űł ŰšÛŒŰŽŰȘŰ±ÛŒ ŰźÙˆŰ§Ù‡ÛŒŰŻ ۯۧێŰȘ. Ù‡Ù†ÚŻŰ§Ù… ŰȘÙˆŰłŰčه ۚ۳ŰȘÙ‡â€ŒÙ‡Ű§ÛŒ Ù‚Ű§ŰšÙ„ ۧ۳ŰȘÙŰ§ŰŻÙ‡ Ù…ŰŹŰŻŰŻ ÛŒŰ§ Ű§ŰŹŰ±Ű§ÛŒ یک ŰȘÙˆÙ„ÛŒŰŻ Ú†Ù†ŰŻ Ù…ŰŽŰȘŰ±ÛŒ ۚۧ ÙŸÛŒÚ©Ű±ŰšÙ†ŰŻÛŒâ€ŒÙ‡Ű§ÛŒ Ù…ŰźŰȘلف و Ù†ŰłŰźÙ‡â€ŒÙ‡Ű§ÛŒ Nodeی CI ŰšŰ§ÛŒŰŻ ۟۷ لوله ŰąŰČÙ…Ű§ÛŒŰŽâ€ŒÙ‡Ű§ ۱ۧ Ű±ÙˆÛŒ همه ŰŹŰ§ÛŒÚŻŰŽŰȘâ€ŒÙ‡Ű§ÛŒ ÙŸÛŒÚ©Ű±ŰšÙ†ŰŻÛŒâ€ŒÙ‡Ű§ ۧۏ۱ۧ Ú©Ù†ŰŻ. ŰšÙ‡ ŰčÙ†ÙˆŰ§Ù† Ù…Ű«Ű§Ù„ŰŒ ۚۧ ÙŰ±Ű¶ Ű§ÛŒÙ†Ú©Ù‡ Ù…Ű§ ۧŰČ MySQL ŰšŰ±Ű§ÛŒ ŰšŰ±ŰźÛŒ ۧŰČ Ù…ŰŽŰȘŰ±ÛŒŰ§Ù† و ۧŰČ Postgres ŰšŰ±Ű§ÛŒ ŰšŰ±ŰźÛŒ ŰŻÛŒÚŻŰ± ۧ۳ŰȘÙŰ§ŰŻÙ‡ می‌کنیم - ŰšŰ±ŰźÛŒ ۧŰČ ÙŰ±ÙˆŰŽÙ†ŰŻÚŻŰ§Ù† CI ۧŰČ ÙˆÛŒÚ˜ÚŻÛŒ ŰšÙ‡ Ù†Ű§Ù… «Matrix» ÙŸŰŽŰȘÛŒŰšŰ§Ù†ÛŒ Ù…ÛŒâ€ŒÚ©Ù†Ù†ŰŻ که Ű§Ù…Ú©Ű§Ù† Ű§ŰŹŰ±Ű§ÛŒ Ù…ŰŹÙ…ÙˆŰčه ŰąŰČÙ…Ű§ÛŒŰŽÛŒ ۱ۧ ۯ۱ ۚ۱ۧۚ۱ همه ŰŹŰ§ÛŒÚŻŰŽŰȘâ€ŒÙ‡Ű§ÛŒ MySQLی Postgres و Ú†Ù†ŰŻÛŒÙ† Ù†ŰłŰźÙ‡ Node Ù…Ű§Ù†Ù†ŰŻ 8ی 9 و 10 Ù…ÛŒâ€ŒŰŻÙ‡ŰŻ. Ű§ÛŒÙ† کۧ۱ ÙÙ‚Ű· ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ ÙŸÛŒÚ©Ű±ŰšÙ†ŰŻÛŒ ŰšŰŻÙˆÙ† هیچ ŰȘÙ„Ű§ŰŽ Ű§Ű¶Ű§ÙÛŒ Ű§Ù†ŰŹŰ§Ù… می ŰŽÙˆŰŻ (ۚۧ ÙŰ±Ű¶ Ű§ÛŒÙ†Ú©Ù‡ ŰȘŰłŰȘ ÛŒŰ§ Ù‡Ű± ÚŻÙˆÙ†Ù‡ ŰšŰ±Ű±ŰłÛŒ کیفیŰȘ ŰŻÛŒÚŻŰ±ÛŒ ۯۧێŰȘه ŰšŰ§ŰŽÛŒŰŻ). ŰłŰ§ÛŒŰ± CI که ۧŰČ Matrix ÙŸŰŽŰȘÛŒŰšŰ§Ù†ÛŒ نمی Ú©Ù†Ù†ŰŻ ممکن ۧ۳ŰȘ Ű§ÙŰČونه Ù‡Ű§ ÛŒŰ§ ŰȘŰ±ÙÙ†ŰŻÙ‡Ű§ÛŒÛŒ ŰšŰ±Ű§ÛŒ ۧۏۧŰČه ŰŻŰ§ŰŻÙ† ŰšÙ‡ ŰąÙ† ۯۧێŰȘه ŰšŰ§ŰŽÙ†ŰŻ +
    + +❌ **ۯ۱ ŰșÛŒŰ± Ű§ÛŒÙ† Ű”ÙˆŰ±ŰȘ:** ŰšÙ†Ű§ŰšŰ±Ű§ÛŒÙ†ŰŒ ÙŸŰł ۧŰČ Ű§Ù†ŰŹŰ§Ù… Ű§ÛŒÙ† همه کۧ۱ ۳۟ŰȘ ۯ۱ ŰȘŰłŰȘ Ù†ÙˆŰŽŰȘÙ†ŰŒ ŰȘÙ†Ù‡Ű§ ŰšÙ‡ ŰŻÙ„ÛŒÙ„ Ù…ŰŽÚ©Ù„Ű§ŰȘ ÙŸÛŒÚ©Ű±ŰšÙ†ŰŻÛŒŰŒ ۧۏۧŰČه Ù…ÛŒâ€ŒŰŻÙ‡ÛŒÙ… ŰšŰ§ÚŻâ€ŒÙ‡Ű§ ÙŸÙ†Ù‡Ű§Ù† ŰŽÙˆÙ†ŰŻ.? + +
    + +
    ✏ نمونه Ú©ŰŻ + +
    + +### :clap: Ù…Ű«Ű§Ù„: ۧ۳ŰȘÙŰ§ŰŻÙ‡ ۧŰČ ŰȘŰčŰ±ÛŒÙ ۳ۧ۟ŰȘ ŰȘŰ±Ű§ÙˆÛŒŰł (ÙŰ±ÙˆŰŽÙ†ŰŻÙ‡ CI) ŰšŰ±Ű§ÛŒ Ű§ŰŹŰ±Ű§ÛŒ Ù‡Ù…Ű§Ù† ŰąŰČÙ…Ű§ÛŒŰŽ Ű±ÙˆÛŒ Ú†Ù†ŰŻÛŒÙ† Ù†ŰłŰźÙ‡ Node + +
    ŰČŰšŰ§Ù†: node_js
    node_js:
    - "7"
    - "6"
    - "5"
    - "4"
    install:
    - npm install
    script:
    - npm run test
    +
    + +

    + +# Team + +## Yoni Goldberg + +
    + +
    + +**Role:** Writer + +**About:** I'm an independent consultant who works with Fortune 500 companies and garage startups on polishing their JS & Node.js applications. More than any other topic I'm fascinated by and aims to master the art of testing. I'm also the author of [Node.js Best Practices](https://github.com/goldbergyoni/nodebestpractices) + +**📗 Online Course:** Liked this guide and wish to take your testing skills to the extreme? Consider visiting my comprehensive course [Testing Node.js & JavaScript From A To Z](https://www.testjavascript.com) + +
    + +**Follow:** + +- [🐩 Twitter](https://twitter.com/goldbergyoni/) +- [📞 Contact](https://testjavascript.com/contact-2/) +- [✉ Newsletter](https://testjavascript.com/newsletter//) + +
    +
    +
    + +## [Bruno Scheufler](https://github.com/BrunoScheufler) + +**Role:** Tech reviewer and advisor + +Took care to revise, improve, lint and polish all the texts + +**About:** full-stack web engineer, Node.js & GraphQL enthusiast + +
    +
    + +## [Ido Richter](https://github.com/idori) + +**Role:** Concept, design and great advice + +**About:** A savvy frontend developer, CSS expert and emojis freak + +## [Kyle Martin](https://github.com/js-kyle) + +**Role:** Helps keep this project running, and reviews security related practices + +**About:** Loves working on Node.js projects and web application security. diff --git a/readme-pt-br.md b/readme-pt-br.md index e127ab7d..9d0e4075 100644 --- a/readme-pt-br.md +++ b/readme-pt-br.md @@ -31,6 +31,7 @@ Comece entendendo as prĂĄticas de teste onipresentes que sĂŁo a base para qualqu ### TraduçÔes - leia em seu prĂłprio idioma * 🇹🇳[Chinese](readme-zh-CN.md) - cortesia de [Yves yao](https://github.com/yvesyao) * đŸ‡°đŸ‡·[Korean](readme.kr.md) - cortesia de [Rain Byun](https://github.com/ragubyun) +* đŸ‡ș🇩[Ukrainian](readme-ua.md) - cortesia de [Serhii Shramko](https://github.com/Shramkoweb) * Deseja traduzir para o seu prĂłprio idioma? abra uma issue 💜 @@ -1800,7 +1801,7 @@ Na prĂĄtica alguns fornecedores de IC (exemplo: [CircleCI local CLI](https://cir

    -# âšȘ 5.3 Realize testes e2eem um verdadeiro espelho de produção +# âšȘ 5.3 Realize testes e2e em um verdadeiro espelho de produção :white_check_mark: **Faça:** Os testes de ponta a ponta (e2e) sĂŁo o principal desafio de cada pipeline de IC—criar um espelho efĂȘmero idĂȘntico de produção em tempo real com todos os serviços em nuvem relacionados pode ser entediante e caro. Encontrar o melhor comprometimento Ă© o seu jogo: [Docker-compose](https://serverless.com/) permite criar ambiente docker isolado com contĂȘineres idĂȘnticos usando um Ășnico arquivo de texto sem formatação, mas as tecnologias de suporte (por exemplo. rede, modelo de implantação) Ă© diferente das produçÔes do mundo real. VocĂȘ pode combinĂĄ-lo com [‘AWS Local’](https://github.com/localstack/localstack) para trabalhar com um esboço dos serviços reais da AWS. Se vocĂȘ usar [serverless](https://serverless.com/) vĂĄrios frameworks como serverless e [AWS SAM](https://docs.aws.amazon.com/lambda/latest/dg/serverless_app.html) permite a chamada local de cĂłdigos FaaS. diff --git a/readme-ru.md b/readme-ru.md new file mode 100644 index 00000000..239e63bc --- /dev/null +++ b/readme-ru.md @@ -0,0 +1,2190 @@ + + +
    + +# 👇 ĐŸĐŸŃ‡Đ”ĐŒŃƒ ŃŃ‚ĐŸ руĐșĐŸĐČĐŸĐŽŃŃ‚ĐČĐŸ ĐČыĐČДЎДт ĐČашО ĐœĐ°ĐČыĐșĐž Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ ĐœĐ° ĐœĐŸĐČыĐč ŃƒŃ€ĐŸĐČĐ”ĐœŃŒ + +
    + +## 📗 46+ ĐœĐ°ĐžĐ»ŃƒŃ‡ŃˆĐžŃ… ŃĐżĐŸŃĐŸĐ±ĐŸĐČ: ОсчДрпыĐČающох Đž ĐžĐœŃ„ĐŸŃ€ĐŒĐ°Ń‚ĐžĐČĐœŃ‹Ń… + +Đ”Đ°ĐœĐœĐŸĐ” руĐșĐŸĐČĐŸĐŽŃŃ‚ĐČĐŸ ĐłĐ°Ń€Đ°ĐœŃ‚ĐžŃ€ŃƒĐ”Ń‚ ĐœĐ°ĐŽĐ”Đ¶ĐœĐŸŃŃ‚ŃŒ JavaScript Đž Node.JS ĐŸŃ‚ A ĐŽĐŸ ĐŻ. В ĐșачДстĐČĐ” ĐžŃŃ‚ĐŸŃ‡ĐœĐžĐșа ĐČ ĐŽĐ°ĐœĐœĐŸĐŒ руĐșĐŸĐČĐŸĐŽŃŃ‚ĐČĐ” ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒĐ”Ń‚ŃŃ ĐŸĐ±ĐŸĐ±Ń‰Đ”ĐœĐœĐ°Ń ĐžĐœŃ„ĐŸŃ€ĐŒĐ°Ń†ĐžŃ, ĐČĐ·ŃŃ‚Đ°Ń Оз ŃĐ°ĐŒŃ‹Ń… ĐœĐ°ĐŽĐ”Đ¶ĐœŃ‹Ń… ĐșĐœĐžĐł, статДĐč Đž Đ±Đ»ĐŸĐłĐŸĐČ, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐŒĐŸĐ¶ĐœĐŸ ĐœĐ°Đčто ĐœĐ° Ń€Ń‹ĐœĐșĐ” ĐČ ĐŽĐ°ĐœĐœŃ‹Đč ĐŒĐŸĐŒĐ”ĐœŃ‚. + +## 🚱 ĐŸŃ€ĐŸĐŽĐČĐžĐœŃƒŃ‚Ń‹Đč ŃƒŃ€ĐŸĐČĐ”ĐœŃŒ: Đ’Ń‹Ń…ĐŸĐŽĐžŃ‚ ЎалДĐșĐŸ за прДЎДлы ĐŸŃĐœĐŸĐČ + +ОтпраĐČĐ»ŃĐčŃ‚Đ”ŃŃŒ ĐČ ĐżŃƒŃ‚Đ”ŃˆĐ”ŃŃ‚ĐČОД, ĐșĐŸŃ‚ĐŸŃ€ĐŸĐ” ĐČŃ‹Ń…ĐŸĐŽĐžŃ‚ ЎалДĐșĐŸ за прДЎДлы Đ±Đ°Đ·ĐŸĐČых праĐșтоĐș Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ Đž ĐČĐșлючаДт ĐČ ŃĐ”Đ±Ń таĐșОД ĐżŃ€ĐŸĐŽĐČĐžĐœŃƒŃ‚Ń‹Đ” Ń‚Đ”ĐŒŃ‹, ĐșаĐș: Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” ĐČ Ń€Đ°Đ±ĐŸŃ‡Đ”Đč срДЎД (TIP), ĐŒŃƒŃ‚Đ°Ń†ĐžĐŸĐœĐœĐŸĐ” Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” (mutation testing), Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” ĐœĐ° ĐŸŃĐœĐŸĐČĐ” сĐČĐŸĐčстĐČ (property-based testing) Đž ĐŒĐœĐŸĐłĐžĐ” ĐŽŃ€ŃƒĐłĐžĐ” ĐżŃ€ĐŸŃ„Đ”ŃŃĐžĐŸĐœĐ°Đ»ŃŒĐœŃ‹Đ” ĐżĐŸĐŽŃ…ĐŸĐŽŃ‹. ĐŸĐŸŃĐ»Đ” ĐżŃ€ĐŸŃ‡Ń‚Đ”ĐœĐžŃ ĐŽĐ°ĐœĐœĐŸĐłĐŸ руĐșĐŸĐČĐŸĐŽŃŃ‚ĐČа, ĐČашО ĐœĐ°ĐČыĐșĐž Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ ŃŃ‚Đ°ĐœŃƒŃ‚ ĐœĐ°ĐŒĐœĐŸĐłĐŸ ĐČŃ‹ŃˆĐ” ŃŃ€Đ”ĐŽĐœĐ”ĐłĐŸ. + +## 🌐 Full-stack: frontend, backend, CI Đž ĐŽŃ€ŃƒĐłĐŸĐ” + +ĐĐ°Ń‡ĐœĐžŃ‚Đ” с ĐżĐŸĐœĐžĐŒĐ°ĐœĐžŃ ĐŸĐ±Ń‰Đ”ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒĐ”ĐŒŃ‹Ń… ŃĐżĐŸŃĐŸĐ±ĐŸĐČ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ, яĐČĐ»ŃŃŽŃ‰ĐžĐ”ŃŃ ĐŸŃĐœĐŸĐČĐœŃ‹ĐŒĐž ĐŽĐ»Ń ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐč Đ»ŃŽĐ±ĐŸĐłĐŸ ŃƒŃ€ĐŸĐČĐœŃ, а Đ·Đ°Ń‚Đ”ĐŒ ŃƒĐłĐ»ŃƒĐ±ĐžŃ‚Đ”ŃŃŒ ĐČ ĐČŃ‹Đ±Ń€Đ°ĐœĐœŃƒŃŽ ĐČĐ°ĐŒĐž ĐŸĐ±Đ»Đ°ŃŃ‚ŃŒ: frontend/UI, backend, CI ОлО ĐČсё ĐČĐŒĐ”ŃŃ‚Đ”. + +
    + +## 🚀 Đ”Đ»Ń ĐŸŃ‚Ń€Đ°Đ±ĐŸŃ‚ĐșĐž ĐžĐ·ŃƒŃ‡Đ”ĐœĐœŃ‹Ń… ĐČ ĐżŃ€ĐŸŃ†Đ”ŃŃĐ” Ń‡Ń‚Đ”ĐœĐžŃ ĐœĐ°ĐČыĐșĐŸĐČ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ Вы ĐŒĐŸĐ¶Đ”Ń‚Đ” ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČать ĐœĐ°Ńˆ [Node.js starter - Practica.js](https://github.com/practicajs/practica). Вы ŃĐŒĐŸĐ¶Đ”Ń‚Đ” ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČать Đ”ĐłĐŸ ĐșаĐș ĐŽĐ»Ń ŃĐŸĐ·ĐŽĐ°ĐœĐžŃ ĐœĐŸĐČĐŸĐłĐŸ ŃˆĐ°Đ±Đ»ĐŸĐœĐ°, таĐș Đž ĐŽĐ»Ń праĐșтоĐșĐž с ĐżŃ€ĐžĐŒĐ”Ń€Đ°ĐŒĐž ĐșĐŸĐŽĐ°. + +### АĐČŃ‚ĐŸŃ€ руĐșĐŸĐČĐŸĐŽŃŃ‚ĐČа - Yoni Goldberg + +- ĐšĐŸĐœŃŃƒĐ»ŃŒŃ‚Đ°ĐœŃ‚ ĐżĐŸ ĐČĐŸĐżŃ€ĐŸŃĐ°ĐŒ JavaScript & Node.js +- 📗 [Testing Node.js & JavaScript From A To Z](https://www.testjavascript.com) - ĐœĐŸĐč ĐżĐŸĐ»ĐœŃ‹Đč ĐŸĐœĐ»Đ°ĐčĐœ-Đșурс, ĐČĐșлючающОĐč Đ±ĐŸĐ»Đ”Đ”, Ń‡Đ”ĐŒ [7 Ń‡Đ°ŃĐŸĐČ ĐČĐžĐŽĐ”ĐŸ](https://www.testjavascript.com), 14 Ń‚ĐžĐżĐŸĐČ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ Đž 40+ праĐșтОчДсĐșох Đ·Đ°ĐœŃŃ‚ĐžĐč +- [ĐœĐŸĐč тĐČОттДр](https://twitter.com/goldbergyoni/) +- [ĐĄĐ»Đ”ĐŽŃƒŃŽŃ‰ĐžĐč workshop: Verona, Italy 🇼đŸ‡č, April 20th](https://2022.jsday.it/workshop/nodejs_testing.html) + +
    + +### Đ”ĐŸŃŃ‚ŃƒĐżĐœŃ‹Đ” пДрДĐČĐŸĐŽŃ‹ + +- 🇹🇳[КотаĐčсĐșĐžĐč](readme-zh-CN.md) - ĐŸĐ”Ń€Đ”ĐČĐ”ĐŽĐ”ĐœĐŸ [Yves yao](https://github.com/yvesyao) +- đŸ‡°đŸ‡·[KoрДĐčсĐșĐžĐč](readme.kr.md) - ĐŸĐ”Ń€Đ”ĐČĐ”ĐŽĐ”ĐœĐŸ [Rain Byun](https://github.com/ragubyun) +- đŸ‡”đŸ‡±[ĐŸĐŸĐ»ŃŒŃĐșĐžĐč](readme-pl.md) - ĐŸĐ”Ń€Đ”ĐČĐ”ĐŽĐ”ĐœĐŸ [Michal Biesiada](https://github.com/mbiesiad) +- đŸ‡Ș🇾[Đ˜ŃĐżĐ°ĐœŃĐșĐžĐč](readme-es.md) - ĐŸĐ”Ń€Đ”ĐČĐ”ĐŽĐ”ĐœĐŸ [Miguel G. Sanguino](https://github.com/sanguino) +- đŸ‡§đŸ‡·[ĐŸĐŸŃ€Ń‚ŃƒĐłĐ°Đ»ŃŒŃĐșĐžĐč](readme-pt-br.md) - ĐŸĐ”Ń€Đ”ĐČĐ”ĐŽĐ”ĐœĐŸ [Iago Angelim Costa Cavalcante](https://github.com/iagocavalcante) , [Douglas Mariano Valero](https://github.com/DouglasMV) and [koooge](https://github.com/koooge) +- đŸ‡«đŸ‡·[Đ€Ń€Đ°ĐœŃ†ŃƒĐ·ŃĐșĐžĐč](readme-fr.md) - ĐŸĐ”Ń€Đ”ĐČĐ”ĐŽĐ”ĐœĐŸ [Mathilde El Mouktafi](https://github.com/mel-mouk) +- đŸ‡ŻđŸ‡”[ĐŻĐżĐŸĐœŃĐșĐžĐč (Ń‡Đ”Ń€ĐœĐŸĐČĐžĐș)](https://github.com/yuichkun/javascript-testing-best-practices/blob/master/readme-jp.md) - ĐŸĐ”Ń€Đ”ĐČĐ”ĐŽĐ”ĐœĐŸ of [Yuichi Yogo](https://github.com/yuichkun) and [ryo](https://github.com/kawamataryo) +- đŸ‡čđŸ‡Œ[ĐąŃ€Đ°ĐŽĐžŃ†ĐžĐŸĐœĐœŃ‹Đč ĐșотаĐčсĐșĐžĐč](readme-zh-TW.md) - ĐŸĐ”Ń€Đ”ĐČĐ”ĐŽĐ”ĐœĐŸ [Yubin Hsu](https://github.com/yubinTW) +- đŸ‡·đŸ‡ș [РуссĐșĐžĐč](ссылĐșа) - ĐŸĐ”Ń€Đ”ĐČĐ”ĐŽĐ”ĐœĐŸ [Alex Popov](https://github.com/Saimon398) +- Đ„ĐŸŃ‚ĐžŃ‚Đ” пДрДĐČДстО ĐœĐ° ŃĐŸĐ±ŃŃ‚ĐČĐ”ĐœĐœŃ‹Đč ŃĐ·Ń‹Đș? ĐŸĐ”Ń€Đ”Ń…ĐŸĐŽĐžŃ‚Đ” ĐČ Issues 💜 + +

    + +## `ĐĄĐŸĐŽĐ”Ń€Đ¶Đ°ĐœĐžĐ”` + +#### [`РазЎДл 0: Đ—ĐŸĐ»ĐŸŃ‚ĐŸĐ” праĐČĐžĐ»ĐŸ`](#section-0ïžâƒŁ-the-golden-rule) + +КлючДĐČĐŸĐ” праĐČĐžĐ»ĐŸ ĐŽĐ»Ń ĐœĐ°ĐżĐžŃĐ°ĐœĐžŃ ĐșачДстĐČĐ”ĐœĐœŃ‹Ń… Ń‚Đ”ŃŃ‚ĐŸĐČ (1 ĐżŃƒĐœĐșт) + +#### [`РазЎДл 1: ĐĐœĐ°Ń‚ĐŸĐŒĐžŃ Ń‚Đ”ŃŃ‚ĐŸĐČ`](#section-1-the-test-anatomy-1) + +Đ€ŃƒĐœĐŽĐ°ĐŒĐ”ĐœŃ‚ - струĐșтура чостых Ń‚Đ”ŃŃ‚ĐŸĐČ (12 ĐżŃƒĐœĐșŃ‚ĐŸĐČ) + +#### [`РазЎДл 2: Backend`](#section-2ïžâƒŁ-backend-testing) + +Đ Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Đșа ŃŃ„Ń„Đ”ĐșтоĐČĐœŃ‹Ń… Backend and Microservices Ń‚Đ”ŃŃ‚ĐŸĐČ (13 ĐżŃƒĐœĐșŃ‚ĐŸĐČ) + +#### [`РазЎДл 3: Frontend`](#section-3ïžâƒŁ-frontend-testing) + +ĐĐ°ĐżĐžŃĐ°ĐœĐžĐ” Ń‚Đ”ŃŃ‚ĐŸĐČ ĐŽĐ»Ń UI, ĐČĐșĐ»ŃŽŃ‡Đ°Ń Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ĐŸĐČ Đž E2E тДсты (11 ĐżŃƒĐœĐșŃ‚ĐŸĐČ) + +#### [`РазЎДл 4: Đ˜Đ·ĐŒĐ”Ń€Đ”ĐœĐžĐ” ŃŃ„Ń„Đ”ĐșтоĐČĐœĐŸŃŃ‚Đž Ń‚Đ”ŃŃ‚ĐŸĐČ`](#section-4ïžâƒŁ-measuring-test-effectiveness) + +Đ˜Đ·ĐŒĐ”Ń€Đ”ĐœĐžĐ” ĐșачДстĐČа Ń‚Đ”ŃŃ‚ĐŸĐČ (4 ĐżŃƒĐœĐșта) + +#### [`РазЎДл 5: Continuous Integration`](#section-5ïžâƒŁ-ci-and-other-quality-measures) + +РуĐșĐŸĐČĐŸĐŽŃŃ‚ĐČĐŸ ĐŽĐ»Ń CI ĐČ ĐŒĐžŃ€Đ” JS (9 ĐżŃƒĐœĐșŃ‚ĐŸĐČ) + +

    + +# РазЎДл 0ïžâƒŁ: Đ—ĐŸĐ»ĐŸŃ‚ĐŸĐ” праĐČĐžĐ»ĐŸ + +
    + +## âšȘ 0 Đ—ĐŸĐ»ĐŸŃ‚ĐŸĐ” праĐČĐžĐ»ĐŸ: КаĐș ĐżŃ€ĐŸĐ”ĐșŃ‚ĐžŃ€ĐŸĐČать тДсты + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** +йДсты - ŃŃ‚ĐŸ ĐœĐ” ĐżŃ€ĐŸĐŽĐ°ĐșŃˆĐœ-ĐșĐŸĐŽ. ĐąĐ”ŃŃ‚ĐŸĐČыĐč ĐșĐŸĐŽ ĐŽĐŸĐ»Đ¶Đ”Đœ Đ±Ń‹Ń‚ŃŒ ĐșĐŸŃ€ĐŸŃ‚ĐșĐžĐŒ, ĐżĐ»ĐŸŃĐșĐžĐŒ Đž ĐżŃ€ĐŸŃŃ‚Ń‹ĐŒ, Ń‡Ń‚ĐŸĐ±Ń‹ с ĐœĐžĐŒ Đ±Ń‹Đ»ĐŸ ĐżŃ€ĐžŃŃ‚ĐœĐŸ Ń€Đ°Đ±ĐŸŃ‚Đ°Ń‚ŃŒ. ĐžĐœ ĐŽĐŸĐ»Đ¶Đ”Đœ Đ±Ń‹Ń‚ŃŒ таĐșĐžĐŒ, Ń‡Ń‚ĐŸĐ±Ń‹ ĐČĐ·ĐłĐ»ŃĐœŃƒĐČ ĐœĐ° ĐœĐ”ĐłĐŸ, ĐŒĐŸĐ¶ĐœĐŸ Đ±Ń‹Đ»ĐŸ ŃŃ€Đ°Đ·Ńƒ ĐżĐŸĐœŃŃ‚ŃŒ Đ·Đ°ĐŒŃ‹ŃĐ”Đ» Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐșа. + +Đ’ĐŸ ĐČŃ€Đ”ĐŒŃ Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚ĐșĐž, ĐœĐ°Ńˆ Ń€Đ°Đ·ŃƒĐŒ ĐżĐŸĐ»ĐœĐŸŃŃ‚ŃŒŃŽ ŃŃ„ĐŸĐșŃƒŃĐžŃ€ĐŸĐČĐ°Đœ ĐœĐ° ĐżŃ€ĐŸĐŽĐ°ĐșŃˆĐœ-ĐșĐŸĐŽĐ”. Наш ĐžĐœŃ‚Đ”Đ»Đ»Đ”Đșт ĐżĐŸĐ»ĐœĐŸŃŃ‚ŃŒŃŽ ĐżĐŸĐłŃ€ŃƒĐ¶Đ”Đœ ĐČ Ń€Đ°Đ±ĐŸŃ‚Ńƒ Đž, ĐșаĐș праĐČĐžĐ»ĐŸ, ĐČĐČĐ”ĐŽĐ”ĐœĐžĐ” ĐŽĐŸĐżĐŸĐ»ĐœĐžŃ‚Đ”Đ»ŃŒĐœĐŸĐč ŃĐ»ĐŸĐ¶ĐœĐŸŃŃ‚Đž ĐŒĐŸĐ¶Đ”Ń‚ проĐČДстО Đș "ĐżĐ”Ń€Đ”ĐłŃ€ŃƒĐ·ĐșĐ”". ЕслО ĐŒŃ‹ ĐżĐŸĐżŃ‹Ń‚Đ°Đ”ĐŒŃŃ ĐŽĐŸĐ±Đ°ĐČоть ДщД ĐŸĐŽĐœŃƒ ĐșĐŸĐŒĐżĐ»Đ”ĐșŃĐœŃƒŃŽ ŃĐžŃŃ‚Đ”ĐŒŃƒ, ŃŃ‚ĐŸ ĐŒĐŸĐ¶Đ”Ń‚ проĐČДстО Đș Ń‚ĐŸŃ€ĐŒĐŸĐ¶Đ”ĐœĐžŃŽ Ń†Đ”Đ»ĐŸĐłĐŸ Ń€Đ°Đ±ĐŸŃ‡Đ”ĐłĐŸ ĐżŃ€ĐŸŃ†Đ”ŃŃĐ°, Ń‡Ń‚ĐŸ ĐżŃ€ĐŸŃ‚ĐžĐČĐŸŃ€Đ”Ń‡ĐžŃ‚ ŃĐ°ĐŒĐŸĐč ОЎДД Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ. На праĐșтоĐșĐ”, ĐŒĐœĐŸĐłĐžĐ” ĐșĐŸĐŒĐ°ĐœĐŽŃ‹ Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐșĐŸĐČ ĐżŃ€Đ”ĐœĐ”Đ±Ń€Đ”ĐłĐ°ŃŽŃ‚ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ”ĐŒ. + +ĐąĐ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” ĐœŃƒĐ¶ĐœĐŸ Ń€Đ°ŃŃĐŒĐ°Ń‚Ń€ĐžĐČать, ĐșаĐș Đ»ĐžŃ‡ĐœĐŸĐłĐŸ ĐżĐŸĐŒĐŸŃ‰ĐœĐžĐșа, ĐČŃ‚ĐŸŃ€ĐŸĐłĐŸ ĐżĐžĐ»ĐŸŃ‚Đ°, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč за ĐœĐ”Đ±ĐŸĐ»ŃŒŃˆŃƒŃŽ ĐżĐ»Đ°Ń‚Ńƒ ĐżŃ€Đ”ĐŽĐŸŃŃ‚Đ°ĐČĐ»ŃĐ”Ń‚ Đ±Đ”ŃŃ†Đ”ĐœĐœŃ‹Đ” услугО Đž ĐżĐŸĐ»ŃŒĐ·Ńƒ. ĐŁŃ‡Đ”ĐœŃ‹Đ” утĐČДржЎают, Ń‡Ń‚ĐŸ у Ń‡Đ”Đ»ĐŸĐČĐ”Đșа Đ”ŃŃ‚ŃŒ 2 топа ĐŒŃ‹ŃˆĐ»Đ”ĐœĐžŃ: топ 1 ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒĐ”Ń‚ŃŃ про ĐČŃ‹ĐżĐŸĐ»ĐœĐ”ĐœĐžĐž ĐżŃ€ĐŸŃŃ‚Ń‹Ń… заЎач, ĐœĐ” Ń‚Ń€Đ”Đ±ŃƒŃŽŃ‰ĐžŃ… усОлОĐč, таĐșох ĐșаĐș ĐČĐŸĐ¶ĐŽĐ”ĐœĐžĐ” аĐČŃ‚ĐŸĐŒĐŸĐ±ĐžĐ»Ń ĐżĐŸ ĐżŃƒŃŃ‚ĐŸĐč ĐŽĐŸŃ€ĐŸĐłĐ”, Đž топ 2, ĐżŃ€Đ”ĐŽĐœĐ°Đ·ĐœĐ°Ń‡Đ”ĐœĐœŃ‹Đč ĐŽĐ»Ń ŃĐ»ĐŸĐ¶ĐœŃ‹Ń… ĐŒŃ‹ŃĐ»ĐžŃ‚Đ”Đ»ŃŒĐœŃ‹Ń… ĐżŃ€ĐŸŃ†Đ”ŃŃĐŸĐČ, ĐșаĐș, ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, Ń€Đ”ŃˆĐ”ĐœĐžĐ” ĐŒĐ°Ń‚Đ”ĐŒĐ°Ń‚ĐžŃ‡Đ”ŃĐșох ураĐČĐœĐ”ĐœĐžĐč. +йДсты ĐŽĐŸĐ»Đ¶ĐœŃ‹ Đ±Ń‹Ń‚ŃŒ ĐœĐ°ĐżĐžŃĐ°ĐœŃ‹ таĐș, Ń‡Ń‚ĐŸĐ±Ń‹ про ĐżŃ€ĐŸŃĐŒĐŸŃ‚Ń€Đ” ĐœĐ° ĐșĐŸĐŽ ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Đ»ŃŃ 1 топ ĐŒŃ‹ŃˆĐ»Đ”ĐœĐžŃ, Đž ŃŃ‚ĐŸ Đ±Ń‹Đ»ĐŸ ĐżĐŸŃ…ĐŸĐ¶Đ” ĐœĐ° ĐżŃ€ĐŸŃŃ‚ĐŸĐ” ĐžĐ·ĐŒĐ”ĐœĐ”ĐœĐžĐ” HTML-ĐŽĐŸĐșŃƒĐŒĐ”ĐœŃ‚Đ°, а ĐœĐ” Ń€Đ”ŃˆĐ”ĐœĐžĐ” ĐŒĐ°Ń‚Đ”ĐŒĐ°Ń‚ĐžŃ‡Đ”ŃĐșĐŸĐłĐŸ ураĐČĐœĐ”ĐœĐžŃ 2 × (17 × 24). + +Đ­Ń‚ĐŸĐłĐŸ ĐŒĐŸĐ¶ĐœĐŸ ĐŽĐŸŃŃ‚ĐžŃ‡ŃŒ ĐżŃƒŃ‚Đ”ĐŒ Ń‚Ń‰Đ°Ń‚Đ”Đ»ŃŒĐœĐŸĐłĐŸ ĐČŃ‹Đ±ĐŸŃ€Đ° ŃĐżĐŸŃĐŸĐ±ĐŸĐČ, ĐžĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚ĐŸĐČ Đž цДлДĐč Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” Đ±ŃƒĐŽŃƒŃ‚ ĐŸĐŽĐœĐŸĐČŃ€Đ”ĐŒĐ”ĐœĐœĐŸ Đž ŃŃ„Ń„Đ”ĐșтоĐČĐœŃ‹ĐŒĐž Đž "ĐŸĐșŃƒĐżĐ°Đ”ĐŒŃ‹ĐŒĐž". ĐąĐ”ŃŃ‚ĐžŃ€ŃƒĐčтД Ń‚ĐŸĐ»ŃŒĐșĐŸ Ń‚ĐŸ, Ń‡Ń‚ĐŸ ĐœĐ”ĐŸĐ±Ń…ĐŸĐŽĐžĐŒĐŸ. СтараĐčŃ‚Đ”ŃŃŒ уĐČĐ”Đ»ĐžŃ‡ĐžŃ‚ŃŒ сĐșĐŸŃ€ĐŸŃŃ‚ŃŒ Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚ĐșĐž. Đ˜ĐœĐŸĐłĐŽĐ° ЎажД ŃŃ‚ĐŸĐžŃ‚ ĐŸŃ‚ĐșĐ°Đ·Đ°Ń‚ŃŒŃŃ ĐŸŃ‚ ĐœĐ”ĐșĐŸŃ‚ĐŸŃ€Ń‹Ń… Ń‚Đ”ŃŃ‚ĐŸĐČ, Ń‡Ń‚ĐŸĐ±Ń‹ ŃĐŽĐ”Đ»Đ°Ń‚ŃŒ ĐșĐŸĐŽ Đ±ĐŸĐ»Đ”Đ” ĐżŃ€ĐŸŃŃ‚Ń‹ĐŒ Đž гОбĐșĐžĐŒ. + +![alt text](/assets/headspace.png "We have no head room for additional complexity") + +Đ‘ĐŸĐ»ŃŒŃˆĐžĐœŃŃ‚ĐČĐŸ ĐŽĐ°Đ»ŃŒĐœĐ”Đčшох ŃĐŸĐČĐ”Ń‚ĐŸĐČ ŃĐČĐ»ŃŃŽŃ‚ŃŃ ĐżŃ€ĐŸĐžĐ·ĐČĐŸĐŽĐœŃ‹ĐŒĐž ĐŸŃ‚ ŃŃ‚ĐŸĐłĐŸ ĐżŃ€ĐžĐœŃ†ĐžĐżĐ°. + +### Đ“ĐŸŃ‚ĐŸĐČы? + +

    + +# РазЎДл 1: ĐĐœĐ°Ń‚ĐŸĐŒĐžŃ Ń‚Đ”ŃŃ‚ĐŸĐČ + +
    + +## âšȘ  1.1 ĐšĐ°Đ¶ĐŽĐŸĐ” ĐŸĐżĐžŃĐ°ĐœĐžĐ” тДста ĐČĐșлючаДт ĐČ ŃĐ”Đ±Ń 3 часто + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** ОтчДт ĐŸ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐž ĐŽĐŸĐ»Đ¶Đ”Đœ ĐżŃ€Đ”ĐŽĐŸŃŃ‚Đ°ĐČĐ»ŃŃ‚ŃŒ ĐžĐœŃ„ĐŸŃ€ĐŒĐ°Ń†ĐžŃŽ ĐŸ Ń‚ĐŸĐŒ, ŃƒĐŽĐŸĐČлДтĐČĐŸŃ€ŃĐ”Ń‚ лО тДĐșущая ĐČĐ”Ń€ŃĐžŃ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ Ń‚Ń€Đ”Đ±ĐŸĐČĐ°ĐœĐžŃĐŒ Ń‡Đ”Đ»ĐŸĐČĐ”Đșа, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč Ń‡Đ°ŃŃ‚ĐŸ ĐœĐ”Đ·ĐœĐ°ĐșĐŸĐŒ с ĐșĐŸĐŽĐŸĐŒ ОлО забыл Đ”ĐłĐŸ: Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČщоĐș, DevOps-ĐžĐœĐ¶Đ”ĐœĐ”Ń€, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč разĐČĐŸŃ€Đ°Ń‡ĐžĐČаДт ĐżŃ€ĐŸĐ”Đșт, а таĐșжД Вы ŃĐ°ĐŒĐž чДрДз 2 ĐłĐŸĐŽĐ°. +ĐĐ°ĐžĐ»ŃƒŃ‡ŃˆĐžĐč ŃĐżĐŸŃĐŸĐ± ĐŽĐŸĐ±ĐžŃ‚ŃŒŃŃ ŃŃ‚ĐŸĐłĐŸ, ДслО тДсты Đ±ŃƒĐŽŃƒŃ‚ ĐżŃ€ĐŸĐČĐŸĐŽĐžŃ‚ŃŒŃŃ ĐœĐ° ŃƒŃ€ĐŸĐČĐœĐ” Ń‚Ń€Đ”Đ±ĐŸĐČĐ°ĐœĐžĐč, а Đ”ĐłĐŸ ĐŸĐżĐžŃĐ°ĐœĐžĐ” ŃĐŸŃŃ‚ĐŸŃŃ‚ŃŒ Оз 3-х частДĐč: + +(1) Đ§Ń‚ĐŸ ĐžĐŒĐ”ĐœĐœĐŸ Ń‚Đ”ŃŃ‚ĐžŃ€ŃƒĐ”Ń‚ŃŃ? ĐĐ°ĐżŃ€ĐžĐŒĐ”Ń€: ProductsService.addNewProduct method + +(2) Про ĐșаĐșох ĐŸĐ±ŃŃ‚ĐŸŃŃ‚Đ”Đ»ŃŒŃŃ‚ĐČах? ĐĐ°ĐżŃ€ĐžĐŒĐ”Ń€: no price is passed to the method + +(3) КаĐșĐŸĐč ĐŸĐ¶ĐžĐŽĐ°Đ”ĐŒŃ‹Đč Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚? ĐĐ°ĐżŃ€ĐžĐŒĐ”Ń€: the new product is not approved + +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** A deployment just failed, a test named “Add product” failed. Đ“ĐŸĐČĐŸŃ€ĐžŃ‚ лО ŃŃ‚ĐŸ ĐČĐ°ĐŒ ĐŸ Ń‚ĐŸĐŒ, ĐČ Ń‡Đ”ĐŒ ĐžĐŒĐ”ĐœĐœĐŸ заĐșĐ»ŃŽŃ‡Đ°Đ”Ń‚ŃŃ ŃĐ±ĐŸĐč? +
    + +**👇 ОбратО ĐČĐœĐžĐŒĐ°ĐœĐžĐ”:** КажЎыĐč ĐżŃƒĐœĐșт ŃĐŸĐŽĐ”Ń€Đ¶ĐžŃ‚ ĐżŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ°, а ĐžĐœĐŸĐłĐŽĐ° Đž ОллюстрацОю Đș ĐœĐ”ĐŒŃƒ. ĐĐ°Đ¶ĐŒĐžŃ‚Đ”, Ń‡Ń‚ĐŸĐ±Ń‹ ĐŸŃ‚Đșрыть +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: ĐžĐżĐžŃĐ°ĐœĐžĐ” тДста ŃĐŸĐŽĐ”Ń€Đ¶ĐžŃ‚ 3 часто + +![](https://img.shields.io/badge/🔹%20Example%20using%20Mocha-blue.svg "Using Mocha to illustrate the idea") + +```javascript +//1. ĐąĐ”ŃŃ‚ĐžŃ€ŃƒĐ”ĐŒŃ‹Đč Đ±Đ»ĐŸĐș +describe('Products Service', function() { + describe('Add new product', function() { + //2. ŃŃ†Đ”ĐœĐ°Ń€ĐžĐč and 3. ĐŸĐ¶ĐžĐŽĐ°ĐœĐžĐ” + it('When no price is specified, then the product status is pending approval', ()=> { + const newProduct = new ProductService().add(...); + expect(newProduct.status).to.equal('pendingApproval'); + }); + }); +}); + +``` + +
    + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: ĐžĐżĐžŃĐ°ĐœĐžĐ” тДста ŃĐŸĐŽĐ”Ń€Đ¶ĐžŃ‚ 3 часто + +![alt text](/assets/bp-1-3-parts.jpeg "ĐžĐżĐžŃĐ°ĐœĐžĐ” тДста ŃĐŸĐŽĐ”Ń€Đ¶ĐžŃ‚ 3 часто") + +
    + +
    +
    © Чотать ЎалДД... + 1. Roy Osherove - Naming standards for unit tests +
    + +

    + +## âšȘ  1.2 РазЎДлОтД тДсты ĐżĐŸ AAA-струĐșŃ‚ŃƒŃ€Đ” + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** РазЎДлОтД ĐœĐ°ĐżĐžŃĐ°ĐœĐœŃ‹Đ” тДсты ŃĐŸĐłĐ»Đ°ŃĐœĐŸ 3 ĐșĐ°Ń‚Đ”ĐłĐŸŃ€ĐžŃĐŒ: Arrange, Act & Assert (AAA). ĐĄĐ»Đ”ĐŽĐŸĐČĐ°ĐœĐžĐ” ĐŽĐ°ĐœĐœĐŸĐč струĐșŃ‚ŃƒŃ€Đ” ĐżĐŸĐ·ĐČĐŸĐ»ĐžŃ‚ Ń‡Đ”Đ»ĐŸĐČĐ”Đșу, Đ°ĐœĐ°Đ»ĐžĐ·ĐžŃ€ŃƒŃŽŃ‰Đ”ĐŒŃƒ тДсты, быстрДД Ń€Đ°Đ·ĐŸĐ±Ń€Đ°Ń‚ŃŒŃŃ ĐČ ŃŃ‚Ń€Đ°Ń‚Đ”ĐłĐžĐž Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ. + +1. A - ĐžŃ€ĐłĐ°ĐœĐžĐ·ŃƒĐčтД (Arrange): В ĐŽĐ°ĐœĐœŃƒŃŽ ĐșĐ°Ń‚Đ”ĐłĐŸŃ€ĐžŃŽ ĐČŃ…ĐŸĐŽĐžŃ‚ ĐœĐ°ŃŃ‚Ń€ĐŸĐčĐșĐž, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” проĐČĐ”ĐŽŃƒŃ‚ ĐșĐŸĐŽ Đș Ń‚ĐŸĐŒŃƒ ŃŃ†Đ”ĐœĐ°Ń€ĐžŃŽ, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč ĐŽĐŸĐ»Đ¶Đ”Đœ Đ±Ń‹Ń‚ŃŒ ĐžĐŒĐžŃ‚ĐžŃ€ĐŸĐČĐ°Đœ. ĐžĐœĐž ĐČĐșлючают ĐČ ŃĐ”Đ±Ń ĐŸŃ€ĐłĐ°ĐœĐžĐ·Đ°Ń†ĐžŃ Ń‚Ń€Đ”ŃŃ‚ĐžŃ€ŃƒĐ”ĐŒĐŸĐłĐŸ Đ±Đ»ĐŸĐșа, ĐŽĐŸĐ±Đ°ĐČĐ»Đ”ĐœĐžŃ запОсДĐč ĐČ Đ‘Đ”, mocking/stubbing Đž ĐŒĐœĐŸĐłĐŸĐ” ĐŽŃ€ŃƒĐłĐŸĐ”. + +2. A - ДДĐčстĐČуĐčтД (Act): Đ’Ń‹ĐżĐŸĐ»ĐœĐžŃ‚Đ” Ń‚Đ”ŃŃ‚ĐžŃ€ŃƒĐ”ĐŒŃ‹Đč Đ±Đ»ĐŸĐș ĐșĐŸĐŽĐ°. КаĐș праĐČĐžĐ»ĐŸ, Đ·Đ°ĐœĐžĐŒĐ°Đ”Ń‚ 1 ŃŃ‚Ń€ĐŸĐșу ĐșĐŸĐŽĐ°. + +3. A - УтĐČДрЎОтД (Assert): ĐŁĐ±Đ”ĐŽĐžŃ‚Đ”ŃŃŒ, Ń‡Ń‚ĐŸ ĐżĐŸĐ»ŃƒŃ‡Đ”ĐœĐœŃ‹Đč Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚ ŃĐŸĐŸŃ‚ĐČДтстĐČŃƒĐ”Ń‚ ĐŸĐ¶ĐžĐŽĐ°Đ”ĐŒĐŸĐŒŃƒ. КаĐș праĐČĐžĐ»ĐŸ, Đ·Đ°ĐœĐžĐŒĐ°Đ”Ń‚ 1 ŃŃ‚Ń€ĐŸĐșу ĐșĐŸĐŽĐ°. + +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** Вы ĐœĐ” Ń‚ĐŸĐ»ŃŒĐșĐŸ тратОтД ĐČŃ€Đ”ĐŒŃ ĐœĐ° Ń‚ĐŸ, Ń‡Ń‚ĐŸĐ±Ń‹ ĐżĐŸĐœŃŃ‚ŃŒ, ĐșаĐș Ń€Đ°Đ±ĐŸŃ‚Đ°Đ”Ń‚ ĐŸŃĐœĐŸĐČĐœĐŸĐč ĐșĐŸĐŽ, ĐœĐŸ Đž ĐœĐ° Ń‚ĐŸ, ĐșаĐș Ń„ŃƒĐœĐșŃ†ĐžĐŸĐœĐžŃ€ŃƒŃŽŃ‚ тДсты, ĐœĐ”ŃĐŒĐŸŃ‚Ń€Ń ĐœĐ° Ń‚ĐŸ, Ń‡Ń‚ĐŸ ŃŃ‚ĐŸ ĐŽĐŸĐ»Đ¶ĐœĐŸ Đ±Ń‹Ń‚ŃŒ ĐżŃ€ĐŸŃŃ‚ĐŸĐč заЎачДĐč. +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: йДст, ĐŸŃ„ĐŸŃ€ĐŒĐ»Đ”ĐœĐœŃ‹Đč ĐżĐŸ струĐșŃ‚ŃƒŃ€Đ” AAA + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Jest") ![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "Examples with Mocha") + +```javascript +describe("Customer classifier", () => { + test("When customer spent more than 500$, should be classified as premium", () => { + // Arrange + const customerToClassify = { spent: 505, joined: new Date(), id: 1 }; + const DBStub = sinon + .stub(dataAccess, "getCustomer") + .reply({ id: 1, classification: "regular" }); + + // Act + const receivedClassification = + customerClassifier.classifyCustomer(customerToClassify); + + // Assert + expect(receivedClassification).toMatch("premium"); + }); +}); +``` + +
    + +### :thumbsdown: ĐĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœĐŸ: Đ Đ°Đ·ĐŽĐ”Đ»Đ”ĐœĐžĐ” ĐŸŃ‚ŃŃƒŃ‚ŃŃ‚ĐČŃƒĐ”Ń‚, ŃĐżĐ»ĐŸŃˆĐœĐŸĐč ĐșĐŸĐŽ, Ń‚ŃĐ¶Đ”Đ»ĐŸ Ń€Đ°Đ·ĐŸĐ±Ń€Đ°Ń‚ŃŒ ĐœĐ°ĐżĐžŃĐ°ĐœĐœĐŸĐ” + +```javascript +test("Should be classified as premium", () => { + const customerToClassify = { spent: 505, joined: new Date(), id: 1 }; + const DBStub = sinon + .stub(dataAccess, "getCustomer") + .reply({ id: 1, classification: "regular" }); + const receivedClassification = + customerClassifier.classifyCustomer(customerToClassify); + expect(receivedClassification).toMatch("premium"); +}); +``` + +
    + +

    + +## âšȘ 1.3 ĐžĐżĐžŃˆĐžŃ‚Đ” ĐŸĐ¶ĐžĐŽĐ°ĐœĐžŃ ĐœĐ° ŃĐ·Ń‹ĐșĐ” ĐżŃ€ĐŸĐŽŃƒĐșта: ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒĐčтД утĐČĐ”Ń€Đ¶ĐŽĐ”ĐœĐžŃ ĐČ ŃŃ‚ĐžĐ»Đ” BDD + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** ĐĐ°ĐżĐžŃĐ°ĐœĐžĐ” Ń‚Đ”ŃŃ‚ĐŸĐČ ĐČ ĐŽĐ”ĐșларатОĐČĐœĐŸĐŒ стОлД ĐżĐŸĐ·ĐČĐŸĐ»ŃĐ”Ń‚ чОтатДлю ĐŒĐłĐœĐŸĐČĐ”ĐœĐœĐŸ ĐżĐŸĐœŃŃ‚ŃŒ ŃĐŒŃ‹ŃĐ» ĐżŃ€ĐŸĐžŃŃ…ĐŸĐŽŃŃ‰Đ”ĐłĐŸ, ĐŸŃĐŸĐ±ĐŸ ĐœĐ” ĐœĐ°ĐżŃ€ŃĐłĐ°ŃŃŃŒ. ĐšĐŸĐłĐŽĐ° ĐČы ĐżĐžŃˆĐ”Ń‚Đ” ĐșĐŸĐŽ ĐČ ĐžĐŒĐżĐ”Ń€Đ°Ń‚ĐžĐČĐœĐŸĐŒ стОлД, ĐœĐ°ĐżĐŸĐ»ĐœĐ”ĐœĐœŃ‹Đč ŃƒŃĐ»ĐŸĐČĐœŃ‹ĐŒĐž ĐșĐŸĐœŃŃ‚Ń€ŃƒĐșŃ†ĐžŃĐŒĐž, ĐœĐ”ĐŸĐ±Ń…ĐŸĐŽĐžĐŒĐŸ проĐșлаЎыĐČать ĐœĐ”ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ŃƒŃĐžĐ»ĐžŃ, Ń‡Ń‚ĐŸĐ±Ń‹ Đ”ĐłĐŸ Ń€Đ°Đ·ĐŸĐ±Ń€Đ°Ń‚ŃŒ. В ŃŃ‚ĐŸĐŒ ŃĐ»ŃƒŃ‡Đ°Đ” ĐœĐ”ĐŸĐ±Ń…ĐŸĐŽĐžĐŒĐŸ посать ĐșĐŸĐŽ ĐČ ŃŃ‚ĐžĐ»Đ” "Ń‡Đ”Đ»ĐŸĐČДчДсĐșĐŸĐč" рДчО, ĐżŃ€ĐžĐŒĐ”ĐœŃŃ BDD-ĐżĐŸĐŽŃ…ĐŸĐŽ с ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°ĐœĐžĐ”ĐŒ `expect` ОлО `should` Đž ĐžĐ·Đ±Đ”ĐłĐ°Ń ĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»ŃŒŃĐșох ĐșĐŸĐœŃŃ‚Ń€ŃƒĐșцоĐč. В ŃĐ»ŃƒŃ‡Đ°Đ”, ДслО Chai & Jest ĐœĐ” ŃĐŸĐŽĐ”Ń€Đ¶Đ°Ń‚ ĐœŃƒĐ¶ĐœŃ‹Đ” assertions, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” Ń‡Đ°ŃŃ‚ĐŸ ĐżĐŸĐČŃ‚ĐŸŃ€ŃŃŽŃ‚ŃŃ про ĐœĐ°ĐżĐžŃĐ°ĐœĐžĐž, Ń‚ĐŸ Ń€Đ°ŃŃĐŒĐŸŃ‚Ń€ĐžŃ‚Đ” [extending Jest matcher (Jest)](https://jestjs.io/docs/en/expect#expectextendmatchers) ОлО ĐœĐ°ĐżĐžŃĐ°ĐœĐžĐ” [custom Chai plugin](https://www.chaijs.com/guide/plugins/) +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** Про Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚ĐșĐ” Đ±ŃƒĐŽĐ”Ń‚ ĐœĐ°ĐżĐžŃĐ°ĐœĐŸ ĐŒĐ”ĐœŃŒŃˆĐ” Ń‚Đ”ŃŃ‚ĐŸĐČ, а ĐœĐ”ĐœŃƒĐ¶ĐœŃ‹Đ” тДсты Đ±ŃƒĐŽŃƒŃ‚ ĐżŃ€ĐŸĐžĐłĐœĐŸŃ€ĐžŃ€ĐŸĐČĐ°ĐœŃ‹ с ĐżĐŸĐŒĐŸŃ‰ŃŒŃŽ .skip(). + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ°
    + +![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "Examples with Mocha & Chai") ![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Jest") + +### :thumbsdown: ĐĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœĐŸ: Đ§Ń‚ĐŸĐ±Ń‹ ĐżŃ€ĐŸŃĐŒĐŸŃ‚Ń€Đ”Ń‚ŃŒ рДалОзацОю Ń‚Đ”ŃŃ‚ĐŸĐČ, Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐș ĐŽĐŸĐ»Đ¶Đ”Đœ ĐżŃ€ĐŸŃĐŒĐŸŃ‚Ń€Đ”Ń‚ŃŒ ĐČĐ”ŃŃŒ ĐŸĐ±ŃŠŃ‘ĐŒĐœŃ‹Đč Đž ĐžĐŒĐżĐ”Ń€Đ°Ń‚ĐžĐČĐœŃ‹Đč ĐșĐŸĐŽ + +```javascript +test("When asking for an admin, ensure only ordered admins in results", () => { + // ĐżŃ€Đ”ĐŽĐżĐŸĐ»ĐŸĐ¶ĐžĐŒ, Ń‡Ń‚ĐŸ ĐŒŃ‹ ĐŽĐŸĐ±Đ°ĐČОлО Đ·ĐŽĐ”ŃŃŒ "admin1", "admin2" and "user1" + const allAdmins = getUsers({ adminOnly: true }); + + let admin1Found, + adming2Found = false; + + allAdmins.forEach((aSingleUser) => { + if (aSingleUser === "user1") { + assert.notEqual(aSingleUser, "user1", "A user was found and not admin"); + } + if (aSingleUser === "admin1") { + admin1Found = true; + } + if (aSingleUser === "admin2") { + admin2Found = true; + } + }); + + if (!admin1Found || !admin2Found) { + throw new Error("Not all admins were returned"); + } +}); +``` + +
    + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: ĐŸŃ€ĐŸŃĐŒĐŸŃ‚Ń€Đ”Ń‚ŃŒ ĐŽĐ°ĐœĐœŃ‹Đč ĐșĐŸĐŽ ĐČ ĐŽĐ”ĐșларатОĐČĐœĐŸĐŒ стОлД ĐœĐ” ŃĐŸŃŃ‚Đ°ĐČĐ»ŃĐ”Ń‚ труЮа + +```javascript +it("When asking for an admin, ensure only ordered admins in results", () => { + // ĐżŃ€Đ”ĐŽĐżĐŸĐ»ĐŸĐ¶ĐžĐŒ, Ń‡Ń‚ĐŸ ĐŒŃ‹ ĐŽĐŸĐ±Đ°ĐČОлО Đ·ĐŽĐ”ŃŃŒ 2-х Đ°ĐŽĐŒĐžĐœĐžŃŃ‚Ń€Đ°Ń‚ĐŸŃ€ĐŸĐČ + const allAdmins = getUsers({ adminOnly: true }); + + expect(allAdmins) + .to.include.ordered.members(["admin1", "admin2"]) + .but.not.include.ordered.members(["user1"]); +}); +``` + +
    + +

    + +## âšȘ  1.4 ĐŸŃ€ĐžĐŽĐ”Ń€Đ¶ĐžĐČаĐčŃ‚Đ”ŃŃŒ black-box Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ: ĐąĐ”ŃŃ‚ĐžŃ€ŃƒĐčтД Ń‚ĐŸĐ»ŃŒĐșĐŸ public-ĐŒĐ”Ń‚ĐŸĐŽŃ‹ + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** ĐąĐ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” ĐČĐœŃƒŃ‚Ń€Đ”ĐœĐœĐžŃ… ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ĐŸĐČ ŃĐŸĐżŃ€ĐŸĐČĐŸĐ¶ĐŽĐ°Đ”Ń‚ŃŃ Đ±ĐŸĐ»ŃŒŃˆĐžĐŒĐž Đ·Đ°Ń‚Ń€Đ°Ń‚Đ°ĐŒĐž. ЕслО ĐČаш ĐșĐŸĐŽ/API Ń€Đ°Đ±ĐŸŃ‚Đ°Đ”Ń‚ ĐșĐŸŃ€Ń€Đ”ĐșŃ‚ĐœĐŸ, Ń†Đ”Đ»Đ”ŃĐŸĐŸĐ±Ń€Đ°Đ·ĐœĐŸ лО Đ±ŃƒĐŽĐ”Ń‚ ĐżĐŸŃ‚Ń€Đ°Ń‚ĐžŃ‚ŃŒ ŃĐ»Đ”ĐŽŃƒŃŽŃ‰ĐžĐ” 3 часа ĐœĐ° ĐœĐ°ĐżĐžŃĐ°ĐœĐžĐ” Ń‚Đ”ŃŃ‚ĐŸĐČ ĐŽĐ»Ń ĐČĐœŃƒŃ‚Ń€Đ”ĐœĐœĐ”Đč рДалОзацОО Đž ĐżĐŸŃ‚ĐŸĐŒ ĐČсД ŃŃ‚ĐŸ ĐżĐŸĐŽĐŽĐ”Ń€Đ¶ĐžĐČать? КажЎыĐč раз, ĐșĐŸĐłĐŽĐ° Ń‚Đ”ŃŃ‚ĐžŃ€ŃƒĐ”Ń‚ŃŃ ĐČĐœĐ”ŃˆĐœĐ”Đ” ĐżĐŸĐČĐ”ĐŽĐ”ĐœĐžĐ”, ĐČĐœŃƒŃ‚Ń€Đ”ĐœĐœŃŃ Ń€Đ”Đ°Đ»ĐžĐ·Đ°Ń†ĐžŃ таĐșжД ĐżŃ€ĐŸĐČĐ”Ń€ŃĐ”Ń‚ŃŃ ĐœĐ”ŃĐČĐœŃ‹ĐŒ ĐŸĐ±Ń€Đ°Đ·ĐŸĐŒ. йДсты ĐŒĐŸĐłŃƒŃ‚ упасть Ń‚ĐŸĐ»ŃŒĐșĐŸ про ĐČĐŸĐ·ĐœĐžĐșĐœĐŸĐČĐ”ĐœĐžĐž ĐŸĐżŃ€Đ”ĐŽĐ”Đ»Đ”ĐœĐœĐŸĐč ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒŃ‹ (ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, ĐœĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœŃ‹Đč ĐČыĐČĐŸĐŽ). йаĐșĐŸĐč ĐżĐŸĐŽŃ…ĐŸĐŽ таĐșжД ĐœĐ°Đ·Ń‹ĐČают `ĐżĐŸĐČĐ”ĐŽĐ”ĐœŃ‡Đ”ŃĐșĐŸĐ” Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” (behavioral testing)`. ĐĄ ĐŽŃ€ŃƒĐłĐŸĐč ŃŃ‚ĐŸŃ€ĐŸĐœŃ‹, ДслО ĐČы Ń‚Đ”ŃŃ‚ĐžŃ€ŃƒĐ”Ń‚Đ” ĐČĐœŃƒŃ‚Ń€Đ”ĐœĐœĐžĐ” ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Ń‹ (white-box Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ”), ĐČаш Ń„ĐŸĐșус ĐŒĐŸĐ¶Đ”Ń‚ ŃĐŒĐ”ŃŃ‚ĐžŃ‚ŃŒŃŃ с ĐżĐ»Đ°ĐœĐžŃ€ĐŸĐČĐ°ĐœĐžŃ Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚Đ° Ń€Đ°Đ±ĐŸŃ‚Ń‹ ĐŽĐ°ĐœĐœĐŸĐłĐŸ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đ° ĐœĐ° ĐŒĐ”Đ»ĐșОД ЎДталО. КаĐș слДЎстĐČОД, тДст ĐŒĐŸĐ¶Đ”Ń‚ упасть Оз-за ĐœĐ”Đ·ĐœĐ°Ń‡ĐžŃ‚Đ”Đ»ŃŒĐœŃ‹Ń… оспраĐČĐ»Đ”ĐœĐžĐč ĐČ ĐșĐŸĐŽĐ”, ĐœĐ”ŃĐŒĐŸŃ‚Ń€Ń ĐœĐ° Ń‚ĐŸ, Ń‡Ń‚ĐŸ Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚ Ń€Đ°Đ±ĐŸŃ‚Ń‹ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đ° Đ±ŃƒĐŽĐ”Ń‚ ŃƒĐŽĐŸĐČлДтĐČĐŸŃ€ĐžŃ‚Đ”Đ»ŃŒĐœŃ‹ - ŃŃ‚ĐŸ ŃƒŃĐ»ĐŸĐ¶ĐœŃĐ”Ń‚ ĐżĐŸĐŽĐŽĐ”Ń€Đ¶Đșу таĐșĐŸĐłĐŸ ĐșĐŸĐŽĐ°. +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** Вашо тДсты ĐœĐ°Ń‡ĐžĐœĐ°ŃŽŃ‚ Ń€Đ°Đ±ĐŸŃ‚Đ°Ń‚ŃŒ ŃĐ»ĐŸĐČĐœĐŸ [ĐŒĐ°Đ»ŃŒŃ‡ĐžĐș, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč ĐșрОчал: "Đ’ĐŸĐ»Đș!"](https://ru.wikipedia.org/wiki/ĐœĐ°Đ»ŃŒŃ‡ĐžĐș,_ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč_ĐșрОчал:_Â«Đ’ĐŸĐ»Đș!»), ŃĐžĐłĐœĐ°Đ»ĐžĐ·ĐžŃ€ŃƒŃ ĐŸ Đ»ĐŸĐ¶ĐœŃ‹Ń… срабатыĐČĐ°ĐœĐžŃŃ… (ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, A test fails because a private variable name was changed). ĐĐ”ŃƒĐŽĐžĐČĐžŃ‚Đ”Đ»ŃŒĐœĐŸ, Ń‡Ń‚ĐŸ люЎО ĐČсĐșĐŸŃ€Đ” ĐœĐ°Ń‡ĐœŃƒŃ‚ ĐžĐłĐœĐŸŃ€ĐžŃ€ĐŸĐČать CI-уĐČĐ”ĐŽĐŸĐŒĐ»Đ”ĐœĐžŃ, ĐżĐŸĐșа ĐČ ĐŸĐŽĐžĐœ прДĐșŃ€Đ°ŃĐœŃ‹Đč ĐŽĐ”ĐœŃŒ ĐœĐ” ĐżŃ€ĐŸĐžĐłĐœĐŸŃ€ĐžŃ€ŃƒŃŽŃ‚ ĐœĐ°ŃŃ‚ĐŸŃŃ‰ŃƒŃŽ ĐŸŃˆĐžĐ±Đșу... + +
    +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :thumbsdown: ĐĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœĐŸ: ĐąĐ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” ĐČĐœŃƒŃ‚Ń€Đ”ĐœĐœĐžŃ… ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ĐŸĐČ Đ±Đ”Đ· ĐżŃ€ĐžŃ‡ĐžĐœŃ‹ + +![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "Examples with Mocha & Chai") + +```javascript +class ProductService { + // ŃŃ‚ĐŸŃ‚ ĐŒĐ”Ń‚ĐŸĐŽ ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒĐ”Ń‚ŃŃ Ń‚ĐŸĐ»ŃŒĐșĐŸ ĐČĐœŃƒŃ‚Ń€Đž + // ĐžĐ·ĐŒĐ”ĐœĐ”ĐœĐžĐ” ĐžĐŒĐ”ĐœĐž проĐČДЎДт Đș ĐżĐ°ĐŽĐ”ĐœĐžŃŽ Ń‚Đ”ŃŃ‚ĐŸĐČ + calculateVATAdd(priceWithoutVAT) { + return { finalPrice: priceWithoutVAT * 1.2 }; + // ĐžĐ·ĐŒĐ”ĐœĐ”ĐœĐžĐ” Ń„ĐŸŃ€ĐŒĐ°Ń‚Đ° Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚Đ° ОлО Đșлюча ĐČŃ‹ŃˆĐ” проĐČДЎДт ĐżĐ°ĐŽĐ”ĐœĐžŃŽ Ń‚Đ”ŃŃ‚ĐŸĐČ + } + // public method + getPrice(productId) { + const desiredProduct = DB.getProduct(productId); + finalPrice = this.calculateVATAdd(desiredProduct.price).finalPrice; + return finalPrice; + } +} + +it("White-box test: When the internal methods get 0 vat, it return 0 response", async () => { + // ĐœĐ”Ń‚ ĐœĐžĐșаĐșох Ń‚Ń€Đ”Đ±ĐŸĐČĐ°ĐœĐžĐč, Ń‡Ń‚ĐŸĐ±Ń‹ ĐżĐŸĐ»ŃŒĐ·ĐŸĐČатДлО ĐŒĐŸĐłĐ»Đž рассчотать НДС, Ń‚ĐŸĐ»ŃŒĐșĐŸ ĐżĐŸĐșĐ°Đ·Đ°Ń‚ŃŒ ĐșĐŸĐœĐ”Ń‡ĐœŃƒŃŽ Ń†Đ”ĐœŃƒ. ĐąĐ”ĐŒ ĐœĐ” ĐŒĐ”ĐœĐ”Đ”, ĐŒŃ‹ ĐŸŃˆĐžĐ±ĐŸŃ‡ĐœĐŸ ĐœĐ°ŃŃ‚Đ°ĐžĐČĐ°Đ”ĐŒ ĐœĐ° ŃŃ‚ĐŸĐŒ, Ń‡Ń‚ĐŸĐ±Ń‹ ĐżŃ€ĐŸŃ‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČать ĐČĐœŃƒŃ‚Ń€Đ”ĐœĐœĐ”Đ” ŃƒŃŃ‚Ń€ĐŸĐčстĐČĐŸ Đșласса + expect(new ProductService().calculateVATAdd(0).finalPrice).to.equal(0); +}); +``` + +
    + +

    + +## âšȘ  1.5 ВыбОраĐčтД праĐČĐžĐ»ŃŒĐœŃ‹Đ” ĐžĐŒĐžŃ‚Đ°Ń†ĐžĐž: ĐżŃ€Đ”ĐŽĐżĐŸŃ‡ĐžŃ‚Đ°ĐčтД stubs Đž spies ĐČĐŒĐ”ŃŃ‚ĐŸ mocks + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** Đ˜ĐŒĐžŃ‚Đ°Ń†ĐžĐž, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒŃŽŃ‚ŃŃ ĐČ Ń‚Đ”ŃŃ‚Đ°Ń…, ĐŒĐŸĐ¶ĐœĐŸ ĐœĐ°Đ·ĐČать Đ·Đ»ĐŸĐŒ ĐČĐŸ Đ±Đ»Đ°ĐłĐŸ. ĐžĐœĐž сĐČŃĐ·Đ°ĐœŃ‹ с ĐČĐœŃƒŃ‚Ń€Đ”ĐœĐœĐžĐŒĐž ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đ°ĐŒĐž, ĐœĐŸ ĐœĐ”ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” Оз ĐœĐžŃ… ĐżŃ€ĐžĐœĐŸŃŃŃ‚ ĐŸĐłŃ€ĐŸĐŒĐœŃƒŃŽ ĐżĐŸĐ»ŃŒĐ·Ńƒ ([ЧотаĐčтД Đ·ĐŽĐ”ŃŃŒ ĐœĐ°ĐżĐŸĐŒĐžĐœĐ°ĐœĐžĐ” ĐŸ ĐŽĐČĐŸĐčĐœĐžĐșах Ń‚Đ”ŃŃ‚ĐŸĐČ: mocks vs stubs vs spies](https://martinfowler.com/articles/mocksArentStubs.html)). + +ĐŸŃ€Đ”Đ¶ĐŽĐ” Ń‡Đ”ĐŒ ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČать ĐŸĐ±ŃŠĐ”Đșты-ĐžĐŒĐžŃ‚Đ°Ń†ĐžĐž, ĐČы ĐŽĐŸĐ»Đ¶ĐœŃ‹ ŃĐżŃ€ĐŸŃĐžŃ‚ŃŒ ŃĐ”Đ±Ń: ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒĐ”Ń‚ŃŃ лО ĐŸĐœ ĐŽĐ»Ń Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ Ń„ŃƒĐœĐșŃ†ĐžĐŸĐœĐ°Đ»ŃŒĐœĐŸŃŃ‚Đž, ĐșĐŸŃ‚ĐŸŃ€Đ°Ń Ń‚Ń€Đ”Đ±ŃƒĐ”Ń‚ŃŃ ĐČ ĐżŃ€ĐŸŃ†Đ”ŃŃĐ” Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚ĐșĐž?. ЕслО ĐœĐ”Ń‚, Ń‚ĐŸ ŃŃ‚ĐŸ ĐżĐŸŃĐČĐ»Đ”ĐœĐžĐ” ĐżŃ€Đ”ĐŽĐżĐŸŃŃ‹Đ»ĐŸĐș white-box Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ. + +ĐĐ°ĐżŃ€ĐžĐŒĐ”Ń€, ДслО ĐČы Ń…ĐŸŃ‚ĐžŃ‚Đ” ĐżŃ€ĐŸŃ‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČать ĐșĐŸŃ€Ń€Đ”ĐșŃ‚ĐœŃƒŃŽ Ń€Đ°Đ±ĐŸŃ‚Ńƒ ĐČĐ°ŃˆĐ”ĐłĐŸ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ, ĐČ Ń‚ĐŸ ĐČŃ€Đ”ĐŒŃ, ĐșаĐș ĐżĐ»Đ°Ń‚Đ”Đ¶ĐœŃ‹Đč сДрĐČОс ĐœĐ” Ń€Đ°Đ±ĐŸŃ‚Đ°Đ”Ń‚, Ń‚ĐŸ ĐŒĐŸĐ¶Đ”Ń‚Đ” ĐżĐŸŃŃ‚Đ°ĐČоть заглушĐșу `(stub)` ĐœĐ° ĐżĐ»Đ°Ń‚Đ”Đ¶ĐœŃ‹Đč сДрĐČОс Đž ĐČĐ”Ń€ĐœŃƒŃ‚ŃŒ ĐœĐ”ŃĐșĐŸĐ»ŃŒĐșĐŸ 'No Response', Ń‡Ń‚ĐŸĐ±Ń‹ ŃƒĐ±Đ”ĐŽĐžŃ‚ŃŃ ĐČ Ń‚ĐŸĐŒ, Ń‡Ń‚ĐŸ Ń‚Đ”ŃŃ‚ĐžŃ€ŃƒĐ”ĐŒŃ‹Đč ĐŒĐŸĐŽŃƒĐ»ŃŒ ĐČĐŸĐ·ĐČращаДт праĐČĐžĐ»ŃŒĐœĐŸĐ” Đ·ĐœĐ°Ń‡Đ”ĐœĐžĐ”. йаĐșĐŸĐč ĐżĐŸĐŽŃ…ĐŸĐŽ ĐżŃ€ĐŸĐČĐ”Ń€ŃĐ”Ń‚ ĐżĐŸĐČĐ”ĐŽĐ”ĐœĐžĐ”/ĐŸŃ‚ĐČДт/Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚ Ń€Đ°Đ±ĐŸŃ‚Ń‹ ĐœĐ°ŃˆĐ”ĐłĐŸ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ про ĐŸĐżŃ€Đ”ĐŽĐ”Đ»Ń‘ĐœĐœŃ‹Ń… ŃŃ†Đ”ĐœĐ°Ń€ĐžŃŃ…. йаĐșжД ĐŒĐŸĐ¶ĐœĐŸ ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČать `spy` ĐŽĐ»Ń ĐżĐŸĐŽŃ‚ĐČĐ”Ń€Đ¶ĐŽĐ”ĐœĐžŃ Ń‚ĐŸĐłĐŸ, Ń‡Ń‚ĐŸ ĐżĐžŃŃŒĐŒĐŸ Đ±Ń‹Đ»ĐŸ ĐŸŃ‚ĐżŃ€Đ°ĐČĐ»Đ”ĐœĐŸ ĐČ ŃĐžŃ‚ŃƒĐ°Ń†ĐžŃŃ…, ĐșĐŸĐłĐŽĐ° сДрĐČОс упал. йаĐșая ĐżŃ€ĐŸĐČДрĐșа ĐżĐŸĐČĐ”ĐŽĐ”ĐœĐžŃ таĐșжД ĐŒĐŸĐ¶Đ”Ń‚ Đ±Ń‹Ń‚ŃŒ уĐșĐ°Đ·Đ°ĐœĐ° ĐČ Ń‚Đ”Ń…ĐœĐžŃ‡Đ”ŃĐșĐŸĐŒ Đ·Đ°ĐŽĐ°ĐœĐžĐž (“Send an email if payment couldn’t be saved”). C ĐŽŃ€ŃƒĐłĐŸĐč ŃŃ‚ĐŸŃ€ĐŸĐœŃ‹, ĐŒĐŸĐșĐžĐœĐł ĐżĐ»Đ°Ń‚Đ”Đ¶ĐœĐŸĐłĐŸ сДрĐČОса Đž ĐŽĐ°Đ»ŃŒĐœĐ”ĐčшОĐč Đ”ĐłĐŸ ĐČŃ‹Đ·ĐŸĐČ Ń ĐșĐŸŃ€Ń€Đ”ĐșŃ‚ĐœŃ‹ĐŒĐž Ń‚ĐžĐżĐ°ĐŒĐž ĐŽĐ°ĐœĐœŃ‹Ń… ĐłĐŸĐČĐŸŃ€ĐžŃ‚ ĐŸ Ń‚ĐŸĐŒ, Ń‡Ń‚ĐŸ ĐČаш тДст ĐżŃ€Đ”ĐŽĐœĐ°Đ·ĐœĐ°Ń‡Đ”Đœ ĐŽĐ»Ń ĐżŃ€ĐŸĐČДрĐșĐž ĐČĐœŃƒŃ‚Ń€Đ”ĐœĐœĐ”Đč рДалОзацОО, ĐșĐŸŃ‚ĐŸŃ€Đ°Ń ĐœĐ” ĐžĐŒĐ”Đ”Ń‚ ĐŸŃ‚ĐœĐŸŃˆĐ”ĐœĐžŃ Đș Ń„ŃƒĐœĐșŃ†ĐžĐŸĐœĐ°Đ»ŃŒĐœĐŸŃŃ‚Đž ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ Đž ĐŒĐŸĐ¶Đ”Ń‚ Ń‡Đ°ŃŃ‚ĐŸ ĐżĐŸĐŽĐČĐ”Ń€ĐłĐ°Ń‚ŃŒŃŃ ĐžĐ·ĐŒĐ”ĐœĐ”ĐœĐžŃĐŒ. +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** Đ›ŃŽĐ±ĐŸĐč рДфаĐșŃ‚ĐŸŃ€ĐžĐœĐł ĐșĐŸĐŽĐ° Ń‚Ń€Đ”Đ±ŃƒĐ”Ń‚ ĐżĐŸĐžŃĐșа ĐČсДх `mocks` ĐČ ĐșĐŸĐŽĐ” Đž ĐżĐŸŃĐ»Đ”ĐŽŃƒŃŽŃ‰Đ”ĐłĐŸ ĐŸĐ±ĐœĐŸĐČĐ»Đ”ĐœĐžŃ. йДсты ĐœĐ°Ń‡ĐžĐœĐ°ŃŽŃ‚ ĐČŃ€Đ”ĐŽĐžŃ‚ŃŒ, ĐœĐ”Đ¶Đ”Đ»Đž, Ń‡Đ”ĐŒ ĐżŃ€ĐžĐœĐŸŃĐžŃ‚ŃŒ ĐżĐŸĐ»ŃŒĐ·Ńƒ + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :thumbsdown: ĐĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœĐŸ: ĐœĐŸĐșĐžĐœĐł ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒĐ”Ń‚ŃŃ ĐŽĐ»Ń Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ ĐČĐœŃƒŃ‚Ń€Đ”ĐœĐœĐ”Đč рДалОзацОО + +![](https://img.shields.io/badge/🔧%20Example%20using%20Sinon-blue.svg "Examples with Sinon") + +```javascript +it("When a valid product is about to be deleted, ensure data access DAL was called once, with the right product and right config", async () => { + // ĐżŃ€Đ”ĐŽĐżĐŸĐ»ĐŸĐ¶ĐžĐŒ, Ń‡Ń‚ĐŸ ĐŒŃ‹ ужД ĐŽĐŸĐ±Đ°ĐČОлО ĐżŃ€ĐŸĐŽŃƒĐșт + const dataAccessMock = sinon.mock(DAL); + // Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” ĐČĐœŃƒŃ‚Ń€Đ”ĐœĐœĐžŃ… ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ĐŸĐČ ŃĐČĐ»ŃĐ”Ń‚ŃŃ ĐœĐ°ŃˆĐ”Đč глаĐČĐœĐŸĐč Ń†Đ”Đ»ŃŒŃŽ, а ĐœĐ” ĐżŃ€ĐŸŃŃ‚ĐŸ ĐżĐŸĐ±ĐŸŃ‡ĐœŃ‹ĐŒ ŃŃ„Ń„Đ”ĐșŃ‚ĐŸĐŒ + dataAccessMock + .expects("deleteProduct") + .once() + .withArgs(DBConfig, theProductWeJustAdded, true, false); + new ProductService().deletePrice(theProductWeJustAdded); + dataAccessMock.verify(); +}); +``` + +
    + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: spies ŃĐŸŃŃ€Đ”ĐŽĐŸŃ‚ĐŸŃ‡Đ”ĐœŃ‹ ĐœĐ° ĐżŃ€ĐŸĐČДрĐșĐ” Ń‚Ń€Đ”Đ±ĐŸĐČĐ°ĐœĐžĐč, ĐœĐŸ ĐČ ĐșачДстĐČĐ” ĐżĐŸĐ±ĐŸŃ‡ĐœĐŸĐłĐŸ ŃŃ„Ń„Đ”Đșта ĐœĐ”ĐžĐ·Đ±Đ”Đ¶ĐœĐŸ затрагОĐČают ĐČĐœŃƒŃ‚Ń€Đ”ĐœĐœĐžĐ” ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Ń‹ + +```javascript +it("When a valid product is about to be deleted, ensure an email is sent", async () => { + // ĐżŃ€Đ”ĐŽĐżĐŸĐ»ĐŸĐ¶ĐžĐŒ, Ń‡Ń‚ĐŸ ĐŒŃ‹ ужД ĐŽĐŸĐ±Đ°ĐČОлО ĐżŃ€ĐŸĐŽŃƒĐșт + const spy = sinon.spy(Emailer.prototype, "sendEmail"); + new ProductService().deletePrice(theProductWeJustAdded); + // ĐŒŃ‹ ĐžĐŒĐ”Đ”ĐŒ ĐŽĐ”Đ»ĐŸ с ĐČĐœŃƒŃ‚Ń€Đ”ĐœĐœĐžĐŒĐž ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đ°ĐŒĐž, ĐœĐŸ ĐșаĐș ĐżĐŸĐ±ĐŸŃ‡ĐœŃ‹Đč ŃŃ„Ń„Đ”Đșт Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ + expect(spy.calledOnce).to.be.true; +}); +``` + +
    + +

    + +## 📗 Đ„ĐŸŃ‡Đ”ŃˆŃŒ ĐžĐ·ŃƒŃ‡ĐžŃ‚ŃŒ ĐŽĐ°ĐœĐœŃ‹Đ” ĐżĐŸĐŽŃ…ĐŸĐŽŃ‹ ĐœĐ° ĐČĐžĐŽĐ”ĐŸ? + +### ĐŸĐ”Ń€Đ”Ń…ĐŸĐŽĐž ĐżĐŸ ссылĐșĐ” ĐœĐ° ĐŒĐŸĐč ĐŸĐœĐ»Đ°ĐčĐœ-Đșурс [Testing Node.js & JavaScript From A To Z](https://www.testjavascript.com) + +

    + +## âšȘ 1.6 ĐĐ” ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒĐč "foo". Đ˜ŃĐżĐŸĐ»ŃŒĐ·ŃƒĐč Ń€Đ”Đ°Đ»ĐžŃŃ‚ĐžŃ‡ĐœŃ‹Đ” ĐČŃ…ĐŸĐŽĐœŃ‹Đ” ĐŽĐ°ĐœĐœŃ‹Đ” + +:white_check_mark: **Đ”Đ”Đ»Đ°Ń‚ŃŒ:** Đ§Đ°ŃŃ‚ĐŸ ĐżŃ€ĐŸĐžĐ·ĐČĐŸĐŽŃŃ‚ĐČĐ”ĐœĐœŃ‹Đ” ĐŸŃˆĐžĐ±ĐșĐž ĐŸĐ±ĐœĐ°Ń€ŃƒĐ¶ĐžĐČаются про ĐŸŃ‡Đ”ĐœŃŒ спДцОфОчДсĐșох Đž ĐœĐ”ĐŸĐ¶ĐžĐŽĐ°ĐœĐœŃ‹Ń… ĐČŃ…ĐŸĐŽĐœŃ‹Ń… ĐŽĐ°ĐœĐœŃ‹Ń… - Ń‡Đ”ĐŒ Ń€Đ”Đ°Đ»ĐžŃŃ‚ĐžŃ‡ĐœĐ”Đ” Ń‚Đ”ŃŃ‚ĐŸĐČыД ĐŽĐ°ĐœĐœŃ‹Đ”, Ń‚Đ”ĐŒ Đ±ĐŸĐ»ŃŒŃˆĐ” ŃˆĐ°ĐœŃĐŸĐČ ĐŸĐ±ĐœĐ°Ń€ŃƒĐ¶ĐžŃ‚ŃŒ ĐŸŃˆĐžĐ±ĐșĐž ĐœĐ° Ń€Đ°ĐœĐœĐ”Đč стаЮоо. Đ˜ŃĐżĐŸĐ»ŃŒĐ·ŃƒĐčтД ŃĐżĐ”Ń†ĐžĐ°Đ»ŃŒĐœŃ‹Đ” Đ±ĐžĐ±Đ»ĐžĐŸŃ‚Đ”ĐșĐž, таĐșОД ĐșаĐș [Chance](https://github.com/chancejs/chancejs) ОлО [Faker](https://www.npmjs.com/package/faker), ĐŽĐ»Ń ĐłĐ”ĐœĐ”Ń€Đ°Ń†ĐžĐž псДĐČĐŽĐŸŃ€Đ”Đ°Đ»ŃŒĐœŃ‹Ń… ĐŽĐ°ĐœĐœŃ‹Ń…, ĐœĐ°ĐżĐŸĐŒĐžĐœĐ°ŃŽŃ‰ĐžŃ… ĐżĐŸ Ń€Đ°Đ·ĐœĐŸĐŸĐ±Ń€Đ°Đ·ĐžŃŽ Đž Ń„ĐŸŃ€ĐŒĐ” ĐżŃ€ĐŸĐžĐ·ĐČĐŸĐŽŃŃ‚ĐČĐ”ĐœĐœŃ‹Đ” ĐŽĐ°ĐœĐœŃ‹Đ”. ĐĐ°ĐżŃ€ĐžĐŒĐ”Ń€, таĐșОД Đ±ĐžĐ±Đ»ĐžĐŸŃ‚Đ”ĐșĐž ĐŒĐŸĐłŃƒŃ‚ ĐłĐ”ĐœĐ”Ń€ĐžŃ€ĐŸĐČать Ń€Đ”Đ°Đ»ĐžŃŃ‚ĐžŃ‡ĐœŃ‹Đ” Ń‚Đ”Đ»Đ”Ń„ĐŸĐœĐœŃ‹Đ” ĐœĐŸĐŒĐ”Ń€Đ°, ĐžĐŒĐ”ĐœĐ° ĐżĐŸĐ»ŃŒĐ·ĐŸĐČатДлДĐč, ĐșŃ€Đ”ĐŽĐžŃ‚ĐœŃ‹Đ” Đșарты, ĐœĐ°Đ·ĐČĐ°ĐœĐžŃ ĐșĐŸĐŒĐżĐ°ĐœĐžĐč Đž ЎажД тДĐșст "lorem ipsum". Вы таĐșжД ĐŒĐŸĐ¶Đ”Ń‚Đ” ŃĐŸĐ·ĐŽĐ°Ń‚ŃŒ ĐœĐ”ŃĐșĐŸĐ»ŃŒĐșĐŸ Ń‚Đ”ŃŃ‚ĐŸĐČ (ĐżĐŸĐČДрх ĐŒĐŸĐŽŃƒĐ»ŃŒĐœŃ‹Ń… Ń‚Đ”ŃŃ‚ĐŸĐČ, а ĐœĐ” ĐČ ĐșачДстĐČĐ” Đ·Đ°ĐŒĐ”ĐœŃ‹), ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” Ń€Đ°ĐœĐŽĐŸĐŒĐžĐ·ĐžŃ€ŃƒŃŽŃ‚ ĐżĐŸĐŽĐŽĐ”Đ»ŃŒĐœŃ‹Đ” ĐŽĐ°ĐœĐœŃ‹Đ”, Ń‡Ń‚ĐŸĐ±Ń‹ Ń€Đ°ŃŃ‚ŃĐœŃƒŃ‚ŃŒ Ń‚Đ”ŃŃ‚ĐžŃ€ŃƒĐ”ĐŒŃ‹Đč ĐŒĐŸĐŽŃƒĐ»ŃŒ ОлО ЎажД ĐžĐŒĐżĐŸŃ€Ń‚ĐžŃ€ĐŸĐČать Ń€Đ”Đ°Đ»ŃŒĐœŃ‹Đ” ĐŽĐ°ĐœĐœŃ‹Đ” Оз ĐżŃ€ĐŸĐžĐ·ĐČĐŸĐŽŃŃ‚ĐČĐ”ĐœĐœĐŸĐč срДЎы. Đ„ĐŸŃ‚ĐžŃ‚Đ” пДрДĐčто ĐœĐ° ĐœĐŸĐČыĐč ŃƒŃ€ĐŸĐČĐ”ĐœŃŒ? ĐĄĐŒĐŸŃ‚Ń€ĐžŃ‚Đ” ŃĐ»Đ”ĐŽŃƒŃŽŃ‰ĐžĐč ĐżŃƒĐœĐșт (Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” ĐœĐ° ĐŸŃĐœĐŸĐČĐ” сĐČĐŸĐčстĐČ). +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** ВсД тДсты про Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚ĐșĐ” ĐŒĐŸĐłŃƒŃ‚ Đ»ĐŸĐ¶ĐœĐŸ ĐżĐŸĐșĐ°Đ·Đ°Ń‚ŃŒ Đ·Đ”Đ»Đ”ĐœŃ‹Đč цĐČДт про ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°ĐœĐžĐž ĐŽĐ°ĐœĐœŃ‹Ń… топа "Foo". ĐžĐŽĐœĐ°ĐșĐŸ, ĐČсД ĐŒĐŸĐ¶Đ”Ń‚ упасть, ДслО ĐœĐ° ĐČŃ…ĐŸĐŽ ĐżĐŸĐżĐ°ĐŽĐ”Ń‚ ŃŃ‚Ń€ĐŸĐșа ĐČОЎа "@3e2ddsf . ##' 1 fdsfds . fds432 AAAA". + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :thumbsdown: ĐĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœĐŸ: ĐĐ°Đ±ĐŸŃ€ Ń‚Đ”ŃŃ‚ĐŸĐČ, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč ĐżŃ€ĐŸŃ…ĐŸĐŽĐžŃ‚ Оз-за ĐœĐ”Ń€Đ”Đ°Đ»ĐžŃŃ‚ĐžŃ‡ĐœŃ‹Ń… ĐŽĐ°ĐœĐœŃ‹Ń… + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Jest") + +```javascript +const addProduct = (name, price) => { + const productNameRegexNoSpace = /^\S*$/; //no white-space allowed + + if (!productNameRegexNoSpace.test(name)) return false; //this path never reached due to dull input + + // Đ·ĐŽĐ”ŃŃŒ ĐșаĐșая-Ń‚ĐŸ Đ»ĐŸĐłĐžĐșа + return true; +}; + +test("Wrong: When adding new product with valid properties, get successful confirmation", async () => { + // ŃŃ‚Ń€ĐŸĐșа "Foo", ĐșĐŸŃ‚ĐŸŃ€Đ°Ń ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒĐ”Ń‚ŃŃ ĐČĐŸ ĐČсДх тДстах, ĐœĐžĐșĐŸĐłĐŽĐ° ĐœĐ” ĐČызыĐČаДт Đ»ĐŸĐ¶ĐœĐŸĐłĐŸ Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚Đ° + const addProductResult = addProduct("Foo", 5); + expect(addProductResult).toBe(true); + // positive-false: ĐŸĐżĐ”Ń€Đ°Ń†ĐžŃ ĐżŃ€ĐŸŃˆĐ»Đ° ŃƒŃĐżĐ”ŃˆĐœĐŸ, таĐș ĐșаĐș ĐŒŃ‹ ĐœĐ” ĐżŃ€ĐŸĐ±ĐŸĐČалО ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČать ĐŽĐ»ĐžĐœĐœĐŸĐ” ĐœĐ°Đ·ĐČĐ°ĐœĐžĐ” ĐżŃ€ĐŸĐŽŃƒĐșта, ĐČĐșлючающДД ĐżŃ€ĐŸĐ±Đ”Đ»Ń‹ +}); +``` + +
    + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: Đ“Đ”ĐœĐ”Ń€Đ°Ń†ĐžŃ Ń€Đ”Đ°Đ»ĐžŃŃ‚ĐžŃ‡ĐœŃ‹Ń… ĐČŃ…ĐŸĐŽĐœŃ‹Ń… ĐŽĐ°ĐœĐœŃ‹Ń… + +```javascript +it("Better: When adding new valid product, get successful confirmation", async () => { + const addProductResult = addProduct( + faker.commerce.productName(), + faker.random.number() + ); + // ŃĐ»ŃƒŃ‡Đ°ĐčĐœĐŸ ŃĐłĐ”ĐœĐ”Ń€ĐžŃ€ĐŸĐČĐ°ĐœĐœŃ‹Đ” ĐČŃ…ĐŸĐŽĐœŃ‹Đ” ĐŽĐ°ĐœĐœŃ‹Đ”: {'Sleek Cotton Computer', 85481} + expect(addProductResult).to.be.true; + // тДст ĐżŃ€ĐŸĐČĐ°Đ»ĐžĐ»ŃŃ: ĐŽĐ°ĐœĐœŃ‹Đ”, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐŒŃ‹ ĐżĐŸĐŽĐ°Đ»Đž ĐœĐ° ĐČŃ…ĐŸĐŽ, ŃŃ€Đ°Đ±ĐŸŃ‚Đ°Đ»Đž ĐœĐ” таĐș, ĐșаĐș ĐżĐ»Đ°ĐœĐžŃ€ĐŸĐČĐ°Đ»ĐŸŃŃŒ + // ĐŒŃ‹ ĐŸĐ±ĐœĐ°Ń€ŃƒĐ¶ĐžĐ»Đž ĐŸŃˆĐžĐ±Đșу +}); +``` + +
    + +

    + +## âšȘ  1.7 ĐąĐ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” ĐŒĐœĐŸĐ¶Đ”ŃŃ‚ĐČа ĐșĐŸĐŒĐ±ĐžĐœĐ°Ń†ĐžĐč ĐČŃ…ĐŸĐŽĐœŃ‹Ń… ĐŽĐ°ĐœĐœŃ‹Ń… с ĐżĐŸĐŒĐŸŃ‰ŃŒŃŽ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ ĐœĐ° ĐŸŃĐœĐŸĐČĐ” сĐČĐŸĐčстĐČ + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** ĐžĐ±Ń‹Ń‡ĐœĐŸ ĐŽĐ»Ń Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ ĐČŃ‹Đ±ĐžŃ€Đ°Đ”Ń‚ŃŃ ĐœĐ”ŃĐșĐŸĐ»ŃŒĐșĐŸ ĐșĐŸĐŒĐ±ĐžĐœĐ°Ń†ĐžĐč ĐČŃ…ĐŸĐŽĐœŃ‹Ń… ĐŽĐ°ĐœĐœŃ‹Ń…. ДажД ĐșĐŸĐłĐŽĐ° ĐŸĐœĐž ĐœĐ°ĐżĐŸĐŒĐžĐœĐ°ŃŽŃ‚ Ń€Đ”Đ°Đ»ŃŒĐœŃ‹Đ” ĐŽĐ°ĐœĐœŃ‹Đ” (ŃĐŒĐŸŃ‚Ń€Đž ĐżŃƒĐœĐșт [‘1.6’](https://github.com/goldbergyoni/javascript-testing-best-practices#-%EF%B8%8F16-dont-foo-use-realistic-input-data)), ĐŒŃ‹ ĐŸŃ…ĐČатыĐČĐ°Đ”ĐŒ Ń‚ĐŸĐ»ŃŒĐșĐŸ ĐœĐ”ŃĐșĐŸĐ»ŃŒĐșĐŸ ĐœĐ°Đ±ĐŸŃ€ĐŸĐČ ĐČŃ…ĐŸĐŽĐœŃ‹Ń… ĐŽĐ°ĐœĐœŃ‹Ń… (method(‘’, true, 1), method(“string” , false , 0)), ĐžĐŽĐœĐ°ĐșĐŸ, ĐČ ĐżŃ€ĐŸĐŽĐ°ĐșŃˆĐ”ĐœĐ”, API ĐČызыĐČĐ°Đ”ĐŒŃ‹Đč с 5 ĐżĐ°Ń€Đ°ĐŒĐ”Ń‚Ń€Đ°ĐŒĐž, ĐŒĐŸĐ¶Đ”Ń‚ Đ±Ń‹Ń‚ŃŒ ĐČызĐČĐ°Đœ с Ń‚Ń‹ŃŃŃ‡Đ°ĐŒĐž Ń€Đ°Đ·Đ»ĐžŃ‡ĐœŃ‹Ń… ĐżĐ”Ń€Đ”ŃŃ‚Đ°ĐœĐŸĐČĐŸĐș, ĐŸĐŽĐœĐ° Оз ĐșĐŸŃ‚ĐŸŃ€Ń‹Ń… ĐŒĐŸĐ¶Đ”Ń‚ ŃƒŃ€ĐŸĐœĐžŃ‚ŃŒ ĐČĐ”ŃŃŒ ĐżŃ€ĐŸŃ†Đ”ŃŃ ([ŃĐŒ. Fuzz Testing](https://en.wikipedia.org/wiki/Fuzzing)). ĐŸŃ€Đ”ĐŽŃŃ‚Đ°ĐČŃŒŃ‚Đ”, Ń‡Ń‚ĐŸ ĐČы бы ĐŒĐŸĐłĐ»Đž ĐœĐ°ĐżĐžŃĐ°Ń‚ŃŒ тДст, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč аĐČŃ‚ĐŸĐŒĐ°Ń‚ĐžŃ‡Đ”ŃĐșĐž ĐłĐ”ĐœĐ”Ń€ĐžŃ€ŃƒĐ”Ń‚ 1000 ĐżĐ”Ń€Đ”ŃŃ‚Đ°ĐœĐŸĐČĐŸĐș Ń€Đ°Đ·Đ»ĐžŃ‡ĐœŃ‹Ń… ĐČŃ…ĐŸĐŽĐœŃ‹Ń… ĐŽĐ°ĐœĐœŃ‹Ń… Đž ĐŸŃ‚ŃĐ»Đ”Đ¶ĐžĐČаДт тД ĐŽĐ°ĐœĐœŃ‹Đ”, ĐœĐ° ĐșĐŸŃ‚ĐŸŃ€Ń‹Ń… ĐșĐŸĐŽ паЎаДт? ĐąĐ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” ĐœĐ° ĐŸŃĐœĐŸĐČĐ” сĐČĐŸĐčстĐČ ĐżĐŸĐ·ĐČĐŸĐ»ŃĐ”Ń‚ ŃŃ‚ĐŸ ŃĐŽĐ”Đ»Đ°Ń‚ŃŒ: ĐŸŃ‚ĐżŃ€Đ°ĐČĐ»ŃŃ ĐœĐ° ĐČŃ…ĐŸĐŽ ĐŒĐœĐŸĐ¶Đ”ŃŃ‚ĐČĐŸ ĐșĐŸĐŒĐ±ĐžĐœĐ°Ń†ĐžĐž Ń€Đ°Đ·Đ»ĐžŃ‡ĐœŃ‹Ń… ĐŽĐ°ĐœĐœŃ‹Ń…, ĐČы уĐČДлОчОĐČаДтД ĐČĐ”Ń€ĐŸŃŃ‚ĐœĐŸŃŃ‚ŃŒ ŃĐ»ŃƒŃ‡Đ°ĐčĐœĐŸĐłĐŸ ĐŸĐ±ĐœĐ°Ń€ŃƒĐ¶Đ”ĐœĐžŃ ĐŸŃˆĐžĐ±ĐșĐž. ĐĐ°ĐżŃ€ĐžĐŒĐ”Ń€, про ĐœĐ°Đ»ĐžŃ‡ĐžĐž ĐŒĐ”Ń‚ĐŸĐŽĐ° addNewProduct(id, name, isDiscount) ĐżĐŸĐŽĐŽĐ”Ń€Đ¶ĐžĐČающОД Đ±ĐžĐ±Đ»ĐžĐŸŃ‚Đ”ĐșĐž Đ±ŃƒĐŽŃƒŃ‚ ĐČызыĐČать ŃŃ‚ĐŸŃ‚ ĐŒĐ”Ń‚ĐŸĐŽ ŃĐŸ ĐŒĐœĐŸĐłĐžĐŒĐž ĐșĐŸĐŒĐ±ĐžĐœĐ°Ń†ĐžŃĐŒĐž (number, string, boolean), ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€ (1, "iPhone", false), (2, "Galaxy", true). йаĐșĐŸĐč топ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ ĐŒĐŸĐ¶Đ”Ń‚ Đ±Ń‹Ń‚ŃŒ ĐŸŃ€ĐłĐ°ĐœĐžĐ·ĐŸĐČĐ°Đœ, ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒŃ Ń€Đ°Đ·ĐœŃ‹Đ” Ń‚Đ”ŃŃ‚ĐŸĐČыД фрДĐčĐŒĐČĐŸŃ€ĐșĐž (Mocha, Jest, etc) с Ń€Đ°Đ·Đ»ĐžŃ‡ĐœŃ‹ĐŒĐž Đ±ĐžĐ±Đ»ĐžĐŸŃ‚Đ”ĐșĐ°ĐŒĐž [js-verify](https://github.com/jsverify/jsverify) ОлО [testcheck](https://github.com/leebyron/testcheck-js) (ĐžĐŒĐ”Đ”Ń‚ ĐŽĐŸĐșŃƒĐŒĐ”ĐœŃ‚Đ°Ń†ĐžŃŽ Đ»ŃƒŃ‡ŃˆĐ”). ĐžĐ±ĐœĐŸĐČĐ»Đ”ĐœĐžĐ”: Nicolas Dubien прДЎлагаДт ĐœĐžĐ¶Đ” таĐșую Đ±ĐžĐ±Đ»ĐžĐŸŃ‚Đ”Đșу, ĐșаĐș [checkout fast-check](https://github.com/dubzzz/fast-check#readme), ĐșĐŸŃ‚ĐŸŃ€Đ°Ń ĐžĐŒĐ”Đ”Ń‚ ĐŽĐŸĐżĐŸĐ»ĐœĐžŃ‚Đ”Đ»ŃŒĐœŃ‹Đ” ĐČĐŸĐ·ĐŒĐŸĐ¶ĐœĐŸŃŃ‚Đž Đž таĐșжД аĐșтоĐČĐœĐŸ ĐżĐŸĐŽĐŽĐ”Ń€Đ¶ĐžĐČĐ°Đ”Ń‚ŃŃ. +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** ĐĐ”ĐŸŃĐŸĐ·ĐœĐ°ĐœĐœĐŸ ĐČы ĐŸŃ‚ĐżŃ€Đ°ĐČĐ»ŃĐ”Ń‚Đ” ĐœĐ° ĐČŃ…ĐŸĐŽ Ń€Đ°Đ·Đ»ĐžŃ‡ĐœŃ‹Đ” ĐŽĐ°ĐœĐœŃ‹Đ”, с ĐșĐŸŃ‚ĐŸŃ€Ń‹ĐŒĐž ĐșĐŸĐŽ, ĐșаĐș праĐČĐžĐ»ĐŸ, Ń€Đ°Đ±ĐŸŃ‚Đ°Đ”Ń‚. К ŃĐŸĐ¶Đ°Đ»Đ”ĐœĐžŃŽ, ŃŃ‚ĐŸ ŃĐœĐžĐ¶Đ°Đ”Ń‚ ŃŃ„Ń„Đ”ĐșтоĐČĐœĐŸŃŃ‚ŃŒ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ, ĐșаĐș ĐžĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Đ° ĐŽĐ»Ń ĐČыяĐČĐ»Đ”ĐœĐžŃ ĐŸŃˆĐžĐ±ĐŸĐș. + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: ĐąĐ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” ĐŒĐœĐŸĐ¶Đ”ŃŃ‚ĐČа ĐČŃ…ĐŸĐŽĐœŃ‹Ń… ĐŽĐ°ĐœĐœŃ‹Ń… с ĐżĐŸĐŒĐŸŃ‰ŃŒŃŽ Đ±ĐžĐ±Đ»ĐžĐŸŃ‚Đ”ĐșĐž “fast-check” + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Jest") + +```javascript +import fc from "fast-check"; + +describe("Product service", () => { + describe("Adding new", () => { + // Đ·Đ°ĐżŃƒŃŃ‚ĐžŃ‚ŃŒŃŃ 100 раз с Ń€Đ°Đ·ĐœŃ‹ĐŒĐž сĐČĐŸĐčстĐČĐ°ĐŒĐž + it("Add new product with random yet valid properties, always successful", () => + fc.assert( + fc.property(fc.integer(), fc.string(), (id, name) => { + expect(addNewProduct(id, name).status).toEqual("approved"); + }) + )); + }); +}); +``` + +
    + +

    + +## âšȘ  1.8 Про ĐœĐ”ĐŸĐ±Ń…ĐŸĐŽĐžĐŒĐŸŃŃ‚Đž ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒĐčтД Ń‚ĐŸĐ»ŃŒĐșĐŸ ĐșĐŸŃ€ĐŸŃ‚ĐșОД Đž ĐżĐŸŃĐ»Đ”ĐŽĐŸĐČĐ°Ń‚Đ”Đ»ŃŒĐœŃ‹Đ” snapshots + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** ЕслО ŃŃƒŃ‰Đ”ŃŃ‚ĐČŃƒĐ”Ń‚ ĐœĐ”ĐŸĐ±Ń…ĐŸĐŽĐžĐŒĐŸŃŃ‚ŃŒ ĐČ [snapshot testing](https://jestjs.io/docs/en/snapshot-testing), ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒĐčтД ĐșĐŸŃ€ĐŸŃ‚ĐșОД ŃĐœĐžĐŒĐșĐž (i.e. 3-7 lines), ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” яĐČĐ»ŃŃŽŃ‚ŃŃ частью тДста ([Inline Snapshot](https://jestjs.io/docs/en/snapshot-testing#inline-snapshots)), а ĐœĐ” ĐČĐŸ ĐČĐœĐ”ŃˆĐœĐžŃ… фаĐčлах. ĐĄĐŸĐ±Đ»ŃŽĐŽĐ”ĐœĐžĐ” ŃŃ‚ĐŸĐłĐŸ праĐČОла ĐżĐŸĐ·ĐČĐŸĐ»ĐžŃ‚ ĐČĐ°ŃˆĐžĐŒ Ń‚Đ”ŃŃ‚Đ°ĐŒ ĐŸŃŃ‚Đ°ĐČаться ĐżĐŸĐœŃŃ‚ĐœŃ‹ĐŒĐž Đž ĐœĐ°ĐŽĐ”Đ¶ĐœŃ‹ĐŒĐž. + +ĐĄ ĐŽŃ€ŃƒĐłĐŸĐč ŃŃ‚ĐŸŃ€ĐŸĐœŃ‹, руĐșĐŸĐČĐŸĐŽŃŃ‚ĐČа ĐżĐŸ "ĐșлассОчДсĐșĐžĐŒ ŃĐœĐžĐŒĐșĐ°ĐŒ" прОзыĐČают Ń…Ń€Đ°ĐœĐžŃ‚ŃŒ Đ±ĐŸĐ»ŃŒŃˆĐžĐ” фаĐčлы (ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, Ń€Đ°Đ·ĐŒĐ”Ń‚Đșу Ń€Đ”ĐœĐŽĐ”Ń€ĐžĐœĐłĐ° ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đ°, Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚ API JSON) ĐœĐ° ĐșаĐșĐŸĐŒ-Ń‚ĐŸ ĐČĐœĐ”ŃˆĐœĐ”ĐŒ ĐœĐŸŃĐžŃ‚Đ”Đ»Đ” Đž ĐșажЎыĐč раз про ĐČŃ‹ĐżĐŸĐ»ĐœĐ”ĐœĐžĐž тДста сраĐČĐœĐžĐČать ĐżĐŸĐ»ŃƒŃ‡Đ”ĐœĐœŃ‹Đč Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚ с ŃĐŸŃ…Ń€Đ°ĐœĐ”ĐœĐœĐŸĐč ĐČДрсОДĐč. Đ­Ń‚ĐŸ ĐŒĐŸĐ¶Đ”Ń‚ проĐČДстО Đș Ń‚ĐŸĐŒŃƒ, Ń‡Ń‚ĐŸ, тДст Đ±ŃƒĐŽĐ”Ń‚ проĐČŃĐ·Đ°Đœ Đș ĐŸĐłŃ€ĐŸĐŒĐœĐŸĐŒŃƒ ĐșĐŸĐ»ĐžŃ‡Đ”ŃŃ‚ĐČу ĐŽĐ°ĐœĐœŃ‹Ń… (ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, 1000 ŃŃ‚Ń€ĐŸĐș), ĐŸ ĐșĐŸŃ‚ĐŸŃ€Ń‹Ń… Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐș ĐœĐžĐșĐŸĐłĐŽĐ° ĐœĐ” Đ·Đ°ĐŽŃƒĐŒŃ‹ĐČĐ°Đ»ŃŃ. ĐŸĐŸŃ‡Đ”ĐŒŃƒ таĐș ĐœĐ”Đ»ŃŒĐ·Ń ĐŽĐ”Đ»Đ°Ń‚ŃŒ? аĐșĐžĐŒ ĐŸĐ±Ń€Đ°Đ·ĐŸĐŒ, ŃŃƒŃ‰Đ”ŃŃ‚ĐČŃƒĐ”Ń‚ 1000 ĐżŃ€ĐžŃ‡ĐžĐœ, ĐżĐŸ ĐșĐŸŃ‚ĐŸŃ€Ń‹ĐŒ ĐČаш тДст ĐŒĐŸĐ¶Đ”Ń‚ ĐœĐ” ĐżŃ€ĐŸĐčто - ĐŽĐŸŃŃ‚Đ°Ń‚ĐŸŃ‡ĐœĐŸ ĐžĐ·ĐŒĐ”ĐœĐ”ĐœĐžŃ ĐŸĐŽĐœĐŸĐč ŃŃ‚Ń€ĐŸĐșĐž, Ń‡Ń‚ĐŸĐ±Ń‹ ŃĐœĐžĐŒĐŸĐș стал ĐœĐ”ĐŽĐ”ĐčстĐČĐžŃ‚Đ”Đ»ŃŒĐœŃ‹ĐŒ, а ŃŃ‚ĐŸ, сĐșĐŸŃ€Đ”Đ” ĐČŃĐ”ĐłĐŸ, Đ±ŃƒĐŽĐ”Ń‚ ĐżŃ€ĐŸĐžŃŃ…ĐŸĐŽĐžŃ‚ŃŒ Ń‡Đ°ŃŃ‚ĐŸ. КаĐș Ń‡Đ°ŃŃ‚ĐŸ? КажЎыĐč ĐżŃ€ĐŸĐ±Đ”Đ», ĐșĐŸĐŒĐŒĐ”ĐœŃ‚Đ°Ń€ĐžĐč ОлО ĐœĐ”Đ·ĐœĐ°Ń‡ĐžŃ‚Đ”Đ»ŃŒĐœĐŸĐ” ĐžĐ·ĐŒĐ”ĐœĐ”ĐœĐžĐ” HTML/CSS. ĐœĐ°Đ»ĐŸ Ń‚ĐŸĐłĐŸ, Ń‡Ń‚ĐŸ ĐœĐ°Đ·ĐČĐ°ĐœĐžĐ” тДста ĐœĐ” ЎаДт прДЎстаĐČĐ»Đ”ĐœĐžŃ ĐŸ Ń‚ĐŸĐŒ, Ń‡Ń‚ĐŸ ĐżĐŸŃˆĐ»ĐŸ ĐœĐ” таĐș, ĐżĐŸŃĐșĐŸĐ»ŃŒĐșу ĐŸĐœ ĐżŃ€ĐŸŃŃ‚ĐŸ ĐżŃ€ĐŸĐČĐ”Ń€ŃĐ”Ń‚, Ń‡Ń‚ĐŸ 1000 ŃŃ€ĐŸĐș ĐșĐŸĐŽĐ° ĐŸŃŃ‚Đ°Đ»ĐžŃŃŒ ĐœĐ”ĐžĐ·ĐŒĐ”ĐœĐœŃ‹, таĐș ДщД Đž ĐČĐČĐŸĐŽĐžŃ‚ Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐșа ĐČ Đ·Đ°Đ±Đ»ŃƒĐ¶ĐŽĐ”ĐœĐžĐ”, застаĐČĐ»ŃŃ ĐżŃ€ĐžĐœĐžĐŒĐ°Ń‚ŃŒ за Đ¶Đ”Đ»Đ°Đ”ĐŒŃƒŃŽ ĐžŃŃ‚ĐžĐœŃƒ ĐŽĐŸĐșŃƒĐŒĐ”ĐœŃ‚, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč ĐŸĐœ ĐœĐ” ŃĐŒĐŸĐ¶Đ”Ń‚ ĐżŃ€ĐŸĐČĐ”Ń€ĐžŃ‚ŃŒ. ВсД ŃŃ‚ĐŸ яĐČĐ»ŃĐ”Ń‚ŃŃ ĐżŃ€ĐžĐ·ĐœĐ°ĐșĐ°ĐŒĐž тДста, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč ŃŃ‚Ń€Đ”ĐŒĐžŃ‚ŃŒŃŃ захĐČатоть ŃŃ€Đ°Đ·Ńƒ ĐŒĐœĐŸĐłĐŸĐ”. + +ĐĄŃ‚ĐŸĐžŃ‚ ĐŸŃ‚ĐŒĐ”Ń‚ĐžŃ‚ŃŒ, Ń‡Ń‚ĐŸ ĐžĐœĐŸĐłĐŽĐ°, таĐșОД Đ±ĐŸĐ»ŃŒŃˆĐžĐ” ŃĐœĐžĐŒĐșĐž ĐżŃ€ĐžĐ”ĐŒĐ»Đ”ĐŒŃ‹ - ĐșĐŸĐłĐŽĐ° ĐżŃ€ĐŸĐČĐ”Ń€ŃĐ”Ń‚ŃŃ ŃŃ…Đ”ĐŒĐ°, а ĐœĐ” ĐŽĐ°ĐœĐœŃ‹Đ” (ОзĐČĐ»Đ”Ń‡Đ”ĐœĐžĐ” Đ·ĐœĐ°Ń‡Đ”ĐœĐžĐč Đž Ń„ĐŸĐșус ĐœĐ° ĐŸĐżŃ€Đ”ĐŽĐ”Đ»Đ”ĐœĐœĐŸĐŒ ĐżĐŸĐ»Đ”) ОлО про рДЎĐșĐŸĐŒ ĐžĐ·ĐŒĐ”ĐœĐ”ĐœĐžĐž ĐŽĐŸĐșŃƒĐŒĐ”ĐœŃ‚Đ°. +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** UI тДст ĐżŃ€ĐŸĐČĐ°Đ»ĐžĐ»ŃŃ. ĐšĐŸĐŽ ĐșĐ°Đ¶Đ”Ń‚ŃŃ праĐČĐžĐ»ŃŒĐœŃ‹ĐŒ, а ĐœĐ° эĐșŃ€Đ°ĐœĐ” ĐŸŃ‚ĐŸĐ±Ń€Đ°Đ¶Đ°ŃŽŃ‚ŃŃ ĐžĐŽĐ”Đ°Đ»ŃŒĐœŃ‹Đ” пОĐșсДлО. йаĐș Ń‡Ń‚ĐŸ жД ŃĐ»ŃƒŃ‡ĐžĐ»ĐŸŃŃŒ? Đ’Đ°ŃˆĐ” snapshot-Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” ĐżŃ€ĐŸŃŃ‚ĐŸ ĐŸĐ±ĐœĐ°Ń€ŃƒĐ¶ĐžĐ»ĐŸ Ń€Đ°Đ·ĐœĐžŃ†Ńƒ ĐŒĐ”Đ¶ĐŽŃƒ ĐžŃŃ…ĐŸĐŽĐœŃ‹ĐŒ ĐŽĐŸĐșŃƒĐŒĐ”ĐœŃ‚ĐŸĐŒ Đž тДĐșŃƒŃ‰ĐžĐŒ ĐżĐŸĐ»ŃƒŃ‡Đ”ĐœĐœŃ‹ĐŒ - ĐČ Ń€Đ°Đ·ĐŒĐ”Ń‚Đșу был ĐŽĐŸĐ±Đ°ĐČĐ»Đ”Đœ 1 ĐżŃ€ĐŸĐ±Đ”Đ»... + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :thumbsdown: ĐĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœĐŸ: ĐąĐ”ŃŃ‚ĐžŃ€ŃƒĐ”ĐŒ ĐœĐ”ĐČĐžĐŽĐžĐŒŃ‹Đ” 2000 ŃŃ‚Ń€ĐŸĐș ĐșĐŸĐŽĐ° + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Jest") + +```javascript +it("TestJavaScript.com is renderd correctly", () => { + // Arrange + + // Act + const receivedPage = renderer + .create( + + {" "} + Test JavaScript{" "} + + ) + .toJSON(); + + // Assert + expect(receivedPage).toMatchSnapshot(); + // Ń‚Đ”ĐżĐ”Ń€ŃŒ ĐŒŃ‹ ĐœĐ”ŃĐČĐœĐŸ ĐżĐŸĐŽĐŽĐ”Ń€Đ¶ĐžĐČĐ°Đ”ĐŒ ĐŽĐŸĐșŃƒĐŒĐ”ĐœŃ‚ ĐŽĐ»ĐžĐœĐŸĐč ĐČ 2000 ŃŃ‚Ń€ĐŸĐș + // ĐșажЎыĐč ĐŽĐŸĐżĐŸĐ»ĐœĐžŃ‚Đ”Đ»ŃŒĐœŃ‹Đč ĐżĐ”Ń€Đ”ĐœĐŸŃ ŃŃ‚Ń€ĐŸĐșĐž ОлО ĐșĐŸĐŒĐŒĐ”ĐœŃ‚Đ°Ń€ĐžĐč проĐČДЎДт Đș ĐżĐ°ĐŽĐ”ĐœĐžŃŽ ŃŃ‚ĐŸĐłĐŸ тДста +}); +``` + +
    + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: Đ—ĐŽĐ”ŃŃŒ ĐČсД ĐœĐ° ĐČОЎу + +```javascript +it("When visiting TestJavaScript.com home page, a menu is displayed", () => { + // Arrange + + // Act + const receivedPage = renderer + .create( + + {" "} + Test JavaScript{" "} + + ) + .toJSON(); + + // Assert + + const menu = receivedPage.content.menu; + expect(menu).toMatchInlineSnapshot(` + +`); +}); +``` + +
    + +

    + +## âšȘ ïžĐšĐŸĐżĐžŃ€ŃƒĐčтД Ń‚ĐŸĐ»ŃŒĐșĐŸ ĐœĐ”ĐŸĐ±Ń…ĐŸĐŽĐžĐŒŃ‹Đč ĐșĐŸĐŽ + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** ВĐșлючаĐčтД ĐČ Ń‚Đ”ŃŃ‚ Ń‚ĐŸĐ»ŃŒĐșĐŸ ĐœĐ”ĐŸĐ±Ń…ĐŸĐŽĐžĐŒĐŸĐ”, Ń‡Ń‚ĐŸ ĐČĐ»ĐžŃĐ”Ń‚ ĐœĐ° Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚ тДста, ĐœĐŸ ĐœĐ” Đ±ĐŸĐ»Đ”Đ” Ń‚ĐŸĐłĐŸ. В ĐșачДстĐČĐ” ĐżŃ€ĐžĐŒĐ”Ń€Đ°, Ń€Đ°ŃŃĐŒĐŸŃ‚Ń€ĐžĐŒ тДст, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč ĐŽĐŸĐ»Đ¶Đ”Đœ учотыĐČать 100 ŃŃ‚Ń€ĐŸĐș JSON. бащоть за ŃĐŸĐ±ĐŸĐč ŃŃ‚ĐŸĐ»ŃŒĐșĐŸ ŃŃ‚Ń€ĐŸĐș ĐČ ĐșажЎыĐč тДст - ŃƒŃ‚ĐŸĐŒĐžŃ‚Đ”Đ»ŃŒĐœĐŸ. ЕслО ОзĐČлДĐșать ŃŃ‚Ń€ĐŸĐșĐž за прДЎДлы transferFactory.getJSON(), Ń‚ĐŸ тДст ŃŃ‚Đ°ĐœĐ”Ń‚ ĐœĐ”ŃŃĐœŃ‹ĐŒ, таĐș ĐșаĐș бДз ĐŽĐ°ĐœĐœŃ‹Ń… Ń‚Ń€ŃƒĐŽĐœĐŸ ŃĐŸĐŸŃ‚ĐœĐ”ŃŃ‚Đž Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚ тДста Đž ĐżŃ€ĐžŃ‡ĐžĐœŃƒ (ĐżĐŸŃ‡Đ”ĐŒŃƒ ĐŸĐœ ĐŽĐŸĐ»Đ¶Đ”Đœ ĐČĐ”Ń€ĐœŃƒŃ‚ŃŒ статус 400?). В ĐșĐœĐžĐłĐ” x-unit patterns, таĐșĐŸĐč ĐżĐ°Ń‚Ń‚Đ”Ń€Đœ ĐœĐ°Đ·Ń‹ĐČĐ°Đ”Ń‚ŃŃ "Ń‚Đ°ĐžĐœŃŃ‚ĐČĐ”ĐœĐœŃ‹Đč ĐłĐŸŃŃ‚ŃŒ" - Ń‡Ń‚ĐŸ-Ń‚ĐŸ сĐșŃ€Ń‹Ń‚ĐŸĐ” ĐżĐŸĐČĐ»ĐžŃĐ»ĐŸ ĐœĐ° Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚ ĐœĐ°ŃˆĐžŃ… Ń‚Đ”ŃŃ‚ĐŸĐČ, ĐœĐŸ ĐŒŃ‹ ĐœĐ” Đ·ĐœĐ°Đ”ĐŒ, Ń‡Ń‚ĐŸ ĐžĐŒĐ”ĐœĐœĐŸ. +Đ”Đ»Ń ŃƒĐ»ŃƒŃ‡ŃˆĐ”ĐœĐžŃ сотуацоо, ĐŒŃ‹ ĐŒĐŸĐ¶Đ”ĐŒ ОзĐČĐ»Đ”Ń‡ŃŒ ĐżĐŸĐČŃ‚ĐŸŃ€ŃŃŽŃ‰ĐžĐ”ŃŃ ЎДталО, ĐŸŃŃ‚Đ°ĐČĐ»ŃŃ Ń‚ĐŸĐ»ŃŒĐșĐŸ Ń‚ĐŸ, Ń‡Ń‚ĐŸ ĐžĐŒĐ”Đ”Ń‚ Đ·ĐœĐ°Ń‡Đ”ĐœĐžĐ” ĐŽĐ»Ń тДста. КаĐș, ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, ĐČ ĐŽĐ°ĐœĐœĐŸĐč сотуацоо: transferFactory.getJSON({sender: undefined}). Đ—ĐŽĐ”ŃŃŒ ĐČĐžĐŽĐœĐŸ, Ń‡Ń‚ĐŸ ĐżŃƒŃŃ‚ĐŸĐ” ĐżĐŸĐ»Đ” ĐŸŃ‚ĐżŃ€Đ°ĐČĐžŃ‚Đ”Đ»Ń яĐČĐ»ŃĐ”Ń‚ŃŃ Ń‚ĐŸĐč ŃĐ°ĐŒĐŸĐč ĐżŃ€ĐžŃ‡ĐžĐœĐŸĐč, ĐșĐŸŃ‚ĐŸŃ€Đ°Ń проĐČДЎДт Đș ĐŸŃˆĐžĐ±ĐșĐ” ĐČалОЎацОО. +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** ĐšĐŸĐżĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” ĐŸĐłŃ€ĐŸĐŒĐœĐŸĐłĐŸ ĐșĐŸĐ»ĐžŃ‡Đ”ŃŃ‚ĐČа ŃŃ‚Ń€ĐŸĐș JSON проĐČДЎДт Đș Ń‚ĐŸĐŒŃƒ, Ń‡Ń‚ĐŸ ĐČашО тДсты ŃŃ‚Đ°ĐœŃƒŃ‚ ĐœĐ”Ń‡ĐžŃ‚Đ°Đ”ĐŒŃ‹ĐŒĐž Đž Ń‚Ń€ŃƒĐŽĐœĐŸ ĐżĐŸĐŽĐŽĐ”Ń€Đ¶ĐžĐČĐ°Đ”ĐŒŃ‹ĐŒĐž. + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :thumbsdown: ĐĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœĐŸ: ĐąŃĐ¶Đ”Đ»ĐŸ ĐżĐŸĐœŃŃ‚ŃŒ ĐżŃ€ĐžŃ‡ĐžĐœŃƒ ĐŸŃˆĐžĐ±ĐșĐž, таĐș ĐșаĐș ĐŸĐœĐ° сĐșрыта ĐŸŃ‚ глаз ĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»Ń ĐČ Đ±ĐŸĐ»ŃŒŃˆĐŸĐŒ ĐșĐŸĐ»ĐžŃ‡Đ”ŃŃ‚ĐČĐ” ŃŃ‚Ń€ĐŸĐș JSON + +![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "Examples with Mocha") + +```javascript +test("When no credit, then the transfer is declined", async () => { + // Arrange + const transferRequest = testHelpers.factorMoneyTransfer(); // ĐČĐ”Ń€ĐœĐ”ĐŒŃŃ Đș 200 ŃŃ‚Ń€ĐŸĐșĐ°ĐŒ JSON; + const transferServiceUnderTest = new TransferService(); + + // Act + const transferResponse = await transferServiceUnderTest.transfer( + transferRequest + ); + + // Assert + expect(transferResponse.status).toBe(409); + // ĐŸĐŸŃ‡Đ”ĐŒŃƒ ĐŒŃ‹ Đ¶ĐŽĐ”ĐŒ, Ń‡Ń‚ĐŸ тДст ŃƒĐżĐ°ĐŽĐ”Ń‚, ĐČĐ”ĐŽŃŒ ĐČсД ĐČŃ‹ĐłĐ»ŃĐŽĐžŃ‚ ĐșĐŸŃ€Ń€Đ”ĐșŃ‚ĐœŃ‹ĐŒ đŸ€”? +}); +``` + +
    + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: В ĐŽĐ°ĐœĐœĐŸĐŒ ŃĐ»ŃƒŃ‡Đ°Đ” тДст ĐČыяĐČĐ»ŃĐ”Ń‚ Ń‚ĐŸ, Ń‡Ń‚ĐŸ яĐČĐ»ŃĐ”Ń‚ŃŃ ĐżŃ€ĐžŃ‡ĐžĐœĐŸĐč ĐșĐŸĐœĐ”Ń‡ĐœĐŸĐłĐŸ Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚Đ° + +```javascript +test("When no credit, then the transfer is declined ", async () => { + // Arrange + const transferRequest = testHelpers.factorMoneyTransfer({ + userCredit: 100, + transferAmount: 200, + }); // ĐŸŃ‡Đ”ĐČĐžĐŽĐœĐŸ, Ń‡Ń‚ĐŸ Đ·ĐŽĐ”ŃŃŒ ĐœĐ”ĐŽĐŸŃŃ‚Đ°Ń‚ĐŸĐș срДЎстĐČ + const transferServiceUnderTest = new TransferService({ + disallowOvercharge: true, + }); + + // Act + const transferResponse = await transferServiceUnderTest.transfer( + transferRequest + ); + + // Assert + expect(transferResponse.status).toBe(409); // ОчДĐČĐžĐŽĐœĐŸ, Ń‡Ń‚ĐŸ ДслО у ĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»Ń ĐœĐ” хĐČатаДт срДЎстĐČ, Ń‚ĐŸ ĐČсД ŃƒĐżĐ°ĐŽĐ”Ń‚ +}); +``` + +
    + +

    + +## âšȘ  1.10 ĐĐ” ĐŸŃ‚Đ»Đ°ĐČлОĐČаĐčтД ĐŸŃˆĐžĐ±ĐșĐž - ĐŸĐ¶ĐžĐŽĐ°ĐčтД ох + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** Про ĐżĐŸĐżŃ‹Ń‚ĐșĐ” ĐŸŃ‚Đ»ĐŸĐČоть, Ń‡Ń‚ĐŸ ĐœĐ”ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč ĐČĐČĐŸĐŽ ĐŽĐ°ĐœĐœŃ‹Ń… проĐČĐŸĐŽĐžŃ‚ Đș ĐŸŃˆĐžĐ±ĐșĐ”, ĐŒĐŸĐ¶Đ”Ń‚ ĐżĐŸĐșĐ°Đ·Đ°Ń‚ŃŒŃŃ праĐČĐžĐ»ŃŒĐœŃ‹ĐŒ ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°ĐœĐžĐ” ĐșĐŸĐœŃŃ‚Ń€ŃƒĐșцоо try-catch-finally Đž утĐČĐ”Ń€Đ¶ĐŽĐ°Ń‚ŃŒ, Ń‡Ń‚ĐŸ Đ±Ń‹Đ»ĐŸ ĐČĐČĐ”ĐŽĐ”ĐœĐŸ catch. В Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚Đ”, ĐżĐŸĐ»ŃƒŃ‡Đ°Đ”Ń‚ŃŃ ĐœĐ”ŃƒĐŽĐŸĐ±ĐœŃ‹Đč Đž ĐŸĐ±ŃŠĐ”ĐŒĐœŃ‹Đč тДст (ĐżŃ€ĐžĐŒĐ”Ń€ ĐœĐžĐ¶Đ”), ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč сĐșрыĐČаДт ŃĐ°ĐŒŃƒ ОЎДю тДста Đž ĐżĐŸĐ»ŃƒŃ‡Đ”ĐœĐžŃ Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚Đ°. + +Đ‘ĐŸĐ»Đ”Đ” ŃĐ»Đ”ĐłĐ°ĐœŃ‚ĐœĐŸĐč Đ°Đ»ŃŒŃ‚Đ”Ń€ĐœĐ°Ń‚ĐžĐČĐŸĐč яĐČĐ»ŃĐ”Ń‚ŃŃ ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°ĐœĐžĐ” ĐŸĐŽĐœĐŸŃŃ‚Ń€ĐŸŃ‡ĐœĐŸĐłĐŸ ĐŒĐ°Ń‚Ń‡Đ”Ń€Đ° Chai: expect(method).to.throw (ОлО ĐČ Jest: expect(method).toThrow()). ĐžĐ±ŃĐ·Đ°Ń‚Đ”Đ»ŃŒĐœĐŸ ŃƒĐ±Đ”ĐŽĐžŃ‚Đ”ŃŃŒ, Ń‡Ń‚ĐŸ ОсĐșĐ»ŃŽŃ‡Đ”ĐœĐžĐ” ŃĐŸĐŽĐ”Ń€Đ¶ĐžŃ‚ сĐČĐŸĐčстĐČĐŸ, ĐșĐŸŃ‚ĐŸŃ€ĐŸĐ” уĐșазыĐČаДт ĐœĐ° топ ĐŸŃˆĐžĐ±ĐșĐž, ĐžĐœĐ°Ń‡Đ”, ĐżŃ€ĐŸŃŃ‚ĐŸ ĐżĐŸĐ»ŃƒŃ‡ĐžĐČ ĐŸĐ±Ń‰ŃƒŃŽ ĐŸŃˆĐžĐ±Đșу, ĐżĐŸĐ»ŃŒĐ·ĐŸĐČатДлю Đ±ŃƒĐŽĐ”Ń‚ ĐżĐŸĐșĐ°Đ·Đ°ĐœĐŸ ŃĐŸĐŸĐ±Ń‰Đ”ĐœĐžĐ”, ĐșĐŸŃ‚ĐŸŃ€ĐŸĐ” Đ”ĐłĐŸ Ń€Đ°Đ·ĐŸŃ‡Đ°Ń€ŃƒĐ”Ń‚. +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** Из ĐŸŃ‚Ń‡Đ”Ń‚ĐŸĐČ ĐŸ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐž (ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, ĐŸŃ‚Ń‡Đ”Ń‚ĐŸĐČ CI) Đ±ŃƒĐŽĐ”Ń‚ ŃĐ»ĐŸĐ¶ĐœĐŸ ŃĐŽĐ”Đ»Đ°Ń‚ŃŒ ĐČыĐČĐŸĐŽ ĐŸ Ń‚ĐŸĐŒ, Ń‡Ń‚ĐŸ ĐżĐŸŃˆĐ»ĐŸ ĐœĐ” таĐș + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :thumbsdown: ĐĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœĐŸ: ĐžĐ±ŃŠĐ”ĐŒĐœŃ‹Đč тДст, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč ĐżŃ‹Ń‚Đ°Đ”Ń‚ŃŃ ĐżŃ€ĐŸĐČĐ”Ń€ĐžŃ‚ŃŒ ĐŸŃˆĐžĐ±Đșу чДрДз try-catch + +![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "Examples with Mocha") + +```javascript +it("When no product name, it throws error 400", async () => { + let errorWeExceptFor = null; + try { + const result = await addNewProduct({}); + } catch (error) { + expect(error.code).to.equal("InvalidInput"); + errorWeExceptFor = error; + } + expect(errorWeExceptFor).not.to.be.null; + // ДслО ŃŃ‚ĐŸ утĐČĐ”Ń€Đ¶ĐŽĐ”ĐœĐžĐ” ĐœĐ” ŃŃ€Đ°Đ±ĐŸŃ‚Đ°Đ”Ń‚, Ń‚ĐŸ Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚Ń‹ Ń‚Đ”ŃŃ‚ĐŸĐČ ĐżĐŸĐșĐ°Đ¶ŃƒŃ‚, + // Ń‡Ń‚ĐŸ ĐșаĐșĐŸĐ”-Ń‚ĐŸ Đ·ĐœĐ°Ń‡Đ”ĐœĐžĐ” раĐČĐœĐŸ null, а ĐŸĐ± ĐŸŃ‚ŃŃƒŃ‚ŃŃ‚ĐČŃƒŃŽŃ‰Đ”ĐŒ ОсĐșĐ»ŃŽŃ‡Đ”ĐœĐžĐž ĐœĐ” Đ±ŃƒĐŽĐ”Ń‚ ĐœĐž ŃĐ»ĐŸĐČа +}); +``` + +
    + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: йДст, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč ĐŒĐŸĐ¶Đ”Ń‚ лДгĐșĐŸ ĐżĐŸĐœŃŃ‚ŃŒ ЎажД QA ОлО product-ĐŒĐ”ĐœĐ”ĐŽĐ¶Đ”Ń€ + +```javascript +it("When no product name, it throws error 400", async () => { + await expect(addNewProduct({})) + .to.eventually.throw(AppError) + .with.property("code", "InvalidInput"); +}); +``` + +
    + +

    + +## âšȘ  1.11 МарĐșоруĐč сĐČĐŸĐž тДсты + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** Đ Đ°Đ·ĐœŃ‹Đ” тДсты ĐŽĐŸĐ»Đ¶ĐœŃ‹ запусĐșаться ĐżĐŸ-Ń€Đ°Đ·ĐœĐŸĐŒŃƒ: quick smoke, IO-less, тДсты ĐŽĐŸĐ»Đ¶ĐœŃ‹ запусĐșаться, ĐșĐŸĐłĐŽĐ° Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐș ŃĐŸŃ…Ń€Đ°ĐœŃĐ”Ń‚ ĐșĐŸĐŽ ОлО ЎДлаДт ĐșĐŸĐŒĐŒĐžŃ‚. ĐĄĐșĐČĐŸĐ·ĐœŃ‹Đ” тДсты запусĐșаются про ĐżĐŸĐżŃ‹Ń‚ĐșĐ” ĐœĐŸĐČĐŸĐłĐŸ pull-request Đž ĐŒĐœĐŸĐłĐŸĐ” ĐŽŃ€ŃƒĐłĐŸĐ”. Đ­Ń‚ĐŸĐłĐŸ ĐŒĐŸĐ¶ĐœĐŸ ĐŽĐŸŃŃ‚ĐžŃ‡ŃŒ, ДслО ĐżĐŸĐŒĐ”Ń‡Đ°Ń‚ŃŒ тДсты ĐșлючДĐČŃ‹ĐŒĐž ŃĐ»ĐŸĐČĐ°ĐŒĐž #cold, #api, #sanity, Ń‡Ń‚ĐŸĐ±Ń‹ ĐČы ĐŒĐŸĐłĐ»Đž ОсĐșать ох с ĐżĐŸĐŒĐŸŃ‰ŃŒŃŽ ĐČашДĐč ŃĐžŃŃ‚Đ”ĐŒŃ‹ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ Đž ĐČызыĐČать ŃŃ€Đ°Đ·Ńƒ ĐœŃƒĐ¶ĐœĐŸĐ” ĐșĐŸĐ»ĐžŃ‡Đ”ŃŃ‚ĐČĐŸ ĐŸĐżŃ€Đ”ĐŽĐ”Đ»Đ”ĐœĐœŃ‹Ń… Ń‚Đ”ŃŃ‚ĐŸĐČ. ĐĐ°ĐżŃ€ĐžĐŒĐ”Ń€, ĐČĐŸŃ‚ ĐșаĐș ĐŒĐŸĐ¶ĐœĐŸ ĐČызĐČать группу Ń‚Đ”ŃŃ‚ĐŸĐČ Ń ĐżĐŸĐŒĐŸŃ‰ŃŒŃŽ Mocha: mocha — grep ‘sanity’. +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** ЗапусĐș ĐČсДх ŃŃƒŃ‰Đ”ŃŃ‚ĐČующох Ń‚Đ”ŃŃ‚ĐŸĐČ, ĐČĐșĐ»ŃŽŃ‡Đ°Ń тДсты, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐČŃ‹ĐżĐŸĐ»ĐœŃŃŽŃ‚ ĐŽĐ”ŃŃŃ‚ĐșĐž Đ·Đ°ĐżŃ€ĐŸŃĐŸĐČ Đș БД, ĐșажЎыĐč раз ĐșĐŸĐłĐŽĐ° Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐș ĐČĐœĐŸŃĐžŃ‚ ĐœĐ”Đ±ĐŸĐ»ŃŒŃˆĐŸĐ” ĐžĐ·ĐŒĐ”ĐœĐ”ĐœĐžĐ”, ĐŒĐŸĐ¶Đ”Ń‚ Đ±Ń‹Ń‚ŃŒ ĐŒĐ”ĐŽĐ»Đ”ĐœĐœŃ‹ĐŒ Đž ĐŸŃ‚ĐČлДĐșать Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐșĐŸĐČ ĐŸŃ‚ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: ĐŸĐŸĐŒĐ”Ń‚Đșа Ń‚Đ”ŃŃ‚ĐŸĐČ ĐșаĐș '#cold-test' ĐżĐŸĐ·ĐČĐŸĐ»ŃĐ”Ń‚ ĐżŃ€ĐŸĐłŃ€Đ°ĐŒĐŒĐ” ĐČŃ‹ĐżĐŸĐ»ĐœŃŃ‚ŃŒ Ń‚ĐŸĐ»ŃŒĐșĐŸ быстрыД тДсты (cold===быстрыД тДсты, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐœĐ” ЎДлают IO Đž ĐŒĐŸĐłŃƒŃ‚ ĐČŃ‹ĐżĐŸĐ»ĐœŃŃ‚ŃŒŃŃ Ń‡Đ°ŃŃ‚ĐŸ, ЎажД ĐżĐŸĐșа Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐș ĐœĐ°Đ±ĐžŃ€Đ°Đ”Ń‚ тДĐșст). + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Jest") + +```javascript +// ŃŃ‚ĐŸ быстрыĐč тДст, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč ĐżĐŸĐŒĐ”Ń‡Đ”Đœ ŃĐŸĐŸŃ‚ĐČДстĐČŃƒŃŽŃ‰ĐžĐŒ ĐŸĐ±Ń€Đ°Đ·ĐŸĐŒ, +// Ń‡Ń‚ĐŸĐ±Ń‹ ĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»ŃŒ ОлО CI ĐŒĐŸĐłĐ»Đž Ń‡Đ°ŃŃ‚ĐŸ Đ”ĐłĐŸ запусĐșать +describe("Order service", function () { + describe("Add new order #cold-test #sanity", function () { + test("Scenario - no currency was supplied. Expectation - Use the default currency #sanity", function () { + // Đ·ĐŽĐ”ŃŃŒ Đ»ĐŸĐłĐžĐșа + }); + }); +}); +``` + +
    + +

    + +## âšȘ  1.12 СтруĐșтуроруĐčтД тДсты, ĐșаĐș ĐŒĐžĐœĐžĐŒŃƒĐŒ, ĐœĐ° 2 ŃƒŃ€ĐŸĐČĐœŃ + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** ПроЮаĐčтД ĐœĐ°Đ±ĐŸŃ€Ńƒ Ń‚Đ”ŃŃ‚ĐŸĐČ ĐœĐ”Đșую струĐșтуру, Ń‡Ń‚ĐŸĐ±Ń‹ Đ»ŃŽĐ±ĐŸĐč, ĐșŃ‚ĐŸ ĐżĐŸŃĐŒĐŸŃ‚Ń€ĐžŃ‚ ĐœĐ° ĐœĐžŃ…, ĐŒĐŸĐł лДгĐșĐŸ ĐżĐŸĐœŃŃ‚ŃŒ, Ń‡Ń‚ĐŸ ĐżŃ€ĐŸĐžŃŃ…ĐŸĐŽĐžŃ‚ (тДсты - Đ»ŃƒŃ‡ŃˆĐ°Ń ĐŽĐŸĐșŃƒĐŒĐ”ĐœŃ‚Đ°Ń†ĐžŃ). ĐžĐ±Ń‰ĐžĐŒ ĐŒĐ”Ń‚ĐŸĐŽĐŸĐŒ ĐŽĐ»Ń ŃŃ‚ĐŸĐłĐŸ яĐČĐ»ŃĐ”Ń‚ŃŃ Ń€Đ°Đ·ĐŒĐ”Ń‰Đ”ĐœĐžĐ” ĐșаĐș ĐŒĐžĐœĐžĐŒŃƒĐŒ ĐŽĐČух Đ±Đ»ĐŸĐșĐŸĐČ "describe" ĐœĐ°ĐŽ Ń‚Đ”ŃŃ‚Đ°ĐŒĐž: пДрĐČыĐč - ĐŽĐ»Ń ĐœĐ°Đ·ĐČĐ°ĐœĐžŃ Ń‚Đ”ŃŃ‚ĐžŃ€ŃƒĐ”ĐŒĐŸĐłĐŸ Đ±Đ»ĐŸĐșа, а ĐČŃ‚ĐŸŃ€ĐŸĐč - ĐŽĐ»Ń ĐŽĐŸĐżĐŸĐ»ĐœĐžŃ‚Đ”Đ»ŃŒĐœĐŸĐłĐŸ Ń€Đ°Đ·ĐŽĐ”Đ»Đ”ĐœĐžŃ Ń‚Đ”ŃŃ‚ĐŸĐČ, ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, ŃŃ†Đ”ĐœĐ°Ń€ĐžŃ ОлО ĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»ŃŒŃĐșох ĐșĐ°Ń‚Đ”ĐłĐŸŃ€ĐžĐč (ŃĐŒ. ĐżŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° Đž сĐșŃ€ĐžĐœŃˆĐŸŃ‚ ĐœĐžĐ¶Đ”). +Đ”Đ°ĐœĐœĐŸĐ” Ń€Đ°Đ·ĐŽĐ”Đ»Đ”ĐœĐžĐ” таĐșжД ŃƒĐ»ŃƒŃ‡ŃˆĐžŃ‚ŃŒ Ń„ĐžĐœĐ°Đ»ŃŒĐœŃ‹Đ” ĐŸŃ‚Ń‡Đ”Ń‚Ń‹ ĐżĐŸ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃŽ. Đ§ĐžŃ‚Đ°Ń‚Đ”Đ»ŃŒ ŃĐŒĐŸĐ¶Đ”Ń‚ лДгĐșĐŸ Ń€Đ°Đ·ĐŸĐ±Ń€Đ°Ń‚ŃŒŃŃ с ĐșĐ°Ń‚Đ”ĐłĐŸŃ€ĐžŃĐŒĐž Ń‚Đ”ŃŃ‚ĐŸĐČ, ĐœĐ°Đčто ĐœŃƒĐ¶ĐœŃ‹Đč разЎДл Đž ĐżĐŸĐœŃŃ‚ŃŒ, гЎД ĐŒĐŸĐłĐ»Đ° ĐČĐŸĐ·ĐœĐžĐșĐœŃƒŃ‚ŃŒ ĐŸŃˆĐžĐ±Đșа. Đ‘ĐŸĐ»Đ”Đ” Ń‚ĐŸĐłĐŸ, Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐșу Đ±ŃƒĐŽĐ”Ń‚ лДгчД ĐŸŃ€ĐžĐ”ĐœŃ‚ĐžŃ€ĐŸĐČаться ĐČ ŃĐ»ŃƒŃ‡Đ°Đ” Đ±ĐŸĐ»ŃŒŃˆĐŸĐłĐŸ ĐșĐŸĐ»ĐžŃ‡Đ”ŃŃ‚ĐČа Ń‚Đ”ŃŃ‚ĐŸĐČ. ĐĄŃƒŃ‰Đ”ŃŃ‚ĐČŃƒĐ”Ń‚ ĐœĐ”ŃĐșĐŸĐ»ŃŒĐșĐŸ ŃĐżĐŸŃĐŸĐ±ĐŸĐČ ĐżŃ€ĐžĐŽĐ°Ń‚ŃŒ Ń‚Đ”ŃŃ‚Đ°ĐŒ струĐșтуру, ĐșĐŸŃ‚ĐŸŃ€ĐŸĐ” ĐŒĐŸĐ¶ĐœĐŸ ĐœĐ°Đčто Đ·ĐŽĐ”ŃŃŒ [given-when-then](https://github.com/searls/jasmine-given) Đž Đ·ĐŽĐ”ŃŃŒ [RITE](https://github.com/ericelliott/riteway) + +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** Про ĐżŃ€ĐŸŃĐŒĐŸŃ‚Ń€Đ” ĐŸŃ‚Ń‡Đ”Ń‚Đ° с ĐŽĐ»ĐžĐœĐœŃ‹ĐŒ Đž ĐżĐ»ĐŸŃĐșĐžĐŒ спОсĐșĐŸĐŒ Ń‚Đ”ŃŃ‚ĐŸĐČ ĐżŃ€ĐžŃ…ĐŸĐŽĐžŃ‚ŃŒŃŃ Đ±Đ”ĐłĐ»ĐŸ ĐżŃ€ĐŸŃĐŒĐ°Ń‚Ń€ĐžĐČать ĐŽĐ»ĐžĐœĐœŃ‹Đ” тДĐșсты, Ń‡Ń‚ĐŸĐ±Ń‹ ĐżĐŸĐœŃŃ‚ŃŒ ĐŸŃĐœĐŸĐČĐœŃ‹Đ” ŃŃ†Đ”ĐœĐ°Ń€ĐžĐž Đž ĐČŃ‹ŃŃĐœĐžŃ‚ŃŒ ĐżŃ€ĐžŃ‡ĐžĐœŃƒ ĐœĐ”ŃƒĐŽĐ°Ń‡Đž ĐŸĐżŃ€Đ”ĐŽĐ”Đ»Đ”ĐœĐœŃ‹Ń… Ń‚Đ”ŃŃ‚ĐŸĐČ. Đ Đ°ŃŃĐŒĐŸŃ‚Ń€ĐžĐŒ ŃĐ»Đ”ĐŽŃƒŃŽŃ‰ĐžĐč ŃĐ»ŃƒŃ‡Đ°Đč: ДслО про ĐœĐ°Đ±ĐŸŃ€Đ” ĐČ 100 Ń‚Đ”ŃŃ‚ĐŸĐČ 7 Оз ĐœĐžŃ… ĐŸĐșĐ°Đ¶ŃƒŃ‚ŃŃ ĐœĐ”ŃƒĐŽĐ°Ń‡ĐœŃ‹ĐŒĐž, Ń‚ĐŸ ĐżŃ€ĐžĐŽĐ”Ń‚ŃŃ ĐżŃ€ĐŸŃ‡ĐžŃ‚Đ°Ń‚ŃŒ ох ĐŸĐżĐžŃĐ°ĐœĐžĐ”, Ń‡Ń‚ĐŸĐ±Ń‹ ĐżĐŸĐœŃŃ‚ŃŒ, ĐșаĐș ĐŸĐœĐž Юруг с ĐŽŃ€ŃƒĐłĐŸĐŒ сĐČŃĐ·Đ°ĐœŃ‹. ĐžĐŽĐœĐ°ĐșĐŸ, ДслО ŃŃƒŃ‰Đ”ŃŃ‚ĐČŃƒĐ”Ń‚ Ń€Đ°Đ·ĐŽĐ”Đ»Đ”ĐœĐžĐ” ĐœĐ° струĐșтуры, Ń‚ĐŸ ĐżŃ€ĐžŃ‡ĐžĐœĐ° ĐżĐ°ĐŽĐ”ĐœĐžŃ Ń‚Đ”ŃŃ‚ĐŸĐČ, ĐŒĐŸĐ¶Đ”Ń‚ Đ±Ń‹Ń‚ŃŒ ĐŸĐ±Ń‰Đ”Đč ĐŽĐ»Ń ĐœĐžŃ… Đž Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐș Đ±Ń‹ŃŃ‚Ń€ĐŸ сЎДлаДт ĐČыĐČĐŸĐŽ ĐŸ Ń‚ĐŸĐŒ, Ń‡Ń‚ĐŸ ŃŃ‚Đ°Đ»ĐŸ ĐżŃ€ĐžŃ‡ĐžĐœĐŸĐč ОлО, ĐżĐŸ ĐșраĐčĐœĐ” ĐŒĐ”Ń€Đ”, гЎД ĐŸĐœĐ° ĐœĐ°Ń…ĐŸĐŽĐžŃ‚ŃŃ. +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: Đ Đ°Đ·ĐŽĐ”Đ»Đ”ĐœĐžĐ” ĐœĐ° струĐșтуры ĐœĐ°Đ±ĐŸŃ€Đ° Ń‚Đ”ŃŃ‚ĐŸĐČ ĐżŃ€ĐžĐČĐŸĐŽĐžŃ‚ Đș ŃƒĐŽĐŸĐ±ĐœĐŸĐŒŃƒ ĐŸŃ‚Ń‡Đ”Ń‚Ńƒ + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Jest") + +```javascript +// Ń‚Đ”ŃŃ‚ĐžŃ€ŃƒĐ”ĐŒŃ‹Đč Đ±Đ»ĐŸĐș +describe("Transfer service", () => { + // ŃŃ†Đ”ĐœĐ°Ń€ĐžĐč + describe("When no credit", () => { + // ĐŸĐ¶ĐžĐŽĐ°ĐœĐžĐ” + test("Then the response status should decline", () => {}); + + // ĐŸĐ¶ĐžĐŽĐ°ĐœĐžĐ” + test("Then it should send email to admin", () => {}); + }); +}); +``` + +![alt text](assets/hierarchical-report.png) + +
    + +### :thumbsdown: ĐĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœĐŸ: ĐŸĐ»ĐŸŃĐșĐžĐč ŃĐżĐžŃĐŸĐș Ń‚Đ”ŃŃ‚ĐŸĐČ ŃƒŃĐ»ĐŸĐ¶ĐœŃĐ”Ń‚ ĐżĐŸĐžŃĐș ĐŸŃˆĐžĐ±ĐșĐž + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Mocha") + +```javascript +test("Then the response status should decline", () => {}); + +test("Then it should send email", () => {}); + +test("Then there should not be a new transfer record", () => {}); +``` + +![alt text](assets/flat-report.png) + +
    + +
    + +

    + +## âšȘ 1.13 Đ”ĐŸĐżĐŸĐ»ĐœĐžŃ‚Đ”Đ»ŃŒĐœŃ‹Đ” ĐŸĐ±Ń‰ĐžĐ” праĐČОла ĐżĐŸ ĐœĐ°ĐżĐžŃĐ°ĐœĐžŃŽ Ń‚Đ”ŃŃ‚ĐŸĐČ + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** Эта Đ·Đ°ĐŒĐ”Ń‚Đșа ĐżĐŸŃĐČŃŃ‰Đ”ĐœĐ° ŃĐŸĐČĐ”Ń‚Đ°ĐŒ ĐżĐŸ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃŽ, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” сĐČŃĐ·Đ°ĐœŃ‹ с Node JS ОлО, ĐżĐŸ ĐșраĐčĐœĐ”Đč ĐŒĐ”Ń€Đ”, ĐŒĐŸĐłŃƒŃ‚ Đ±Ń‹Ń‚ŃŒ ĐżŃ€ĐŸĐžĐ»Đ»ŃŽŃŃ‚Ń€ĐžŃ€ĐŸĐČĐ°ĐœŃ‹ ĐœĐ° Đ”ĐłĐŸ ĐżŃ€ĐžĐŒĐ”Ń€Đ”. ĐžĐŽĐœĐ°ĐșĐŸ ĐČ ŃŃ‚ĐŸĐŒ ĐżŃƒĐœĐșтД ŃĐłŃ€ŃƒĐżĐżĐžŃ€ĐŸĐČĐ°ĐœĐŸ ĐœĐ”ŃĐșĐŸĐ»ŃŒĐșĐŸ ŃĐŸĐČĐ”Ń‚ĐŸĐČ, ĐœĐ” сĐČŃĐ·Đ°ĐœĐœŃ‹Ń… с Node, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” Ń…ĐŸŃ€ĐŸŃˆĐŸ ОзĐČĐ”ŃŃ‚ĐœŃ‹ + +Đ˜Đ·ŃƒŃ‡Đ°ĐčтД Đž праĐșтоĐșуĐčтД [ĐżŃ€ĐžĐœŃ†ĐžĐżŃ‹ TDD](https://www.sm-cloud.com/book-review-test-driven-development-by-example-a-tldr/) — ĐŸĐœĐž яĐČĐ»ŃŃŽŃ‚ŃŃ ĐŸŃ‡Đ”ĐœŃŒ Ń†Đ”ĐœĐœŃ‹ĐŒ ĐžĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚ĐŸĐČ ĐŽĐ»Ń ĐŒĐœĐŸĐłĐžŃ… Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐșĐŸĐČ, ĐŸĐŽĐœĐ°ĐșĐŸ ĐœĐ” пугаĐčŃ‚Đ”ŃŃŒ, ДслО ĐŸĐœĐž ĐČĐ°ĐŒ ĐœĐ” ĐżĐŸĐŽĐŸĐčЮут. Đ Đ°ŃŃĐŒĐŸŃ‚Ń€ĐžŃ‚Đ” ĐČĐŸĐ·ĐŒĐŸĐ¶ĐœĐŸŃŃ‚ŃŒ ĐœĐ°ĐżĐžŃĐ°ĐœĐžŃ Ń‚Đ”ŃŃ‚ĐŸĐČ ĐŽĐŸ ĐœĐ°Ń‡Đ°Đ»Đ° Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚ĐșĐž ĐČ ŃŃ‚ĐžĐ»Đ” [red-green-refactor style](https://blog.cleancoder.com/uncle-bob/2014/12/17/TheCyclesOfTDD.html), ŃƒĐ±Đ”ĐŽĐžŃ‚Đ”ŃŃŒ, Ń‡Ń‚ĐŸ ĐșажЎыĐč тДст ĐżŃ€ĐŸĐČĐ”Ń€ŃĐ”Ń‚ Ń€ĐŸĐČĐœĐŸ ĐŸĐŽĐžĐœ ŃĐŒŃ‹ŃĐ»ĐŸĐČĐŸĐč ŃĐ»Đ”ĐŒĐ”ĐœŃ‚ Đž ĐżĐŸŃĐ»Đ” Ń‚ĐŸĐłĐŸ, ĐșаĐș ĐČы ĐœĐ°ĐčЎДтД ĐŸŃˆĐžĐ±Đșу, пДрДЎ Ń‚Đ”ĐŒ ĐșаĐș ДД оспраĐČоть - ĐœĐ°ĐżĐžŃˆĐžŃ‚Đ” тДст, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč ĐŸĐ±ĐœĐ°Ń€ŃƒĐ¶ĐžŃ‚ эту ĐŸŃˆĐžĐ±Đșу ĐČ Đ±ŃƒĐŽŃƒŃ‰Đ”ĐŒ. Пусть ĐșажЎыĐč тДст ŃƒĐżĐ°ĐŽĐ”Ń‚ Ń…ĐŸŃ‚Ń бы ĐŸĐŽĐžĐœ раз, прДжЎД Đ”ĐłĐŸ цĐČДт ŃŃ‚Đ°ĐœĐ”Ń‚ Đ·Đ”Đ»Đ”ĐœŃ‹ĐŒ. ĐĐ°Ń‡ĐœĐžŃ‚Đ” Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Đșу ĐŒĐŸĐŽŃƒĐ»Ń с ĐœĐ°ĐżĐžŃĐ°ĐœĐžŃ ĐżŃ€ĐŸŃŃ‚ĐŸĐłĐŸ Đž Đ±Ń‹ŃŃ‚Ń€ĐŸĐłĐŸ ĐșĐŸĐŽĐ°, ŃƒĐŽĐŸĐČлДтĐČĐŸŃ€ŃŃŽŃ‰Đ”ĐłĐŸ Ń‚Đ”ŃŃ‚Ńƒ, а ĐżĐŸŃĐ»Đ” ĐżĐŸŃŃ‚Đ”ĐżĐ”ĐœĐœĐŸ рДфаĐșŃ‚ĐŸŃ€ĐžŃ‚Đ” Đž ĐŽĐŸĐČДЎОтД Đ”ĐłĐŸ ĐŽĐŸ ŃƒŃ€ĐŸĐČĐœŃ ĐżŃ€ĐŸĐŽĐ°ĐșŃˆĐœ-ĐșĐŸĐŽĐ°. ИзбДгаĐčтД Đ»ŃŽĐ±ĐŸĐč заĐČĐžŃĐžĐŒĐŸŃŃ‚Đž ĐŸŃ‚ ĐŸĐșŃ€ŃƒĐ¶Đ”ĐœĐžŃ (фаĐčĐ»ĐŸĐČыД путо, ОС Đž ĐŽŃ€ŃƒĐłĐŸĐ”). +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** ĐœŃƒĐŽŃ€ĐŸŃŃ‚ŃŒ, ĐșĐŸŃ‚ĐŸŃ€Đ°Ń ĐșĐŸĐżĐžĐ»Đ°ŃŃŒ ĐŽĐ”ŃŃŃ‚ĐžĐ»Đ”Ń‚ĐžŃĐŒĐž, ĐżŃ€ĐŸĐčЎДт ĐŒĐžĐŒĐŸ ĐČас + +

    + +# РазЎДл 2ïžâƒŁ: ĐąĐ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” Backend + +## âšȘ 2.1 ĐŁĐČĐ”Đ»ĐžŃ‡ŃŒŃ‚Đ” Ń€Đ°Đ·ĐœĐŸĐŸĐ±Ń€Đ°Đ·ĐžĐ” сĐČĐŸĐžŃ… Ń‚Đ”ŃŃ‚ĐŸĐČ. ĐĐ” ĐŸĐłŃ€Đ°ĐœĐžŃ‡ĐžĐČаĐčтД ŃĐ”Đ±Ń Ń‚ĐŸĐ»ŃŒĐșĐŸ ŃŽĐœĐžŃ‚-Ń‚Đ”ŃŃ‚Đ°ĐŒĐž Đž ĐżĐžŃ€Đ°ĐŒĐžĐŽĐŸĐč + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** [ĐŸĐžŃ€Đ°ĐŒĐžĐŽĐ° Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ](https://martinfowler.com/bliki/TestPyramid.html), ĐœĐ”ŃĐŒĐŸŃ‚Ń€Ń ĐœĐ° Ń‚ĐŸ, Ń‡Ń‚ĐŸ Đ”Đč ужД Đ±ĐŸĐ»Đ”Đ” 10 лДт, яĐČĐ»ŃĐ”Ń‚ŃŃ ĐŸŃ‚Đ»ĐžŃ‡ĐœĐŸĐč Đž аĐșŃ‚ŃƒĐ°Đ»ŃŒĐœĐŸĐč ĐŒĐŸĐŽĐ”Đ»ŃŒŃŽ, ĐșĐŸŃ‚ĐŸŃ€Đ°Ń прДЎлагаДт 3 топа Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ Đž ĐČĐ»ĐžŃĐ”Ń‚ ĐœĐ° стратДгОю Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ Đ±ĐŸĐ»ŃŒŃˆĐžĐœŃŃ‚ĐČа Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐșĐŸĐČ. В Ń‚ĐŸ жД ĐČŃ€Đ”ĐŒŃ, ĐżĐŸŃĐČĐžĐ»ĐžŃŃŒ ĐŒĐ”Ń‚ĐŸĐŽŃ‹ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” сĐșрыĐČаются ĐČ Ń‚Đ”ĐœĐž ĐŽĐ°ĐœĐœĐŸĐč ĐżĐžŃ€Đ°ĐŒĐžĐŽŃ‹. ĐŸŃ€ĐžĐœĐžĐŒĐ°Ń ĐČĐŸ ĐČĐœĐžĐŒĐ°ĐœĐžĐ” ĐČсД ĐșĐ°Ń€ĐŽĐžĐœĐ°Đ»ŃŒĐœŃ‹Đ” ĐžĐ·ĐŒĐ”ĐœĐ”ĐœĐžŃ, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐœĐ°Đ±Đ»ŃŽĐŽĐ°Đ»ĐžŃŃŒ ĐČ Ń‚Đ”Ń‡Đ”ĐœĐžĐ” ĐżĐŸŃĐ»Đ”ĐŽĐœĐžŃ… 10 лДт (ĐŒĐžĐșŃ€ĐŸŃĐ”Ń€ĐČосы, ĐŸĐ±Đ»Đ°Đșа, бДссДрĐČĐ”Ń€ĐœŃ‹Đ” Ń‚Đ”Ń…ĐœĐŸĐ»ĐŸĐłĐžĐž), ĐČстаДт ĐČĐŸĐżŃ€ĐŸŃ: ĐČĐŸĐ·ĐŒĐŸĐ¶ĐœĐŸ лО ĐżŃ€ĐžĐŒĐ”ĐœĐžŃ‚ŃŒ ĐŸĐŽĐœŃƒ ĐŒĐŸĐŽĐ”Đ»ŃŒ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ ĐŽĐ»Ń ĐČсДх Ń‚ĐžĐżĐŸĐČ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐč? ИлО ĐŒĐŸĐ¶Đ”Ń‚ Đ±Ń‹Ń‚ŃŒ Đ»ŃƒŃ‡ŃˆĐ” Ń€Đ°ŃŃĐŒĐŸŃ‚Ń€Đ”Ń‚ŃŒ ĐČĐŸĐ·ĐŒĐŸĐ¶ĐœĐŸŃŃ‚ŃŒ ĐČĐœĐ”ĐŽŃ€Đ”ĐœĐžŃ ĐœĐŸĐČых праĐșтоĐș? + +ĐĐ” ĐżĐŸĐčĐŒĐžŃ‚Đ” ĐŒĐ”ĐœŃ ĐœĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœĐŸ, ĐČ 2019 ĐłĐŸĐŽŃƒ ĐżĐžŃ€Đ°ĐŒĐžĐŽĐ° Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ, TDD Đž ŃŽĐœĐžŃ‚-тДсты ĐżĐŸ-ĐżŃ€Đ”Đ¶ĐœĐ”ĐŒŃƒ яĐČĐ»ŃŃŽŃ‚ŃŃ ĐŒĐŸŃ‰ĐœĐŸĐč Ń‚Đ”Ń…ĐœĐžĐșĐŸĐč Đž, ĐČĐ”Ń€ĐŸŃŃ‚ĐœĐŸ, Đ»ŃƒŃ‡ŃˆĐ” ĐČŃĐ”ĐłĐŸ ĐżĐŸĐŽŃ…ĐŸĐŽŃŃ‚ ĐŽĐ»Ń ĐŒĐœĐŸĐłĐžŃ… ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐč. ĐąĐŸĐ»ŃŒĐșĐŸ, ĐșаĐș Đž Đ»ŃŽĐ±Đ°Ń Юругая ĐŒĐŸĐŽĐ”Đ»ŃŒ, ĐœĐ”ŃĐŒĐŸŃ‚Ń€Ń ĐœĐ° сĐČĐŸŃŽ Ń†Đ”ĐœĐœĐŸŃŃ‚ŃŒ, [ĐžĐœĐŸĐłĐŽĐ° ĐŸĐœĐ° ЎаДт ŃĐ±ĐŸĐž](https://en.wikipedia.org/wiki/All_models_are_wrong). ĐĐ°ĐżŃ€ĐžĐŒĐ”Ń€, Ń€Đ°ŃŃĐŒĐŸŃ‚Ń€ĐžĐŒ IoT-ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ”, ĐșĐŸŃ‚ĐŸŃ€ĐŸĐ” ĐżĐŸĐ»ŃƒŃ‡Đ°Đ”Ń‚ ĐŒĐœĐŸĐ¶Đ”ŃŃ‚ĐČĐŸ ŃĐŸĐ±Ń‹Ń‚ĐžĐč топа Kafka/RabbitMQ, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” Đ·Đ°Ń‚Đ”ĐŒ ĐżĐŸĐżĐ°ĐŽĐ°ŃŽŃ‚ ĐČ Ń…Ń€Đ°ĐœĐžĐ»ĐžŃ‰Đ” ĐŽĐ°ĐœĐœŃ‹Ń… Đž ĐČ ĐșĐŸĐœĐ”Ń‡ĐœĐŸĐŒ ĐžŃ‚ĐŸĐłĐ” Đ·Đ°ĐżŃ€Đ°ŃˆĐžĐČаются Đ°ĐœĐ°Đ»ĐžŃ‚ĐžŃ‡Đ”ŃĐșĐžĐŒ ĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»ŃŒŃĐșĐžĐŒ ĐžĐœŃ‚Đ”Ń€Ń„Đ”ĐčŃĐŸĐŒ. ДДĐčстĐČĐžŃ‚Đ”Đ»ŃŒĐœĐŸ лО ĐŒŃ‹ ĐŽĐŸĐ»Đ¶ĐœŃ‹ тратоть 50% бюЎжДта ĐœĐ° Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” ĐœĐ° ĐœĐ°ĐżĐžŃĐ°ĐœĐžĐ” ĐŒĐŸĐŽŃƒĐ»ŃŒĐœŃ‹Ń… Ń‚Đ”ŃŃ‚ĐŸĐČ ĐŽĐ»Ń ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ, ĐșĐŸŃ‚ĐŸŃ€ĐŸĐ” ĐŸŃ€ĐžĐ”ĐœŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐŸ ĐœĐ° ĐžĐœŃ‚Đ”ĐłŃ€Đ°Ń†ĐžŃŽ Đž ĐżĐŸŃ‡Ń‚Đž ĐœĐ” ĐžĐŒĐ”Đ”Ń‚ Đ»ĐŸĐłĐžĐșĐž. ĐŸĐŸ ĐŒĐ”Ń€Đ” уĐČĐ”Đ»ĐžŃ‡Đ”ĐœĐžŃ Ń€Đ°Đ·ĐœĐŸĐŸĐ±Ń€Đ°Đ·ĐžŃ Ń‚ĐžĐżĐŸĐČ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐč (Đ±ĐŸŃ‚Ń‹, ĐșŃ€ĐžĐżŃ‚ĐŸĐČалюты, Alexa-skills) таĐșжД растут ŃˆĐ°ĐœŃŃ‹ ĐœĐ°Đčто таĐșОД ŃŃ†Đ”ĐœĐ°Ń€ĐžĐž, ĐČ ĐșĐŸŃ‚ĐŸŃ€Ń‹Ń… ĐżĐžŃ€Đ°ĐŒĐžĐŽĐ° Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ ĐœĐ” яĐČĐ»ŃĐ”Ń‚ŃŃ ĐžĐŽĐ”Đ°Đ»ŃŒĐœŃ‹ĐŒ Ń€Đ”ŃˆĐ”ĐœĐžĐ”ĐŒ. + +ĐŸŃ€ĐžŃˆĐ»ĐŸ ĐČŃ€Đ”ĐŒŃ уĐČĐ”Đ»ĐžŃ‡ĐžŃ‚ŃŒ сĐČĐŸĐ” ĐżĐŸŃ€Ń‚Ń„ĐŸĐ»ĐžĐŸ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČщоĐșа Đž ĐżĐŸĐ·ĐœĐ°ĐșĐŸĐŒĐžŃ‚ŃŒŃŃ ДщД Đ±ĐŸĐ»ŃŒŃˆĐžĐŒ ĐșĐŸĐ»ĐžŃ‡Đ”ŃŃ‚ĐČĐŸĐŒ ĐżĐŸĐŽŃ…ĐŸĐŽĐŸĐČ Đș ĐœĐ°ĐżĐžŃĐ°ĐœĐžŃŽ Ń‚Đ”ŃŃ‚ĐŸĐČ (ŃĐ»Đ”ĐŽŃƒŃŽŃ‰ĐžĐ” ĐżŃƒĐœĐșты ŃĐŸĐŽĐ”Ń€Đ¶Đ°Ń‚ ĐŸĐżŃ€Đ”ĐŽĐ”Đ»Đ”ĐœĐœŃ‹Đ” ОЎДО), ĐœĐ°Ń‡Đ°Ń‚ŃŒ ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČать ĐŒĐŸĐŽĐ”Đ»Đž Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ, ĐżĐŸĐŽĐŸĐ±ĐœŃ‹Đ” ĐżĐžŃ€Đ°ĐŒĐžĐŽĐ” Đž таĐșжД ĐœĐ°ŃƒŃ‡ĐžŃ‚ŃŃ ŃĐŸĐżĐŸŃŃ‚Đ°ĐČĐ»ŃŃ‚ŃŒ топы Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ с Ń€Đ”Đ°Đ»ŃŒĐœŃ‹ĐŒĐž ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐ°ĐŒĐž, с ĐșĐŸŃ‚ĐŸŃ€Ń‹ĐŒĐž ĐČы ĐŒĐŸĐ¶Đ”Ń‚Đ” ŃŃ‚ĐŸĐ»ĐșĐœŃƒŃ‚ŃŒŃŃ ("Đ­Đč, ĐœĐ°Ńˆ API ŃĐ»ĐŸĐŒĐ°Đœ, ЎаĐČаĐčтД ĐœĐ°ĐżĐžŃˆĐ”ĐŒ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” ĐșĐŸĐœŃ‚Ń€Đ°ĐșŃ‚ĐŸĐČ, ĐŸŃ€ĐžĐ”ĐœŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐœĐŸĐ” ĐœĐ° ĐżĐŸŃ‚Ń€Đ”Đ±ĐžŃ‚Đ”Đ»Ń!"). ĐĐ”ĐŸĐ±Ń…ĐŸĐŽĐžĐŒĐŸ таĐșжД ĐœĐ°ŃƒŃ‡ĐžŃ‚ŃŒŃŃ ĐŽĐžĐČĐ”Ń€ŃĐžŃ„ĐžŃ†ĐžŃ€ĐŸĐČать сĐČĐŸĐž тДсты, ĐșаĐș ĐžĐœĐČĐ”ŃŃ‚ĐŸŃ€, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč ŃĐŸĐ·ĐŽĐ°Đ”Ń‚ ĐżĐŸŃ€Ń‚Ń„Đ”Đ»ŃŒ ĐœĐ° ĐŸŃĐœĐŸĐČĐ” Đ°ĐœĐ°Đ»ĐžĐ·Đ° росĐșĐŸĐČ - ĐŸŃ†Đ”ĐœĐžŃ‚ŃŒ, гЎД ĐŒĐŸĐłŃƒŃ‚ ĐČĐŸĐ·ĐœĐžĐșĐœŃƒŃ‚ŃŒ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒŃ‹, Đž ĐżĐŸĐŽĐŸĐ±Ń€Đ°Ń‚ŃŒ прДĐČĐ”ĐœŃ‚ĐžĐČĐœŃ‹Đ” ĐŒĐ”Ń€Ń‹ ĐŽĐ»Ń ŃĐœĐžĐ¶Đ”ĐœĐžŃ этох ĐżĐŸŃ‚Đ”ĐœŃ†ĐžĐ°Đ»ŃŒĐœŃ‹Ń… росĐșĐŸĐČ. + +ĐŸŃ€Đ”ĐŽĐŸŃŃ‚Đ”Ń€Đ”Đ¶Đ”ĐœĐžĐ”: ŃĐżĐŸŃ€ ĐŸ TDD ĐČ ĐŒĐžŃ€Đ” ĐżŃ€ĐŸĐłŃ€Đ°ĐŒĐŒĐœĐŸĐłĐŸ ĐŸĐ±Đ”ŃĐżĐ”Ń‡Đ”ĐœĐžŃ ĐżŃ€ĐžĐœĐžĐŒĐ°Đ”Ń‚ Ń‚ĐžĐżĐžŃ‡ĐœŃ‹Đč ĐŸĐ±Đ»ĐžĐș Đ»ĐŸĐ¶ĐœĐŸĐč ĐŽĐžŃ…ĐŸŃ‚ĐŸĐŒĐžĐž: ĐŸĐŽĐœĐž ĐżŃ€ĐŸĐżĐŸĐČĐ”ĐŽŃƒŃŽŃ‚ Đ”ĐłĐŸ ĐżĐŸĐČŃĐ”ĐŒĐ”ŃŃ‚ĐœĐŸĐ” ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°ĐœĐžĐ”, ĐŽŃ€ŃƒĐłĐžĐ” счотают, Ń‡Ń‚ĐŸ ŃŃ‚ĐŸ ЮьяĐČĐŸĐ». КажЎыĐč, ĐșŃ‚ĐŸ Đ°Đ±ŃĐŸĐ»ŃŽŃ‚ĐœĐŸ уĐČĐ”Ń€Đ”Đœ ĐČ Ń‡Đ”ĐŒ Ń‚ĐŸ ĐŸĐŽĐœĐŸĐŒ - ĐŸŃˆĐžĐ±Đ°Đ”Ń‚ŃŃ :] +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** Вы ĐŒĐŸĐ¶Đ”Ń‚Đ” ĐżŃ€ĐŸĐżŃƒŃŃ‚ĐžŃ‚ŃŒ таĐșОД ĐžĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Ń‹, ĐșаĐș Fuzz, Lint Đž ĐŒŃƒŃ‚Đ°Ń†ĐžĐž, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐŒĐŸĐłŃƒŃ‚ ĐżŃ€ĐžĐœĐ”ŃŃ‚Đž ĐżĐŸĐ»ŃŒĐ·Ńƒ за 10 ĐŒĐžĐœŃƒŃ‚. + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: ĐĄĐžĐœĐŽĐž ĐšŃ€ĐžĐŽŃ…Đ°Ń€Đ°Đœ прДЎлагаДт Đ±ĐŸĐ»ŃŒŃˆĐŸĐ” Ń€Đ°Đ·ĐœĐŸĐŸĐ±Ń€Đ°Đ·ĐžĐ” ĐżĐŸĐŽŃ…ĐŸĐŽĐŸĐČ Đș Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃŽ ĐČ ŃĐČĐŸĐ”ĐŒ Đ·Đ°ĐŒĐ”Ń‡Đ°Ń‚Đ”Đ»ŃŒĐœĐŸĐŒ ĐżĐŸŃŃ‚Đ” ‘Testing Microservices — the same way’. + +![alt text](assets/bp-12-rich-testing.jpeg "Cindy Sridharan suggests a rich testing portfolio in her amazing post ‘Testing Microservices — the sane way’") + +â˜șïžĐŸŃ€ĐžĐŒĐ”Ń€: [YouTube: “Beyond Unit Tests: 5 Shiny Node.JS Test Types (2018)” (Yoni Goldberg)](https://www.youtube.com/watch?v=-2zP494wdUY&feature=youtu.be) + +
    + +![alt text](assets/bp-12-Yoni-Goldberg-Testing.jpeg "A test name that constitutes 3 parts") + +
    + +

    + +## âšȘ 2.2 ĐąĐ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ĐŸĐČ ĐŒĐŸĐ¶Đ”Ń‚ стать ĐČĐ°ŃˆĐžĐŒ Đ»ŃƒŃ‡ŃˆĐžĐŒ ĐŽŃ€ŃƒĐłĐŸĐŒ + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** КажЎыĐč ĐŒĐŸĐŽŃƒĐ»ŃŒĐœŃ‹Đč тДст ĐżĐŸĐșрыĐČаДт ĐœĐ”Đ±ĐŸĐ»ŃŒŃˆŃƒŃŽ часть ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ. ĐŸĐŸĐșрыть Đ”ĐłĐŸ цДлОĐșĐŸĐŒ яĐČĐ»ŃĐ”Ń‚ŃŃ ĐŽĐŸŃ€ĐŸĐłĐžĐŒ ŃƒĐŽĐŸĐČĐŸĐ»ŃŒŃŃ‚ĐČĐžĐ”ĐŒ, ĐČ Ń‚ĐŸ ĐČŃ€Đ”ĐŒŃ ĐșаĐș сĐșĐČĐŸĐ·ĐœĐŸĐ” Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” лДгĐșĐŸ ĐżĐŸĐșрыĐČаДт Đ±ĐŸĐ»ŃŒŃˆŃƒŃŽ часть ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ, ĐœĐŸ яĐČĐ»ŃĐ”Ń‚ŃŃ ĐœĐ”ŃŃ‚Đ°Đ±ĐžĐ»ŃŒĐœŃ‹ĐŒ Đž ĐŒĐ”ĐŽĐ»Đ”ĐœĐœŃ‹ĐŒ. ĐŸĐŸŃ‡Đ”ĐŒŃƒ бы ĐœĐ” ĐżŃ€ĐžĐŒĐ”ĐœĐžŃ‚ŃŒ ŃĐ±Đ°Đ»Đ°ĐœŃĐžŃ€ĐŸĐČĐ°ĐœĐœŃ‹Đč ĐżĐŸĐŽŃ…ĐŸĐŽ, Đž ĐœĐ” посать тДсты, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐżĐŸ Ń€Đ°Đ·ĐŒĐ”Ń€Ńƒ Đ±ĐŸĐ»ŃŒŃˆĐ”, Ń‡Đ”ĐŒ ĐŒĐŸĐŽŃƒĐ»ŃŒĐœŃ‹Đ”, ĐœĐŸ ĐŒĐ”ĐœŃŒŃˆĐ”, Ń‡Đ”ĐŒ сĐșĐČĐŸĐ·ĐœĐŸĐ” Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ”. ĐšĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ĐœĐŸĐ” Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” - ŃŃ‚ĐŸ Ń‚ĐŸ, Ń‡Ń‚ĐŸ ĐČĐŸĐ±Ń€Đ°Đ»ĐŸ ĐČ ŃĐ”Đ±Ń Đ»ŃƒŃ‡ŃˆĐ”Đ” Оз ĐŽĐČух ĐżĐ”Ń€Đ”Ń‡ĐžŃĐ»Đ”ĐœĐœŃ‹Ń… ĐżĐŸĐŽŃ…ĐŸĐŽĐŸĐČ. ĐžĐŽĐœĐŸ ŃĐŸŃ‡Đ”Ń‚Đ°Đ”Ń‚ ĐČ ŃĐ”Đ±Đ” Ń€Đ°Đ·ŃƒĐŒĐœŃƒŃŽ ĐżŃ€ĐŸĐžĐ·ĐČĐŸĐŽĐžŃ‚Đ”Đ»ŃŒĐœĐŸŃŃ‚ŃŒ Đž ĐČĐŸĐ·ĐŒĐŸĐ¶ĐœĐŸŃŃ‚ŃŒ ĐżŃ€ĐžĐŒĐ”ĐœĐ”ĐœĐžŃ ĐżĐ°Ń‚Ń‚Đ”Ń€ĐœĐŸĐČ TDD, а таĐșжД Ń€Đ”Đ°Đ»ĐžŃŃ‚ĐžŃ‡ĐœĐŸĐ” Đž Đ±ĐŸĐ»ŃŒŃˆĐŸĐ” ĐżĐŸĐșрытОД. + +ĐšĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ĐœŃ‹Đ” тДсты Ń„ĐŸĐșусоруются ĐœĐ° "Đ”ĐŽĐžĐœĐžŃ†Đ”" ĐŒĐžĐșŃ€ĐŸŃĐ”Ń€ĐČОса, ĐŸĐœĐž Ń€Đ°Đ±ĐŸŃ‚Đ°ŃŽŃ‚ с API, ĐœĐ” ĐžĐŒĐžŃ‚ĐžŃ€ŃƒŃŽŃ‚ ĐœĐžŃ‡Đ”ĐłĐŸ, Ń‡Ń‚ĐŸ ĐżŃ€ĐžĐœĐ°ĐŽĐ»Đ”Đ¶ĐžŃ‚ ŃĐ°ĐŒĐŸĐŒŃƒ ĐŒĐžĐșŃ€ĐŸŃĐ”Ń€ĐČОсу (ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, Ń€Đ”Đ°Đ»ŃŒĐœŃƒŃŽ БД ОлО, ĐżĐŸ ĐșраĐčĐœĐ”Đč ĐŒĐ”Ń€Đ”, ДД ĐČДрсОю ĐČ ĐżĐ°ĐŒŃŃ‚Đž), ĐœĐŸ затыĐșают ĐČсД, Ń‡Ń‚ĐŸ яĐČĐ»ŃĐ”Ń‚ŃŃ ĐČĐœĐ”ŃˆĐœĐžĐŒ, ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, ĐČŃ‹Đ·ĐŸĐČы Юругох ĐŒĐžĐșŃ€ĐŸŃĐ”Ń€ĐČĐžŃĐŸĐČ. ĐŸĐŸŃŃ‚ŃƒĐżĐ°Ń таĐșĐžĐŒ ĐŸĐ±Ń€Đ°Đ·ĐŸĐŒ, ĐŒŃ‹ Ń‚Đ”ŃŃ‚ĐžŃ€ŃƒĐ”ĐŒ Ń‚ĐŸ, Ń‡Ń‚ĐŸ разĐČДртыĐČĐ°Đ”ĐŒ, ĐżĐŸĐŽŃ…ĐŸĐŽĐžĐŒ Đș ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃŽ ĐŸŃ‚ ĐČĐœĐ”ŃˆĐœĐ”ĐłĐŸ Đș ĐČĐœŃƒŃ‚Ń€Đ”ĐœĐœĐ”ĐŒŃƒ Đž ĐŸĐ±Ń€Đ”Ń‚Đ°Đ”ĐŒ Đ±ĐŸĐ»ŃŒŃˆŃƒŃŽ уĐČĐ”Ń€Đ”ĐœĐœĐŸŃŃ‚ŃŒ за Ń€Đ°Đ·ŃƒĐŒĐœĐŸĐ” ĐČŃ€Đ”ĐŒŃ. + +[ĐŁ ĐœĐ°Ń Đ”ŃŃ‚ŃŒ ĐżĐŸĐ»ĐœĐŸĐ” руĐșĐŸĐČĐŸĐŽŃŃ‚ĐČĐŸ, ĐșĐŸŃ‚ĐŸŃ€ĐŸĐ” ĐżĐŸŃĐČŃŃ‰Đ”ĐœĐŸ ОсĐșĐ»ŃŽŃ‡ĐžŃ‚Đ”Đ»ŃŒĐœĐŸ ĐœĐ°ĐżĐžŃĐ°ĐœĐžŃŽ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ĐœŃ‹Ń… Ń‚Đ”ŃŃ‚ĐŸĐČ ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœŃ‹ĐŒ ŃĐżĐŸŃĐŸĐ±ĐŸĐŒ](https://github.com/testjavascript/nodejs-integration-tests-best-practices) + +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** Вы ĐŒĐŸĐ¶Đ”Ń‚Đ” ĐżĐŸŃ‚Ń€Đ°Ń‚ĐžŃ‚ŃŒ ĐŸĐłŃ€ĐŸĐŒĐœĐŸĐ” ĐșĐŸĐ»ĐžŃ‡Đ”ŃŃ‚ĐČĐŸ ĐČŃ€Đ”ĐŒĐ”ĐœĐž ĐœĐ° ĐœĐ°ĐżĐžŃĐ°ĐœĐžĐ” ĐŒĐŸĐŽŃƒĐ»ŃŒĐœŃ‹Ń… Ń‚Đ”ŃŃ‚ĐŸĐČ Đž ĐŸĐ±ĐœĐ°Ń€ŃƒĐ¶ĐžŃ‚ŃŒ, Ń‡Ń‚ĐŸ ĐżĐŸĐșрытОД ŃĐžŃŃ‚Đ”ĐŒŃ‹ ŃĐŸŃŃ‚Đ°ĐČĐ»ŃĐ”Ń‚ ĐČŃĐ”ĐłĐŸ 20%. + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: Supertest ĐżĐŸĐ·ĐČĐŸĐ»ŃĐ”Ń‚ ĐżŃ€ĐžĐ±Đ»ĐžĐ¶Đ°Ń‚ŃŒŃŃ Đș Express API ĐČ ĐżŃ€ĐŸŃ†Đ”ŃŃĐ” (с ĐČŃ‹ŃĐŸĐșĐŸĐč сĐșĐŸŃ€ĐŸŃŃ‚ŃŒŃŽ Đž ŃˆĐžŃ€ĐŸĐșĐžĐŒ ĐŸŃ…ĐČĐ°Ń‚ĐŸĐŒ ŃƒŃ€ĐŸĐČĐœĐ”Đč) + +![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "Examples with Mocha") + +![alt text](assets/bp-13-component-test-yoni-goldberg.png " [Supertest](https://www.npmjs.com/package/supertest) allows approaching Express API in-process (fast and cover many layers)") + +
    + +

    + +## âšȘ 2.3 ĐŁĐ±Đ”ĐŽĐžŃ‚Đ”ŃŃŒ, Ń‡Ń‚ĐŸ ĐœĐŸĐČыД рДлОзы ĐœĐ” ĐœĐ°Ń€ŃƒŃˆĐ°ŃŽŃ‚ API, ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒŃ ĐșĐŸĐœŃ‚Ń€Đ°ĐșŃ‚ĐœŃ‹Đ” тДсты + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** ИтаĐș, у ĐČĐ°ŃˆĐ”ĐłĐŸ ĐŒĐžĐșŃ€ĐŸŃĐ”Ń€ĐČОса Đ”ŃŃ‚ŃŒ ĐœĐ”ŃĐșĐŸĐ»ŃŒĐșĐŸ ĐșĐ»ĐžĐ”ĐœŃ‚ĐŸĐČ, Đž ĐČы запусĐșаДтД ĐœĐ”ŃĐșĐŸĐ»ŃŒĐșĐŸ ĐČДрсОĐč сДрĐČОса Оз ŃĐŸĐŸĐ±Ń€Đ°Đ¶Đ”ĐœĐžĐč ŃĐŸĐČĐŒĐ”ŃŃ‚ĐžĐŒĐŸŃŃ‚Đž (Ń‡Ń‚ĐŸĐ±Ń‹ ĐČсД былО ĐŽĐŸĐČĐŸĐ»ŃŒĐœŃ‹). Đ—Đ°Ń‚Đ”ĐŒ ĐČы ĐžĐ·ĐŒĐ”ĐœŃĐ”Ń‚Đ” ĐșаĐșĐŸĐ”-Ń‚ĐŸ ĐżĐŸĐ»Đ” Đž "Đ±ŃƒĐŒ!", ĐșаĐșĐŸĐč-Ń‚ĐŸ ĐČĐ°Đ¶ĐœŃ‹Đč ĐșĐ»ĐžĐ”ĐœŃ‚, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč ĐżĐŸĐ»Đ°ĐłĐ°Đ”Ń‚ŃŃ ĐœĐ° ŃŃ‚ĐŸ ĐżĐŸĐ»Đ”, ĐČĐŸĐ·ĐŒŃƒŃ‰Đ”Đœ. Đ­Ń‚ĐŸ Đž Đ”ŃŃ‚ŃŒ Catch-22 ĐČ ĐŒĐžŃ€Đ” ĐžĐœŃ‚Đ”ĐłŃ€Đ°Ń†ĐžĐž: Đ”Đ»Ń сДрĐČĐ”Ń€ĐœĐŸĐč ŃŃ‚ĐŸŃ€ĐŸĐœŃ‹ ĐŸŃ‡Đ”ĐœŃŒ ŃĐ»ĐŸĐ¶ĐœĐŸ ŃƒŃ‡Đ”ŃŃ‚ŃŒ ĐČсД ĐŒĐœĐŸĐłĐŸŃ‡ĐžŃĐ»Đ”ĐœĐœŃ‹Đ” ĐŸĐ¶ĐžĐŽĐ°ĐœĐžŃ ĐșĐ»ĐžĐ”ĐœŃ‚ĐŸĐČ— ĐĄ ĐŽŃ€ŃƒĐłĐŸĐč ŃŃ‚ĐŸŃ€ĐŸĐœŃ‹, ĐșĐ»ĐžĐ”ĐœŃ‚Ń‹ ĐœĐ” ĐŒĐŸĐłŃƒŃ‚ ĐżŃ€ĐŸĐČДстО ĐœĐžĐșаĐșĐŸĐłĐŸ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ, ĐżĐŸŃ‚ĐŸĐŒŃƒ Ń‡Ń‚ĐŸ сДрĐČДр ĐșĐŸĐœŃ‚Ń€ĐŸĐ»ĐžŃ€ŃƒĐ”Ń‚ Юаты ĐČыпусĐșа. ĐĄŃƒŃ‰Đ”ŃŃ‚ĐČŃƒĐ”Ń‚ цДлыĐč ряЮ ĐŒĐ”Ń‚ĐŸĐŽĐŸĐČ, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐŒĐŸĐłŃƒŃ‚ ŃĐŒŃĐłŃ‡ĐžŃ‚ŃŒ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒŃƒ ĐșĐŸĐœŃ‚Ń€Đ°ĐșŃ‚ĐŸĐČ, ĐœĐ”ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” Оз ĐœĐžŃ… ĐżŃ€ĐŸŃŃ‚Ń‹, ĐŽŃ€ŃƒĐłĐžĐ” Đ±ĐŸĐ»Đ”Đ” Ń„ŃƒĐœĐșŃ†ĐžĐŸĐœĐ°Đ»ŃŒĐœŃ‹ Đž Ń‚Ń€Đ”Đ±ŃƒŃŽŃ‚ Đ±ĐŸĐ»Đ”Đ” ŃĐ»ĐŸĐ¶ĐœĐŸĐłĐŸ ĐŸĐ±ŃƒŃ‡Đ”ĐœĐžŃ. +Про ĐżŃ€ĐŸŃŃ‚ĐŸĐŒ ĐżĐŸĐŽŃ…ĐŸĐŽĐ”, API ĐżŃ€Đ”ĐŽĐŸŃŃ‚Đ°ĐČĐ»ŃĐ”Ń‚ŃŃ ĐČĐŒĐ”ŃŃ‚Đ” с npm-паĐșĐ”Ń‚ĐŸĐŒ с тОпОзацОДĐč (JSDoc, TypeScript). ĐŸĐŸŃ‚Ń€Đ”Đ±ĐžŃ‚Đ”Đ»Đž ĐŽĐ°ĐœĐœĐŸĐłĐŸ паĐșДта ĐŒĐŸĐłŃƒŃ‚ ĐżĐŸĐ»ŃƒŃ‡ĐžŃ‚ŃŒ Đ±ĐžĐ±Đ»ĐžĐŸŃ‚Đ”Đșу Đž ĐČĐŸŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČаться ĐżŃ€Đ”ĐžĐŒŃƒŃ‰Đ”ŃŃ‚ĐČĐ°ĐŒĐž аĐČŃ‚ĐŸĐŽĐŸĐżĐŸĐ»ĐœĐ”ĐœĐžŃ (IntelliSense) Đž ĐČĐ°Đ»ĐžĐŽĐ°Ń†ĐžŃ ĐČĐŸ ĐČŃ€Đ”ĐŒŃ Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚ĐșĐž. Đ‘ĐŸĐ»Đ”Đ” ŃĐ»ĐŸĐ¶ĐœŃ‹Đč ĐżĐŸĐŽŃ…ĐŸĐŽ ĐČĐșлючаДт ĐČ ŃĐ”Đ±Ń [PACT](https://docs.pact.io/), ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč был ŃĐŸĐ·ĐŽĐ°Đœ ĐŽĐ»Ń Ń„ĐŸŃ€ĐŒĐ°Đ»ĐžĐ·Đ°Ń†ĐžĐž ŃŃ‚ĐŸĐłĐŸ ĐżŃ€ĐŸŃ†Đ”ŃŃĐ° с ĐżĐŸĐŒĐŸŃ‰ŃŒŃŽ ĐŸŃ‡Đ”ĐœŃŒ Ń€Đ°Đ·Ń€ŃƒŃˆĐžŃ‚Đ”Đ»ŃŒĐœĐŸĐłĐŸ ĐżĐŸĐŽŃ…ĐŸĐŽĐ° - ĐœĐ” сДрĐČДр ĐŸĐżŃ€Đ”ĐŽĐ”Đ»ŃĐ”Ń‚ ĐżĐ»Đ°Đœ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ ĐŽĐ»Ń ŃĐ”Đ±Ń, а ĐșĐ»ĐžĐ”ĐœŃ‚ ĐŸĐżŃ€Đ”ĐŽĐ”Đ»ŃĐ”Ń‚ тДсты ĐŽĐ»Ń... сДрĐČДра! PACT ĐŒĐŸĐ¶Đ”Ń‚ запОсыĐČать ĐŸĐ¶ĐžĐŽĐ°ĐœĐžŃ ĐșĐ»ĐžĐ”ĐœŃ‚Đ° Đž ĐżĐŸĐŒĐ”Ń‰Đ°Ń‚ŃŒ ох ĐČ ĐŸĐ±Ń‰Đ”Đ” ĐŒĐ”ŃŃ‚ĐŸ, "broker", таĐș Ń‡Ń‚ĐŸ сДрĐČДр ĐŒĐŸĐ¶Đ”Ń‚ ОзĐČлДĐșать это ĐŸĐ¶ĐžĐŽĐ°ĐœĐžŃ Đž запусĐșать ĐœĐ° ĐșĐ°Đ¶ĐŽĐŸĐč ŃĐ±ĐŸŃ€ĐșĐ”, ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒŃ Đ±ĐžĐ±Đ»ĐžĐŸŃ‚Đ”Đșу PACT, Ń‡Ń‚ĐŸĐ±Ń‹ ĐŸĐ±ĐœĐ°Ń€ŃƒĐ¶ĐžŃ‚ŃŒ ĐœĐ°Ń€ŃƒŃˆĐ”ĐœĐœŃ‹Đ” ĐșĐŸĐœŃ‚Ń€Đ°Đșты - ĐŸĐ¶ĐžĐŽĐ°ĐœĐžŃ ĐșĐ»ĐžĐ”ĐœŃ‚Đ°, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐœĐ” ĐČŃ‹ĐżĐŸĐ»ĐœĐ”ĐœŃ‹. йаĐșĐžĐŒ ĐŸĐ±Ń€Đ°Đ·ĐŸĐŒ, ĐČсД ĐœĐ”ŃĐŸĐŸŃ‚ĐČДтстĐČоя API сДрĐČДра Đž ĐșĐ»ĐžĐ”ĐœŃ‚Đ° Đ±ŃƒĐŽŃƒŃ‚ ĐŸĐ±ĐœĐ°Ń€ŃƒĐ¶Đ”ĐœŃ‹ ĐœĐ° Ń€Đ°ĐœĐœĐžŃ… стаЮоях ŃĐ±ĐŸŃ€ĐșĐž Đž ĐŒĐŸĐłŃƒŃ‚ ŃƒĐ±Đ”Ń€Đ”Ń‡ŃŒ Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐșа ĐŸŃ‚ Đ±ĐŸĐ»ŃŒŃˆĐžŃ… ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒ. +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** ĐĐ»ŃŒŃ‚Đ”Ń€ĐœĐ°Ń‚ĐžĐČĐŸĐč яĐČĐ»ŃĐ”Ń‚ŃŃ Ń€ŃƒŃ‡ĐœĐŸĐ” Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” ОлО разĐČДртыĐČĐ°ĐœĐžĐ” + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: + +![](https://img.shields.io/badge/🔧%20Example%20using%20PACT-blue.svg "Examples with PACT") + +![alt text](assets/bp-14-testing-best-practices-contract-flow.png) + +
    + +

    + +## âšȘ  2.4 ĐąĐ”ŃŃ‚ĐžŃ€ŃƒĐčтД middlewares ĐžĐ·ĐŸĐ»ĐžŃ€ĐŸĐČĐ°ĐœĐœĐŸ + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** Đ‘ĐŸĐ»ŃŒŃˆĐžĐœŃŃ‚ĐČĐŸ Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐșĐŸĐČ ĐżŃ€Đ”ĐœĐ”Đ±Ń€Đ”ĐłĐ°ŃŽŃ‚ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ”ĐŒ Middleware, ĐżĐŸŃ‚ĐŸĐŒŃƒ Ń‡Ń‚ĐŸ ĐŸĐœĐž яĐČĐ»ŃŃŽŃ‚ŃŃ ĐœĐ”Đ±ĐŸĐ»ŃŒŃˆĐŸĐč частью ŃĐžŃŃ‚Đ”ĐŒŃ‹ Đž Ń‚Ń€Đ”Đ±ŃƒŃŽŃ‚ жОĐČĐŸĐłĐŸ сДрĐČДра Express. ОбД ĐżŃ€ĐžŃ‡ĐžĐœŃ‹ ĐŸŃˆĐžĐ±ĐŸŃ‡ĐœŃ‹, таĐș ĐșаĐș Middleware, ĐœĐ”ŃĐŒĐŸŃ‚Ń€Ń ĐœĐ° сĐČĐŸĐč Ń€Đ°Đ·ĐŒĐ”Ń€, ĐžĐŒĐ”ŃŽŃ‚ ĐŸĐłŃ€ĐŸĐŒĐœĐŸĐ” ĐČĐ»ĐžŃĐœĐžĐ” ĐœĐ° ĐČсД ОлО Đ±ĐŸĐ»ŃŒŃˆĐžĐœŃŃ‚ĐČĐŸ Đ·Đ°ĐżŃ€ĐŸŃĐŸĐČ Đž ĐŒĐŸĐłŃƒŃ‚ Đ±Ń‹Ń‚ŃŒ лДгĐșĐŸ ĐżŃ€ĐŸŃ‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœŃ‹, ĐșаĐș чОстыД Ń„ŃƒĐœĐșцоо. Đ”Đ»Ń Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ Ń„ŃƒĐœĐșцоо middleware ĐœŃƒĐ¶ĐœĐŸ ĐżŃ€ĐŸŃŃ‚ĐŸ ĐČызĐČать ДД Đž "ŃˆĐżĐžĐŸĐœĐžŃ‚ŃŒ" ([ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, с ĐżĐŸĐŒĐŸŃ‰ŃŒŃŽ Sinon](https://www.npmjs.com/package/sinon)) за ĐČĐ·Đ°ĐžĐŒĐŸĐŽĐ”ĐčстĐČĐžĐ”ĐŒ с ĐŸĐ±ŃŠĐ”ĐșŃ‚Đ°ĐŒĐž {req,res}, Ń‡Ń‚ĐŸĐ±Ń‹ ŃƒĐ±Đ”ĐŽĐžŃ‚ŃŒŃŃ, Ń‡Ń‚ĐŸ Ń„ŃƒĐœĐșцоя ĐŸŃ‚Ń€Đ°Đ±ĐŸŃ‚Đ°Đ»Đ° ĐșĐŸŃ€Ń€Đ”ĐșŃ‚ĐœĐŸ. Đ‘ĐžĐ±Đ»ĐžĐŸŃ‚Đ”Đșа [node-mock-http](https://www.npmjs.com/package/node-mocks-http) ОЎДт ДщД ĐŽĐ°Đ»ŃŒŃˆĐ” Đž Đ°ĐœĐ°Đ»ĐžĐ·ĐžŃ€ŃƒĐ”Ń‚ ĐŸĐ±ŃŠĐ”Đșты {req,res}, а таĐșжД слДЎОт за ох ĐżĐŸĐČĐ”ĐŽĐ”ĐœĐžĐ”ĐŒ. ĐĐ°ĐżŃ€ĐžĐŒĐ”Ń€, ĐŸĐœ ĐŒĐŸĐ¶Đ”Ń‚ утĐČĐ”Ń€Đ¶ĐŽĐ°Ń‚ŃŒ, ŃĐŸĐŸŃ‚ĐČДтстĐČŃƒĐ”Ń‚ лО статус http, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč был ŃƒŃŃ‚Đ°ĐœĐŸĐČĐ»Đ”Đœ ĐœĐ° ĐŸĐ±ŃŠĐ”ĐșтД res, ĐŸĐ¶ĐžĐŽĐ°Đ”ĐŒĐŸĐŒŃƒ (ŃĐŒ. ĐżŃ€ĐžĐŒĐ”Ń€ ĐœĐžĐ¶Đ”). +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** ĐžŃˆĐžĐ±Đșа ĐČ middleware Express === ĐŸŃˆĐžĐ±Đșа Đ±ĐŸĐ»ŃŒŃˆĐžĐœŃŃ‚ĐČа/ĐČсДх Đ·Đ°ĐżŃ€ĐŸŃĐŸĐČ + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: ĐąĐ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” middleware ĐžĐ·ĐŸĐ»ĐžŃ€ĐŸĐČĐ°ĐœĐœĐŸ бДз ĐČŃ‹ĐżĐŸĐ»ĐœĐ”ĐœĐžŃ сДтДĐČых Đ·Đ°ĐżŃ€ĐŸŃĐŸĐČ Đž ĐČĐșĐ»ŃŽŃ‡Đ”ĐœĐžŃ Express ĐżĐŸĐ»ĐœĐŸŃŃ‚ŃŒŃŽ + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Jest") + +```javascript +// middleware, ĐșĐŸŃ‚ĐŸŃ€ĐŸĐ” ĐŒŃ‹ Ń…ĐŸŃ‚ĐžĐŒ ĐżŃ€ĐŸŃ‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČать +const unitUnderTest = require("./middleware"); +const httpMocks = require("node-mocks-http"); +// ŃĐžĐœŃ‚Đ°ĐșсОс ĐČ Jest, раĐČĐœŃ‹Đč describe() Đž it() ĐČ Mocha +test("A request without authentication header, should return http status 403", () => { + const request = httpMocks.createRequest({ + method: "GET", + url: "/user/42", + headers: { + authentication: "", + }, + }); + const response = httpMocks.createResponse(); + unitUnderTest(request, response); + expect(response.statusCode).toBe(403); +}); +``` + +
    + +

    + +## âšȘ 2.5 Đ˜Đ·ĐŒĐ”Ń€Đ”ĐœĐžĐ” Đž рДфаĐșŃ‚ĐŸŃ€ĐžĐœĐł с ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°ĐœĐžĐ”ĐŒ ĐžĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚ĐŸĐČ ŃŃ‚Đ°Ń‚ĐžŃŃ‚ĐžŃ‡Đ”ŃĐșĐŸĐłĐŸ Đ°ĐœĐ°Đ»ĐžĐ·Đ° + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** Đ˜ŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°ĐœĐžĐ” ĐžĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚ĐŸĐČ ŃŃ‚Đ°Ń‚ĐžŃ‡Đ”ŃĐșĐŸĐłĐŸ Đ°ĐœĐ°Đ»ĐžĐ·Đ° ĐżĐŸĐŒĐŸĐłĐ°Đ”Ń‚ ĐŸĐ±ŃŠĐ”ĐșтоĐČĐœĐŸ ŃƒĐ»ŃƒŃ‡ŃˆĐžŃ‚ŃŒ ĐșачДстĐČĐŸ ĐșĐŸĐŽĐ° Đž ŃĐŸŃ…Ń€Đ°ĐœĐžŃ‚ŃŒ Đ”ĐłĐŸ Ń€Đ°Đ±ĐŸŃ‚ĐŸŃĐżĐŸŃĐŸĐ±ĐœĐŸŃŃ‚ŃŒ. Вы ĐŒĐŸĐ¶Đ”Ń‚Đ” ĐŽĐŸĐ±Đ°ĐČоть ĐžĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Ń‹ статОстОчДсĐșĐŸĐłĐŸ Đ°ĐœĐ°Đ»ĐžĐ·Đ° ĐČ ŃĐ±ĐŸŃ€Đșу CI, Ń‡Ń‚ĐŸĐ±Ń‹ ĐŸŃ‚ŃĐ»Đ”ĐŽĐžŃ‚ŃŒ "запахО" ĐČĐ°ŃˆĐ”ĐłĐŸ ĐșĐŸĐŽĐ° ĐœĐ° ŃŃ‚Đ°ĐżĐ” ŃĐ±ĐŸŃ€ĐșĐž. ĐžŃĐœĐŸĐČĐœŃ‹ĐŒĐž ĐżŃ€Đ”ĐžĐŒŃƒŃ‰Đ”ŃŃ‚ĐČĐ°ĐŒĐž статОчДсĐșĐŸĐłĐŸ Đ°ĐœĐ°Đ»ĐžĐ·Đ° пДрДЎ ĐŸĐ±Ń‹Ń‡ĐœŃ‹ĐŒ Đ»ĐžĐœŃ‚ĐžĐœĐłĐŸĐŒ яĐČĐ»ŃŃŽŃ‚ŃŃ ĐČĐŸĐ·ĐŒĐŸĐ¶ĐœĐŸŃŃ‚ŃŒ ĐżŃ€ĐŸĐČДрĐșĐž ĐșачДстĐČа ĐČ ĐșĐŸĐœŃ‚Đ”ĐșстД ĐœĐ”ŃĐșĐŸĐ»ŃŒĐșох фаĐčĐ»ĐŸĐČ (ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, ĐŸĐ±ĐœĐ°Ń€ŃƒĐ¶Đ”ĐœĐžĐ” ЎублОĐșĐ°Ń‚ĐŸĐČ), ĐČŃ‹ĐżĐŸĐ»ĐœĐ”ĐœĐžĐ” Ń€Đ°ŃŃˆĐžŃ€Đ”ĐœĐœĐŸĐłĐŸ Đ°ĐœĐ°Đ»ĐžĐ·Đ° (ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, ŃĐ»ĐŸĐ¶ĐœĐŸŃŃ‚Đž ĐșĐŸĐŽĐ°) Đž ĐŸŃ‚ŃĐ»Đ”Đ¶ĐžĐČĐ°ĐœĐžĐ” ĐžŃŃ‚ĐŸŃ€ĐžĐž Đž ĐżŃ€ĐŸĐłŃ€Đ”ŃŃĐ° ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒ ĐșĐŸĐŽĐ°. Вы ĐŒĐŸĐ¶Đ”Ń‚Đ” ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČать ŃĐ»Đ”ĐŽŃƒŃŽŃ‰ĐžĐ” сДрĐČосы: [SonarQube](https://www.sonarqube.org/) (4,900+ [stars](https://github.com/SonarSource/sonarqube)) Đž [Code Climate](https://codeclimate.com/) (2,000+ [stars](https://github.com/codeclimate/codeclimate)) + +Đ‘Đ»Đ°ĐłĐŸĐŽĐ°Ń€ĐœĐŸŃŃ‚ŃŒ: [Keith Holliday](https://github.com/TheHollidayInn) + +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** Про ĐœĐžĐ·ĐșĐŸĐŒ ĐșачДстĐČĐ” ĐșĐŸĐŽĐ° ĐŸŃˆĐžĐ±ĐșĐž Đž ĐżŃ€ĐŸĐžĐ·ĐČĐŸĐŽĐžŃ‚Đ”Đ»ŃŒĐœĐŸŃŃ‚ŃŒ ĐČсДгЎа Đ±ŃƒĐŽŃƒŃ‚ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐŸĐč, ĐșĐŸŃ‚ĐŸŃ€ŃƒŃŽ ĐœĐ” ŃĐŒĐŸĐłŃƒŃ‚ оспраĐČоть ĐœĐž ĐœĐŸĐČыД Đ±ĐžĐ±Đ»ĐžĐŸŃ‚Đ”ĐșĐž, ĐœĐž ŃĐŸĐČŃ€Đ”ĐŒĐ”ĐœĐœŃ‹Đč Ń„ŃƒĐœĐșŃ†ĐžĐŸĐœĐ°Đ». + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: CodeClimate - ŃŃ‚ĐŸ ĐžĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč ĐżŃ€Đ”ĐŽĐŸŃŃ‚Đ°ĐČĐ»ŃĐ”Ń‚ ĐșĐŸĐŒĐżĐ»Đ”ĐșŃĐœŃ‹Đ” ĐŒĐ”Ń‚ĐŸĐŽŃ‹ Đ°ĐœĐ°Đ»ĐžĐ·Đ° + +![](https://img.shields.io/badge/🔧%20Example%20using%20Code%20Climate-blue.svg "Examples with CodeClimate") + +![alt text](assets/bp-16-yoni-goldberg-quality.png "CodeClimate, a commercial tool that can identify complex methods:") + +
    + +

    + +## âšȘ  2.6 Check your readiness for Node-related chaos + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** ĐĄŃ‚Ń€Đ°ĐœĐœĐŸ, ĐœĐŸ Đ±ĐŸĐ»ŃŒŃˆĐžĐœŃŃ‚ĐČĐŸ Ń‚Đ”ŃŃ‚ĐŸĐČ ĐœĐ°ĐżŃ€Đ°ĐČĐ»Đ”ĐœĐŸ ĐœĐ° ĐżŃ€ĐŸĐČДрĐșу Đ»ĐŸĐłĐžĐșĐž Đž ĐŽĐ°ĐœĐœŃ‹Ń…, ĐČ Ń‚ĐŸ ĐČŃ€Đ”ĐŒŃ ĐșаĐș ĐžĐœĐŸĐłĐŽĐ° ĐœĐ°ĐžĐ±ĐŸĐ»ŃŒŃˆĐ”Đč ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐŸĐč (Đž ŃŃ‚ĐŸ праĐČЎа Ń‚Ń€ŃƒĐŽĐœĐŸ оспраĐČоть) ĐŒĐŸĐ¶Đ”Ń‚ стать ĐČĐŸĐżŃ€ĐŸŃŃ‹, ĐșĐ°ŃĐ°ŃŽŃ‰ĐžĐ”ŃŃ ĐžĐœŃ„Ń€Đ°ŃŃ‚Ń€ŃƒĐșтуры. ĐĐ°ĐżŃ€ĐžĐŒĐ”Ń€, ĐČы ĐșĐŸĐłĐŽĐ°-ĐœĐžĐ±ŃƒĐŽŃŒ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČалО ĐżĐ”Ń€Đ”ĐłŃ€ŃƒĐ·Đșу ĐżĐ°ĐŒŃŃ‚Đž, ĐżĐ°ĐŽĐ”ĐœĐžĐ” сДрĐČДра? Ваша ŃĐžŃŃ‚Đ”ĐŒĐ° ĐŸŃ‚ŃĐ»Đ”Đ¶ĐžĐČĐ°ĐœĐžŃ ĐżĐŸĐœĐžĐŒĐ°Đ”Ń‚, ĐșĐŸĐłĐŽĐ° ĐČаш API ŃŃ‚Đ°ĐœĐŸĐČотся ĐČ ĐżĐŸĐ»ĐŸĐČĐžĐœŃƒ ĐŒĐ”ĐŽĐ»Đ”ĐœĐœĐ”Đ”? Đ”Đ»Ń Ń‚ĐŸĐłĐŸ, Ń‡Ń‚ĐŸĐ±Ń‹ ĐżŃ€ĐŸŃ‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČать Đž оспраĐČоть таĐșОД сотуацоо Netflix ĐżŃ€ĐžĐŽŃƒĐŒĐ°Đ»Đž [Chaos engineering](https://principlesofchaos.org/). Đ•ĐłĐŸ Ń†Đ”Đ»ŃŒŃŽ яĐČĐ»ŃĐ”Ń‚ŃŃ ĐŸŃĐČĐ”ĐŽĐŸĐŒĐ»Đ”ĐœĐœĐŸŃŃ‚ŃŒ ĐŽĐ»Ń Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ ŃƒŃŃ‚ĐŸĐčчоĐČĐŸŃŃ‚Đž ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ Đș chaos-ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐ°ĐŒ, а таĐșжД Ń€Đ°Đ·Đ»ĐžŃ‡ĐœŃ‹Đ” фрДĐčĐŒĐČĐŸŃ€ĐșĐž, Ń‡Ń‚ĐŸĐ±Ń‹ ŃŃ‚ĐŸ ĐŒĐŸĐ¶ĐœĐŸ Đ±Ń‹Đ»ĐŸ ĐżŃ€ĐŸŃ‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČать. ĐĐ°ĐżŃ€ĐžĐŒĐ”Ń€, ĐŸĐŽĐžĐœ Оз Đ”ĐłĐŸ ОзĐČĐ”ŃŃ‚ĐœŃ‹Ń… ĐžĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚ĐŸĐČ, [the chaos monkey](https://github.com/Netflix/chaosmonkey), ŃĐ»ŃƒŃ‡Đ°ĐčĐœŃ‹ĐŒ ĐŸĐ±Ń€Đ°Đ·ĐŸĐŒ ĐșлаЎДт сДрĐČДры, Ń‡Ń‚ĐŸĐ±Ń‹ ŃƒĐ±Đ”ĐŽĐžŃ‚ŃŒŃŃ, Ń‡Ń‚ĐŸ ĐœĐ°Ńˆ сДрĐČОс ĐČсД ДщД ĐŒĐŸĐ¶Đ”Ń‚ ĐŸĐ±ŃĐ»ŃƒĐ¶ĐžĐČать ĐżĐŸĐ»ŃŒĐ·ĐŸĐČатДлДĐč Đž ĐœĐ” ĐżĐŸĐ»Đ°ĐłĐ°Đ”Ń‚ŃŃ ĐœĐ° ĐŸĐŽĐžĐœ сДрĐČДр (Đ”ŃŃ‚ŃŒ таĐșжД ĐČĐ”Ń€ŃĐžŃ ĐŽĐ»Ń Kubernetes, [kube-monkey](https://github.com/asobti/kube-monkey)). ВсД это ĐžĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Ń‹ Ń€Đ°Đ±ĐŸŃ‚Đ°ŃŽŃ‚ ĐœĐ° ŃƒŃ€ĐŸĐČĐœĐ” Ń…ĐŸŃŃ‚ĐžĐœĐłĐ° ОлО ĐżĐ»Đ°Ń‚Ń„ĐŸŃ€ĐŒŃ‹, ĐœĐŸ Ń‡Ń‚ĐŸ, ДслО ĐČы Ń…ĐŸŃ‚ĐžŃ‚Đ” ĐżŃ€ĐŸŃ‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČать чОстДĐčшОĐč Node-Ń…Đ°ĐŸŃ. ĐĐ°ĐżŃ€ĐžĐŒĐ”Ń€, ĐČы Ń…ĐŸŃ‚ĐžŃ‚Đ” ĐżŃ€ĐŸĐČĐ”Ń€ĐžŃ‚ŃŒ, ĐșаĐș ĐČаш Node спраĐČĐ»ŃĐ”Ń‚ŃŃ с ĐŸŃˆĐžĐ±ĐșĐ°ĐŒĐž, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” былО ĐœĐ” ĐżĐŸĐčĐŒĐ°ĐœŃ‹, ĐŸŃˆĐžĐ±ĐșĐ°ĐŒĐž ĐżŃ€ĐŸĐŒĐžŃĐŸĐČ, ĐżĐ”Ń€Đ”ĐłŃ€ŃƒĐ·ĐșĐŸĐč v8 про ĐŒĐ°ĐșŃĐžĐŒĐ°Đ»ŃŒĐœĐŸ ĐŽĐŸĐżŃƒŃŃ‚ĐžĐŒŃ‹Ń… 1.7GB. ВЮруг ĐČы Ń…ĐŸŃ‚ĐžŃ‚Đ” ĐżŃ€ĐŸĐČĐ”Ń€ĐžŃ‚ŃŒ, ĐŸŃŃ‚Đ°Đ”Ń‚ŃŃ лО ĐČаш UX ŃƒĐŽĐŸĐČлДтĐČĐŸŃ€ĐžŃ‚Đ”Đ»ŃŒĐœŃ‹Ń… про частых Đ±Đ»ĐŸĐșĐžŃ€ĐŸĐČĐșах цоĐșла ŃĐŸĐ±Ń‹Ń‚ĐžĐč? Đ”Đ»Ń Ń€Đ”ŃˆĐ”ĐœĐžŃ ĐŽĐ°ĐœĐœĐŸĐč ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒŃ‹ я ĐœĐ°ĐżĐžŃĐ°Đ» [node-chaos](https://github.com/i0natan/node-chaos-monkey) (alpha), ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč ĐłĐ”ĐœĐ”Ń€ĐžŃ€ŃƒĐ”Ń‚ ĐČсД ĐČĐŸĐ·ĐŒĐŸĐ¶ĐœŃ‹Đ” ĐČоЮы Ń…Đ°ĐŸŃĐ°, сĐČŃĐ·Đ°ĐœĐœŃ‹Ń… с Node. +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** От ŃŃ‚ĐŸĐłĐŸ ĐœĐ” спрятаться. ЗаĐșĐŸĐœ ĐœĐ”Ń€Ń„Đž Đ±ŃƒĐŽĐ”Ń‚ ĐżŃ€Đ”ŃĐ»Đ”ĐŽĐŸĐČать ĐČас ĐČДзЎД. + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: Đ“Đ”ĐœĐ”Ń€Đ°Ń†ĐžŃ ĐČсДĐČĐŸĐ·ĐŒĐŸĐ¶ĐœŃ‹Ń… ĐČĐ°Ń€ĐžĐ°ĐœŃ‚ĐŸĐČ Node-Ń…Đ°ĐŸŃĐ° ĐŽĐ»Ń ĐżŃ€ĐŸĐČДрĐșĐž ŃƒŃŃ‚ĐŸĐčчоĐČĐŸŃŃ‚Đž ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ + +![alt text](assets/bp-17-yoni-goldberg-chaos-monkey-nodejs.png "Node-chaos can generate all sort of Node.js pranks so you can test how resilience is your app to chaos") + +
    + +
    + +## âšȘ 2.7 ИзбДгаĐčтД ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°ĐœĐžŃ ĐłĐ»ĐŸĐ±Đ°Đ»ŃŒĐœŃ‹Ń… фоĐșстур Đž seeds. Đ”Đ»Ń ĐșĐ°Đ¶ĐŽĐŸĐłĐŸ тДста ĐŽĐŸĐ»Đ¶ĐœŃ‹ Đ±Ń‹Ń‚ŃŒ сĐČĐŸĐž ŃĐŸĐ±ŃŃ‚ĐČĐ”ĐœĐœŃ‹Đ” ĐŽĐ°ĐœĐœŃ‹Đ” + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** ĐĄĐŸĐłĐ»Đ°ŃĐœĐŸ Đ·ĐŸĐ»ĐŸŃ‚ĐŸĐŒŃƒ праĐČОлу (ĐżŃƒĐœĐșт 0), ĐșажЎыĐč тДст ĐŽĐŸĐ»Đ¶Đ”Đœ ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČать сĐČĐŸĐč ŃĐŸĐ±ŃŃ‚ĐČĐ”ĐœĐœŃ‹Đč ĐœĐ°Đ±ĐŸŃ€ ŃŃ‚Ń€ĐŸĐș ĐČ Đ‘Đ”, Ń‡Ń‚ĐŸĐ±Ń‹ ĐžĐ·Đ±Đ”Đ¶Đ°Ń‚ŃŒ пДрДĐșрытоя ĐŽĐ°ĐœĐœŃ‹Ń…. На ЎДлД жД, ĐŽĐ°ĐœĐœĐŸĐ” праĐČĐžĐ»ĐŸ Ń‡Đ°ŃŃ‚ĐŸ ĐœĐ°Ń€ŃƒŃˆĐ°Đ”Ń‚ŃŃ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČщоĐșĐ°ĐŒĐž, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” Đ·Đ°ĐłŃ€ŃƒĐ¶Đ°ŃŽŃ‚ ĐŽĐ°ĐœĐœŃ‹Đ” ĐČ Đ‘Đ” пДрДЎ запусĐșĐŸĐŒ Ń‚Đ”ŃŃ‚ĐŸĐČ (фоĐșстуры), Ń‡Ń‚ĐŸĐ±Ń‹ ŃƒĐ»ŃƒŃ‡ŃˆĐžŃ‚ŃŒ ĐżŃ€ĐŸĐžĐ·ĐČĐŸĐŽĐžŃ‚Đ”Đ»ŃŒĐœĐŸŃŃ‚ŃŒ. Đ„ĐŸŃ‚Ń ĐżŃ€ĐŸĐžĐ·ĐČĐŸĐŽĐžŃ‚Đ”Đ»ŃŒĐœĐŸŃŃ‚ŃŒ яĐČĐ»ŃĐ”Ń‚ŃŃ Ń€Đ”Đ°Đ»ŃŒĐœĐŸĐč ĐżŃ€ĐžŃ‡ĐžĐœĐŸĐč ĐŽĐ»Ń Đ±Đ”ŃĐżĐŸĐșĐŸĐčстĐČа - ĐŸĐœĐ° ЎалДĐșĐŸ ĐœĐ” глаĐČĐœĐ°Ń (ŃĐŒ. ĐżŃƒĐœĐșт "ĐšĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ĐœĐŸĐ” Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ”"). ĐĄĐ»ĐŸĐ¶ĐœĐŸŃŃ‚ŃŒ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ - ĐłĐŸŃ€Đ°Đ·ĐŽĐŸ Đ±ĐŸĐ»Đ”Đ” Đ±ĐŸĐ»Đ”Đ·ĐœĐ”ĐœĐœĐ°Ń ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐ°, Ń€Đ”ŃˆĐ”ĐœĐžĐ” ĐșĐŸŃ‚ĐŸŃ€ĐŸĐč ĐČ Đ±ĐŸĐ»ŃŒŃˆĐžĐœŃŃ‚ĐČĐ” ŃĐ»ŃƒŃ‡Đ°Đ”ĐČ ĐŸĐżŃ€Đ”ĐŽĐ”Đ»ŃĐ”Ń‚ŃŃ ĐŽŃ€ŃƒĐłĐžĐŒĐž ŃĐŸĐŸĐ±Ń€Đ°Đ¶Đ”ĐœĐžŃĐŒĐž. На праĐșтоĐșĐ”, ĐœŃƒĐ¶ĐœĐŸ ŃĐŽĐ”Đ»Đ°Ń‚ŃŒ таĐș, Ń‡Ń‚ĐŸĐ±Ń‹ ĐșажЎыĐč тДст яĐČĐœŃ‹ĐŒ ĐŸĐ±Ń€Đ°Đ·ĐŸĐŒ ĐŽĐŸĐ±Đ°ĐČĐ»ŃĐ”Ń‚ ĐœŃƒĐ¶ĐœŃ‹Đ” Đ”ĐŒŃƒ запОсО ĐČ Đ‘Đ” Đž Ń€Đ°Đ±ĐŸŃ‚Đ°Đ» Ń‚ĐŸĐ»ŃŒĐșĐŸ с ĐœĐžĐŒĐž. ЕслО ĐżŃ€ĐŸĐžĐ·ĐČĐŸĐŽĐžŃ‚Đ”Đ»ŃŒĐœĐŸŃŃ‚ŃŒ ŃŃ‚Đ°ĐœĐŸĐČотся ĐșрОтОчДсĐșĐŸĐč ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐŸĐč - ĐșĐŸĐŒĐżŃ€ĐŸĐŒĐžŃŃĐŸĐŒ ĐŒĐŸĐ¶Đ”Ń‚ Đ±Ń‹Ń‚ŃŒ Ń€Đ”Đ°Đ»ĐžĐ·Đ°Ń†ĐžŃ Ń‚Đ”ŃŃ‚ĐŸĐČ, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐœĐ” ĐžĐ·ĐŒĐ”ĐœŃŃŽŃ‚ ĐŽĐ°ĐœĐœŃ‹Đ” (ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, Đ·Đ°ĐżŃ€ĐŸŃŃ‹). +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** КаĐșОД-Ń‚ĐŸ тДсты ĐœĐ” ŃŃ€Đ°Đ±ĐŸŃ‚Đ°Đ»Đž, разĐČДртыĐČĐ°ĐœĐžĐ” ĐżŃ€ĐŸĐ”Đșта прДрĐČĐ°ĐœĐŸ Đž Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐșĐž тратят ĐČŃ€Đ”ĐŒŃ ĐœĐ° ĐČŃ‹ŃŃĐœĐ”ĐœĐžĐ” ĐŸŃˆĐžĐ±ĐșĐž. А Đ”ŃŃ‚ŃŒ лО ĐŸĐœĐ°? ĐšĐ°Đ¶Đ”Ń‚ŃŃ, Ń‡Ń‚ĐŸ ĐœĐ”Ń‚, ĐČĐ”ĐŽŃŒ ĐŽĐČа тДста ĐżŃ€ĐŸŃŃ‚ĐŸ ĐžĐ·ĐŒĐ”ĐœĐžĐ»Đž ĐŸĐŽĐœĐž Đž тД жД seed-ĐŽĐ°ĐœĐœŃ‹Đ”. + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :thumbsdown: ĐĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœĐŸ: тДсты ĐœĐ” яĐČĐ»ŃŃŽŃ‚ŃŃ ĐœĐ”Đ·Đ°ĐČĐžŃĐžĐŒŃ‹ĐŒĐž Đž ĐżĐŸĐ»Đ°ĐłĐ°ŃŽŃ‚ŃŃ ĐœĐ° ĐœĐ”ĐșĐžĐč ĐłĐ»ĐŸĐ±Đ°Đ»ŃŒĐœŃ‹Đč хуĐș ĐŽĐ»Ń ĐżĐŸĐ»ŃƒŃ‡Đ”ĐœĐžŃ ĐłĐ»ĐŸĐ±Đ°Đ»ŃŒĐœŃ‹Ń… ĐŽĐ°ĐœĐœŃ‹Ń… + +![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "Examples with Mocha") + +```javascript +before(async () => { + // ĐŽĐŸĐ±Đ°ĐČĐ»Đ”ĐœĐžĐ” ĐŽĐ°ĐœĐœŃ‹Ń… ĐŸ саĐčтах Đž Đ°ĐŽĐŒĐžĐœĐžŃŃ‚Ń€Đ°Ń‚ĐŸŃ€Đ°Ń… ĐČ Đ‘Đ”. ГЎД ĐŸĐœĐž сДĐčчас ĐœĐ°Ń…ĐŸĐŽŃŃ‚ŃŃ? ĐĄĐœĐ°Ń€ŃƒĐ¶Đž, ĐČĐŸ ĐČĐœĐ”ŃˆĐœĐ”ĐŒ JSON ОлО фрДĐčĐŒĐČĐŸŃ€ĐșĐ” + await DB.AddSeedDataFromJson("seed.json"); +}); +it("When updating site name, get successful confirmation", async () => { + // я Đ·ĐœĐ°ŃŽ, Ń‡Ń‚ĐŸ ĐœĐ°Đ·ĐČĐ°ĐœĐžĐ” саĐčта "portal" ŃŃƒŃ‰Đ”ŃŃ‚ĐČŃƒĐ”Ń‚ - я ĐČОЎДл Đ”ĐłĐŸ ĐČ seed-фаĐčлах + const siteToUpdate = await SiteService.getSiteByName("Portal"); + const updateNameResult = await SiteService.changeName( + siteToUpdate, + "newName" + ); + expect(updateNameResult).to.be(true); +}); +it("When querying by site name, get the right site", async () => { + // я Đ·ĐœĐ°ŃŽ, Ń‡Ń‚ĐŸ ĐœĐ°Đ·ĐČĐ°ĐœĐžĐ” саĐčта "portal" ŃŃƒŃ‰Đ”ŃŃ‚ĐČŃƒĐ”Ń‚ - я ĐČОЎДл Đ”ĐłĐŸ ĐČ seed-фаĐčлах + const siteToCheck = await SiteService.getSiteByName("Portal"); + expect(siteToCheck.name).to.be.equal("Portal"); // ĐĐ”ŃƒĐŽĐ°Ń‡Đ°! ĐŸŃ€Đ”ĐŽŃ‹ĐŽŃƒŃ‰ĐžĐč тДст ĐŒĐ”ĐœŃĐ”Ń‚ ĐœĐ°Đ·ĐČĐ°ĐœĐžĐ” :[ +}); +``` + +
    + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: КажЎыĐč тДст ĐŽĐ”ĐčстĐČŃƒĐ”Ń‚ ĐœĐ° сĐČĐŸĐ”ĐŒ ŃĐŸĐ±ŃŃ‚ĐČĐ”ĐœĐœĐŸĐŒ ĐœĐ°Đ±ĐŸŃ€Đ” ĐŽĐ°ĐœĐœŃ‹Ń…, Ń‡Ń‚ĐŸ ĐżĐŸĐ·ĐČĐŸĐ»ŃĐ”Ń‚ ĐŸŃŃ‚Đ°ĐČаться ĐČ Ń€Đ°ĐŒĐșах тДста + +```javascript +it("When updating site name, get successful confirmation", async () => { + // тДст ĐŽĐŸĐ±Đ°ĐČĐ»ŃĐ”Ń‚ ĐœĐŸĐČыД запОсО Đž Ń€Đ°Đ±ĐŸŃ‚Đ°Đ”Ń‚ Ń‚ĐŸĐ»ŃŒĐșĐŸ с ĐœĐžĐŒĐž + const siteUnderTest = await SiteService.addSite({ + name: "siteForUpdateTest", + }); + const updateNameResult = await SiteService.changeName( + siteUnderTest, + "newName" + ); + expect(updateNameResult).to.be(true); +}); +``` + +
    + +
    + +## âšȘ 2.8 ВыбДрОтД чДтĐșую стратДгОю ĐŸŃ‡ĐžŃŃ‚ĐșĐž ĐŽĐ°ĐœĐœŃ‹Ń…: After-all (рДĐșĐŸĐŒĐ”ĐœĐŽŃƒĐ”Ń‚ŃŃ) ОлО after-each + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** Đ’Ń€Đ”ĐŒŃ, ĐșĐŸĐłĐŽĐ° тДсты ĐœĐ°Ń‡ĐžĐœĐ°ŃŽŃ‚ ŃƒĐŽĐ°Đ»Đ”ĐœĐžĐ” ĐŽĐ°ĐœĐœŃ‹Ń…, ĐŸĐżŃ€Đ”ĐŽĐ”Đ»ŃĐ”Ń‚ ŃĐżĐŸŃĐŸĐ± ох ĐœĐ°ĐżĐžŃĐ°ĐœĐžŃ. ĐĐ°ĐžĐ±ĐŸĐ»Đ”Đ” Đ¶ĐžĐ·ĐœĐ”ŃĐżĐŸŃĐŸĐ±ĐœŃ‹Đ” ĐČĐ°Ń€ĐžĐ°ĐœŃ‚Ń‹: after-all Đž after-each. Про ĐČŃ‹Đ±ĐŸŃ€Đ” ĐČŃ‚ĐŸŃ€ĐŸĐłĐŸ ĐČĐ°Ń€ĐžĐ°ĐœŃ‚Đ°, ŃƒĐŽĐ°Đ»Đ”ĐœĐžĐ” ĐŽĐ°ĐœĐœŃ‹Ń… ĐłĐ°Ń€Đ°ĐœŃ‚ĐžŃ€ŃƒĐ”Ń‚ ĐżĐŸĐ»ĐœŃƒŃŽ ĐŸŃ‡ĐžŃŃ‚Đșу Đž ŃĐŸĐ·ĐŽĐ°Đ”Ń‚ ĐżŃ€Đ”ĐžĐŒŃƒŃ‰Đ”ŃŃ‚ĐČа ĐŽĐ»Ń Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐșа. В ĐœĐ°Ń‡Đ°Đ»Đ” тДста ĐœĐ” ŃŃƒŃ‰Đ”ŃŃ‚ĐČŃƒĐ”Ń‚ Юругох запОсДĐč. ĐœĐŸĐ¶ĐœĐŸ Đ±Ń‹Ń‚ŃŒ уĐČĐ”Ń€Đ”ĐœĐœŃ‹ĐŒ, ĐșаĐșОД ĐŽĐ°ĐœĐœŃ‹Đ” Đ·Đ°ĐżŃ€Đ°ŃˆĐžĐČаются. Đ˜ĐœĐŸĐłĐŽĐ° ЎажД ĐČĐŸĐ·ĐœĐžĐșаДт ŃĐŸĐ±Đ»Đ°Đ·Đœ ĐżĐŸŃŃ‡ĐžŃ‚Đ°Ń‚ŃŒ ŃŃ‚Ń€ĐŸĐșĐž про ĐżŃ€ĐŸĐČДрĐșĐ” assertions. ĐžĐŽĐœĐ°ĐșĐŸ, ŃŃƒŃ‰Đ”ŃŃ‚ĐČуют Đž ŃĐ”Ń€ŃŒĐ”Đ·ĐœŃ‹Đ” ĐœĐ”ĐŽĐŸŃŃ‚Đ°Ń‚ĐșĐž. Про Ń€Đ°Đ±ĐŸŃ‚Đ” ĐČ ĐŒŃƒĐ»ŃŒŃ‚Đž-ĐżŃ€ĐŸŃ†Đ”ŃŃĐœĐŸĐŒ Ń€Đ”Đ¶ĐžĐŒĐ” тДсты ĐŒĐŸĐłŃƒŃ‚ ĐŒĐ”ŃˆĐ°Ń‚ŃŒ Юруг Юругу. ĐŸĐŸĐșа ĐżŃ€ĐŸŃ†Đ”ŃŃ-1 ĐŸŃ‡ĐžŃ‰Đ°Đ”Ń‚ таблОцы, ĐżŃ€ĐŸŃ†Đ”ŃŃ-2 ĐČ Ń‚ĐŸŃ‚ жД ĐŒĐŸĐŒĐ”ĐœŃ‚ Đ·Đ°ĐżŃ€Đ°ŃˆĐžĐČаДт ĐŽĐ°ĐœĐœŃ‹Đ” Đž паЎаДт (ĐżĐŸŃ‚ĐŸĐŒŃƒ Ń‡Ń‚ĐŸ БД была ĐČĐœĐ”Đ·Đ°ĐżĐœĐŸ ŃƒĐŽĐ°Đ»Đ”ĐœĐ° ĐżŃ€ĐŸŃ†Đ”ŃŃĐŸĐŒ-1). ĐšŃ€ĐŸĐŒĐ” Ń‚ĐŸĐłĐŸ, ŃĐ»ĐŸĐ¶ĐœĐ”Đ” оспраĐČоть ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒŃ‹ ĐČ ĐœĐ”ŃƒĐŽĐ°Ń‡ĐœŃ‹Ń… тДстах - про ĐżĐŸŃĐ”Ń‰Đ”ĐœĐžĐž БД ĐœĐ” Đ±ŃƒĐŽĐ”Ń‚ ĐŸĐ±ĐœĐ°Ń€ŃƒĐ¶Đ”ĐœĐŸ ĐœĐžĐșаĐșох запОсДĐč. + +Đ’Ń‚ĐŸŃ€ĐŸĐč ĐČĐ°Ń€ĐžĐ°ĐœŃ‚ - ŃŃ‚ĐŸ after-all - ŃƒĐŽĐ°Đ»Đ”ĐœĐžĐ” ĐŽĐ°ĐœĐœŃ‹Ń… ĐżĐŸŃĐ»Đ” заĐČĐ”Ń€ŃˆĐ”ĐœĐžŃ ĐČсДх Ń‚Đ”ŃŃ‚ĐŸĐČ (ОлО ЎажД Đ”Đ¶Đ”ĐŽĐœĐ”ĐČĐœĐŸ!). йаĐșĐŸĐč ĐżĐŸĐŽŃ…ĐŸĐŽ ĐŸĐ·ĐœĐ°Ń‡Đ°Đ”Ń‚, Ń‡Ń‚ĐŸ ĐŸĐŽĐœĐ° Đž та жД БД с ŃŃƒŃ‰Đ”ŃŃ‚ĐČŃƒŃŽŃ‰ĐžĐŒĐž Đ·Đ°ĐżĐžŃŃĐŒĐž ŃĐ»ŃƒĐ¶ĐžŃ‚ ĐŽĐ»Ń ĐČсДх ĐżŃ€ĐŸŃ†Đ”ŃŃĐŸĐČ Đž Ń‚Đ”ŃŃ‚ĐŸĐČ. Đ§Ń‚ĐŸĐ±Ń‹ ĐœĐ” ĐœĐ°ŃŃ‚ŃƒĐżĐ°Ń‚ŃŒ Юруг Юругу ĐœĐ° пятĐșĐž, тДсты ĐŽĐŸĐ»Đ¶ĐœŃ‹ Ń€Đ°Đ±ĐŸŃ‚Đ°Ń‚ŃŒ с ĐșĐŸĐœĐșŃ€Đ”Ń‚ĐœŃ‹ĐŒĐž Đ·Đ°ĐżĐžŃŃĐŒĐž, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐŸĐœĐž ĐŽĐŸĐ±Đ°ĐČОлО. ĐŃƒĐ¶ĐŽĐ°Đ”Ń‚Đ”ŃŃŒ ĐČ ĐżŃ€ĐŸĐČДрĐșĐ”, ĐșаĐșая Đ·Đ°ĐżĐžŃŃŒ была ĐŽĐŸĐ±Đ°ĐČĐ»Đ”ĐœĐ°? ĐŸŃ€Đ”ĐŽĐżĐŸĐ»ĐŸĐ¶ĐžŃ‚Đ”, Ń‡Ń‚ĐŸ Đ”ŃŃ‚ŃŒ ДщД тысячо запОсДĐč, Đž сЎДлаĐčтД Đ·Đ°ĐżŃ€ĐŸŃ Đș запОсО, ĐșĐŸŃ‚ĐŸŃ€Đ°Ń была ĐŽĐŸĐ±Đ°ĐČĐ»Đ”ĐœĐ° яĐČĐœĐŸ. ĐŃƒĐ¶ĐœĐŸ ĐżŃ€ĐŸĐČĐ”Ń€ĐžŃ‚ŃŒ, Ń‡Ń‚ĐŸ Đ·Đ°ĐżĐžŃŃŒ ŃƒĐŽĐ°Đ»Đ”ĐœĐ°? ĐĐ”Đ»ŃŒĐ·Ń ĐżŃ€Đ”ĐŽĐżĐŸĐ»Đ°ĐłĐ°Ń‚ŃŒ, Ń‡Ń‚ĐŸ таблОца пуста - ĐżŃ€ĐŸĐČĐ”Ń€ŃŒŃ‚Đ”, Ń‡Ń‚ĐŸ ŃŃ‚ĐŸĐč ĐșĐŸĐœĐșŃ€Đ”Ń‚ĐœĐŸĐč запОсО Ń‚Đ°ĐŒ ĐœĐ”Ń‚. йаĐșая Ń‚Đ”Ń…ĐœĐžĐșа ЎаДт ĐœĐ”ŃĐșĐŸĐ»ŃŒĐșĐŸ ĐżŃ€Đ”ĐžĐŒŃƒŃ‰Đ”ŃŃ‚ĐČ: ĐŸĐœĐ° Ń€Đ°Đ±ĐŸŃ‚Đ°Đ”Ń‚ ĐŒŃƒĐ»ŃŒŃ‚Đž-ĐżŃ€ĐŸŃ†Đ”ŃŃĐœĐŸĐŒ Ń€Đ”Đ¶ĐžĐŒĐ”, ĐșĐŸĐłĐŽĐ° Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐș Ń…ĐŸŃ‡Đ”Ń‚ ĐżĐŸĐœŃŃ‚ŃŒ, Ń‡Ń‚ĐŸ ĐżŃ€ĐŸĐžĐ·ĐŸŃˆĐ»ĐŸ - ĐŽĐ°ĐœĐœŃ‹Đ” Đ”ŃŃ‚ŃŒ Đž ĐœĐ” ŃƒĐŽĐ°Đ»Đ”ĐœŃ‹. йаĐșжД ŃŃ‚ĐŸ уĐČДлОчОĐČаДт ŃˆĐ°ĐœŃ ĐœĐ°Đčто ĐŸŃˆĐžĐ±ĐșĐž таĐș ĐșаĐș БД ĐżĐŸĐ»ĐœĐ° запОсДĐč. [ĐĄĐŒĐŸŃ‚Ń€ĐžŃ‚Đ” ĐżĐŸĐ»ĐœŃƒŃŽ Ń‚Đ°Đ±Đ»ĐžŃ†Ńƒ сраĐČĐœĐ”ĐœĐžĐč Đ·ĐŽĐ”ŃŃŒ](https://github.com/testjavascript/nodejs-integration-tests-best-practices/blob/master/graphics/db-clean-options.png). +
    . + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** Про ĐŸŃ‚ŃŃƒŃ‚ŃŃ‚ĐČОО Ń€Đ°Đ·ĐŽĐ”Đ»Đ”ĐœĐžŃ запОсДĐč ОлО ŃƒĐŽĐ°Đ»Đ”ĐœĐžŃ - тДсты Đ±ŃƒĐŽŃƒŃ‚ ĐœĐ°ŃŃ‚ŃƒĐżĐ°Ń‚ŃŒ ĐœĐ° пятĐșĐž ŃĐ°ĐŒĐž сДбД. Đ˜ŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°ĐœĐžĐ” Ń‚Ń€Đ°ĐœĐ·Đ°ĐșцоĐč Ń€Đ°Đ±ĐŸŃ‚Đ°Đ”Ń‚ Ń‚ĐŸĐ»ŃŒĐșĐŸ ĐČ Ń€Đ”Đ»ŃŃ†ĐžĐŸĐœĐœŃ‹Ń… БД Đž, про ĐżĐŸŃĐČĐ»Đ”ĐœĐžĐž ĐČĐœŃƒŃ‚Ń€Đ”ĐœĐœĐžŃ… Ń‚Ń€Đ°ĐœĐ·Đ°ĐșцоĐč, ĐŒĐŸĐ¶Đ”Ń‚ стать ŃĐ»ĐŸĐ¶ĐœĐ”Đ”. + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :clap: After-all: ĐĐ”ĐŸĐ±ŃĐ·Đ°Ń‚Đ”Đ»ŃŒĐœĐŸ ŃƒĐŽĐ°Đ»ŃŃ‚ŃŒ ĐŽĐ°ĐœĐœŃ‹Đ” ĐżĐŸŃĐ»Đ” ĐșĐ°Đ¶ĐŽĐŸĐłĐŸ запусĐșа. Đ§Đ”ĐŒ Đ±ĐŸĐ»ŃŒŃˆĐ” ĐŽĐ°ĐœĐœŃ‹Ń… у ĐœĐ°Ń Đ”ŃŃ‚ŃŒ ĐČĐŸ ĐČŃ€Đ”ĐŒŃ ĐČŃ‹ĐżĐŸĐ»ĐœĐ”ĐœĐžŃ Ń‚Đ”ŃŃ‚ĐŸĐČ, Ń‚Đ”ĐŒ Đ±ĐŸĐ»ŃŒŃˆĐ” ŃŃ‚ĐŸ ĐżĐŸŃ…ĐŸĐ¶Đ” ĐœĐ° ĐżŃ€ĐŸĐŽĐ°ĐșŃˆĐœ. + +```javascript +// after-all ĐŸŃ‡ĐžŃŃ‚Đșа (рДĐșĐŸĐŒĐ”ĐœĐŽŃƒĐ”Ń‚ŃŃ) +// global-teardown.js +module.exports = async () => { + // ... + if (Math.ceil(Math.random() * 10) === 10) { + await new OrderRepository().cleanup(); + } +}; +``` + +
    + +
    + +## âšȘ 2.9 Đ˜Đ·ĐŸĐ»ĐžŃ€ŃƒĐčтД ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ с ĐżĐŸĐŒĐŸŃ‰ŃŒŃŽ HTTP-пДрДхĐČатчоĐșа + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** Đ˜Đ·ĐŸĐ»ĐžŃ€ŃƒĐčтД Ń‚Đ”ŃŃ‚ĐžŃ€ŃƒĐ”ĐŒŃ‹Đč ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚, пДрДхĐČатыĐČая Đ»ŃŽĐ±ĐŸĐč ĐžŃŃ…ĐŸĐŽŃŃ‰ĐžĐč HTTP-Đ·Đ°ĐżŃ€ĐŸŃ Đž ĐżŃ€Đ”ĐŽĐŸŃŃ‚Đ°ĐČĐ»ŃŃ Đ¶Đ”Đ»Đ°Đ”ĐŒŃ‹Đč ĐŸŃ‚ĐČДт, Ń‡Ń‚ĐŸĐ±Ń‹ HTTP API ĐœĐ” ĐżĐŸŃŃ‚Ń€Đ°ĐŽĐ°Đ». Nock - ĐŸŃ‚Đ»ĐžŃ‡ĐœŃ‹Đč ĐžĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚ ĐŽĐ»Ń ŃŃ‚ĐŸĐč заЎачО, ĐżĐŸŃĐșĐŸĐ»ŃŒĐșу ĐŸĐœ ĐżŃ€Đ”ĐŽĐŸŃŃ‚Đ°ĐČĐ»ŃĐ”Ń‚ ŃƒĐŽĐŸĐ±ĐœŃ‹Đč ŃĐžĐœŃ‚Đ°ĐșсОс ĐŽĐ»Ń ĐŸĐżŃ€Đ”ĐŽĐ”Đ»Đ”ĐœĐžŃ ĐżĐŸĐČĐ”ĐŽĐ”ĐœĐžŃ ĐČĐœĐ”ŃˆĐœĐžŃ… сДрĐČĐžŃĐŸĐČ. Đ˜Đ·ĐŸĐ»ŃŃ†ĐžŃ ĐœĐ”ĐŸĐ±Ń…ĐŸĐŽĐžĐŒĐ° ĐŽĐ»Ń ĐżŃ€Đ”ĐŽĐŸŃ‚ĐČŃ€Đ°Ń‰Đ”ĐœĐžŃ ŃˆŃƒĐŒĐ° Đž ŃĐœĐžĐ¶Đ”ĐœĐžŃ ĐżŃ€ĐŸĐžĐ·ĐČĐŸĐŽĐžŃ‚Đ”Đ»ŃŒĐœĐŸŃŃ‚Đž, ĐœĐŸ ĐČ ĐŸŃĐœĐŸĐČĐœĐŸĐŒ ĐŽĐ»Ń ĐŒĐŸĐŽĐ”Đ»ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ Ń€Đ°Đ·Đ»ĐžŃ‡ĐœŃ‹Ń… ŃŃ†Đ”ĐœĐ°Ń€ĐžĐ”ĐČ Đž ĐŸŃ‚ĐČĐ”Ń‚ĐŸĐČ - Ń…ĐŸŃ€ĐŸŃˆĐžĐč ŃĐžĐŒŃƒĐ»ŃŃ‚ĐŸŃ€ ĐżĐŸĐ»Đ”Ń‚Đ° ĐœĐ” Ń€ĐžŃŃƒĐ”Ń‚ Ń‡ĐžŃŃ‚ĐŸĐ” ĐłĐŸĐ»ŃƒĐ±ĐŸĐ” ĐœĐ”Đ±ĐŸ, а ĐżŃ€ĐžĐœĐŸŃĐžŃ‚ ŃĐżĐŸĐșĐŸĐčĐœŃ‹Đ” Đ±ŃƒŃ€Đž. Đ­Ń‚ĐŸ усОлОĐČĐ°Đ”Ń‚ŃŃ ĐČ ĐŒĐžĐșŃ€ĐŸŃĐ”Ń€ĐČĐžŃĐœĐŸĐč архОтДĐșŃ‚ŃƒŃ€Đ”, гЎД Ń„ĐŸĐșус ĐČсДгЎа ĐŽĐŸĐ»Đ¶Đ”Đœ Đ±Ń‹Ń‚ŃŒ ŃĐŸŃŃ€Đ”ĐŽĐŸŃ‚ĐŸŃ‡Đ”Đœ ĐœĐ° ĐŸĐŽĐœĐŸĐŒ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đ” бДз проĐČĐ»Đ”Ń‡Đ”ĐœĐžŃ ĐŸŃŃ‚Đ°Đ»ŃŒĐœŃ‹Ń…. Đ„ĐŸŃ‚Ń ĐŒĐŸĐ¶ĐœĐŸ ĐžĐŒĐžŃ‚ĐžŃ€ĐŸĐČать ĐżĐŸĐČĐ”ĐŽĐ”ĐœĐžĐ” ĐČĐœĐ”ŃˆĐœĐ”ĐłĐŸ сДрĐČОса с ĐżĐŸĐŒĐŸŃ‰ŃŒŃŽ Ń‚Đ”ŃŃ‚ĐŸĐČ-ĐŽĐČĐŸĐčĐœĐžĐșĐŸĐČ (mocking), ĐżŃ€Đ”ĐŽĐżĐŸŃ‡Ń‚ĐžŃ‚Đ”Đ»ŃŒĐœĐ”Đ” ĐœĐ” Ń‚Ń€ĐŸĐłĐ°Ń‚ŃŒ разĐČĐ”Ń€ĐœŃƒŃ‚Ń‹Đč ĐșĐŸĐŽ Đž ĐŽĐ”ĐčстĐČĐŸĐČать ĐœĐ° сДтДĐČĐŸĐŒ ŃƒŃ€ĐŸĐČĐœĐ”, Ń‡Ń‚ĐŸĐ±Ń‹ тДсты былО Ń‡ĐžŃŃ‚ĐŸ "Ń‡Đ”Ń€ĐœŃ‹ĐŒ ящоĐșĐŸĐŒ". ĐĐ”ĐŽĐŸŃŃ‚Đ°Ń‚ĐșĐŸĐŒ ĐžĐ·ĐŸĐ»ŃŃ†ĐžĐž яĐČĐ»ŃĐ”Ń‚ŃŃ ĐœĐ”ĐČĐŸĐ·ĐŒĐŸĐ¶ĐœĐŸŃŃ‚ŃŒ ĐŸĐ±ĐœĐ°Ń€ŃƒĐ¶Đ”ĐœĐžŃ ĐžĐ·ĐŒĐ”ĐœĐ”ĐœĐžĐč ĐČ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đ” ĐșĐŸĐ»Đ»Đ°Đ±ĐŸŃ€Đ°Ń‚ĐŸŃ€Đ° Đž ĐœĐ”ĐŽĐŸĐżĐŸĐœĐžĐŒĐ°ĐœĐžŃ ĐŒĐ”Đ¶ĐŽŃƒ ĐŽĐČŃƒĐŒŃ сДрĐČĐžŃĐ°ĐŒĐž - ĐŸĐ±ŃĐ·Đ°Ń‚Đ”Đ»ŃŒĐœĐŸ ĐșĐŸĐŒĐżĐ”ĐœŃĐžŃ€ŃƒĐčтД ŃŃ‚ĐŸ с ĐżĐŸĐŒĐŸŃ‰ŃŒŃŽ ĐœĐ”ŃĐșĐŸĐ»ŃŒĐșох ĐșĐŸĐœŃ‚Ń€Đ°ĐșŃ‚ĐœŃ‹Ń… ОлО E2E-Ń‚Đ”ŃŃ‚ĐŸĐČ. +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** ĐĐ”ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” сДрĐČосы ĐżŃ€Đ”ĐŽĐŸŃŃ‚Đ°ĐČĐ»ŃŃŽŃ‚ фДĐčĐș-ĐČДрсОю, ĐșĐŸŃ‚ĐŸŃ€Đ°Ń ĐŒĐŸĐ¶Đ”Ń‚ Đ±Ń‹Ń‚ŃŒ разĐČĐ”Ń€ĐœŃƒŃ‚Đ° Đ»ĐŸĐșĐ°Đ»ŃŒĐœĐŸ, ĐŸĐ±Ń‹Ń‡ĐœĐŸ с ĐżĐŸĐŒĐŸŃ‰ŃŒŃŽ Docker-Đ­Ń‚ĐŸ сЎДлаДт ĐœĐ°ŃŃ‚Ń€ĐŸĐčĐșу лДгчД Đž уĐČДлОчОт ĐżŃ€ĐŸĐžĐ·ĐČĐŸĐŽĐžŃ‚Đ”Đ»ŃŒĐœĐŸŃŃ‚ŃŒ. ĐžĐŽĐœĐ°ĐșĐŸ, ŃŃ‚ĐŸ ĐœĐ” Ń€Đ”ŃˆĐžŃ‚ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒŃƒ ĐŒĐŸĐŽĐ”Đ»ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ Ń€Đ°Đ·Đ»ĐžŃ‡ĐœŃ‹Ń… responses. ĐĐ”ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” сДрĐČосы ĐżŃ€Đ”ĐŽĐŸŃŃ‚Đ°ĐČĐ»ŃŃŽŃ‚ "ĐżĐ”ŃĐŸŃ‡ĐœĐžŃ†Ńƒ" таĐșĐžĐŒ ĐŸĐ±Ń€Đ°Đ·ĐŸĐŒ, Ń‡Ń‚ĐŸ Ń€Đ”Đ°Đ»ŃŒĐœŃ‹Đč сДрĐČОс Ń€Đ°Đ±ĐŸŃ‚Đ°Đ”Ń‚, ĐœĐŸ бДз ĐżĐŸĐ±ĐŸŃ‡ĐœŃ‹Ń… ŃŃ„Ń„Đ”ĐșŃ‚ĐŸĐČ. йаĐșĐŸĐč ĐČĐ°Ń€ĐžĐ°ĐœŃ‚ ĐœĐ” ĐżĐŸĐŒĐŸĐ¶Đ”Ń‚ ĐŒĐŸĐŽĐ”Đ»ĐžŃ€ĐŸĐČать Ń€Đ°Đ·Đ»ĐžŃ‡ĐœŃ‹Đ” ŃŃ†Đ”ĐœĐ°Ń€ĐžĐž Đž ŃĐœĐžĐ·ĐžŃ‚ŃŒ ŃˆŃƒĐŒ. + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :clap: ĐŸŃ€Đ”ĐŽĐŸŃ‚ĐČŃ€Đ°Ń‰Đ”ĐœĐžĐ” сДтДĐČых ĐŸĐ±Ń€Đ°Ń‰Đ”ĐœĐžĐč Đș ĐČĐœĐ”ŃˆĐœĐžĐŒ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đ°ĐŒ ĐżĐŸĐ·ĐČĐŸĐ»ŃĐ”Ń‚ ĐŒĐŸĐŽĐ”Đ»ĐžŃ€ĐŸĐČать ŃŃ†Đ”ĐœĐ°Ń€ĐžĐž Đž ĐŒĐžĐœĐžĐŒĐžĐ·ĐžŃ€ĐŸĐČать ŃˆŃƒĐŒ + +````javascript +// пДрДхĐČат Đ·Đ°ĐżŃ€ĐŸŃĐŸĐČ Đș ŃŃ‚ĐŸŃ€ĐŸĐœĐœĐ”ĐŒŃƒ API Đž ĐČĐŸĐ·ĐČрат Đ·Đ°Ń€Đ°ĐœĐ”Đ” ĐŸĐżŃ€Đ”ĐŽĐ”Đ»Đ”ĐœĐœĐŸĐłĐŸ ĐŸŃ‚ĐČДта +beforeEach(() => { + nock('http://localhost/user/').get(`/1`).reply(200, { + id: 1, + name: 'John', + }); +});``` +```` + +
    + +
    + +## âšȘ 2.10 ĐąĐ”ŃŃ‚ĐžŃ€ŃƒĐčтД ŃŃ…Đ”ĐŒŃƒ ĐŸŃ‚ĐČДта ĐČ ĐŸŃĐœĐŸĐČĐœĐŸĐŒ про ĐœĐ°Đ»ĐžŃ‡ĐžĐž аĐČŃ‚ĐŸĐŒĐ°Ń‚ĐžŃ‡Đ”ŃĐșĐž ĐłĐ”ĐœĐ”Ń€ĐžŃ€ŃƒĐ”ĐŒŃ‹Ń… ĐżĐŸĐ»Đ”Đč + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** ЕслО ĐœĐ”ĐČĐŸĐ·ĐŒĐŸĐ¶ĐœĐŸ ĐżĐŸĐŽŃ‚ĐČĐ”Ń€ĐŽĐžŃ‚ŃŒ ĐœĐ°Đ»ĐžŃ‡ĐžĐ” ĐŸĐżŃ€Đ”ĐŽĐ”Đ»Đ”ĐœĐœŃ‹Ń… ĐŽĐ°ĐœĐœŃ‹Ń…, ĐżŃ€ĐŸĐČĐ”Ń€ŃĐčтД ĐœĐ°Đ»ĐžŃ‡ĐžĐ” Đž топы ĐŸĐ±ŃĐ·Đ°Ń‚Đ”Đ»ŃŒĐœŃ‹Ń… ĐżĐŸĐ»Đ”Đč. Đ˜ĐœĐŸĐłĐŽĐ° ĐŸŃ‚ĐČДт ĐŒĐŸĐ¶Đ”Ń‚ ŃĐŸĐŽĐ”Ń€Đ¶Đ°Ń‚ŃŒ ĐČĐ°Đ¶ĐœŃ‹Đ” ĐżĐŸĐ»Ń с ĐŽĐžĐœĐ°ĐŒĐžŃ‡Đ”ŃĐșĐžĐŒĐž ĐŽĐ°ĐœĐœŃ‹ĐŒĐž, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐœĐ”ĐČĐŸĐ·ĐŒĐŸĐ¶ĐœĐŸ ĐżŃ€Đ”ĐŽŃƒĐłĐ°ĐŽĐ°Ń‚ŃŒ про ĐœĐ°ĐżĐžŃĐ°ĐœĐžĐž тДста, таĐșОД ĐșаĐș Юаты. В ŃĐ»ŃƒŃ‡Đ°Đ”, ĐșĐŸĐłĐŽĐ° Ń‚ĐŸŃ‡ĐœĐŸ ОзĐČĐ”ŃŃ‚ĐœĐŸ, Ń‡Ń‚ĐŸ ĐżĐŸĐ»Ń ĐœĐ” Đ±ŃƒĐŽŃƒŃ‚ ĐžĐŒĐ”Ń‚ŃŒ Đ·ĐœĐ°Ń‡Đ”ĐœĐžŃ null, а таĐșжД Đ±ŃƒĐŽŃƒŃ‚ ŃĐŸĐŽĐ”Ń€Đ¶Đ°Ń‚ŃŒ праĐČĐžĐ»ŃŒĐœŃ‹Đ” топы, Ń‚ĐŸ ĐČсД раĐČĐœĐŸ ĐœŃƒĐ¶ĐœĐŸ ĐŸĐ±ŃĐ·Đ°Ń‚Đ”Đ»ŃŒĐœĐŸ ŃŃ‚ĐŸ ĐżŃ€ĐŸĐČĐ”Ń€ĐžŃ‚ŃŒ. Đ‘ĐŸĐ»ŃŒŃˆĐžĐœŃŃ‚ĐČĐŸ Đ±ĐžĐ±Đ»ĐžĐŸŃ‚Đ”Đș ĐżĐŸĐŽĐŽĐ”Ń€Đ¶ĐžĐČают ĐżŃ€ĐŸĐČДрĐșу Ń‚ĐžĐżĐŸĐČ. ЕслО ĐŸŃ‚ĐČДт - ĐœĐ”Đ±ĐŸĐ»ŃŒŃˆĐŸĐč, Ń‚ĐŸ ĐŒĐŸĐ¶ĐœĐŸ ĐČŃ‹ĐżĐŸĐ»ĐœĐžŃ‚ŃŒ ĐżŃ€ĐŸĐČДрĐșу ĐČ Ń€Đ°ĐŒĐșах ĐŸĐŽĐœĐŸĐłĐŸ утĐČĐ”Ń€Đ¶ĐŽĐ”ĐœĐžŃ (ŃĐŒ. ĐżŃ€ĐžĐŒĐ”Ń€ ĐșĐŸĐŽĐ°). Đ”Ń€ŃƒĐłĐŸĐč ĐČĐ°Ń€ĐžĐ°ĐœŃ‚ - ĐżŃ€ĐŸĐČДрĐșа ĐżĐŸĐ»ĐœĐŸĐłĐŸ ĐŸŃ‚ĐČДта ĐżĐŸ OpenAPI (Swagger). Đ‘ĐŸĐ»ŃŒŃˆĐžĐœŃŃ‚ĐČĐŸ фрДĐčĐŒĐČĐŸŃ€ĐșĐŸĐČ ĐŽĐ»Ń Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ ĐžĐŒĐ”ŃŽŃ‚ Ń€Đ°ŃŃˆĐžŃ€Đ”ĐœĐžŃ, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐżŃ€ĐŸĐČĐ”Ń€ŃŃŽŃ‚ ĐŸŃ‚ĐČДты API, ŃĐŸĐłĐ»Đ°ŃĐœĐŸ ох ĐŽĐŸĐșŃƒĐŒĐ”ĐœŃ‚Đ°Ń†ĐžĐž. + +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** ĐĐ”ŃĐŒĐŸŃ‚Ń€Ń ĐœĐ° Ń‚ĐŸ, Ń‡Ń‚ĐŸ ĐČызыĐČающоĐč ĐșĐŸĐŽ ОлО API ĐżĐŸĐ»Đ°ĐłĐ°Đ”Ń‚ŃŃ ĐœĐ° ĐœĐ”ĐșĐŸŃ‚ĐŸŃ€ĐŸĐ” ĐżĐŸĐ»Đ” с ĐŽĐžĐœĐ°ĐŒĐžŃ‡Đ”ŃĐșĐžĐŒĐž ĐŽĐ°ĐœĐœŃ‹ĐŒĐž (ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, ID ОлО Юата), ĐŸĐœĐŸ ĐŒĐŸĐ¶Đ”Ń‚ ĐœĐ” ĐČĐ”Ń€ĐœŃƒŃ‚ŃŒŃŃ ĐČ ĐŸŃ‚ĐČДтД + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :clap: ĐŸŃ€ĐŸĐČДрĐșа, Ń‡Ń‚ĐŸ ĐżĐŸĐ»Ń с ĐŽĐžĐœĐ°ĐŒĐžŃ‡Đ”ŃĐșĐžĐŒ Đ·ĐœĐ°Ń‡Đ”ĐœĐžĐ”ĐŒ ŃŃƒŃ‰Đ”ŃŃ‚ĐČуют Đž ĐžĐŒĐ”ŃŽŃ‚ праĐČĐžĐ»ŃŒĐœŃ‹Đč топ + +```javascript +test("When adding a new valid order, Then should get back approval with 200 response", async () => { + // ... + // Assert + expect(receivedAPIResponse).toMatchObject({ + status: 200, + data: { + id: expect.any(Number), // Any number satisfies this test + mode: "approved", + }, + }); +}); +``` + +
    + +
    + +## âšȘ 2.12 ĐŸŃ€ĐŸĐČДрĐșа ĐșраĐčĐœĐžŃ… ŃĐ»ŃƒŃ‡Đ°Đ”ĐČ ĐžĐœŃ‚Đ”ĐłŃ€Đ°Ń†ĐžĐž + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** Про ĐżŃ€ĐŸĐČДрĐșĐ” ĐžĐœŃ‚Đ”ĐłŃ€Đ°Ń†ĐžĐč ĐœĐ”Đ»ŃŒĐ·Ń ĐŸĐłŃ€Đ°ĐœĐžŃ‡ĐžĐČаться Ń‚ĐŸĐ»ŃŒĐșĐŸ ĐżŃ€ĐŸĐČДрĐșĐ°ĐŒĐž Đ»ĐžĐ±ĐŸ "happy", Đ»ĐžĐ±ĐŸ "sad" ĐČĐ°Ń€ĐžĐ°ĐœŃ‚ĐŸĐČ. ĐŸĐŸĐŒĐžĐŒĐŸ ĐżŃ€ĐŸĐČДрĐșĐž ĐœĐ° ĐŸŃˆĐžĐ±ĐșĐž (ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, HTTP 500), ĐœŃƒĐ¶ĐœĐŸ таĐș жД ŃĐŒĐŸŃ‚Ń€Đ”Ń‚ŃŒ ĐœĐ° Đ°ĐœĐŸĐŒĐ°Đ»ĐžĐž ĐœĐ° ŃƒŃ€ĐŸĐČĐœĐ” сДтО ОлО сĐșĐŸŃ€ĐŸŃŃ‚ŃŒ ĐŸŃ‚ĐČĐ”Ń‚ĐŸĐČ Ń‚Đ°ĐčĐŒĐ”Ń€Đ°. Đ­Ń‚ĐŸ ĐŽĐ”ĐŒĐŸĐœŃŃ‚Ń€ĐžŃ€ŃƒĐ”Ń‚ Ń‚ĐŸ, Ń‡Ń‚ĐŸ ĐČаш ĐșĐŸĐŽ ŃƒŃŃ‚ĐŸĐčчоĐČ Đž ĐŒĐŸĐ¶Đ”Ń‚ ĐŸĐ±Ń€Đ°Đ±Đ°Ń‚Ń‹ĐČать Ń€Đ°Đ·Đ»ĐžŃ‡ĐœŃ‹Đ” сДтДĐČыД ŃŃ†Đ”ĐœĐ°Ń€ĐžĐž. ĐĐ°ĐŽĐ”Đ¶ĐœŃ‹Đ” пДрДхĐČатчоĐșĐž ĐŒĐŸĐłŃƒŃ‚ лДгĐșĐŸ ĐžĐŒĐžŃ‚ĐžŃ€ĐŸĐČать Ń€Đ°Đ·Đ»ĐžŃ‡ĐœĐŸĐ” ĐżĐŸĐČĐ”ĐŽĐ”ĐœĐžĐ” сДтДĐČĐŸĐłĐŸ ĐČĐ·Đ°ĐžĐŒĐŸĐŽĐ”ĐčстĐČоя. ĐžĐœ ЎажД ĐŒĐŸĐ¶Đ”Ń‚ ĐżĐŸĐœŃŃ‚ŃŒ, ĐșĐŸĐłĐŽĐ° ŃŃ‚Đ°ĐœĐŽĐ°Ń€Ń‚ĐœĐŸĐ” Đ·ĐœĐ°Ń‡Đ”ĐœĐžĐ” таĐčĐŒ-аута HTTP-ĐșĐ»ĐžĐ”ĐœŃ‚Đ° Đ±ĐŸĐ»ŃŒŃˆĐ”, Ń‡Đ”ĐŒ ŃĐŒĐŸĐŽĐ”Đ»ĐžŃ€ĐŸĐČĐ°ĐœĐœĐŸĐ” ĐČŃ€Đ”ĐŒŃ ĐŸŃ‚ĐČДта, Đž ŃŃ€Đ°Đ·Ńƒ жД Đ±Ń€ĐŸŃĐžŃ‚ŃŒ ОсĐșĐ»ŃŽŃ‡Đ”ĐœĐžĐ” ĐżĐŸ таĐčĐŒ-ауту, ĐœĐ” ĐŽĐŸĐ¶ĐžĐŽĐ°ŃŃŃŒ ĐŸŃ‚ĐČДта. + +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** ВсД тДсты ĐżŃ€ĐŸĐčЮут, ĐŸĐŽĐœĐ°ĐșĐŸ, ĐżŃ€ĐŸĐŽĐ°ĐșŃˆĐœ ĐœĐ” ŃĐŒĐŸĐ¶Đ”Ń‚ ĐșĐŸŃ€Ń€Đ”ĐșŃ‚ĐœĐŸ ŃĐŸĐŸĐ±Ń‰ĐžŃ‚ŃŒ ĐŸĐ± ĐŸŃˆĐžĐ±ĐșĐ”, ĐșĐŸĐłĐŽĐ° ŃĐŸ ŃŃ‚ĐŸŃ€ĐŸĐœŃ‹ Đ±ŃƒĐŽŃƒŃ‚ ĐżŃ€ĐžŃ…ĐŸĐŽĐžŃ‚ŃŒ ОсĐșĐ»ŃŽŃ‡Đ”ĐœĐžŃ + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :clap: ĐŸĐŸĐŽŃ‚ĐČĐ”Ń€Đ¶ĐŽĐ”ĐœĐžĐ”, Ń‡Ń‚ĐŸ про ŃĐ±ĐŸŃŃ… ĐČ ŃĐ”Ń‚Đž, аĐČŃ‚ĐŸĐŒĐ°Ń‚ĐžŃ‡Đ”ŃĐșĐžĐč ĐČыĐșĐ»ŃŽŃ‡Đ°Ń‚Đ”Đ»ŃŒ ĐŒĐŸĐ¶Đ”Ń‚ спасто сотуацою + +```javascript +test("When users service replies with 503 once and retry mechanism is applied, then an order is added successfully", async () => { + // Arrange + nock.removeInterceptor(userServiceNock.interceptors[0]); + nock("http://localhost/user/") + .get("/1") + .reply(503, undefined, { "Retry-After": 100 }); + nock("http://localhost/user/").get("/1").reply(200); + const orderToAdd = { + userId: 1, + productId: 2, + mode: "approved", + }; + + // Act + const response = await axiosAPIClient.post("/order", orderToAdd); + + // Assert + expect(response.status).toBe(200); +}); +``` + +
    + +
    + +## âšȘ 2.13 ĐąĐ”ŃŃ‚ĐžŃ€ŃƒĐčтД ĐœĐ” ĐŒĐ”ĐœĐ”Đ” 5 ĐżĐŸŃ‚Đ”ĐœŃ†ĐžĐ°Đ»ŃŒĐœĐŸ ĐČĐŸĐ·ĐŒĐŸĐ¶ĐœŃ‹Ń… Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚ĐŸĐČ + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** Про Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚ĐșĐ” Ń‚Đ”ŃŃ‚ĐŸĐČ, ĐżŃ€ĐŸĐŽŃƒĐŒĐ°ĐčтД, ĐșаĐș ĐŒĐžĐœĐžĐŒŃƒĐŒ, ĐŸŃ…ĐČатоть 5 ĐżĐŸŃ‚ĐŸĐșĐŸĐČ ĐČыĐČĐŸĐŽĐ°. ĐšĐŸĐłĐŽĐ° ĐČаш тДст ĐČызыĐČаДт ĐșаĐșĐŸĐč-Ń‚ĐŸ ĐŽĐ”ĐčстĐČОД (ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, ĐČŃ‹Đ·ĐŸĐČ API), ĐČĐŸĐ·ĐœĐžĐșаДт сотуацоя, ĐșĐŸĐłĐŽĐ° ĐœŃƒĐ¶ĐœĐŸ ĐżŃ€ĐŸŃ‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČать Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚ ŃŃ‚ĐŸĐłĐŸ ĐŽĐ”ĐčстĐČоя. Đ’Đ°Đ¶ĐœĐŸ ĐżĐŸĐœĐžĐŒĐ°Ń‚ŃŒ, Ń‡Ń‚ĐŸ ĐœĐ°Ń ĐœĐ” ĐžĐœŃ‚Đ”Ń€Đ”ŃŃƒĐ”Ń‚, ĐșаĐș ĐČсД Ń€Đ°Đ±ĐŸŃ‚Đ°Đ”Ń‚. ĐĐ°ŃˆĐ” ĐČĐœĐžĐŒĐ°ĐœĐžĐ” ŃĐŸŃŃ€Đ”ĐŽĐŸŃ‚ĐŸŃ‡Đ”ĐœĐŸ ĐœĐ° ĐșĐŸĐœĐ”Ń‡ĐœĐŸĐŒ Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚Đ”, ĐœĐ° Ń‚ĐŸĐŒ, Ń‡Ń‚ĐŸ ĐŒĐŸĐ¶Đ”Ń‚ ĐżĐŸĐČĐ»ĐžŃŃ‚ŃŒ ĐœĐ° ĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»Ń. ĐšĐŸĐœĐ”Ń‡ĐœŃ‹Đ” Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚Ń‹ ĐŒĐŸĐ¶ĐœĐŸ Ń€Đ°Đ·ĐŽĐ”Đ»ĐžŃ‚ŃŒ ĐœĐ° ŃĐ»Đ”ĐŽŃƒŃŽŃ‰ĐžĐ” ĐșĐ°Ń‚Đ”ĐłĐŸŃ€ĐžĐž: + +‱ ОтĐČДт - йДст ĐČызыĐČаДт ĐŽĐ”ĐčстĐČОД (ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, чДрДз API) Đž ĐżĐŸĐ»ŃƒŃ‡Đ°Đ”Ń‚ ĐŸŃ‚ĐČДт. ĐąĐ”ĐżĐ”Ń€ŃŒ ĐŸĐœ Đ·Đ°ĐœĐžĐŒĐ°Đ”Ń‚ŃŃ ĐżŃ€ĐŸĐČДрĐșĐŸĐč ĐșĐŸŃ€Ń€Đ”ĐșŃ‚ĐœĐŸŃŃ‚Đž ĐŽĐ°ĐœĐœŃ‹Ń… ĐŸŃ‚ĐČДта, ŃŃ…Đ”ĐŒŃ‹ Đž статуса HTTP. + +‱ ĐĐŸĐČĐŸĐ” ŃĐŸŃŃ‚ĐŸŃĐœĐžĐ” - ĐŸĐŸŃĐ»Đ” ĐČŃ‹Đ·ĐŸĐČа ĐŽĐ”ĐčстĐČоя ĐœĐ”ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” **ĐŸĐ±Ń‰Đ”ĐŽĐŸŃŃ‚ŃƒĐżĐœŃ‹Đ”** ĐŽĐ°ĐœĐœŃ‹Đ”, ĐČĐ”Ń€ĐŸŃŃ‚ĐœĐŸ, Đ±ŃƒĐŽŃƒŃ‚ ĐžĐ·ĐŒĐ”ĐœĐ”ĐœŃ‹. + +‱ Đ’ĐœĐ”ŃˆĐœĐžĐ” ĐČŃ‹Đ·ĐŸĐČы - ĐŸĐŸŃĐ»Đ” ĐČŃ‹Đ·ĐŸĐČа ĐŽĐ”ĐčстĐČоя ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ” ĐŒĐŸĐ¶Đ”Ń‚ ĐČызĐČать ĐČĐœĐ”ŃˆĐœĐžĐč ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ чДрДз HTTP ОлО Đ»ŃŽĐ±ĐŸĐč ĐŽŃ€ŃƒĐłĐŸĐč Ń‚Ń€Đ°ĐœŃĐżĐŸŃ€Ń‚. ĐĐ°ĐżŃ€ĐžĐŒĐ”Ń€, ĐČŃ‹Đ·ĐŸĐČ ĐŽĐ»Ń ĐŸŃ‚ĐżŃ€Đ°ĐČĐșĐž SMS, ŃĐ»Đ”ĐșŃ‚Ń€ĐŸĐœĐœĐŸĐč ĐżĐŸŃ‡Ń‚Ń‹ ОлО ŃĐżĐžŃĐ°ĐœĐžŃ срДЎстĐČ Ń ĐșŃ€Đ”ĐŽĐžŃ‚ĐœĐŸĐč Đșарты. + +‱ ОчДрДЎО ŃĐŸĐŸĐ±Ń‰Đ”ĐœĐžĐč - Đ Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚ĐŸĐŒ ĐżĐŸŃ‚ĐŸĐșа ĐŒĐŸĐ¶Đ”Ń‚ Đ±Ń‹Ń‚ŃŒ ŃĐŸĐŸĐ±Ń‰Đ”ĐœĐžĐ” ĐČ ĐŸŃ‡Đ”Ń€Đ”ĐŽĐž. + +‱ ĐĐ°Đ±Đ»ŃŽĐŽĐ°Đ”ĐŒĐŸŃŃ‚ŃŒ - ĐĐ”ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐČДщО ĐœĐ”ĐŸĐ±Ń…ĐŸĐŽĐžĐŒĐŸ ĐŸŃ‚ŃĐ»Đ”Đ¶ĐžĐČать, ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, ĐŸŃˆĐžĐ±ĐșĐž ОлО Đ·ĐœĐ°Ń‡ĐžĐŒŃ‹Đ” Đ±ĐžĐ·ĐœĐ”Ń-ŃĐŸĐ±Ń‹Ń‚ĐžŃ. ĐšĐŸĐłĐŽĐ° Ń‚Ń€Đ°ĐœĐ·Đ°Đșцоя тДрпОт ĐœĐ”ŃƒĐŽĐ°Ń‡Ńƒ, ĐŒŃ‹ ĐŸĐ¶ĐžĐŽĐ°Đ”ĐŒ ĐœĐ” Ń‚ĐŸĐ»ŃŒĐșĐŸ праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐŸŃ‚ĐČДта, ĐœĐŸ Đž ĐșĐŸŃ€Ń€Đ”ĐșŃ‚ĐœĐŸĐč ĐŸĐ±Ń€Đ°Đ±ĐŸŃ‚ĐșĐž ĐŸŃˆĐžĐ±ĐŸĐș Đž праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐżŃ€ĐŸŃ‚ĐŸĐșĐŸĐ»ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ/ĐŒĐ”Ń‚Ń€ĐžĐșĐž. Эта ĐžĐœŃ„ĐŸŃ€ĐŒĐ°Ń†ĐžŃ ĐżĐŸŃŃ‚ŃƒĐżĐ°Đ”Ń‚ ĐœĐ”ĐżĐŸŃŃ€Đ”ĐŽŃŃ‚ĐČĐ”ĐœĐœĐŸ Đș ĐŸŃ‡Đ”ĐœŃŒ ĐČĐ°Đ¶ĐœĐŸĐŒŃƒ ĐżĐŸĐ»ŃŒĐ·ĐŸĐČатДлю - ĐŸĐżĐ”Ń€Đ°Ń‚ĐžĐČĐœĐŸĐŒŃƒ ĐżĐŸĐ»ŃŒĐ·ĐŸĐČатДлю (т.Đ”. ĐżŃ€ĐŸĐžĐ·ĐČĐŸĐŽŃŃ‚ĐČĐ”ĐœĐœĐŸĐŒŃƒ SRE/Đ°ĐŽĐŒĐžĐœŃƒ). + + + +

    + +# РазЎДл 3ïžâƒŁ: ĐąĐ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” Frontend + +## âšȘ  3.1 ОтЎДлОтД ĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»ŃŒŃĐșĐžĐč ĐžĐœŃ‚Đ”Ń€Ń„Đ”Đčс ĐŸŃ‚ Ń„ŃƒĐœĐșŃ†ĐžĐŸĐœĐ°Đ»ŃŒĐœĐŸŃŃ‚Đž + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** ĐšĐŸĐłĐŽĐ° ĐČы Ń„ĐŸĐșŃƒŃĐžŃ€ŃƒĐ”Ń‚Đ”ŃŃŒ ĐœĐ° Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐž Đ»ĐŸĐłĐžĐșĐž ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ĐŸĐČ, ЎДталО ĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»ŃŒŃĐșĐŸĐłĐŸ ĐžĐœŃ‚Đ”Ń€Ń„Đ”Đčса ŃŃ‚Đ°ĐœĐŸĐČятся ŃˆŃƒĐŒĐŸĐŒ, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč ĐœĐ”ĐŸĐ±Ń…ĐŸĐŽĐžĐŒĐŸ ŃƒĐŽĐ°Đ»ĐžŃ‚ŃŒ, Ń‡Ń‚ĐŸĐ±Ń‹ ĐČашО тДсты ĐŒĐŸĐłĐ»Đž ŃĐŸŃŃ€Đ”ĐŽĐŸŃ‚ĐŸŃ‡ĐžŃ‚ŃŒŃŃ ĐœĐ° чостых ĐŽĐ°ĐœĐœŃ‹Ń…. ИзĐČлДĐșаĐčтД ĐœŃƒĐ¶ĐœŃ‹Đ” ĐŽĐ°ĐœĐœŃ‹Đ” Оз Ń€Đ°Đ·ĐŒĐ”Ń‚ĐșĐž абстраĐșŃ‚ĐœŃ‹ĐŒ ŃĐżĐŸŃĐŸĐ±ĐŸĐŒ, ĐœĐ” слОшĐșĐŸĐŒ сĐČŃĐ·Đ°ĐœĐœŃ‹ĐŒ с графОчДсĐșĐŸĐč рДалОзацОДĐč, Ń‚Đ”ŃŃ‚ĐžŃ€ŃƒĐčтД Ń‚ĐŸĐ»ŃŒĐșĐŸ чОстыД ĐŽĐ°ĐœĐœŃ‹Đ” (ĐČ ĐŸŃ‚Đ»ĐžŃ‡ĐžĐ” ĐŸŃ‚ графОчДсĐșох ЎДталДĐč HTML/CSS) Đž ĐŸŃ‚ĐșлючаĐčтД Đ°ĐœĐžĐŒĐ°Ń†ĐžŃŽ, ĐșĐŸŃ‚ĐŸŃ€Đ°Ń Đ·Đ°ĐŒĐ”ĐŽĐ»ŃĐ”Ń‚ Ń€Đ°Đ±ĐŸŃ‚Ńƒ. ĐŁ ĐČас ĐŒĐŸĐ¶Đ”Ń‚ ĐČĐŸĐ·ĐœĐžĐșĐœŃƒŃ‚ŃŒ ŃĐŸĐ±Đ»Đ°Đ·Đœ ĐžĐ·Đ±Đ”Đ¶Đ°Ń‚ŃŒ Ń€Đ”ĐœĐŽĐ”Ń€ĐžĐœĐłĐ° Đž Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČать Ń‚ĐŸĐ»ŃŒĐșĐŸ Đ·Đ°ĐŽĐœŃŽŃŽ часть ĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»ŃŒŃĐșĐŸĐłĐŸ ĐžĐœŃ‚Đ”Ń€Ń„Đ”Đčса (ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, сДрĐČосы, ĐŽĐ”ĐčстĐČоя, ĐŒĐ°ĐłĐ°Đ·ĐžĐœ), ĐœĐŸ ŃŃ‚ĐŸ проĐČДЎДт Đș фоĐșтоĐČĐœŃ‹ĐŒ Ń‚Đ”ŃŃ‚Đ°ĐŒ, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐœĐ” ĐżĐŸŃ…ĐŸĐ¶Đž ĐœĐ° Ń€Đ”Đ°Đ»ŃŒĐœĐŸŃŃ‚ŃŒ Đž ĐœĐ” ĐČыяĐČят ŃĐ»ŃƒŃ‡Đ°Đž, ĐșĐŸĐłĐŽĐ° ĐœŃƒĐ¶ĐœŃ‹Đ” ĐŽĐ°ĐœĐœŃ‹Đ” ЎажД ĐœĐ” ĐżĐŸĐżĐ°ĐŽĐ°ŃŽŃ‚ ĐČ ĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»ŃŒŃĐșĐžĐč ĐžĐœŃ‚Đ”Ń€Ń„Đ”Đčс. + +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** ЧОстыД Ń€Đ°ŃŃ‡Đ”Ń‚ĐœŃ‹Đ” ĐŽĐ°ĐœĐœŃ‹Đ” ĐČĐ°ŃˆĐ”ĐłĐŸ тДста ĐŒĐŸĐłŃƒŃ‚ Đ±Ń‹Ń‚ŃŒ ĐłĐŸŃ‚ĐŸĐČы за 10 ĐŒŃ, ĐœĐŸ Ń‚ĐŸĐłĐŽĐ° ĐČĐ”ŃŃŒ тДст Đ±ŃƒĐŽĐ”Ń‚ ĐŽĐ»ĐžŃ‚ŃŒŃŃ 500 ĐŒŃ (100 Ń‚Đ”ŃŃ‚ĐŸĐČ = 1 ĐŒĐžĐœ) Оз-за ĐœĐ” ĐžĐŒĐ”ŃŽŃ‰Đ”Đč ĐŸŃ‚ĐœĐŸŃˆĐ”ĐœĐžŃ Đș ЎДлу Đ°ĐœĐžĐŒĐ°Ń†ĐžĐž. + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: Đ Đ°Đ·ĐŽĐ”Đ»Đ”ĐœĐžĐ” ĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»ŃŒŃĐșĐŸĐłĐŸ ĐžĐœŃ‚Đ”Ń€Ń„Đ”Đčса + +![](https://img.shields.io/badge/🔧%20Example%20using%20React-blue.svg "Examples with React") ![](https://img.shields.io/badge/🔧%20Example%20using%20React%20Testing%20Library-blue.svg "Examples with react-testing-library") + +```javascript +test("When users-list is flagged to show only VIP, should display only VIP members", () => { + // Arrange + const allUsers = [ + { id: 1, name: "Yoni Goldberg", vip: false }, + { id: 2, name: "John Doe", vip: true }, + ]; + + // Act + const { getAllByTestId } = render( + + ); + + // Assert - спДрĐČа ОзĐČлДĐșОтД ĐŽĐ°ĐœĐœŃ‹Đ” Оз UI + const allRenderedUsers = getAllByTestId("user").map( + (uiElement) => uiElement.textContent + ); + const allRealVIPUsers = allUsers + .filter((user) => user.vip) + .map((user) => user.name); + expect(allRenderedUsers).toEqual(allRealVIPUsers); // сраĐČĐœĐ”ĐœĐžĐ” ĐŽĐ°ĐœĐœŃ‹Ń… +}); +``` + +
    + +### :thumbsdown: ĐĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœĐŸ: ĐĄĐŸĐČĐŒĐ”ŃŃ‚ĐœĐŸĐ” Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ĐŸĐČ UI Đž ĐŽĐ°ĐœĐœŃ‹Ń… + +```javascript +test("When flagging to show only VIP, should display only VIP members", () => { + // Arrange + const allUsers = [ + { id: 1, name: "Yoni Goldberg", vip: false }, + { id: 2, name: "John Doe", vip: true }, + ]; + + // Act + const { getAllByTestId } = render( + + ); + + // Assert - ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°ĐœĐžĐ” UI Đž ĐŽĐ°ĐœĐœŃ‹Ń… ĐČ assertion + expect(getAllByTestId("user")).toEqual( + '[
  • John Doe
  • ]' + ); +}); +``` + +
    + +

    + +## âšȘ  3.2 ЗапрашоĐČать ŃĐ»Đ”ĐŒĐ”ĐœŃ‚Ń‹ HTML ĐœĐ° ĐŸŃĐœĐŸĐČĐ” Đ°Ń‚Ń€ĐžĐ±ŃƒŃ‚ĐŸĐČ, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐČряЮ лО ĐžĐ·ĐŒĐ”ĐœŃŃ‚ŃŃ + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** ЗапрашоĐČаĐčтД ŃĐ»Đ”ĐŒĐ”ĐœŃ‚Ń‹ HTML ĐœĐ° ĐŸŃĐœĐŸĐČĐ” Đ°Ń‚Ń€ĐžĐ±ŃƒŃ‚ĐŸĐČ, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ”, сĐșĐŸŃ€Đ”Đ” ĐČŃĐ”ĐłĐŸ, пДрДжОĐČут графОчДсĐșОД ĐžĐ·ĐŒĐ”ĐœĐ”ĐœĐžŃ, ĐČ ĐŸŃ‚Đ»ĐžŃ‡ĐžĐ” ĐŸŃ‚ сДлДĐșŃ‚ĐŸŃ€ĐŸĐČ CSS Đž, ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, ĐŒĐ”Ń‚ĐŸĐș Ń„ĐŸŃ€ĐŒŃ‹. ЕслО Đ·Đ°ĐŽĐ°ĐœĐœŃ‹Đč ŃĐ»Đ”ĐŒĐ”ĐœŃ‚ ĐœĐ” ĐžĐŒĐ”Đ”Ń‚ таĐșох Đ°Ń‚Ń€ĐžĐ±ŃƒŃ‚ĐŸĐČ, ŃĐŸĐ·ĐŽĐ°ĐčтД ŃĐżĐ”Ń†ĐžĐ°Đ»ŃŒĐœŃ‹Đč Ń‚Đ”ŃŃ‚ĐŸĐČыĐč Đ°Ń‚Ń€ĐžĐ±ŃƒŃ‚, ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€ 'test-id-submit-button'. йаĐșĐŸĐč путь ĐœĐ” Ń‚ĐŸĐ»ŃŒĐșĐŸ ĐłĐ°Ń€Đ°ĐœŃ‚ĐžŃ€ŃƒĐ”Ń‚, Ń‡Ń‚ĐŸ ĐČашО Ń„ŃƒĐœĐșŃ†ĐžĐŸĐœĐ°Đ»ŃŒĐœŃ‹Đ”/Đ»ĐŸĐłĐžŃ‡Đ”ŃĐșОД тДсты ĐœĐžĐșĐŸĐłĐŽĐ° ĐœĐ” ŃĐ»ĐŸĐŒĐ°ŃŽŃ‚ŃŃ Оз-за ĐžĐ·ĐŒĐ”ĐœĐ”ĐœĐžĐč ĐČĐœĐ”ŃˆĐœĐ”ĐłĐŸ ĐČОЎа, ĐœĐŸ Đž Юаст ĐżĐŸĐœŃŃ‚ŃŒ, Ń‡Ń‚ĐŸ ĐŽĐ°ĐœĐœŃ‹Đč ŃĐ»Đ”ĐŒĐ”ĐœŃ‚ Đž Đ°Ń‚Ń€ĐžĐ±ŃƒŃ‚ ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒŃŽŃ‚ŃŃ Ń‚Đ”ŃŃ‚Đ°ĐŒĐž Đž ĐœĐ” ĐŽĐŸĐ»Đ¶ĐœŃ‹ Đ±Ń‹Ń‚ŃŒ ŃƒĐŽĐ°Đ»Đ”ĐœŃ‹. + +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** Вы Ń…ĐŸŃ‚ĐžŃ‚Đ” ĐżŃ€ĐŸŃ‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČать Ń„ŃƒĐœĐșŃ†ĐžĐŸĐœĐ°Đ»ŃŒĐœĐŸŃŃ‚ŃŒ ĐČŃ…ĐŸĐŽĐ° ĐČ ŃĐžŃŃ‚Đ”ĐŒŃƒ, ĐșĐŸŃ‚ĐŸŃ€Đ°Ń ĐŸŃ…ĐČатыĐČаДт ĐŒĐœĐŸĐ¶Đ”ŃŃ‚ĐČĐŸ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ĐŸĐČ, Đ»ĐŸĐłĐžĐșĐž Đž сДрĐČĐžŃĐŸĐČ, ĐČсД ĐœĐ°ŃŃ‚Ń€ĐŸĐ”ĐœĐŸ ĐžĐŽĐ”Đ°Đ»ŃŒĐœĐŸ - stubs, spies, ĐČŃ‹Đ·ĐŸĐČы Ajax ĐžĐ·ĐŸĐ»ĐžŃ€ĐŸĐČĐ°ĐœŃ‹. ВсД ĐșĐ°Đ¶Đ”Ń‚ŃŃ ĐžĐŽĐ”Đ°Đ»ŃŒĐœŃ‹ĐŒ. Đ—Đ°Ń‚Đ”ĐŒ тДст ĐœĐ” ĐżŃ€ĐŸŃ…ĐŸĐŽĐžŃ‚, ĐżĐŸŃ‚ĐŸĐŒŃƒ Ń‡Ń‚ĐŸ ЎОзаĐčĐœĐ”Ń€ ĐžĐ·ĐŒĐ”ĐœĐžĐ» CSS-Đșласс div с "thick-border" ĐœĐ° "thin-border". + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: Đ—Đ°ĐżŃ€ĐŸŃ ŃĐ»Đ”ĐŒĐ”ĐœŃ‚Đ° с ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°ĐœĐžĐ”ĐŒ ŃĐżĐ”Ń†ĐžĐ°Đ»ŃŒĐœĐŸĐłĐŸ Đ°Ń‚Ń€ĐžĐ±ŃƒŃ‚Đ° ĐŽĐ»Ń Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ + +![](https://img.shields.io/badge/🔧%20Example%20using%20React-blue.svg "Examples with React") + +```html +// ĐșĐŸĐŽ Ń€Đ°Đ·ĐŒĐ”Ń‚ĐșĐž (часть ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đ° React) +

    + + {value} + + +

    +``` + +```javascript +// ĐČ ĐŽĐ°ĐœĐœĐŸĐŒ ĐżŃ€ĐžĐŒĐ”Ń€Đ” ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒĐ”Ń‚ŃŃ react-testing-library +test("Whenever no data is passed to metric, show 0 as default", () => { + // Arrange + const metricValue = undefined; + + // Act + const { getByTestId } = render(); + + expect(getByTestId("errorsLabel").text()).toBe("0"); +}); +``` + +
    + +### :thumbsdown: ĐĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœĐŸ: ĐĐ°ĐŽĐ”Đ¶ĐŽĐ° ĐœĐ° CSS-Đ°Ń‚Ń€ĐžĐ±ŃƒŃ‚Ń‹ + +```html + +{value} + +``` + +```javascript +// ĐČ ĐŽĐ°ĐœĐœĐŸĐŒ ĐżŃ€ĐžĐŒĐ”Ń€Đ” ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒĐ”Ń‚ŃŃ enzyme +test("Whenever no data is passed, error metric shows zero", () => { + // ... + + expect(wrapper.find("[className='d-flex-column']").text()).toBe("0"); +}); +``` + +
    + +
    + +## âšȘ  3.3 ĐŸĐŸ ĐČĐŸĐ·ĐŒĐŸĐ¶ĐœĐŸŃŃ‚Đž Ń‚Đ”ŃŃ‚ĐžŃ€ŃƒĐčтД с Ń€Đ”Đ°Đ»ĐžŃŃ‚ĐžŃ‡ĐœŃ‹ĐŒ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ĐŸĐŒ, ĐżĐŸŃĐ»Đ” Ń€Đ”ĐœĐŽĐ”Ń€ĐžĐœĐłĐ° + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** Про Đ»ŃŽĐ±ĐŸĐŒ Ń€Đ°Đ·ŃƒĐŒĐœĐŸĐŒ Ń€Đ°Đ·ĐŒĐ”Ń€Đ” ĐœŃƒĐ¶ĐœĐŸ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČать ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ ŃĐœĐ°Ń€ŃƒĐ¶Đž, ĐșаĐș ŃŃ‚ĐŸ ЎДлают ĐżĐŸĐ»ŃŒĐ·ĐŸĐČатДлО. ĐŸĐŸĐ»ĐœĐŸŃŃ‚ŃŒŃŽ Ń€Đ”ĐœĐŽĐ”Ń€ĐžŃ‚Đ” UI Đž ĐżŃ€ĐŸĐČĐ”Ń€ŃĐčтД, Ń‡Ń‚ĐŸ ĐŸĐœ ĐČДЎДт ŃĐ”Đ±Ń, ĐșаĐș ĐŸĐ¶ĐžĐŽĐ°Đ”Ń‚ŃŃ. ИзбДгаĐčтД ĐŒĐŸĐșĐžĐœĐłĐ° Đž Ń‡Đ°ŃŃ‚ĐžŃ‡ĐœĐŸĐłĐŸ Ń€Đ”ĐœĐŽĐ”Ń€ĐžĐœĐłĐ° - таĐșĐŸĐč ĐżĐŸĐŽŃ…ĐŸĐŽ ĐŒĐŸĐ¶Đ”Ń‚ проĐČДстО Đș ĐœĐ”ĐČыяĐČĐ»Đ”ĐœĐœŃ‹ĐŒ ĐŸŃˆĐžĐ±ĐșĐ°ĐŒ Оз-за ĐœĐ”ĐŽĐŸŃŃ‚Đ°Ń‚Đșа ЎДталДĐč Đž ŃĐŽĐ”Đ»Đ°Ń‚ŃŒ ĐżĐŸĐŽĐŽĐ”Ń€Đ¶Đșу ŃĐ»ĐŸĐ¶ĐœĐ”Đ”, таĐș ĐșаĐș тДсты ĐœĐ°Ń‡ĐžĐœĐ°ŃŽŃ‚ ĐČĐ»ĐžŃŃ‚ŃŒ ĐœĐ° ĐČĐœŃƒŃ‚Ń€Đ”ĐœĐœĐžĐ” ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Ń‹ (ŃĐŒĐŸŃ‚Ń€Đž ĐżŃƒĐœĐșт ['Favour blackbox testing'](https://github.com/goldbergyoni/javascript-testing-best-practices#-%EF%B8%8F-14-stick-to-black-box-testing-test-only-public-methods)). ЕслО ĐŸĐŽĐžĐœ Оз ĐŽĐŸŃ‡Đ”Ń€ĐœĐžŃ… ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ĐŸĐČ Đ·ĐœĐ°Ń‡ĐžŃ‚Đ”Đ»ŃŒĐœĐŸ Đ·Đ°ĐŒĐ”ĐŽĐ»ŃĐ”Ń‚ Ń€Đ°Đ±ĐŸŃ‚Ńƒ (ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, Đ°ĐœĐžĐŒĐ°Ń†ĐžŃ) ОлО ŃƒŃĐ»ĐŸĐ¶ĐœŃĐ”Ń‚ ĐœĐ°ŃŃ‚Ń€ĐŸĐčĐșу - ĐżĐŸĐŽŃƒĐŒĐ°ĐčтД ĐŸ яĐČĐœĐŸĐč Đ·Đ°ĐŒĐ”ĐœĐ” Đ”ĐłĐŸ ĐœĐ° фДĐčĐșĐŸĐČыĐč. + +УчотыĐČая ĐČсД ĐČŃ‹ŃˆĐ”ŃĐșĐ°Đ·Đ°ĐœĐœĐŸĐ”, ŃĐ»Đ”ĐŽŃƒĐ”Ń‚ сĐșĐ°Đ·Đ°Ń‚ŃŒ: эта Ń‚Đ”Ń…ĐœĐžĐșа Ń€Đ°Đ±ĐŸŃ‚Đ°Đ”Ń‚ ĐŽĐ»Ń ĐœĐ”Đ±ĐŸĐ»ŃŒŃˆĐžŃ…/ŃŃ€Đ”ĐŽĐœĐžŃ… ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ĐŸĐČ, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ŃĐŸĐŽĐ”Ń€Đ¶Đ°Ń‚ Ń€Đ°Đ·ŃƒĐŒĐœĐŸĐ” ĐșĐŸĐ»ĐžŃ‡Đ”ŃŃ‚ĐČĐŸ ĐŽĐŸŃ‡Đ”Ń€ĐœĐžŃ… ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ĐŸĐČ. ĐŸĐŸĐ»ĐœŃ‹Đč Ń€Đ”ĐœĐŽĐ”Ń€ĐžĐœĐł ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đ° ŃĐŸ слОшĐșĐŸĐŒ Đ±ĐŸĐ»ŃŒŃˆĐžĐŒ ĐșĐŸĐ»ĐžŃ‡Đ”ŃŃ‚ĐČĐŸĐŒ ĐŽĐŸŃ‡Đ”Ń€ĐœĐžŃ… ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ĐŸĐČ ĐżĐŸĐČлДчДт за ŃĐŸĐ±ĐŸĐč Ń‚Ń€ŃƒĐŽĐœĐŸŃŃ‚Đž ĐČ Đ°ĐœĐ°Đ»ĐžĐ·Đ” (Đ°ĐœĐ°Đ»ĐžĐ· пДрĐČĐŸĐżŃ€ĐžŃ‡ĐžĐœŃ‹) Đž ĐŒĐŸĐ¶Đ”Ń‚ стать слОшĐșĐŸĐŒ ĐŒĐ”ĐŽĐ»Đ”ĐœĐœŃ‹ĐŒ. В таĐșох ŃĐ»ŃƒŃ‡Đ°ŃŃ… ĐżĐžŃˆĐžŃ‚Đ” ĐœĐ”ŃĐșĐŸĐ»ŃŒĐșĐŸ Ń‚Đ”ŃŃ‚ĐŸĐČ ĐœĐ° ĐŸĐŽĐžĐœ Đ±ĐŸĐ»ŃŒŃˆĐŸĐč Ń€ĐŸĐŽĐžŃ‚Đ”Đ»ŃŒŃĐșĐžĐč ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ Đž Đ±ĐŸĐ»ŃŒŃˆĐ” Ń‚Đ”ŃŃ‚ĐŸĐČ ĐœĐ° ĐŽĐŸŃ‡Đ”Ń€ĐœĐžĐ” ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Ń‹. + +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** ЕслО Đ·Đ°Đ»Đ”Đ·Ń‚ŃŒ ĐČĐŸ ĐČĐœŃƒŃ‚Ń€Đ”ĐœĐœĐŸŃŃ‚Đž ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đ°, ĐČызыĐČая Đ”ĐłĐŸ проĐČĐ°Ń‚ĐœŃ‹Đ” ĐŒĐ”Ń‚ĐŸĐŽŃ‹, Đž ĐżŃ€ĐŸĐČĐ”Ń€ĐžŃ‚ŃŒ - ĐČĐ°ĐŒ ĐżŃ€ĐžĐŽĐ”Ń‚ŃŃ рДфаĐșŃ‚ĐŸŃ€ĐžŃ‚ŃŒ ĐČсД тДсты ĐČ ŃĐ»ŃƒŃ‡Đ°Đ” ĐžĐ·ĐŒĐ”ĐœĐ”ĐœĐžŃ рДалОзацОО ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đ°. ĐŁ ĐČас ĐŽĐ”ĐčстĐČĐžŃ‚Đ”Đ»ŃŒĐœĐŸ Đ”ŃŃ‚ŃŒ ĐČĐŸĐ·ĐŒĐŸĐ¶ĐœĐŸŃŃ‚Đž ĐŽĐ»Ń таĐșĐŸĐłĐŸ ŃƒŃ€ĐŸĐČĐœŃ ŃĐŸĐżŃ€ĐŸĐČĐŸĐ¶ĐŽĐ”ĐœĐžŃ? + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: ĐŸĐŸĐ»ĐœĐŸŃŃ‚ŃŒŃŽ Ń€Đ”Đ°Đ»ĐžŃŃ‚ĐžŃ‡ĐœŃ‹Đ” ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Ń‹ ĐżĐŸŃĐ»Đ” Ń€Đ”ĐœĐŽĐ”Ń€ĐžĐœĐłĐ° + +![](https://img.shields.io/badge/🔧%20Example%20using%20React-blue.svg "Examples with React") ![](https://img.shields.io/badge/🔧%20Example%20using%20Enzyme-blue.svg "Examples with Enzyme") + +```javascript +class Calendar extends React.Component { + static defaultProps = { showFilters: false }; + + render() { + return ( +
    + A filters panel with a button to hide/show filters + +
    + ); + } +} + +// ĐČ ĐżŃ€ĐžĐŒĐ”Ń€Đ°Ń… ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒŃŽŃ‚ŃŃ React & Enzyme +test("Realistic approach: When clicked to show filters, filters are displayed", () => { + // Arrange + const wrapper = mount(); + + // Act + wrapper.find("button").simulate("click"); + + // Assert + expect(wrapper.text().includes("Choose Filter")); + // таĐș ĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»ŃŒ Đ±ŃƒĐŽĐ”Ń‚ ĐŸĐ±Ń€Đ°Ń‰Đ°Ń‚ŃŒŃŃ Đș ŃŃ‚ĐŸĐŒŃƒ ŃĐ»Đ”ĐŒĐ”ĐœŃ‚Ńƒ +}); +``` + +### :thumbsdown: ĐĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœĐŸ: ĐœĐŸĐșĐžĐœĐł ĐŽĐ°ĐœĐœŃ‹Ń… Đž Ń‡Đ°ŃŃ‚ĐžŃ‡ĐœŃ‹Đč Ń€Đ”ĐœĐŽĐ”Ń€ĐžĐœĐł + +```javascript +test("Shallow/mocked approach: When clicked to show filters, filters are displayed", () => { + // Arrange + const wrapper = shallow( + + ); + + // Act + wrapper.find("filtersPanel").instance().showFilters(); + // ĐŸĐ±Ń€Đ°Ń‚ĐžŃ‚Đ”ŃŃŒ Đș ĐČĐœŃƒŃ‚Ń€Đ”ĐœĐœĐžĐŒ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đ°ĐŒ, ĐœĐ” затрагОĐČая UI Đž ĐČŃ‹Đ·ĐŸĐČОтД ĐŒĐ”Ń‚ĐŸĐŽ (white-box) + + // Assert + expect(wrapper.find("Filter").props()).toEqual({ title: "Choose Filter" }); + // Ń‡Ń‚ĐŸ ДслО ĐŒŃ‹ ĐžĐ·ĐŒĐ”ĐœĐžĐŒ ĐžĐŒŃ сĐČĐŸĐčстĐČа ОлО ĐœĐžŃ‡Đ”ĐłĐŸ ĐœĐ” ĐżĐ”Ń€Đ”ĐŽĐ°ĐŽĐžĐŒ +}); +``` + +
    + +
    + +## âšȘ  3.4 Đ˜ŃĐżĐŸĐ»ŃŒĐ·ŃƒĐčтД ĐČŃŃ‚Ń€ĐŸĐ”ĐœĐœŃƒŃŽ ĐżĐŸ фрДĐčĐŒĐČĐŸŃ€ĐșĐž ĐżĐŸĐŽĐŽĐ”Ń€Đ¶Đșу Đ°ŃĐžĐœŃ…Ń€ĐŸĐœĐœĐŸĐłĐŸ ĐșĐŸĐŽĐ°. ĐŸĐŸŃŃ‚Đ°Ń€Đ°ĐčŃ‚Đ”ŃŃŒ усĐșĐŸŃ€ĐžŃ‚ŃŒ Ń€Đ°Đ±ĐŸŃ‚Ńƒ. + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** Đ’ĐŸ ĐŒĐœĐŸĐłĐžŃ… ŃĐ»ŃƒŃ‡Đ°ŃŃ… ĐČŃ€Đ”ĐŒŃ заĐČĐ”Ń€ŃˆĐ”ĐœĐžŃ Ń‚Đ”ŃŃ‚ĐžŃ€ŃƒĐ”ĐŒĐŸĐłĐŸ Đ±Đ»ĐŸĐșа ĐżŃ€ĐŸŃŃ‚ĐŸ ĐœĐ”ĐžĐ·ĐČĐ”ŃŃ‚ĐœĐŸ (ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, Đ°ĐœĐžĐŒĐ°Ń†ĐžŃ ĐżŃ€ĐžĐŸŃŃ‚Đ°ĐœĐ°ĐČлОĐČаДт ĐżĐŸŃĐČĐ»Đ”ĐœĐžĐ” ŃĐ»Đ”ĐŒĐ”ĐœŃ‚Đ°) - ĐČ ŃŃ‚ĐŸĐŒ ŃĐ»ŃƒŃ‡Đ°Đ” ĐżŃ€Đ”ĐŽĐżĐŸŃ‡ĐžŃ‚Đ°ĐčтД ĐŽĐ”Ń‚Đ”Ń€ĐŒĐžĐœĐžŃ€ĐŸĐČĐ°ĐœĐœŃ‹Đ” ĐŒĐ”Ń‚ĐŸĐŽŃ‹, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐżŃ€Đ”ĐŽĐŸŃŃ‚Đ°ĐČĐ»ŃŃŽŃ‚ Đ±ĐŸĐ»ŃŒŃˆĐžĐœŃŃ‚ĐČĐŸ ĐżĐ»Đ°Ń‚Ń„ĐŸŃ€ĐŒ. ĐĐ”ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” Đ±ĐžĐ±Đ»ĐžĐŸŃ‚Đ”ĐșĐž ĐżĐŸĐ·ĐČĐŸĐ»ŃŃŽŃ‚ ĐŸĐ¶ĐžĐŽĐ°Ń‚ŃŒ ĐČŃ‹ĐżĐŸĐ»ĐœĐ”ĐœĐžĐ” ĐŸĐżĐ”Ń€Đ°Ń†ĐžĐč (ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, [Cypress cy.request('url')](https://docs.cypress.io/guides/references/best-practices.html#Unnecessary-Waiting)), ĐŽŃ€ŃƒĐłĐžĐ” ĐżŃ€Đ”ĐŽĐŸŃŃ‚Đ°ĐČĐ»ŃŃŽŃ‚ API ĐŽĐ»Ń ĐŸĐ¶ĐžĐŽĐ°ĐœĐžŃ, ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€ [@testing-library/dom method wait(expect(element))](https://testing-library.com/docs/guide-disappearance). Đ˜ĐœĐŸĐłĐŽĐ° Đ±ĐŸĐ»Đ”Đ” ŃĐ»Đ”ĐłĐ°ĐœŃ‚ĐœŃ‹ĐŒ ŃĐżĐŸŃĐŸĐ±ĐŸĐŒ яĐČĐ»ŃĐ”Ń‚ŃŃ заглушĐșа, ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°ĐœĐœĐ°Ń ĐČĐŒĐ”ŃŃ‚ĐŸ ĐŒĐ”ĐŽĐ»Đ”ĐœĐœĐŸ ĐČŃ‹ĐżĐŸĐ»ĐœŃŃŽŃ‰Đ”ĐłĐŸŃŃ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đ°, ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, API, а Đ·Đ°Ń‚Đ”ĐŒ, ĐșĐŸĐłĐŽĐ° ĐŒĐŸĐŒĐ”ĐœŃ‚ ĐżĐŸĐ»ŃƒŃ‡Đ”ĐœĐžŃ ĐŸŃ‚ĐČДта ŃŃ‚Đ°ĐœĐ”Ń‚ ĐŽĐ”Ń‚Đ”Ń€ĐŒĐžĐœĐžŃ€ĐŸĐČĐ°ĐœĐœŃ‹ĐŒ, ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ ĐŒĐŸĐ¶ĐœĐŸ ĐŸŃ‚Ń€Đ”ĐœĐŽĐ”Ń€ĐžŃ‚ŃŒ яĐČĐœŃ‹ĐŒ ŃĐżĐŸŃĐŸĐ±ĐŸĐŒ. ЕслО ŃŃƒŃ‰Đ”ŃŃ‚ĐČŃƒĐ”Ń‚ заĐČĐžŃĐžĐŒĐŸŃŃ‚ŃŒ ĐŸŃ‚ ĐșаĐșĐŸĐłĐŸ-Ń‚ĐŸ "ŃĐżŃŃ‰Đ”ĐłĐŸ" ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đ°, Ń‚ĐŸ ĐŒĐŸĐ¶Đ”Ń‚ ĐŸĐșĐ°Đ·Đ°Ń‚ŃŒŃŃ ĐżĐŸĐ»Đ”Đ·ĐœŃ‹ĐŒ [ĐżĐŸŃ‚ĐŸŃ€ĐŸĐżĐžŃ‚ŃŒ часы](https://jestjs.io/docs/en/timer-mocks). СпящоĐč ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ - ŃŃ‚ĐŸ ĐżĐ°Ń‚Ń‚Đ”Ń€Đœ, ĐșĐŸŃ‚ĐŸŃ€ĐŸĐłĐŸ ŃĐ»Đ”ĐŽŃƒĐ”Ń‚ ĐžĐ·Đ±Đ”ĐłĐ°Ń‚ŃŒ, ĐżĐŸŃĐșĐŸĐ»ŃŒĐșу ĐŸĐœ застаĐČĐ»ŃĐ”Ń‚ ĐČаш тДст Đ±Ń‹Ń‚ŃŒ ĐŒĐ”ĐŽĐ»Đ”ĐœĐœŃ‹ĐŒ (про слОшĐșĐŸĐŒ ĐșĐŸŃ€ĐŸŃ‚ĐșĐŸĐŒ ĐżĐ”Ń€ĐžĐŸĐŽĐ” ĐŸĐ¶ĐžĐŽĐ°ĐœĐžŃ). ĐšĐŸĐłĐŽĐ° спящоĐč Ń€Đ”Đ¶ĐžĐŒ ĐœĐ”ĐžĐ·Đ±Đ”Đ¶Đ”Đœ, а ĐżĐŸĐŽĐŽĐ”Ń€Đ¶Đșа ŃĐŸ ŃŃ‚ĐŸŃ€ĐŸĐœŃ‹ фрДĐčĐŒĐČĐŸŃ€Đșа ĐŽĐ»Ń Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐž ĐŸŃ‚ŃŃƒŃ‚ŃŃ‚ĐČŃƒĐ”Ń‚, ĐœĐ”ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” Đ±ĐžĐ±Đ»ĐžĐŸŃ‚Đ”ĐșĐž npm, таĐșОД ĐșаĐș [wait-for-expect](https://www.npmjs.com/package/wait-for-expect), ĐŒĐŸĐłŃƒŃ‚ ĐżĐŸĐŒĐŸŃ‡ŃŒ с ĐżĐŸĐ»ŃƒĐŽĐ”Ń‚Đ”Ń€ĐŒĐžĐœĐžŃ€ĐŸĐČĐ°ĐœĐœŃ‹ĐŒ Ń€Đ”ŃˆĐ”ĐœĐžĐ”ĐŒ. +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** Про ĐŽĐ»ĐžŃ‚Đ”Đ»ŃŒĐœĐŸĐŒ ŃĐżŃŃ‰Đ”ĐŒ Ń€Đ”Đ¶ĐžĐŒĐ” тДсты Đ±ŃƒĐŽŃƒŃ‚ Ń€Đ°Đ±ĐŸŃ‚Đ°Ń‚ŃŒ ĐœĐ° ĐżĐŸŃ€ŃĐŽĐŸĐș ĐŒĐ”ĐŽĐ»Đ”ĐœĐœĐ”Đ”. ĐŸĐŸĐżŃ‹Ń‚Đșа "Đ·Đ°ŃĐœŃƒŃ‚ŃŒ" ĐœĐ° ĐœĐ”Đ±ĐŸĐ»ŃŒŃˆĐŸĐ” ĐșĐŸĐ»ĐžŃ‡Đ”ŃŃ‚ĐČĐŸ ĐČŃ€Đ”ĐŒĐ”ĐœĐž проĐČДЎДт Đș Ń‚ĐŸĐŒŃƒ, Ń‡Ń‚ĐŸ тДст Đ±ŃƒĐŽĐ”Ń‚ паЮать, ĐșажЎыĐč раз, ĐșĐŸĐłĐŽĐ° Ń‚Đ”ŃŃ‚ĐžŃ€ŃƒĐ”ĐŒŃ‹Đč ĐŒĐŸĐŽŃƒĐ»ŃŒ ĐœĐ” ĐŸŃ‚Ń€Đ”Đ°ĐłĐžŃ€ĐŸĐČал ĐČĐŸĐČŃ€Đ”ĐŒŃ. йаĐșĐžĐŒ ĐŸĐ±Ń€Đ°Đ·ĐŸĐŒ, ĐČсД сĐČĐŸĐŽĐžŃ‚ŃŃ Đș ĐșĐŸĐŒĐżŃ€ĐŸĐŒĐžŃŃŃƒ ĐŒĐ”Đ¶ĐŽŃƒ хрупĐșĐŸŃŃ‚ŃŒŃŽ Đž ĐżĐ»ĐŸŃ…ĐŸĐč ĐżŃ€ĐŸĐžĐ·ĐČĐŸĐŽĐžŃ‚Đ”Đ»ŃŒĐœĐŸŃŃ‚ŃŒŃŽ. +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: E2E API, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč Ń€Đ°Đ·Ń€Đ”ŃˆĐ°Đ”Ń‚ŃŃ Ń‚ĐŸĐ»ŃŒĐșĐŸ ĐżĐŸŃĐ»Đ” ĐČŃ‹ĐżĐŸĐ»ĐœĐ”ĐœĐžŃ Đ°ŃĐžĐœŃ…Ń€ĐŸĐœĐœŃ‹Ń… ĐŸĐżĐ”Ń€Đ°Ń†ĐžĐč (Cypress) + +![](https://img.shields.io/badge/🔹%20Example%20using%20Cypress-blue.svg "Using Cypress to illustrate the idea") +![](https://img.shields.io/badge/🔧%20Example%20using%20React%20Testing%20Library-blue.svg "Examples with react-testing-library") + +```javascript +// ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒĐ”ĐŒ Cypress +cy.get("#show-products").click(); // пДрДĐčЎОтД ĐżĐŸ ссылĐșĐ” +cy.wait("@products"); // ĐŽĐŸĐ¶ĐŽĐžŃ‚Đ”ŃŃŒ ĐżĐŸŃĐČĐ»Đ”ĐœĐžŃ ĐŒĐ°Ń€ŃˆŃ€ŃƒŃ‚Đ° +// эта ŃŃ‚Ń€ĐŸĐșа Đ±ŃƒĐŽĐ”Ń‚ ĐČŃ‹ĐżĐŸĐ»ĐœĐ”ĐœĐ° Ń‚ĐŸĐ»ŃŒĐșĐŸ Ń‚ĐŸĐłĐŽĐ°, ĐșĐŸĐłĐŽĐ° ĐŒĐ°Ń€ŃˆŃ€ŃƒŃ‚ Đ±ŃƒĐŽĐ”Ń‚ ĐłĐŸŃ‚ĐŸĐČ +``` + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: Đ‘ĐžĐ±Đ»ĐžĐŸŃ‚Đ”Đșа ĐŽĐ»Ń Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ, ĐșĐŸŃ‚ĐŸŃ€Đ°Ń ĐŸĐ¶ĐžĐŽĐ°Đ”Ń‚ ŃĐ»Đ”ĐŒĐ”ĐœŃ‚Ń‹ DOM + +```javascript +// @testing-library/dom +test("movie title appears", async () => { + // ŃĐ»Đ”ĐŒĐ”ĐœŃ‚ ĐžĐ·ĐœĐ°Ń‡Đ°Đ»ŃŒĐœĐŸ ĐŸŃ‚ŃŃƒŃŃ‚ĐČŃƒĐ”Ń‚... + + // ĐŸĐ¶ĐžĐŽĐ°ĐčтД ĐżĐŸŃĐČĐ»Đ”ĐœĐžŃ + await wait(() => { + expect(getByText("the lion king")).toBeInTheDocument(); + }); + + // ĐŽĐŸĐ¶ĐŽĐ°Ń‚ŃŒŃŃ ĐżĐŸŃĐČĐ»Đ”ĐœĐžŃ Đž ĐČĐ”Ń€ĐœŃƒŃ‚ŃŒ ŃĐ»Đ”ĐŒĐ”ĐœŃ‚ + const movie = await waitForElement(() => getByText("the lion king")); +}); +``` + +### :thumbsdown: ĐĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœĐŸ: ĐŸĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»ŃŒŃĐșĐžĐč ĐșĐŸĐŽ + +```javascript +test("movie title appears", async () => { + // ŃĐ»Đ”ĐŒĐ”ĐœŃ‚ ĐžĐ·ĐœĐ°Ń‡Đ°Đ»ŃŒĐœĐŸ ĐŸŃ‚ŃŃƒŃŃ‚ĐČŃƒĐ”Ń‚... + + // ĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»ŃŒŃĐșая Đ»ĐŸĐłĐžĐșа (ĐČĐœĐžĐŒĐ°ĐœĐžĐ”: ŃƒĐżŃ€ĐŸŃ‰Đ”ĐœĐœĐ°Ń, бДз timeout) + const interval = setInterval(() => { + const found = getByText("the lion king"); + if (found) { + clearInterval(interval); + expect(getByText("the lion king")).toBeInTheDocument(); + } + }, 100); + + // ĐŽĐŸĐ¶ĐŽĐ°Ń‚ŃŒŃŃ ĐżĐŸŃĐČĐ»Đ”ĐœĐžŃ Đž ĐČĐ”Ń€ĐœŃƒŃ‚ŃŒ ŃĐ»Đ”ĐŒĐ”ĐœŃ‚ + const movie = await waitForElement(() => getByText("the lion king")); +}); +``` + +
    + +
    + +## âšȘ  3.5 ĐĐ°Đ±Đ»ŃŽĐŽĐ°ĐčтД за Ń‚Đ”ĐŒ, ĐșаĐș ĐșĐŸĐœŃ‚Đ”ĐœŃ‚ ĐżĐ”Ń€Đ”ĐŽĐ°Đ”Ń‚ŃŃ ĐżĐŸ сДтО + +![](https://img.shields.io/badge/🔧%20Example%20using%20Google%20LightHouse-blue.svg "Examples with Lighthouse") + +✅ **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** ĐŸŃ€ĐžĐŒĐ”ĐœĐžŃ‚Đ” ĐșаĐșĐŸĐč-ĐœĐžĐ±ŃƒĐŽŃŒ аĐșтоĐČĐœŃ‹Đč ĐŒĐŸĐœĐžŃ‚ĐŸŃ€, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč ĐŸĐ±Đ”ŃĐżĐ”Ń‡ĐžŃ‚ ĐŸĐżŃ‚ĐžĐŒĐžĐ·Đ°Ń†ĐžŃŽ Đ·Đ°ĐłŃ€ŃƒĐ·ĐșĐž ŃŃ‚Ń€Đ°ĐœĐžŃ†Ń‹ а сДтО - ŃŃ‚ĐŸ ĐČĐșлючаДт любыД ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒŃ‹ UX, таĐșОД ĐșаĐș ĐŒĐ”ĐŽĐ»Đ”ĐœĐœĐ°Ń Đ·Đ°ĐłŃ€ŃƒĐ·Đșа ŃŃ‚Ń€Đ°ĐœĐžŃ†Ń‹. ĐĄŃƒŃ‰Đ”ŃŃ‚ĐČŃƒĐ”Ń‚ Đ±ĐŸĐ»ŃŒŃˆĐŸĐ” ĐșĐŸĐ»ĐžŃ‡Đ”ŃŃ‚ĐČĐŸ ĐžĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚ĐŸĐČ ĐŽĐ»Ń ĐżŃ€ĐŸĐČДрĐșĐž, таĐșох ĐșаĐș: [pingdom](https://www.pingdom.com/), AWS CloudWatch, [gcp StackDriver](https://cloud.google.com/monitoring/uptime-checks/). ĐžĐœĐž ĐŒĐŸĐłŃƒŃ‚ Đ±Ń‹Ń‚ŃŒ лДгĐșĐŸ ĐœĐ°ŃŃ‚Ń€ĐŸĐ”ĐœŃ‹ ĐŽĐ»Ń ĐœĐ°Đ±Đ»ŃŽĐŽĐ”ĐœĐžŃ за Ń‚Đ”ĐŒ, жОĐČ Đ»Đž сДрĐČДр Đž ĐŸŃ‚ĐČДчаДт лО ĐŸĐœ ĐČ Ń€Đ°ĐŒĐșах SLA. Đ­Ń‚ĐŸ Đ»ĐžŃˆŃŒ ĐżĐŸĐČĐ”Ń€Ń…ĐœĐŸŃŃ‚ĐœŃ‹Đč ĐŸĐ±Đ·ĐŸŃ€ Ń‚ĐŸĐłĐŸ, Ń‡Ń‚ĐŸ ĐŒĐŸĐ¶Đ”Ń‚ ĐżĐŸĐčто ĐœĐ” таĐș, ĐżĐŸŃŃ‚ĐŸĐŒŃƒ ĐżŃ€Đ”ĐŽĐżĐŸŃ‡Ń‚ĐžŃ‚Đ”Đ»ŃŒĐœĐ”Đ” ĐČŃ‹Đ±ĐžŃ€Đ°Ń‚ŃŒ ĐžĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Ń‹, ŃĐżĐ”Ń†ĐžĐ°Đ»ĐžĐ·ĐžŃ€ŃƒŃŽŃ‰ĐžĐ”ŃŃ ĐœĐ° Ń„Ń€ĐŸĐœŃ‚Đ”ĐœĐŽĐ” (ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, [lighthouse](https://developers.google.com/web/tools/lighthouse/), [pagespeed](https://developers.google.com/speed/pagespeed/insights/)), ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐČŃ‹ĐżĐŸĐ»ĐœŃŃŽŃ‚ Đ±ĐŸĐ»Đ”Đ” ĐșачДстĐČĐ”ĐœĐœŃ‹Đč Đ°ĐœĐ°Đ»ĐžĐ·. В Ń†Đ”ĐœŃ‚Ń€Đ” ĐČĐœĐžĐŒĐ°ĐœĐžŃ ĐŽĐŸĐ»Đ¶ĐœŃ‹ Đ±Ń‹Ń‚ŃŒ ĐŒĐ”Ń‚Ń€ĐžĐșĐž, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐœĐ”ĐżĐŸŃŃ€Đ”ĐŽŃŃ‚ĐČĐ”ĐœĐœĐŸ ĐČĐ»ĐžŃŃŽŃ‚ ĐœĐ° UX, таĐșОД ĐșаĐș ĐČŃ€Đ”ĐŒŃ Đ·Đ°ĐłŃ€ŃƒĐ·ĐșĐž ŃŃ‚Ń€Đ°ĐœĐžŃ†Ń‹, [meaningful paint](https://scotch.io/courses/10-web-performance-audit-tips-for-your-next-billion-users-in-2018/fmp-first-meaningful-paint), [ĐČŃ€Đ”ĐŒŃ, ĐżĐŸĐșа ŃŃ‚Ń€Đ°ĐœĐžŃ†Đ° ĐœĐ” ŃŃ‚Đ°ĐœĐ”Ń‚ ĐžĐœŃ‚Đ”Ń€Đ°ĐșтоĐČĐœĐŸĐč (TTI)](https://calibreapp.com/blog/time-to-interactive/). ĐšŃ€ĐŸĐŒĐ” Ń‚ĐŸĐłĐŸ, ĐŒĐŸĐ¶ĐœĐŸ таĐșжД ŃĐ»Đ”ĐŽĐžŃ‚ŃŒ за Ń‚Đ”Ń…ĐœĐžŃ‡Đ”ŃĐșĐžĐŒĐž ĐżŃ€ĐžŃ‡ĐžĐœĐ°ĐŒĐž, таĐșĐžĐŒĐž ĐșаĐș cжатОД ĐșĐŸĐœŃ‚Đ”ĐœŃ‚Đ°, ĐČŃ€Đ”ĐŒŃ ĐŽĐŸ пДрĐČĐŸĐłĐŸ баĐčта, ĐŸĐżŃ‚ĐžĐŒĐžĐ·Đ°Ń†ĐžŃ ĐžĐ·ĐŸĐ±Ń€Đ°Đ¶Đ”ĐœĐžĐč, ĐŸĐ±Đ”ŃĐżĐ”Ń‡Đ”ĐœĐžĐ” Ń€Đ°Đ·ŃƒĐŒĐœĐŸĐłĐŸ Ń€Đ°Đ·ĐŒĐ”Ń€Đ° DOM, SSL Đž ĐŒĐœĐŸĐłĐžĐ” ĐŽŃ€ŃƒĐłĐžĐ”. Đ–Đ”Đ»Đ°Ń‚Đ”Đ»ŃŒĐœĐŸ прОЎДржОĐČаться ŃŃ‚ĐŸĐłĐŸ праĐČОла, ĐșаĐș ĐČĐŸ ĐČŃ€Đ”ĐŒŃ Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚ĐșĐž, таĐș Đž ĐČ Ń€Đ°ĐŒĐșах CI Đž, ŃĐ°ĐŒĐŸĐ” глаĐČĐœĐŸĐ”, ĐżĐŸŃŃ‚ĐŸŃĐœĐœĐŸ ĐœĐ° ĐżŃ€ĐŸĐŽĐ°ĐșŃˆĐœ-сДрĐČДрах/CDN. +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** Đ‘ŃƒĐŽĐ”Ń‚ ĐŸĐ±ĐžĐŽĐœĐŸ ĐŸŃĐŸĐ·ĐœĐ°ĐČать, Ń‡Ń‚ĐŸ ĐżĐŸŃĐ»Đ” таĐșĐŸĐč Ń‚Ń‰Đ°Ń‚Đ”Đ»ŃŒĐœĐŸĐč ĐżŃ€ĐŸŃ€Đ°Đ±ĐŸŃ‚ĐșĐž ĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»ŃŒŃĐșĐŸĐłĐŸ ĐžĐœŃ‚Đ”Ń€Ń„Đ”Đčса, 100% ĐżŃ€ĐŸŃ…ĐŸĐ¶ĐŽĐ”ĐœĐžŃ Ń„ŃƒĐœĐșŃ†ĐžĐŸĐœĐ°Đ»ŃŒĐœŃ‹Ń… Ń‚Đ”ŃŃ‚ĐŸĐČ Đž ŃĐ»ĐŸĐ¶ĐœĐŸĐč ĐșĐŸĐŒĐżĐ»Đ”Đșтацоо - UX ŃƒĐ¶Đ°ŃĐ”Đœ Đž ĐŒĐ”ĐŽĐ»ĐžŃ‚Đ”Đ»Đ”Đœ Оз-за ĐœĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœĐŸĐč ĐœĐ°ŃŃ‚Ń€ĐŸĐčĐșĐž CDN. + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: Đ˜ŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°ĐœĐžĐ” Lighthouse ĐŽĐ»Ń ĐżŃ€ĐŸĐČДрĐșĐž Đ·Đ°ĐłŃ€ŃƒĐ·ĐșĐž ŃŃ‚Ń€Đ°ĐœĐžŃ†Ń‹ + +![](/assets/lighthouse2.png "Lighthouse page load inspection report") + +
    + +
    + +## âšȘ  3.6 Đ—Đ°ĐłĐ»ŃƒŃˆĐžŃ‚Đ” ĐœĐ”ŃŃ‚Đ°Đ±ĐžĐ»ŃŒĐœŃ‹Đ” Đž ĐŒĐ”ĐŽĐ»Đ”ĐœĐœŃ‹Đ” Ń€Đ”ŃŃƒŃ€ŃŃ‹, ĐșаĐș backend API + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** Про ĐœĐ°ĐżĐžŃĐ°ĐœĐžĐž ĐŸŃĐœĐŸĐČĐœŃ‹Ń… Ń‚Đ”ŃŃ‚ĐŸĐČ (ĐœĐ” E2E-Ń‚Đ”ŃŃ‚ĐŸĐČ) ОзбДгаĐčтД проĐČĐ»Đ”Ń‡Đ”ĐœĐžŃ Ń€Đ”ŃŃƒŃ€ŃĐŸĐČ, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐœĐ°Ń…ĐŸĐŽŃŃ‚ŃŃ ĐČĐœĐ” ĐČашДĐč ĐŸŃ‚ĐČДтстĐČĐ”ĐœĐœĐŸŃŃ‚Đž Đž ĐșĐŸĐœŃ‚Ń€ĐŸĐ»Ń, таĐșох ĐșаĐș Đ±ŃĐșĐ”ĐœĐŽ API, Đž ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒĐčтД ĐČĐŒĐ”ŃŃ‚ĐŸ ĐœĐžŃ… заглушĐșĐž (т.Đ”. test double). Đ’ĐŒĐ”ŃŃ‚ĐŸ Ń€Đ”Đ°Đ»ŃŒĐœŃ‹Ń… сДтДĐČых ĐČŃ‹Đ·ĐŸĐČĐŸĐČ API, ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒĐčтД ĐșаĐșую-ĐœĐžĐ±ŃƒĐŽŃŒ Đ±ĐžĐ±Đ»ĐžĐŸŃ‚Đ”Đșу test double (ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, [Sinon](https://sinonjs.org/), [Test doubles](https://www.npmjs.com/package/testdouble) Đž т.ĐŽ.) ĐŽĐ»Ń ŃĐŸĐ·ĐŽĐ°ĐœĐžŃ заглушĐșĐž ĐŸŃ‚ĐČДта API. ĐžŃĐœĐŸĐČĐœŃ‹ĐŒ ĐżŃ€Đ”ĐžĐŒŃƒŃ‰Đ”ŃŃ‚ĐČĐŸĐŒ яĐČĐ»ŃĐ”Ń‚ŃŃ ĐżŃ€Đ”ĐŽĐŸŃ‚ĐČŃ€Đ°Ń‰Đ”ĐœĐžĐ” флДĐčĐșĐžĐœĐłĐ° - Ń‚Đ”ŃŃ‚ĐŸĐČыД ОлО staging API ĐżĐŸ ĐŸĐżŃ€Đ”ĐŽĐ”Đ»Đ”ĐœĐžŃŽ ĐœĐ” ĐŸŃ‡Đ”ĐœŃŒ ŃŃ‚Đ°Đ±ĐžĐ»ŃŒĐœŃ‹ Đž ĐČŃ€Đ”ĐŒŃ ĐŸŃ‚ ĐČŃ€Đ”ĐŒĐ”ĐœĐž Đ±ŃƒĐŽŃƒŃ‚ ĐżŃ€ĐŸĐČалОĐČать ĐČашО тДсты, Ń…ĐŸŃ‚Ń ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ ĐČДЎДт ŃĐ”Đ±Ń ĐżŃ€ĐŸŃŃ‚ĐŸ ĐŸŃ‚Đ»ĐžŃ‡ĐœĐŸ (production env ĐœĐ” был ĐżŃ€Đ”ĐŽĐœĐ°Đ·ĐœĐ°Ń‡Đ”Đœ ĐŽĐ»Ń Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ Đž ĐŸĐ±Ń‹Ń‡ĐœĐŸ ĐŸĐłŃ€Đ°ĐœĐžŃ‡ĐžĐČаДт Đ·Đ°ĐżŃ€ĐŸŃŃ‹). Đ­Ń‚ĐŸ ĐżĐŸĐ·ĐČĐŸĐ»ĐžŃ‚ ŃĐŒĐŸĐŽĐ”Đ»ĐžŃ€ĐŸĐČать Ń€Đ°Đ·Đ»ĐžŃ‡ĐœĐŸĐ” ĐżĐŸĐČĐ”ĐŽĐ”ĐœĐžĐ” API, ĐșĐŸŃ‚ĐŸŃ€ĐŸĐ” ĐŽĐŸĐ»Đ¶ĐœĐŸ ĐŸĐżŃ€Đ”ĐŽĐ”Đ»ŃŃ‚ŃŒ ĐżĐŸĐČĐ”ĐŽĐ”ĐœĐžĐ” ĐČĐ°ŃˆĐ”ĐłĐŸ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đ°, ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, ĐșĐŸĐłĐŽĐ° ĐŽĐ°ĐœĐœŃ‹Đ” ĐœĐ” ĐœĐ°ĐčĐŽĐ”ĐœŃ‹ ОлО ĐșĐŸĐłĐŽĐ° API ĐČыЎаДт ĐŸŃˆĐžĐ±Đșу. И ĐżĐŸŃĐ»Đ”ĐŽĐœĐ”Đ”, ĐœĐŸ ĐœĐ” ĐŒĐ”ĐœĐ”Đ” ĐČĐ°Đ¶ĐœĐŸĐ”: сДтДĐČыД ĐČŃ‹Đ·ĐŸĐČы Đ·ĐœĐ°Ń‡ĐžŃ‚Đ”Đ»ŃŒĐœĐŸ Đ·Đ°ĐŒĐ”ĐŽĐ»ŃŃ‚ Ń€Đ°Đ±ĐŸŃ‚Ńƒ Ń‚Đ”ŃŃ‚ĐŸĐČ + +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** ĐĄŃ€Đ”ĐŽĐœĐžĐč тДст ĐŽĐ»ĐžŃ‚ŃŃ ĐœĐ” Đ±ĐŸĐ»Đ”Đ” ĐœĐ”ŃĐșĐŸĐ»ŃŒĐșох ĐŒŃ, Ń‚ĐžĐżĐžŃ‡ĐœŃ‹Đč ĐČŃ‹Đ·ĐŸĐČ API ĐŽĐ»ĐžŃ‚ŃŃ 100 ĐŒŃ>, ŃŃ‚ĐŸ ЎДлаДт ĐșажЎыĐč тДст ĐČ ~20 раз ĐŒĐ”ĐŽĐ»Đ”ĐœĐœĐ”Đ”. + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :clap: Doing It Right Example: Stubbing or intercepting API calls + +![](https://img.shields.io/badge/🔧%20Example%20using%20React-blue.svg "Examples with React") ![](https://img.shields.io/badge/🔧%20Example%20using%20React%20Testing%20Library-blue.svg "Examples with react-testing-library") + +```javascript +// ĐąĐ”ŃŃ‚ĐžŃ€ŃƒĐ”ĐŒŃ‹Đč Đ±Đ»ĐŸĐș +export default function ProductsList() { + const [products, setProducts] = useState(false); + + const fetchProducts = async () => { + const products = await axios.get("api/products"); + setProducts(products); + }; + + useEffect(() => { + fetchProducts(); + }, []); + + return products ? ( +
    {products}
    + ) : ( +
    No products
    + ); +} + +// тДст +test("When no products exist, show the appropriate message", () => { + // Arrange + nock("api").get(`/products`).reply(404); + + // Act + const { getByTestId } = render(); + + // Assert + expect(getByTestId("no-products-message")).toBeTruthy(); +}); +``` + +
    + +
    + +## âšȘ  3.7 Đ˜ŃĐżĐŸĐ»ŃŒĐ·ŃƒĐčтД ĐŒĐ°Đ»ĐŸ сĐșĐČĐŸĐ·ĐœŃ‹Ń… Ń‚Đ”ŃŃ‚ĐŸĐČ, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐŸŃ…ĐČатыĐČают ĐČсю ŃĐžŃŃ‚Đ”ĐŒŃƒ + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** ĐĐ”ŃĐŒĐŸŃ‚Ń€Ń ĐœĐ° Ń‚ĐŸ, Ń‡Ń‚ĐŸ E2E (end-to-end) ĐŸĐ±Ń‹Ń‡ĐœĐŸ ĐŸĐ·ĐœĐ°Ń‡Đ°Đ”Ń‚ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” Ń‚ĐŸĐ»ŃŒĐșĐŸ ĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»ŃŒŃĐșĐŸĐłĐŸ ĐžĐœŃ‚Đ”Ń€Ń„Đ”Đčса с ĐżĐŸĐŒĐŸŃ‰ŃŒŃŽ Ń€Đ”Đ°Đ»ŃŒĐœĐŸĐłĐŸ Đ±Ń€Đ°ŃƒĐ·Đ”Ń€Đ° (ŃĐŒ. [ĐżŃƒĐœĐșт 3.6](https://github.com/goldbergyoni/javascript-testing-best-practices#-%EF%B8%8F-36-stub-flaky-and-slow-resources-like-backend-apis)), ĐŽĐ»Ń Юругох - ŃŃ‚ĐŸ ĐŸĐ·ĐœĐ°Ń‡Đ°Đ”Ń‚ тДсты, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐŸŃ…ĐČатыĐČают ĐČсю ŃĐžŃŃ‚Đ”ĐŒŃƒ, ĐČĐșĐ»ŃŽŃ‡Đ°Ń Ń€Đ”Đ°Đ»ŃŒĐœŃ‹Đč backend. ĐŸĐŸŃĐ»Đ”ĐŽĐœĐžĐč топ Ń‚Đ”ŃŃ‚ĐŸĐČ ŃĐČĐ»ŃĐ”Ń‚ŃŃ ĐŸŃ‡Đ”ĐœŃŒ Ń†Đ”ĐœĐœŃ‹ĐŒ, таĐș ĐșаĐș ĐŸĐœ ĐżĐŸĐșрыĐČаДт ĐŸŃˆĐžĐ±ĐșĐž ĐžĐœŃ‚Đ”ĐłŃ€Đ°Ń†ĐžĐž ĐŒĐ”Đ¶ĐŽŃƒ frontend Đž backend, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐŒĐŸĐłŃƒŃ‚ ĐČĐŸĐ·ĐœĐžĐșĐœŃƒŃ‚ŃŒ Оз-за ĐœĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐżĐŸĐœĐžĐŒĐ°ĐœĐžŃ ŃŃ…Đ”ĐŒŃ‹ ĐŸĐ±ĐŒĐ”ĐœĐ°. ĐžĐœĐž таĐșжД яĐČĐ»ŃŃŽŃ‚ŃŃ ŃŃ„Ń„Đ”ĐșтоĐČĐœŃ‹ĐŒ ĐŒĐ”Ń‚ĐŸĐŽĐŸĐŒ ĐŸĐ±ĐœĐ°Ń€ŃƒĐ¶Đ”ĐœĐžŃ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒ ĐžĐœŃ‚Đ”ĐłŃ€Đ°Ń†ĐžĐž ĐŒĐ”Đ¶ĐŽŃƒ Đ±ŃĐșĐ”ĐœĐŽĐ°ĐŒĐž (ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, ĐŒĐžĐșŃ€ĐŸŃĐ”Ń€ĐČОс A ĐżĐŸŃŃ‹Đ»Đ°Đ”Ń‚ ĐœĐ”ĐČĐ”Ń€ĐœĐŸĐ” ŃĐŸĐŸĐ±Ń‰Đ”ĐœĐžĐ” ĐŒĐžĐșŃ€ĐŸŃĐ”Ń€ĐČОсу B) Đž ЎажД ĐČыяĐČĐ»Đ”ĐœĐžŃ ŃĐ±ĐŸĐ”ĐČ Ń€Đ°Đ·ĐČДртыĐČĐ°ĐœĐžŃ - ĐœĐ” ŃŃƒŃ‰Đ”ŃŃ‚ĐČŃƒĐ”Ń‚ Đ±ŃĐșĐ”ĐœĐŽ-фрДĐčĐŒĐČĐŸŃ€ĐșĐŸĐČ ĐŽĐ»Ń Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ E2E, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” былО бы ĐœĐ°ŃŃ‚ĐŸĐ»ŃŒĐșĐŸ жД ĐŽŃ€ŃƒĐ¶Đ”ŃŃ‚ĐČĐ”ĐœĐœŃ‹ĐŒĐž Đž Đ·Ń€Đ”Đ»Ń‹ĐŒĐž, ĐșаĐș UI-фрДĐčĐŒĐČĐŸŃ€ĐșĐž, таĐșОД ĐșаĐș [Cypress](https://www.cypress.io/) Đž [Puppeteer](https://github.com/GoogleChrome/puppeteer). ĐĐ”ĐŽĐŸŃŃ‚Đ°Ń‚ĐșĐŸĐŒ таĐșох Ń‚Đ”ŃŃ‚ĐŸĐČ ŃĐČĐ»ŃĐ”Ń‚ŃŃ ĐČŃ‹ŃĐŸĐșая ŃŃ‚ĐŸĐžĐŒĐŸŃŃ‚ŃŒ ĐœĐ°ŃŃ‚Ń€ĐŸĐčĐșĐž срДЎы с таĐșĐžĐŒ ĐșĐŸĐ»ĐžŃ‡Đ”ŃŃ‚ĐČĐŸĐŒ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ĐŸĐČ, а таĐșжД ох хрупĐșĐŸŃŃ‚ŃŒ - про ĐœĐ°Đ»ĐžŃ‡ĐžĐž 50 ĐŒĐžĐșŃ€ĐŸŃĐ”Ń€ĐČĐžŃĐŸĐČ, ЎажД ДслО ĐŸĐŽĐžĐœ Оз ĐœĐžŃ… ĐŸŃ‚ĐșажДт, Ń‚ĐŸ ĐČĐ”ŃŃŒ E2E ĐżŃ€ĐŸŃŃ‚ĐŸ ĐœĐ” Ń€Đ°Đ±ĐŸŃ‚Đ°Đ”Ń‚. ĐŸĐŸ ŃŃ‚ĐŸĐč ĐżŃ€ĐžŃ‡ĐžĐœĐ” ĐŒŃ‹ ĐŽĐŸĐ»Đ¶ĐœŃ‹ ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČать ĐŽĐ°ĐœĐœŃ‹Đč ĐżĐŸĐŽŃ…ĐŸĐŽ ĐŸŃ‡Đ”ĐœŃŒ эĐșĐŸĐœĐŸĐŒĐœĐŸ Đž, ĐČĐ”Ń€ĐŸŃŃ‚ĐœĐŸ, ĐžĐŒĐ”Ń‚ŃŒ 1-10 таĐșох Ń‚Đ”ŃŃ‚ĐŸĐČ Đž ĐœĐ” Đ±ĐŸĐ»Đ”Đ”. ĐąĐ”ĐŒ ĐœĐ” ĐŒĐ”ĐœĐ”Đ”, ЎажД ĐœĐ”Đ±ĐŸĐ»ŃŒŃˆĐŸĐ” ĐșĐŸĐ»ĐžŃ‡Đ”ŃŃ‚ĐČĐŸ Ń‚Đ”ŃŃ‚ĐŸĐČ E2E, сĐșĐŸŃ€Đ”Đ” ĐČŃĐ”ĐłĐŸ, ĐČыяĐČот тД ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒŃ‹, ĐœĐ° ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐŸĐœĐž ĐœĐ°Ń†Đ”Đ»Đ”ĐœŃ‹ - ĐŸŃˆĐžĐ±ĐșĐž разĐČДртыĐČĐ°ĐœĐžŃ Đž ĐžĐœŃ‚Đ”ĐłŃ€Đ°Ń†ĐžĐž. Đ Đ”ĐșĐŸĐŒĐ”ĐœĐŽŃƒĐ”Ń‚ŃŃ запусĐșать таĐșОД тДсты ĐœĐ° ĐżŃ€ĐŸĐŽĐ°ĐșŃˆĐ”ĐœĐ”. + +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** ĐŸĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»ŃŒŃĐșĐžĐč ĐžĐœŃ‚Đ”Ń€Ń„Đ”Đčс ĐŒĐŸĐ¶Đ”Ń‚ Ń…ĐŸŃ€ĐŸŃˆĐŸ спраĐČĐ»ŃŃ‚ŃŒŃŃ с Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ”ĐŒ сĐČĐŸĐ”Đč Ń„ŃƒĐœĐșŃ†ĐžĐŸĐœĐ°Đ»ŃŒĐœĐŸŃŃ‚Đž, ĐœĐŸ ĐżĐŸĐ·ĐŽĐœĐŸ ĐżĐŸĐœŃŃ‚ŃŒ, Ń‡Ń‚ĐŸ ŃŃ…Đ”ĐŒĐ° ĐŽĐ°ĐœĐœŃ‹Ń…, с ĐșĐŸŃ‚ĐŸŃ€ĐŸĐč ĐŽĐŸĐ»Đ¶Đ”Đœ Ń€Đ°Đ±ĐŸŃ‚Đ°Ń‚ŃŒ UI, ĐŸŃ‚Đ»ĐžŃ‡Đ°Đ”Ń‚ŃŃ ĐŸŃ‚ ĐŸĐ¶ĐžĐŽĐ°Đ”ĐŒĐŸĐč. + +
    + +## âšȘ  3.8 УсĐșĐŸŃ€Đ”ĐœĐžĐ” Ń‚Đ”ŃŃ‚ĐŸĐČ E2E за счДт ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°ĐœĐžŃ ĐżĐŸĐČŃ‚ĐŸŃ€ĐœĐŸĐłĐŸ ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°ĐœĐžŃ ĐŽĐ°ĐœĐœŃ‹Ń… ĐŽĐ»Ń ĐČŃ…ĐŸĐŽĐ° ĐČ ŃĐžŃŃ‚Đ”ĐŒŃƒ + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** В тДстах E2E, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” заЎДĐčстĐČуют Ń€Đ”Đ°Đ»ŃŒĐœŃ‹Đč backend Đž ĐżĐŸĐ»Đ°ĐłĐ°ŃŽŃ‚ŃŃ ĐœĐ° ĐČĐ°Đ»ĐžĐŽĐœŃ‹Đč ĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»ŃŒŃĐșĐžĐč Ń‚ĐŸĐșĐ”Đœ ĐŽĐ»Ń ĐČŃ‹Đ·ĐŸĐČĐŸĐČ API, ĐœĐ” ĐœŃƒĐ¶ĐœĐŸ ĐžĐ·ĐŸĐ»ĐžŃ€ĐŸĐČать тДст ĐŽĐŸ ŃƒŃ€ĐŸĐČĐœŃ, ĐœĐ° ĐșĐŸŃ‚ĐŸŃ€ĐŸĐŒ ĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»ŃŒ ŃĐŸĐ·ĐŽĐ°Đ”Ń‚ŃŃ Đž Ń€Đ”ĐłĐžŃŃ‚Ń€ĐžŃ€ŃƒĐ”Ń‚ŃŃ про ĐșĐ°Đ¶ĐŽĐŸĐŒ Đ·Đ°ĐżŃ€ĐŸŃĐ”. Đ’ĐŒĐ”ŃŃ‚ĐŸ ŃŃ‚ĐŸĐłĐŸ, аĐČŃ‚ĐŸŃ€ĐžĐ·ŃƒĐčŃ‚Đ”ŃŃŒ Ń‚ĐŸĐ»ŃŒĐșĐŸ ĐŸĐŽĐžĐœ раз пДрДЎ ĐœĐ°Ń‡Đ°Đ»ĐŸĐŒ ĐČŃ‹ĐżĐŸĐ»ĐœĐ”ĐœĐžŃ тДста (т.Đ”. before-all hook), ŃĐŸŃ…Ń€Đ°ĐœĐžŃ‚Đ” Ń‚ĐŸĐșĐ”Đœ ĐČ ĐșаĐșĐŸĐŒ-ĐœĐžĐ±ŃƒĐŽŃŒ Đ»ĐŸĐșĐ°Đ»ŃŒĐœĐŸĐŒ Ń…Ń€Đ°ĐœĐžĐ»ĐžŃ‰Đ” Đž ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒĐčтД Đ”ĐłĐŸ ĐżĐŸĐČŃ‚ĐŸŃ€ĐœĐŸ ĐČĐŸ ĐČсДх Đ·Đ°ĐżŃ€ĐŸŃĐ°Ń…. ĐœĐŸĐ¶Đ”Ń‚ ĐșĐ°Đ·Đ°Ń‚ŃŒŃŃ, Ń‡Ń‚ĐŸ ŃŃ‚ĐŸ ĐœĐ°Ń€ŃƒŃˆĐ°Đ”Ń‚ ĐŸĐŽĐžĐœ Оз ĐŸŃĐœĐŸĐČĐœŃ‹Ń… ĐżŃ€ĐžĐœŃ†ĐžĐżĐŸĐČ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ - ŃĐŸŃ…Ń€Đ°ĐœŃŃ‚ŃŒ аĐČŃ‚ĐŸĐœĐŸĐŒĐœĐŸŃŃ‚ŃŒ тДста бДз проĐČŃĐ·ĐșĐž Đș Ń€Đ”ŃŃƒŃ€ŃĐ°ĐŒ. Đ„ĐŸŃ‚Ń ŃŃ‚ĐŸ ĐŸĐ±ĐŸŃĐœĐŸĐČĐ°ĐœĐœĐŸĐ” Đ±Đ”ŃĐżĐŸĐșĐŸĐčстĐČĐŸ, ĐČ Ń‚Đ”ŃŃ‚Đ°Ń… E2E ĐżŃ€ĐŸĐžĐ·ĐČĐŸĐŽĐžŃ‚Đ”Đ»ŃŒĐœĐŸŃŃ‚ŃŒ яĐČĐ»ŃĐ”Ń‚ŃŃ ĐșлючДĐČŃ‹ĐŒ фаĐșŃ‚ĐŸŃ€ĐŸĐŒ, Đž ŃĐŸĐ·ĐŽĐ°ĐœĐžĐ” 1-3 Đ·Đ°ĐżŃ€ĐŸŃĐŸĐČ API пДрДЎ запусĐșĐŸĐŒ ĐșĐ°Đ¶ĐŽĐŸĐłĐŸ ĐŸŃ‚ĐŽĐ”Đ»ŃŒĐœĐŸĐłĐŸ тДста ĐŒĐŸĐ¶Đ”Ń‚ проĐČДстО Đș Đ±ĐŸĐ»ŃŒŃˆĐŸĐŒŃƒ ĐČŃ€Đ”ĐŒĐ”ĐœĐž ĐČŃ‹ĐżĐŸĐ»ĐœĐ”ĐœĐžŃ. ĐŸĐŸĐČŃ‚ĐŸŃ€ĐœĐŸĐ” ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°ĐœĐžĐ” ŃƒŃ‡Đ”Ń‚ĐœŃ‹Ń… ĐŽĐ°ĐœĐœŃ‹Ń… ĐœĐ” ĐŸĐ·ĐœĐ°Ń‡Đ°Đ”Ń‚, Ń‡Ń‚ĐŸ тДсты ĐŽĐŸĐ»Đ¶ĐœŃ‹ Ń€Đ°Đ±ĐŸŃ‚Đ°Ń‚ŃŒ с ĐŸĐŽĐœĐžĐŒĐž Đž Ń‚Đ”ĐŒĐž жД ĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»ŃŒŃĐșĐžĐŒĐž Đ·Đ°ĐżĐžŃŃĐŒĐž - ДслО ĐČы ĐżĐŸĐ»Đ°ĐłĐ°Đ”Ń‚Đ”ŃŃŒ ĐœĐ° ĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»ŃŒŃĐșОД запОсО (ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, ĐžŃŃ‚ĐŸŃ€ĐžŃ платДжДĐč ĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»Ń), ŃƒĐ±Đ”ĐŽĐžŃ‚Đ”ŃŃŒ, Ń‡Ń‚ĐŸ это запОсО ŃĐŸĐ·ĐŽĐ°ŃŽŃ‚ŃŃ ĐČ Ń€Đ°ĐŒĐșах тДста, Đž ĐœĐ” ĐŽĐ”Đ»ĐžŃ‚Đ”ŃŃŒ ох ŃŃƒŃ‰Đ”ŃŃ‚ĐČĐŸĐČĐ°ĐœĐžĐ”ĐŒ с ĐŽŃ€ŃƒĐłĐžĐŒĐž Ń‚Đ”ŃŃ‚Đ°ĐŒĐž. йаĐșжД ĐżĐŸĐŒĐœĐžŃ‚Đ”, Ń‡Ń‚ĐŸ ĐœĐ° backend ĐŒĐŸĐ¶Đ”Ń‚ ŃŃ‚ĐŸŃŃ‚ŃŒ заглушĐșа - ДслО ĐČашО тДсты ŃĐŸŃŃ€Đ”ĐŽĐŸŃ‚ĐŸŃ‡Đ”ĐœŃ‹ ĐœĐ° Ń„Ń€ĐŸĐœŃ‚Đ”ĐœĐŽĐ”, ĐČĐŸĐ·ĐŒĐŸĐ¶ĐœĐŸ, Đ»ŃƒŃ‡ŃˆĐ” ĐžĐ·ĐŸĐ»ĐžŃ€ĐŸĐČать Đ”ĐłĐŸ Đž Đ·Đ°ĐłĐ»ŃƒŃˆĐžŃ‚ŃŒ API Đ±ŃĐșĐ”ĐœĐŽĐ° (ŃĐŒ. [ĐżŃƒĐœĐșт 3.6](https://github.com/goldbergyoni/javascript-testing-best-practices#-%EF%B8%8F-36-stub-flaky-and-slow-resources-like-backend-apis)). + +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** Про ĐœĐ°Đ»ĐžŃ‡ĐžĐž 200 Ń‚Đ”ŃŃ‚ĐŸĐČ Đž, ĐżŃ€Đ”ĐŽĐżĐŸĐ»Đ°ĐłĐ°Ń, Ń‡Ń‚ĐŸ ĐșажЎыĐč login = 100 ms, Ń‡Ń‚ĐŸ ĐČ ŃŃƒĐŒĐŒĐ” ĐșажЎыĐč раз ЎаДт 20 сДĐșŃƒĐœĐŽ Ń‚ĐŸĐ»ŃŒĐșĐŸ ĐŽĐ»Ń ĐČŃ…ĐŸĐŽĐ° ĐČ ŃĐžŃŃ‚Đ”ĐŒŃƒ + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: Đ’Ń…ĐŸĐŽ ĐČ ŃĐžŃŃ‚Đ”ĐŒŃƒ before-all, а ĐœĐ” before-each + +![](https://img.shields.io/badge/🔹%20Example%20using%20Cypress-blue.svg "Using Cypress to illustrate the idea") + +```javascript +let authenticationToken; + +// ĐżŃ€ĐŸĐžŃŃ…ĐŸĐŽĐžŃ‚ ĐŽĐŸ ĐČŃ‹ĐżĐŸĐ»ĐœĐ”ĐœĐžŃ ВХЕЄ Ń‚Đ”ŃŃ‚ĐŸĐČ +before(() => { + cy.request('POST', 'http://localhost:3000/login', { + username: Cypress.env('username'), + password: Cypress.env('password'), + }) + .its('body') + .then((responseFromLogin) => { + authenticationToken = responseFromLogin.token; + }) +}) + +// ĐżŃ€ĐŸĐžŃŃ…ĐŸĐŽĐžŃ‚ пДрДЎ ĐšĐĐ–Đ”Đ«Đœ Ń‚Đ”ŃŃ‚ĐŸĐŒ +beforeEach(setUser => () { + cy.visit('/home', { + onBeforeLoad (win) { + win.localStorage.setItem('token', JSON.stringify(authenticationToken)) + }, + }) +}) + +``` + +
    + +
    + +## âšȘ  3.9 ĐŸŃ€ĐŸĐČДЎОтД ĐŸĐŽĐžĐœ E2E smoke-тДст, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč ĐżŃ€ĐŸŃŃ‚ĐŸ бДгаДт ĐżĐŸ ĐșартД саĐčта + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** Đ”Đ»Ń Ń‚ĐŸĐłĐŸ, Ń‡Ń‚ĐŸĐ±Ń‹ ĐżŃ€ĐŸŃ‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČать ĐșĐŸĐŽ ĐČ ĐżŃ€ĐŸĐŽĐ°ĐșŃˆĐœ-срДЎД, Đ·Đ°ĐżŃƒŃŃ‚ĐžŃ‚Đ” ДщД ĐŸĐŽĐžĐœ тДст E2E, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč ĐżĐŸŃĐ”Ń‰Đ°Đ”Ń‚ Đ±ĐŸĐ»ŃŒŃˆĐžĐœŃŃ‚ĐČĐŸ ОлО ĐČсД ŃŃ‚Đ°ĐœĐžŃ†Ń‹ саĐčта Đž ĐłĐ°Ń€Đ°ĐœŃ‚ĐžŃ€ŃƒĐ”Ń‚, Ń‡Ń‚ĐŸ ĐČсД Ń€Đ°Đ±ĐŸŃ‚Đ°Đ”Ń‚ оспраĐČĐœĐŸ. Đ”Đ°ĐœĐœŃ‹Đč ĐČОЎ тДста ĐżŃ€ĐžĐœĐŸŃĐžŃ‚ Đ±ĐŸĐ»ŃŒŃˆŃƒŃŽ ĐżĐŸĐ»ŃŒĐ·Ńƒ, таĐș ĐșаĐș Đ”ĐłĐŸ лДгĐșĐŸ ĐœĐ°ĐżĐžŃĐ°Ń‚ŃŒ Đž ĐżĐŸĐŽĐŽĐ”Ń€Đ¶ĐžĐČать, ĐČ Ń‚ĐŸ ĐČŃ€Đ”ĐŒŃ, ĐșаĐș ĐŸĐœ ĐŒĐŸĐ¶Đ”Ń‚ ĐŸĐ±ĐœĐ°Ń€ŃƒĐ¶ĐžŃ‚ŃŒ Đ»ŃŽĐ±ŃƒŃŽ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒŃƒ, ĐČĐșĐ»ŃŽŃ‡Đ°Ń сДтДĐČыД ŃĐ±ĐŸĐž Đž ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒŃ‹ про разĐČДртыĐČĐ°ĐœĐžĐž. Đ”Ń€ŃƒĐłĐžĐ” ĐČоЮы smoke-Ń‚Đ”ŃŃ‚ĐŸĐČ ĐœĐ” таĐșОД ĐœĐ°ĐŽŃ‘Đ¶ĐœŃ‹Đ” Đž ĐžĐœŃ„ĐŸŃ€ĐŒĐ°Ń‚ĐžĐČĐœŃ‹Đ” - ĐžĐœĐŸĐłĐŽĐ° ĐżŃ€ĐŸŃŃ‚ĐŸ ĐżĐžĐœĐłŃƒŃŽŃ‚ ĐŽĐŸĐŒĐ°ŃˆĐœŃŽŃŽ ŃŃ‚Ń€Đ°ĐœĐžŃ†Ńƒ ОлО запусĐșают ĐŒĐœĐŸĐ¶Đ”ŃŃ‚ĐČĐŸ ĐžĐœŃ‚Đ”ĐłŃ€Đ°Ń†ĐžĐŸĐœĐœŃ‹Ń… Ń‚Đ”ŃŃ‚ĐŸĐČ, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐœĐ” ĐœĐ°Ń…ĐŸĐŽŃŃ‚ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒ. ОчДĐČĐžĐŽĐœĐŸ, Ń‡Ń‚ĐŸ ĐŽĐ°ĐœĐœŃ‹Đč ĐČОЎ Ń‚Đ”ŃŃ‚ĐŸĐČ ĐœĐ” ĐŒĐŸĐ¶Đ”Ń‚ ĐżĐŸĐ»ĐœĐŸŃŃ‚ŃŒŃŽ Đ·Đ°ĐŒĐ”ĐœĐžŃ‚ŃŒ Ń„ŃƒĐœĐșŃ†ĐžĐŸĐœĐ°Đ»ŃŒĐœŃ‹Đ” тДсты. + +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** ВсД ĐŒĐŸĐ¶Đ”Ń‚ ĐșĐ°Đ·Đ°Ń‚ŃŒŃŃ ĐžĐŽĐ”Đ°Đ»ŃŒĐœŃ‹ĐŒ, ĐČсД тДсты ĐżŃ€ĐŸĐčĐŽĐ”ĐœŃ‹, ĐŸĐŽĐœĐ°ĐșĐŸ, у ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đ° Payment ĐČĐŸĐ·ĐœĐžĐșлО ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒŃ‹ с упаĐșĐŸĐČĐșĐŸĐč ОлО ĐœĐ” ĐŒĐ°Ń€ŃˆŃ€ŃƒŃ‚ ĐŒĐŸĐ¶Đ”Ń‚ ĐœĐ” Ń€Đ”ĐœĐŽĐ”Ń€ĐžŃ‚ŃŒŃŃ + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: ЗапусĐș smoke-тДста ĐżĐŸ ĐČсДĐč ŃŃ‚Ń€Đ°ĐœĐžŃ†Đ” + +![](https://img.shields.io/badge/🔹%20Example%20using%20Cypress-blue.svg "Using Cypress to illustrate the idea") + +```javascript +it("When doing smoke testing over all page, should load them all successfully", () => { + // ĐżŃ€ĐžĐŒĐ”Ń€ с ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°ĐœĐžĐ”ĐŒ Cypress, ĐœĐŸ ĐŒĐŸĐ¶Đ”Ń‚ Đ±Ń‹Ń‚ŃŒ Ń€Đ”Đ°Đ»ĐžĐ·ĐŸĐČĐ°Đœ + // с ĐŽŃ€ŃƒĐłĐžĐŒ ĐœĐ°Đ±ĐŸŃ€ĐŸĐŒ E2E + cy.visit("https://mysite.com/home"); + cy.contains("Home"); + cy.visit("https://mysite.com/Login"); + cy.contains("Login"); + cy.visit("https://mysite.com/About"); + cy.contains("About"); +}); +``` + +
    + +
    + +## âšȘ  3.10 Expose the tests as a live collaborative document + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** ĐŸĐŸĐŒĐžĐŒĐŸ ĐżĐŸĐČŃ‹ŃˆĐ”ĐœĐžŃ ĐœĐ°ĐŽĐ”Đ¶ĐœĐŸŃŃ‚Đž ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ, тДсты таĐșжД ĐżŃ€Đ”ĐŽĐŸŃŃ‚Đ°ĐČĐ»ŃŃŽŃ‚ ĐČĐŸĐ·ĐŒĐŸĐ¶ĐœĐŸŃŃ‚ŃŒ ŃĐ»ŃƒĐ¶ĐžŃ‚ŃŒ ĐČ ĐșачДстĐČĐ” ĐŽĐŸĐșŃƒĐŒĐ”ĐœŃ‚Đ°Ń†ĐžĐž ĐŽĐ»Ń ĐșĐŸĐŽĐ°. ĐŸĐŸŃĐșĐŸĐ»ŃŒĐșу тДсты ĐłĐŸĐČĐŸŃ€ŃŃ‚ ĐœĐ° ĐŒĐ”ĐœĐ”Đ” Ń‚Đ”Ń…ĐœĐžŃ‡Đ”ŃĐșĐŸĐŒ ŃĐ·Ń‹ĐșĐ”, а сĐșĐŸŃ€Đ”Đ” ĐœĐ° ŃĐ·Ń‹ĐșĐ” UX, про ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°ĐœĐžĐž праĐČĐžĐ»ŃŒĐœŃ‹Ń… ĐžĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚ĐŸĐČ ĐŸĐœĐž ĐŒĐŸĐłŃƒŃ‚ ŃĐ»ŃƒĐ¶ĐžŃ‚ŃŒ ĐČ ĐșачДстĐČĐ” ĐŸĐżŃ€Đ”ĐŽĐ”Đ»Đ”ĐœĐœĐŸĐłĐŸ ĐșĐŸĐŒĐŒŃƒĐœĐžĐșĐ°Ń‚ĐŸŃ€Đ°, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč ĐČыраĐČĐœĐžĐČаДт ĐČсДх ŃƒŃ‡Đ°ŃŃ‚ĐœĐžĐșĐŸĐČ ĐżŃ€ĐŸĐ”Đșта - Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐșĐŸĐČ Đž ĐșĐ»ĐžĐ”ĐœŃ‚ĐŸĐČ. ĐĐ”ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” фрДĐčĐŒĐČĐŸŃ€ĐșĐž, ĐżĐŸĐ·ĐČĐŸĐ»ŃŃŽŃ‚ ĐČŃ‹Ń€Đ°Đ·ĐžŃ‚ŃŒ ĐżĐ»Đ°Đœ Ń‚Đ”ŃŃ‚ĐŸĐČ ĐœĐ° "Ń‡Đ”Đ»ĐŸĐČĐ”ĐșĐŸĐżĐŸĐŽĐŸĐ±ĐœĐŸĐŒ" ŃĐ·Ń‹ĐșĐ” таĐș, Ń‡Ń‚ĐŸ Đ»ŃŽĐ±Đ°Ń Đ·Đ°ĐžĐœŃ‚Đ”Ń€Đ”ŃĐŸĐČĐ°ĐœĐœĐ°Ń ŃŃ‚ĐŸŃ€ĐŸĐœĐ°, ĐČĐșĐ»ŃŽŃ‡Đ°Ń ĐŒĐ”ĐœĐ”ĐŽĐ¶Đ”Ń€ĐŸĐČ ĐżŃ€ĐŸĐŽŃƒĐșта, таĐșжД ĐŒĐŸĐ¶Đ”Ń‚ чотать Đž ĐżĐŸĐœĐžĐŒĐ°Ń‚ŃŒ тДсты, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” прДĐČŃ€Đ°Ń‚ĐžĐ»ĐžŃŃŒ ĐČ ĐŽĐŸĐșŃƒĐŒĐ”ĐœŃ‚Đ°Ń†ĐžŃŽ. Đ­Ń‚ĐŸŃ‚ ĐżĐŸĐŽŃ…ĐŸĐŽ таĐșжД ĐœĐ°Đ·Ń‹ĐČают "acceptance test", ĐżĐŸŃĐșĐŸĐ»ŃŒĐșу ĐŸĐœĐ° ĐżĐŸĐ·ĐČĐŸĐ»ŃĐ”Ń‚ заĐșазчОĐșу ĐČŃ‹Ń€Đ°Đ·ĐžŃ‚ŃŒ сĐČĐŸĐž Ń‚Ń€Đ”Đ±ĐŸĐČĐ°ĐœĐžŃ Đș ĐżŃ€ĐŸĐŽŃƒĐșту ĐœĐ° ĐżŃ€ĐŸŃŃ‚ĐŸĐŒ ŃĐ·Ń‹ĐșĐ”. Đ­Ń‚ĐŸ [BDD (behavior-driven testing)] (https://en.wikipedia.org/wiki/Behavior-driven_development) ĐČ Ń‡ĐžŃŃ‚ĐŸĐŒ ĐČОЎД. ĐžĐŽĐœĐžĐŒ Оз ĐżĐŸĐżŃƒĐ»ŃŃ€ĐœŃ‹Ń… фрДĐčĐŒĐČĐŸŃ€ĐșĐŸĐČ, ĐżĐŸĐ·ĐČĐŸĐ»ŃŃŽŃ‰ĐžŃ… ŃŃ‚ĐŸ ŃĐŽĐ”Đ»Đ°Ń‚ŃŒ, яĐČĐ»ŃĐ”Ń‚ŃŃ [Cucumber](https://github.com/cucumber/cucumber-js), ŃĐŒ. ĐżŃ€ĐžĐŒĐ”Ń€ ĐœĐžĐ¶Đ”. ЕщД ĐŸĐŽĐœĐ° ĐżĐŸŃ…ĐŸĐ¶Đ°Ń, ĐœĐŸ Юругая ĐČĐŸĐ·ĐŒĐŸĐ¶ĐœĐŸŃŃ‚ŃŒ, [StoryBook](https://storybook.js.org/), ĐżĐŸĐ·ĐČĐŸĐ»ŃĐ”Ń‚ прДЎстаĐČоть ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Ń‹ ĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»ŃŒŃĐșĐŸĐłĐŸ ĐžĐœŃ‚Đ”Ń€Ń„Đ”Đčса ĐČ ĐČОЎД графОчДсĐșĐŸĐłĐŸ ĐșĐ°Ń‚Đ°Đ»ĐŸĐłĐ°, гЎД ĐŒĐŸĐ¶ĐœĐŸ ĐżŃ€ĐŸĐčтось ĐżĐŸ Ń€Đ°Đ·Đ»ĐžŃ‡ĐœŃ‹ĐŒ ŃĐŸŃŃ‚ĐŸŃĐœĐžŃĐŒ ĐșĐ°Đ¶ĐŽĐŸĐłĐŸ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đ° (ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, ĐŸŃ‚ĐŸĐ±Ń€Đ°Đ·ĐžŃ‚ŃŒ сДтĐșу бДз Ń„ĐžĐ»ŃŒŃ‚Ń€ĐŸĐČ, ĐŸŃ‚ĐŸĐ±Ń€Đ°Đ·ĐžŃ‚ŃŒ сДтĐșу с ĐœĐ”ŃĐșĐŸĐ»ŃŒĐșĐžĐŒĐž Ń€ŃĐŽĐ°ĐŒĐž ОлО бДз ĐœĐžŃ… Đž т.ĐŽ.), ĐżĐŸŃĐŒĐŸŃ‚Ń€Đ”Ń‚ŃŒ, ĐșаĐș ŃŃ‚ĐŸ ĐČŃ‹ĐłĐ»ŃĐŽĐžŃ‚, Đž ĐșаĐș ĐČызĐČать ŃŃ‚ĐŸ ŃĐŸŃŃ‚ĐŸŃĐœĐžĐ” - ŃŃ‚ĐŸ ĐŒĐŸĐ¶Đ”Ń‚ ĐżĐŸĐœŃ€Đ°ĐČоться Đž product-ĐŒĐ”ĐœĐ”ĐŽĐ¶Đ”Ń€Đ°ĐŒ, ĐœĐŸ ĐČ ĐŸŃĐœĐŸĐČĐœĐŸĐŒ ŃŃ‚ĐŸ ĐŽĐŸĐșŃƒĐŒĐ”ĐœŃ‚Đ°Ń†ĐžŃ ĐŽĐ»Ń Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐșĐŸĐČ, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒŃŽŃ‚ это ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Ń‹. + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** ĐŸĐŸŃĐ»Đ” ĐŸĐłŃ€ĐŸĐŒĐœĐŸĐłĐŸ ĐșĐŸĐ»ĐžŃ‡Đ”ŃŃ‚ĐČа усОлОĐč, ĐżĐŸŃ‚Ń€Đ°Ń‡Đ”ĐœĐœĐŸĐłĐŸ ĐœĐ° Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Đșу Ń‚Đ”ŃŃ‚ĐŸĐČ, Đ±ŃƒĐŽĐ”Ń‚ Đ¶Đ°Đ»ŃŒ ĐœĐ” ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČать Đ”ĐłĐŸ ĐČĐŸĐ·ĐŒĐŸĐ¶ĐœĐŸŃŃ‚Đž + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: ĐžĐżĐžŃĐ°ĐœĐžĐ” Ń‚Đ”ŃŃ‚ĐŸĐČ ĐœĐ° Ń‡Đ”Đ»ĐŸĐČĐ”ĐșĐŸĐżĐŸĐŽĐŸĐ±ĐœĐŸĐŒ ŃĐ·Ń‹ĐșĐ” про ĐżĐŸĐŒĐŸŃ‰Đž cucumber-js + +![](https://img.shields.io/badge/🔹%20Example%20using%20Cucumber-blue.svg "Examples using Cucumber") + +```javascript +// таĐș ĐŒĐŸĐ¶ĐœĐŸ ĐŸĐżĐžŃŃ‹ĐČать тДсты с ĐżĐŸĐŒĐŸŃ‰ŃŒŃŽ Cucumber: ĐżŃ€ĐŸŃŃ‚Ń‹ĐŒ ŃĐ·Ń‹ĐșĐŸĐŒ, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč Đ»ŃŽĐ±ĐŸĐč ĐŒĐŸĐ¶Đ”Ń‚ ĐżĐŸĐœŃŃ‚ŃŒ + +Feature: Twitter new tweet + + I want to tweet something in Twitter + + @focus + Scenario: Tweeting from the home page + Given I open Twitter home + Given I click on "New tweet" button + Given I type "Hello followers!" in the textbox + Given I click on "Submit" button + Then I see message "Tweet saved" + +``` + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: Đ’ĐžĐ·ŃƒĐ°Đ»ĐžĐ·Đ°Ń†ĐžŃ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ĐŸĐČ Đž ох ŃĐŸŃŃ‚ĐŸŃĐœĐžĐč с ĐżĐŸĐŒĐŸŃ‰ŃŒŃŽ Storybook + +![](https://img.shields.io/badge/🔹%20Example%20using%20StoryBook-blue.svg "Using StoryBook") + +![alt text](assets/story-book.jpg "Storybook") + +
    + +

    + +## âšȘ  3.11 ĐŸĐŸĐžŃĐș ĐČĐžĐ·ŃƒĐ°Đ»ŃŒĐœŃ‹Ń… ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒ с ĐżĐŸĐŒĐŸŃ‰ŃŒŃŽ аĐČŃ‚ĐŸĐŒĐ°Ń‚ĐžĐ·ĐžŃ€ĐŸĐČĐ°ĐœĐœŃ‹Ń… ĐžĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚ĐŸĐČ + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** ĐĐ°ŃŃ‚Ń€ĐŸĐčтД аĐČŃ‚ĐŸĐŒĐ°Ń‚ĐžĐ·ĐžŃ€ĐŸĐČĐ°ĐœĐœŃ‹Đ” ĐžĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Ń‹ ĐŽĐ»Ń ŃĐŸĐ·ĐŽĐ°ĐœĐžŃ сĐșŃ€ĐžĐœŃˆĐŸŃ‚ĐŸĐČ ĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»ŃŒŃĐșĐŸĐłĐŸ ĐžĐœŃ‚Đ”Ń€Ń„Đ”Đčса про прДЎстаĐČĐ»Đ”ĐœĐžĐž ĐžĐ·ĐŒĐ”ĐœĐ”ĐœĐžĐč Đž ĐŸĐ±ĐœĐ°Ń€ŃƒĐ¶Đ”ĐœĐžŃ ĐČĐžĐ·ŃƒĐ°Đ»ŃŒĐœŃ‹Ń… ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒ, таĐșох ĐșаĐș пДрДĐșрытОД ОлО разрыĐČ ĐșĐŸĐœŃ‚Đ”ĐœŃ‚Đ°. Đ­Ń‚ĐŸ ĐłĐ°Ń€Đ°ĐœŃ‚ĐžŃ€ŃƒĐ”Ń‚, Ń‡Ń‚ĐŸ ĐœĐ” Ń‚ĐŸĐ»ŃŒĐșĐŸ праĐČĐžĐ»ŃŒĐœŃ‹Đ” ĐŽĐ°ĐœĐœŃ‹Đ” Đ±ŃƒĐŽŃƒŃ‚ ĐżĐŸĐŽĐłĐŸŃ‚ĐŸĐČĐ»Đ”ĐœŃ‹, ĐœĐŸ Đž ĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»ŃŒ ŃĐŒĐŸĐ¶Đ”Ń‚ ŃƒĐŽĐŸĐ±ĐœĐŸ ох ĐżĐŸŃĐŒĐŸŃ‚Ń€Đ”Ń‚ŃŒ. Эта Ń‚Đ”Ń…ĐœĐžĐșа ĐœĐ” ĐżĐŸĐ»ŃƒŃ‡ĐžĐ»Đ° ŃˆĐžŃ€ĐŸĐșĐŸĐłĐŸ Ń€Đ°ŃĐżŃ€ĐŸŃŃ‚Ń€Đ°ĐœĐ”ĐœĐžŃ, таĐș ĐșаĐș ĐŒŃ‹ проĐČыĐșлО Đș Ń„ŃƒĐœĐșŃ†ĐžĐŸĐœĐ°Đ»ŃŒĐœŃ‹ĐŒ Ń‚Đ”ŃŃ‚Đ°ĐŒ, ĐœĐŸ ĐžĐŒĐ”ĐœĐœĐŸ ĐČĐžĐ·ŃƒĐ°Đ»ŃŒĐœŃ‹Đ” тДсты яĐČĐ»ŃŃŽŃ‚ŃŃ Ń‚Đ”ĐŒ, Ń‡Ń‚ĐŸ оспытыĐČаДт ĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»ŃŒ, а с таĐșĐžĐŒ ĐșĐŸĐ»ĐžŃ‡Đ”ŃŃ‚ĐČĐŸĐŒ Ń‚ĐžĐżĐŸĐČ ŃƒŃŃ‚Ń€ĐŸĐčстĐČ ĐŸŃ‡Đ”ĐœŃŒ лДгĐșĐŸ упустоть Оз ĐČОЎу ĐșаĐșую-ĐœĐžĐ±ŃƒĐŽŃŒ ĐœĐ”ĐżŃ€ĐžŃŃ‚ĐœŃƒŃŽ ĐŸŃˆĐžĐ±Đșу ĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»ŃŒŃĐșĐŸĐłĐŸ ĐžĐœŃ‚Đ”Ń€Ń„Đ”Đčса. ĐĐ”ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” Đ±Đ”ŃĐżĐ»Đ°Ń‚ĐœŃ‹Đ” ĐžĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Ń‹ ĐŒĐŸĐłŃƒŃ‚ ĐŸĐ±Đ”ŃĐżĐ”Ń‡ĐžŃ‚ŃŒ ĐŸŃĐœĐŸĐČы - ĐłĐ”ĐœĐ”Ń€ĐžŃ€ĐŸĐČать Đž ŃĐŸŃ…Ń€Đ°ĐœŃŃ‚ŃŒ сĐșŃ€ĐžĐœŃˆĐŸŃ‚Ń‹ ĐŽĐ»Ń ĐČĐžĐ·ŃƒĐ°Đ»ŃŒĐœĐŸĐč ĐżŃ€ĐŸĐČДрĐșĐž. Đ„ĐŸŃ‚Ń таĐșĐŸĐč ĐżĐŸĐŽŃ…ĐŸĐŽ ĐŒĐŸĐ¶Đ”Ń‚ Đ±Ń‹Ń‚ŃŒ ĐŸĐżŃ€Đ°ĐČĐŽĐ°ĐœĐœŃ‹ĐŒ ĐŽĐ»Ń ĐœĐ”Đ±ĐŸĐ»ŃŒŃˆĐžŃ… ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐč, ĐŸĐœ ĐœĐ”ŃĐŸĐČĐ”Ń€ŃˆĐ”ĐœĐ”Đœ, ĐșаĐș Đž Đ»ŃŽĐ±ĐŸĐ” ĐŽŃ€ŃƒĐłĐŸĐ” Ń€ŃƒŃ‡ĐœĐŸĐ” Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ”, Ń‚Ń€Đ”Đ±ŃƒŃŽŃ‰Đ”Đ” Ń‡Đ”Đ»ĐŸĐČДчДсĐșĐŸĐłĐŸ труЮа про ĐșĐ°Đ¶ĐŽĐŸĐŒ ĐžĐ·ĐŒĐ”ĐœĐ”ĐœĐžĐž. ĐĄ ĐŽŃ€ŃƒĐłĐŸĐč ŃŃ‚ĐŸŃ€ĐŸĐœŃ‹, ĐŽĐŸĐČĐŸĐ»ŃŒĐœĐŸ ŃĐ»ĐŸĐ¶ĐœĐŸ ĐŸĐ±ĐœĐ°Ń€ŃƒĐ¶ĐžŃ‚ŃŒ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒŃ‹ ĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»ŃŒŃĐșĐŸĐłĐŸ ĐžĐœŃ‚Đ”Ń€Ń„Đ”Đčса аĐČŃ‚ĐŸĐŒĐ°Ń‚ĐžŃ‡Đ”ŃĐșĐž Оз-за ĐŸŃ‚ŃŃƒŃ‚ŃŃ‚ĐČоя чДтĐșĐŸĐłĐŸ ĐŸĐżŃ€Đ”ĐŽĐ”Đ»Đ”ĐœĐžŃ - ĐžĐŒĐ”ĐœĐœĐŸ Đ·ĐŽĐ”ŃŃŒ ĐČŃŃ‚ŃƒĐżĐ°Đ”Ń‚ ĐČ ĐŽĐ”Đ»ĐŸ ĐŸĐ±Đ»Đ°ŃŃ‚ŃŒ "ĐČĐžĐ·ŃƒĐ°Đ»ŃŒĐœĐŸĐč рДгрДссОО", ĐșĐŸŃ‚ĐŸŃ€Đ°Ń Ń€Đ”ŃˆĐ°Đ”Ń‚ эту ĐłĐŸĐ»ĐŸĐČĐŸĐ»ĐŸĐŒĐșу, сраĐČĐœĐžĐČая старыĐč ĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»ŃŒŃĐșĐžĐč ĐžĐœŃ‚Đ”Ń€Ń„Đ”Đčс с ĐżĐŸŃĐ»Đ”ĐŽĐœĐžĐŒĐž ĐžĐ·ĐŒĐ”ĐœĐ”ĐœĐžŃĐŒĐž Đž ĐŸĐ±ĐœĐ°Ń€ŃƒĐ¶ĐžĐČая Ń€Đ°Đ·Đ»ĐžŃ‡ĐžŃ. ĐĐ”ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” Đ±Đ”ŃĐżĐ»Đ°Ń‚ĐœŃ‹Đ” ĐžĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Ń‹ ĐŒĐŸĐłŃƒŃ‚ ĐżŃ€Đ”ĐŽĐŸŃŃ‚Đ°ĐČоть ĐœĐ”ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” Оз этох Ń„ŃƒĐœĐșцоĐč (ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, [wraith](https://github.com/BBC-News/wraith), [PhantomCSS](<[https://github.com/HuddleEng/PhantomCSS](https://github.com/HuddleEng/PhantomCSS)>), ĐœĐŸ ĐŒĐŸĐłŃƒŃ‚ ĐżĐŸŃ‚Ń€Đ”Đ±ĐŸĐČать Đ·ĐœĐ°Ń‡ĐžŃ‚Đ”Đ»ŃŒĐœĐŸĐłĐŸ ĐČŃ€Đ”ĐŒĐ”ĐœĐž ĐœĐ° ĐœĐ°ŃŃ‚Ń€ĐŸĐčĐșу. ĐšĐŸĐŒĐŒĐ”Ń€Ń‡Đ”ŃĐșая Đ»ĐžĐœĐ”ĐčĐșа ĐžĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚ĐŸĐČ (ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, [Applitools](https://applitools.com/), [Percy.io](https://percy.io/)) ЎДлаДт шаг ĐČпДрДЎ, сглажОĐČая ŃƒŃŃ‚Đ°ĐœĐŸĐČĐșу Đž ĐżŃ€Đ”ĐŽĐŸŃŃ‚Đ°ĐČĐ»ŃĐ”Ń‚ Ń€Đ°ŃŃˆĐžŃ€Đ”ĐœĐœŃ‹Đ” ĐČĐŸĐ·ĐŒĐŸĐ¶ĐœĐŸŃŃ‚Đž, таĐșОД ĐșаĐș упраĐČĐ»Đ”ĐœĐžĐ” ĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°Ń‚Đ”Đ»ŃŒŃĐșĐžĐŒ ĐžĐœŃ‚Đ”Ń€Ń„Đ”ĐčŃĐŸĐŒ, ĐŸĐżĐŸĐČĐ”Ń‰Đ”ĐœĐžĐ”, ĐžĐœŃ‚Đ”Đ»Đ»Đ”ĐșŃ‚ŃƒĐ°Đ»ŃŒĐœŃ‹Đč захĐČат ĐżŃƒŃ‚Đ”ĐŒ ŃƒŃŃ‚Ń€Đ°ĐœĐ”ĐœĐžŃ "ĐČĐžĐ·ŃƒĐ°Đ»ŃŒĐœĐŸĐłĐŸ ŃˆŃƒĐŒĐ°" (ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, рДĐșĐ»Đ°ĐŒŃ‹, Đ°ĐœĐžĐŒĐ°Ń†ĐžĐž) Đž ЎажД Đ°ĐœĐ°Đ»ĐžĐ· пДрĐČĐŸĐżŃ€ĐžŃ‡ĐžĐœŃ‹ ĐžĐ·ĐŒĐ”ĐœĐ”ĐœĐžĐč DOM/CSS, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” проĐČДлО Đș ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐ”. + +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** ĐŻĐČĐ»ŃĐ”Ń‚ŃŃ лО Ń…ĐŸŃ€ĐŸŃˆĐ”Đč ŃŃ‚Ń€Đ°ĐœĐžŃ†Đ° с ĐșĐŸĐœŃ‚Đ”ĐœŃ‚ĐŸĐŒ, ĐșĐŸŃ‚ĐŸŃ€Đ°Ń ĐČсД ĐŸŃ‚ĐŸĐ±Ń€Đ°Đ¶Đ°Đ”Ń‚, 100% Ń‚Đ”ŃŃ‚ĐŸĐČ ĐżŃ€ĐŸŃ…ĐŸĐŽĐžŃ‚, Đ±Ń‹ŃŃ‚Ń€ĐŸ Đ·Đ°ĐłŃ€ŃƒĐ¶Đ°Đ”Ń‚ŃŃ, ĐœĐŸ ĐżĐŸĐ»ĐŸĐČĐžĐœĐ° ĐŸĐ±Đ»Đ°ŃŃ‚Đž ĐșĐŸĐœŃ‚Đ”ĐœŃ‚Đ° сĐșрыта? + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :thumbsdown: ĐĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœĐŸ: ĐąĐžĐżĐžŃ‡ĐœĐ°Ń ĐČĐžĐ·ŃƒĐ°Đ»ŃŒĐœĐ°Ń Ń€Đ”ĐłŃ€Đ”ŃŃĐžŃ - ĐșĐŸĐœŃ‚Đ”ĐœŃ‚, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč ĐżĐ»ĐŸŃ…ĐŸ ĐŸĐ±Đ»ŃƒĐ¶ĐžĐČĐ°Đ”Ń‚ŃŃ + +![alt text](assets/amazon-visual-regression.jpeg "Amazon page breaks") + +
    + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: ĐĐ°ŃŃ‚Ń€ĐŸĐčĐșа ĐŽĐ»Ń ĐżĐŸĐ»ŃƒŃ‡Đ”ĐœĐžŃ Đž сраĐČĐœĐ”ĐœĐžŃ ŃĐœĐžĐŒĐșĐŸĐČ UI + +![](https://img.shields.io/badge/🔹%20Example%20using%20Wraith-blue.svg "Using Wraith") + +``` + # Đ”ĐŸĐ±Đ°ĐČŃŒŃ‚Đ” ŃŃ‚ĐŸĐ»ŃŒĐșĐŸ ĐŽĐŸĐŒĐ”ĐœĐŸĐČ, сĐșĐŸĐ»ŃŒĐșĐŸ ĐœŃƒĐ¶ĐœĐŸ. Ключ ĐŽĐ”ĐčстĐČŃƒĐ”Ń‚, ĐșаĐș ĐŒĐ”Ń‚Đșа + +domains: + english: "http://www.mysite.com" + + # ВĐČДЎОтД ŃˆĐžŃ€ĐžĐœŃƒ эĐșŃ€Đ°ĐœĐ° ĐœĐžĐ¶Đ”, ĐČĐŸŃ‚ ĐœĐ”ŃĐșĐŸĐ»ŃŒĐșĐŸ ĐżŃ€ĐžĐŒĐ”Ń€ĐŸĐČ + +screen_widths: + + - 600 + - 768 + - 1024 + - 1280 + + # ВĐČДЎОтД URL ŃŃ‚Ń€Đ°ĐœĐžŃ†Ń‹, ĐČĐŸŃ‚ ĐœĐ”ŃĐșĐŸĐ»ŃŒĐșĐŸ ĐżŃ€ĐžĐŒĐ”Ń€ĐŸĐČ: + about: + path: /about + selector: '.about' + subscribe: + selector: '.subscribe' + path: /subscribe +``` + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: Đ˜ŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°ĐœĐžĐ” Applitools ĐŽĐ»Ń ĐżĐŸĐ»ŃƒŃ‡Đ”ĐœĐžŃ Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚Đ° сраĐČĐœĐ”ĐœĐžŃ ŃĐœĐžĐŒĐșĐŸĐČ Đž Юругох ĐČĐŸĐ·ĐŒĐŸĐ¶ĐœĐŸŃŃ‚Đ”Đč + +![](https://img.shields.io/badge/🔹%20Example%20using%20AppliTools-blue.svg "Using Applitools") ![](https://img.shields.io/badge/🔹%20Example%20using%20Cypress-blue.svg "Using Cypress to illustrate the idea") + +```javascript +import * as todoPage from "../page-objects/todo-page"; + +describe("visual validation", () => { + before(() => todoPage.navigate()); + beforeEach(() => cy.eyesOpen({ appName: "TAU TodoMVC" })); + afterEach(() => cy.eyesClose()); + + it("should look good", () => { + cy.eyesCheckWindow("empty todo list"); + todoPage.addTodo("Clean room"); + todoPage.addTodo("Learn javascript"); + cy.eyesCheckWindow("two todos"); + todoPage.toggleTodo(0); + cy.eyesCheckWindow("mark as completed"); + }); +}); +``` + +
    + +

    + +# РазЎДл 4ïžâƒŁ: Đ˜Đ·ĐŒĐ”Ń€Đ”ĐœĐžĐ” ŃŃ„Ń„Đ”ĐșтоĐČĐœĐŸŃŃ‚Đž Ń‚Đ”ŃŃ‚ĐŸĐČ + +

    + +## âšȘ  4.1 ĐŸĐŸĐ»ŃƒŃ‡ĐžŃ‚Đ” ĐŽĐŸŃŃ‚Đ°Ń‚ĐŸŃ‡ĐœĐŸĐ” ĐżĐŸĐșрытОД Ń‚Đ”ŃŃ‚Đ°ĐŒĐž. ОĐșĐŸĐ»ĐŸ 80% Đ±ŃƒĐŽĐ”Ń‚ ĐČĐżĐŸĐ»ĐœĐ” ĐŽĐŸŃŃ‚Đ°Ń‚ĐŸŃ‡ĐœĐŸ + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** ĐŠĐ”Đ»ŃŒ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ - ĐżĐŸĐ»ŃƒŃ‡ĐžŃ‚ŃŒ ĐŽĐŸŃŃ‚Đ°Ń‚ĐŸŃ‡ĐœĐŸ уĐČĐ”Ń€Đ”ĐœĐœĐŸŃŃ‚ŃŒ ĐŽĐ»Ń Ń‚ĐŸĐłĐŸ, Ń‡Ń‚ĐŸĐ±Ń‹ ĐŽĐČогаться ĐŽĐ°Đ»ŃŒŃˆĐ”. ОчДĐČĐžĐŽĐœĐŸ, Ń‡Ń‚ĐŸ Ń‡Đ”ĐŒ Đ±ĐŸĐ»ŃŒŃˆĐ” ĐșĐŸĐŽĐ° ĐżŃ€ĐŸŃ‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐŸ, Ń‚Đ”ĐŒ Đ±ĐŸĐ»ŃŒŃˆĐ” уĐČĐ”Ń€Đ”ĐœĐœĐŸŃŃ‚ŃŒ, Ń‡Ń‚ĐŸ ĐŒĐŸĐ¶ĐœĐŸ оЮто ĐČпДрДЎ. ĐŸĐŸĐșрытОД - ŃŃ‚ĐŸ ĐŒĐ”Ń€Đ° Ń‚ĐŸĐłĐŸ, сĐșĐŸĐ»ŃŒĐșĐŸ ŃŃ‚Ń€ĐŸĐș ĐșĐŸĐŽĐ° ĐŸŃ…ĐČĐ°Ń‡Đ”ĐœĐ° Ń‚Đ”ŃŃ‚Đ°ĐŒĐž. Đ’ĐŸĐ·ĐœĐžĐșаДт ĐČĐŸĐżŃ€ĐŸŃ: сĐșĐŸĐ»ŃŒĐșĐŸ ĐŽĐŸŃŃ‚Đ°Ń‚ĐŸŃ‡ĐœĐŸ? ОчДĐČĐžĐŽĐœĐŸ, Ń‡Ń‚ĐŸ 10-30% слОшĐșĐŸĐŒ ĐŒĐ°Đ»ĐŸ, Ń‡Ń‚ĐŸĐ±Ń‹ ĐżĐŸĐ»ŃƒŃ‡ĐžŃ‚ŃŒ Ń…ĐŸŃ‚ŃŒ ĐșаĐșĐŸĐ”-Ń‚ĐŸ прДЎстаĐČĐ»Đ”ĐœĐžĐ” ĐŸ ĐșĐŸŃ€Ń€Đ”ĐșŃ‚ĐœĐŸŃŃ‚Đž ŃĐ±ĐŸŃ€ĐșĐž, с ĐŽŃ€ŃƒĐłĐŸĐč ŃŃ‚ĐŸŃ€ĐŸĐœŃ‹, 100% - ŃŃ‚ĐŸ ĐŸŃ‡Đ”ĐœŃŒ ĐŽĐŸŃ€ĐŸĐłĐŸ Đž ĐŒĐŸĐ¶Đ”Ń‚ ŃĐŒĐ”ŃŃ‚ĐžŃ‚ŃŒ Ń„ĐŸĐșус с ĐșрОтОчДсĐșох ĐżŃƒŃ‚Đ”Đč ĐœĐ° ĐŸŃ‡Đ”ĐœŃŒ "эĐșĐ·ĐŸŃ‚ĐžŃ‡Đ”ŃĐșОД ŃƒĐłĐŸĐ»ĐșĐž ĐșĐŸĐŽĐ°". ОтĐČДт заĐșĐ»ŃŽŃ‡Đ°Đ”Ń‚ŃŃ ĐČ Ń‚ĐŸĐŒ, Ń‡Ń‚ĐŸ ŃŃ‚ĐŸ заĐČосот ĐŸŃ‚ ĐŒĐœĐŸĐłĐžŃ… фаĐșŃ‚ĐŸŃ€ĐŸĐČ, таĐșох ĐșаĐș, ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, топ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ. ЕслО ĐČы ŃĐŸĐ·ĐŽĐ°Đ”Ń‚Đ” ŃĐ»Đ”ĐŽŃƒŃŽŃ‰Đ”Đ” ĐżĐŸĐșĐŸĐ»Đ”ĐœĐžŃ Airbus A380, Ń‚ĐŸ 100% ĐŽĐŸĐ»Đ¶ĐœĐŸ Đ±Ń‹Ń‚ŃŒ ĐŸĐ±ŃĐ·Đ°Ń‚Đ”Đ»ŃŒĐœŃ‹ĐŒ ŃƒŃĐ»ĐŸĐČĐžĐ”ĐŒ, а ДслО саĐčт с ĐșĐ°Ń€Ń‚ĐžĐœĐșĐ°ĐŒĐž - 50% ĐŒĐŸĐ¶Đ”Ń‚ Đ±Ń‹Ń‚ŃŒ слОшĐșĐŸĐŒ ĐŒĐœĐŸĐłĐŸ. Đ„ĐŸŃ‚Ń Đ±ĐŸĐ»ŃŒŃˆĐžĐœŃŃ‚ĐČĐŸ ŃĐœŃ‚ŃƒĐ·ĐžĐ°ŃŃ‚ĐŸĐČ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ утĐČДржЎаДт, Ń‡Ń‚ĐŸ праĐČĐžĐ»ŃŒĐœŃ‹Đč ĐżĐŸŃ€ĐŸĐł ĐżĐŸĐșрытоя заĐČосот ĐŸŃ‚ ĐșĐŸĐœŃ‚Đ”Đșста, Đ±ĐŸĐ»ŃŒŃˆĐžĐœŃŃ‚ĐČĐŸ Оз ĐœĐžŃ… таĐșжД ŃƒĐżĐŸĐŒĐžĐœĐ°ŃŽŃ‚ Ń‡ĐžŃĐ»ĐŸ 80% ([Fowler: "ĐČ ĐČĐ”Ń€Ń…ĐœĐžŃ… 80-х ОлО 90-х"] (https://martinfowler.com/bliki/TestCoverage.html)), ĐșĐŸŃ‚ĐŸŃ€ĐŸĐ” ĐżŃ€Đ”ĐŽĐżĐŸĐ»ĐŸĐ¶ĐžŃ‚Đ”Đ»ŃŒĐœĐŸ ĐŽĐŸĐ»Đ¶ĐœĐŸ ŃƒĐŽĐŸĐČлДтĐČĐŸŃ€ŃŃ‚ŃŒ Đ±ĐŸĐ»ŃŒŃˆĐžĐœŃŃ‚ĐČу ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐč. + +ĐĄĐŸĐČДты: ĐœĐŸĐ¶ĐœĐŸ ĐœĐ°ŃŃ‚Ń€ĐŸĐžŃ‚ŃŒ ĐœĐ”ĐżŃ€Đ”Ń€Ń‹ĐČĐœŃƒŃŽ ĐžĐœŃ‚Đ”ĐłŃ€Đ°Ń†ĐžŃŽ (CI) ĐœĐ° ĐżĐŸŃ€ĐŸĐł ĐżĐŸĐșрытоя ([ссылĐșа ĐœĐ° Jest](https://jestjs.io/docs/en/configuration.html#collectcoverage-boolean)) Đž ĐŸŃŃ‚Đ°ĐœĐ°ĐČлОĐČать ŃĐ±ĐŸŃ€Đșу, ĐșĐŸŃ‚ĐŸŃ€Đ°Ń ĐœĐ” ŃĐŸĐŸŃ‚ĐČДтстĐČŃƒĐ”Ń‚ ŃŃ‚ĐŸĐŒŃƒ ŃŃ‚Đ°ĐœĐŽĐ°Ń€Ń‚Ńƒ (таĐșжД ĐŒĐŸĐ¶ĐœĐŸ ĐœĐ°ŃŃ‚Ń€ĐŸĐžŃ‚ŃŒ ĐżĐŸŃ€ĐŸĐł ĐŽĐ»Ń ĐșĐ°Đ¶ĐŽĐŸĐłĐŸ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đ°, ŃĐŒ. ĐżŃ€ĐžĐŒĐ”Ń€ ĐșĐŸĐŽĐ° ĐœĐžĐ¶Đ”). +В ĐŽĐŸĐżĐŸĐ»ĐœĐ”ĐœĐžĐ” Đș ŃŃ‚ĐŸĐŒŃƒ, Ń€Đ°ŃŃĐŒĐŸŃ‚Ń€ĐžŃ‚Đ” ĐČĐŸĐ·ĐŒĐŸĐ¶ĐœĐŸŃŃ‚ŃŒ ĐŸĐ±ĐœĐ°Ń€ŃƒĐ¶Đ”ĐœĐžŃ ŃĐœĐžĐ¶Đ”ĐœĐžŃ ĐżĐŸĐșрытоя ŃĐ±ĐŸŃ€ĐșĐž (ĐșĐŸĐłĐŽĐ° Ń‚ĐŸĐ»ŃŒĐșĐŸ Ń‡Ń‚ĐŸ зафОĐșŃĐžŃ€ĐŸĐČĐ°ĐœĐœŃ‹Đč ĐșĐŸĐŽ ĐžĐŒĐ”Đ”Ń‚ ĐŒĐ”ĐœŃŒŃˆĐ”Đ” ĐżĐŸĐșрытОД) - ŃŃ‚ĐŸ ĐżĐŸĐŽŃ‚ĐŸĐ»ĐșĐœĐ”Ń‚ Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐșĐŸĐČ Đș уĐČĐ”Đ»ĐžŃ‡Đ”ĐœĐžŃŽ ОлО, ĐżĐŸ ĐșраĐčĐœĐ”Đč ĐŒĐ”Ń€Đ”, ŃĐŸŃ…Ń€Đ°ĐœĐ”ĐœĐžŃŽ ĐșĐŸĐ»ĐžŃ‡Đ”ŃŃ‚ĐČа Ń‚Đ”ŃŃ‚ĐžŃ€ŃƒĐ”ĐŒĐŸĐłĐŸ ĐșĐŸĐŽĐ°. Про ĐČŃĐ”ĐŒ ŃŃ‚ĐŸĐŒ, ĐżĐŸĐșрытОД - ŃŃ‚ĐŸ Ń‚ĐŸĐ»ŃŒĐșĐŸ ĐŸĐŽĐœĐ° Оз ĐŒĐ”Ń€, ĐŸŃĐœĐŸĐČĐ°ĐœĐœĐ°Ń ĐœĐ° ĐșĐŸĐ»ĐžŃ‡Đ”ŃŃ‚ĐČĐ”ĐœĐœŃ‹Ń… ĐżĐŸĐșĐ°Đ·Đ°Ń‚Đ”Đ»ŃŃ…, ĐșĐŸŃ‚ĐŸŃ€ĐŸĐč ĐœĐ”ĐŽĐŸŃŃ‚Đ°Ń‚ĐŸŃ‡ĐœĐŸ ĐŽĐ»Ń ĐŸĐżŃ€Đ”ĐŽĐ”Đ»Đ”ĐœĐžŃ ĐœĐ°ĐŽĐ”Đ¶ĐœĐŸŃŃ‚Đž ĐČĐ°ŃˆĐ”ĐłĐŸ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ. ĐŸĐŸĐŒĐžĐŒĐŸ ĐČŃĐ”ĐłĐŸ ĐżŃ€ĐŸŃ‡Đ”ĐłĐŸ, Đ”ĐłĐŸ ĐŒĐŸĐ¶ĐœĐŸ ĐŸĐ±ĐŒĐ°ĐœŃƒŃ‚ŃŒ, ĐșаĐș Đ±ŃƒĐŽĐ”Ń‚ ĐżŃ€ĐŸĐŽĐ”ĐŒĐŸĐœŃŃ‚Ń€ĐžŃ€ĐŸĐČĐ°ĐœĐŸ ĐČ ŃĐ»Đ”ĐŽŃƒŃŽŃ‰ĐžŃ… ĐżŃƒĐœĐșтах. + +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** ĐŁĐČĐ”Ń€Đ”ĐœĐœĐŸŃŃ‚ŃŒ Đž цофры оЮут руĐșа ĐŸĐ± руĐșу, ĐœĐ” Đ·ĐœĐ°Ń, Ń‡Ń‚ĐŸ ĐČы ĐżŃ€ĐŸŃ‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČалО Đ±ĐŸĐ»ŃŒŃˆŃƒŃŽ часть ŃĐžŃŃ‚Đ”ĐŒŃ‹ - Đ±ŃƒĐŽĐ”Ń‚ просутстĐČĐŸĐČать ŃĐŸĐŒĐœĐ”ĐœĐžĐ”, ĐșĐŸŃ‚ĐŸŃ€ĐŸĐ” Đ±ŃƒĐŽĐ”Ń‚ ĐČас Ń‚ĐŸŃ€ĐŒĐŸĐ·ĐžŃ‚ŃŒ. + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :clap: ĐŸŃ€ĐžĐŒĐ”Ń€: ОтчДт ĐŸ ĐżĐŸĐșрытоо Ń‚Đ”ŃŃ‚Đ°ĐŒĐž + +![alt text](assets/bp-18-yoni-goldberg-code-coverage.png "A typical coverage report") + +
    + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: ĐĐ°ŃŃ‚Ń€ĐŸĐčĐșа ĐżĐŸĐșрытоя ĐșĐ°Đ¶ĐŽĐŸĐłĐŸ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đ°, ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒŃ Jest + +![](https://img.shields.io/badge/🔹%20Example%20using%20Jest-blue.svg "Using Jest") + +![alt text](assets/bp-18-code-coverage2.jpeg "Setting up coverage per component (using Jest)") + +
    + +

    + +## âšȘ  4.2 ĐŸŃ€ĐŸŃĐŒĐŸŃ‚Ń€ĐžŃ‚Đ” ĐŸŃ‚Ń‡Đ”Ń‚Ń‹ ĐŸ ĐżĐŸĐșрытоо, Ń‡Ń‚ĐŸĐ±Ń‹ ĐœĐ°Đčто ĐœĐ”ĐżŃ€ĐŸŃ‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐœŃ‹Đ” участо ĐșĐŸĐŽĐ° + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** ĐĐ”ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒŃ‹ с ĐșĐŸĐŽĐŸĐŒ ĐžĐœĐŸĐłĐŽĐ° ĐŸŃŃ‚Đ°ŃŽŃ‚ŃŃ ĐœĐ”Đ·Đ°ĐŒĐ”Ń‡Đ”ĐœĐœŃ‹ĐŒĐž. Их ĐŽĐ”ĐčстĐČĐžŃ‚Đ”Đ»ŃŒĐœĐŸ Ń‚Ń€ŃƒĐŽĐœĐŸ ĐŸĐ±ĐœĐ°Ń€ŃƒĐ¶ĐžŃ‚ŃŒ ЎажД с ĐżĐŸĐŒĐŸŃ‰ŃŒŃŽ Ń‚Ń€Đ°ĐŽĐžŃ†ĐžĐŸĐœĐœŃ‹Ń… ĐžĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚ĐŸĐČ. ĐžĐœĐž ĐœĐ” ĐČсДгЎа яĐČĐ»ŃŃŽŃ‚ŃŃ ĐŸŃˆĐžĐ±ĐșĐ°ĐŒĐž, а ĐžĐœĐŸĐłĐŽĐ° ŃŃ‚ĐŸ сĐșĐŸŃ€Đ”Đ” ĐœĐ”ĐŸĐ¶ĐžĐŽĐ°ĐœĐœĐŸĐ” ĐżĐŸĐČĐ”ĐŽĐ”ĐœĐžĐ” ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ, ĐșĐŸŃ‚ĐŸŃ€ĐŸĐ” ĐŒĐŸĐ¶Đ”Ń‚ ĐžĐŒĐ”Ń‚ŃŒ ŃĐ”Ń€ŃŒĐ”Đ·ĐœŃ‹Đ” ĐżĐŸŃĐ»Đ”ĐŽŃŃ‚ĐČоя. ĐĐ°ĐżŃ€ĐžĐŒĐ”Ń€, Ń‡Đ°ŃŃ‚ĐŸ ĐœĐ”ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐŸĐ±Đ»Đ°ŃŃ‚Đž ĐșĐŸĐŽĐ° рДЎĐșĐŸ ОлО ĐżĐŸŃ‡Ń‚Đž ĐœĐžĐșĐŸĐłĐŽĐ° ĐœĐ” ĐČызыĐČаются - ĐČы ĐŽŃƒĐŒĐ°Đ»Đž, Ń‡Ń‚ĐŸ Đșласс 'PricingCalculator' ĐČсДгЎа ŃƒŃŃ‚Đ°ĐœĐ°ĐČлОĐČаДт Ń†Đ”ĐœŃƒ ĐżŃ€ĐŸĐŽŃƒĐșта, ĐœĐŸ ĐŸĐșĐ°Đ·Đ°Đ»ĐŸŃŃŒ, Ń‡Ń‚ĐŸ ĐŸĐœ фаĐșтОчДсĐșĐž ĐœĐžĐșĐŸĐłĐŽĐ° ĐœĐ” ĐČызыĐČĐ°Đ”Ń‚ŃŃ, Ń…ĐŸŃ‚Ń у ĐœĐ°Ń 10000 ĐżŃ€ĐŸĐŽŃƒĐșŃ‚ĐŸĐČ ĐČ Đ‘Đ” Đž ĐŒĐœĐŸĐłĐŸ ĐżŃ€ĐŸĐŽĐ°Đ¶... ОтчДты ĐŸ ĐżĐŸĐșрытоо ĐșĐŸĐŽĐ° ĐżĐŸĐŒĐŸĐłŃƒŃ‚ ĐČĐ°ĐŒ ĐżĐŸĐœŃŃ‚ŃŒ, ĐČДЎДт лО ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ” ŃĐ”Đ±Ń таĐș, ĐșаĐș ĐČы счОтаДтД. ĐšŃ€ĐŸĐŒĐ” Ń‚ĐŸĐłĐŸ, ĐŸĐœĐž ĐŒĐŸĐłŃƒŃ‚ ĐżĐŸĐșĐ°Đ·Đ°Ń‚ŃŒ, ĐșаĐșОД топы ĐșĐŸĐŽĐ° ĐœĐ” ĐżŃ€ĐŸŃ‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœŃ‹ - ДслО ĐČĐ°ĐŒ ŃĐŸĐŸĐ±Ń‰Đ°ŃŽŃ‚, Ń‡Ń‚ĐŸ 80% ĐșĐŸĐŽĐ° ĐżŃ€ĐŸŃ‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐŸ, ŃŃ‚ĐŸ ДщД ĐœĐ” ĐłĐŸĐČĐŸŃ€ĐžŃ‚ ĐŸ Ń‚ĐŸĐŒ, ĐżĐŸĐșрыты лО ĐșрОтОчДсĐșОД часто. Đ”Đ»Ń ĐłĐ”ĐœĐ”Ń€Đ°Ń†ĐžĐž ĐŸŃ‚Ń‡Đ”Ń‚ĐŸĐČ ĐœŃƒĐ¶ĐœĐŸ ĐżŃ€ĐŸŃŃ‚ĐŸ Đ·Đ°ĐżŃƒŃŃ‚ĐžŃ‚ŃŒ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ Đ»ĐžĐ±ĐŸ ĐČ ĐżŃ€ĐŸĐŽĐ°ĐșŃˆĐœ, Đ»ĐžĐ±ĐŸ ĐČĐŸ ĐČŃ€Đ”ĐŒŃ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ, а Đ·Đ°Ń‚Đ”ĐŒ ох ĐżŃ€ĐŸŃĐŒĐŸŃ‚Ń€Đ”Ń‚ŃŒ. ĐžĐœĐž ĐżĐŸĐșазыĐČают, ĐșаĐș Ń‡Đ°ŃŃ‚ĐŸ ĐČызыĐČĐ°Đ”Ń‚ŃŃ, ĐșĐ°Đ¶ĐŽĐ°Ń ĐŸĐ±Đ»Đ°ŃŃ‚ŃŒ ĐșĐŸĐŽĐ° Đž, ДслО ĐČы ĐżĐŸŃ‚Ń€Đ°Ń‚ĐžŃ‚Đ” ĐČŃ€Đ”ĐŒŃ ĐœĐ° ох ĐžĐ·ŃƒŃ‡Đ”ĐœĐžĐ”, Ń‚ĐŸ ŃĐŒĐŸĐ¶Đ”Ń‚Đ” ĐŸĐ±ĐœĐ°Ń€ŃƒĐ¶ĐžŃ‚ŃŒ ĐœĐ”ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒŃ‹ с ĐșĐŸĐŽĐŸĐŒ. +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** ЕслО ĐČы ĐœĐ” Đ·ĐœĐ°Đ”Ń‚Đ”, ĐșаĐșая часть ĐșĐŸĐŽĐ° у ĐČас ĐœĐ” ĐżŃ€ĐŸŃ‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐ°, Ń‚ĐŸ ĐČы ĐœĐ” ŃĐŒĐŸĐ¶Đ”Ń‚Đ” ŃƒĐ·ĐœĐ°Ń‚ŃŒ, ĐŸŃ‚ĐșуЎа ĐŒĐŸĐłŃƒŃ‚ ĐČĐŸĐ·ĐœĐžĐșĐœŃƒŃ‚ŃŒ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒŃ‹ + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :thumbsdown: ĐĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœĐŸ: Đ§Ń‚ĐŸ ĐœĐ” таĐș с ŃŃ‚ĐžĐŒ ĐŸŃ‚Ń‡Đ”Ń‚ĐŸĐŒ? + +На ĐŸŃĐœĐŸĐČĐ” Ń€Đ”Đ°Đ»ŃŒĐœĐŸĐłĐŸ ŃŃ†Đ”ĐœĐ°Ń€ĐžŃ, ĐșĐŸĐłĐŽĐ° ĐŒŃ‹ ĐŸŃ‚ŃĐ»Đ”Đ¶ĐžĐČалО ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°ĐœĐžĐ” ĐœĐ°ŃˆĐ”ĐłĐŸ ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ ĐČ QA Đž ĐŸĐ±ĐœĐ°Ń€ŃƒĐ¶ĐžĐ»Đž ĐžĐœŃ‚Đ”Ń€Đ”ŃĐœŃ‹Đ” заĐșĐŸĐœĐŸĐŒĐ”Ń€ĐœĐŸŃŃ‚Đž ĐČŃ…ĐŸĐŽĐ° ĐČ ŃĐžŃŃ‚Đ”ĐŒŃƒ (ĐżĐŸĐŽŃĐșазĐșа: ĐșĐŸĐ»ĐžŃ‡Đ”ŃŃ‚ĐČĐŸ ĐŸŃ‚ĐșĐ°Đ·ĐŸĐČ ĐČŃ…ĐŸĐŽĐ° ĐœĐ”ĐżŃ€ĐŸĐżĐŸŃ€Ń†ĐžĐŸĐœĐ°Đ»ŃŒĐœĐŸ, Ń‡Ń‚ĐŸ-Ń‚ĐŸ яĐČĐœĐŸ ĐœĐ” таĐș. В ĐžŃ‚ĐŸĐłĐ” ĐČŃ‹ŃŃĐœĐžĐ»ĐŸŃŃŒ, Ń‡Ń‚ĐŸ ĐșаĐșая-Ń‚ĐŸ ĐŸŃˆĐžĐ±Đșа ĐČĐŸ Ń„Ń€ĐŸĐœŃ‚Đ”ĐœĐŽĐ” ĐżĐŸŃŃ‚ĐŸŃĐœĐœĐŸ Đ±ŃŒĐ”Ń‚ ĐżĐŸ API ĐČŃ…ĐŸĐŽĐ° ĐČ Đ±ŃĐșĐ”ĐœĐŽ). + +![alt text](assets/bp-19-coverage-yoni-goldberg-nodejs-consultant.png "What’s wrong with this coverage report?") + +
    + +

    + +## âšȘ  4.3 Đ˜Đ·ĐŒĐ”Ń€Đ”ĐœĐžĐ” ĐżĐŸĐșрытоя с ĐżĐŸĐŒĐŸŃ‰ŃŒŃŽ mutation-Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** ĐąŃ€Đ°ĐŽĐžŃ†ĐžĐŸĐœĐœĐ°Ń ĐŒĐ”Ń‚Ń€ĐžĐșа Coverage Ń‡Đ°ŃŃ‚ĐŸ лжДт: ĐžĐœĐ° ĐŒĐŸĐ¶Đ”Ń‚ ĐżĐŸĐșĐ°Đ·Đ°Ń‚ŃŒ ĐČĐ°ĐŒ 100% ĐżĐŸĐșрытОД ĐșĐŸĐŽĐ°, ĐœĐŸ ĐœĐž ĐŸĐŽĐœĐ° Оз ĐČашох Ń„ŃƒĐœĐșцоĐč, ЎажД ĐŸĐŽĐœĐ°, ĐœĐ” ĐČĐŸĐ·ĐČращаДт праĐČĐžĐ»ŃŒĐœŃ‹Đč ĐŸŃ‚ĐČДт. КаĐș таĐș? ĐžĐœĐ° ĐżŃ€ĐŸŃŃ‚ĐŸ ĐžĐ·ĐŒĐ”Ń€ŃĐ”Ń‚, ĐČ ĐșаĐșох ŃŃ‚Ń€ĐŸĐșах ĐșĐŸĐŽĐ° ĐżĐŸĐ±Ń‹ĐČал тДст, ĐœĐŸ ĐœĐ” ĐżŃ€ĐŸĐČĐ”Ń€ŃĐ”Ń‚, ĐŽĐ”ĐčстĐČĐžŃ‚Đ”Đ»ŃŒĐœĐŸ лО тДсты Ń‡Ń‚ĐŸ-Ń‚ĐŸ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČалО. КаĐș Ń‡Đ”Đ»ĐŸĐČĐ”Đș, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč ĐżŃƒŃ‚Đ”ŃˆĐ”ŃŃ‚ĐČŃƒĐ”Ń‚ ĐżĐŸ ĐŽĐ”Đ»Đ°ĐŒ Đž ĐżĐŸĐșазыĐČаДт сĐČĐŸĐž ŃˆŃ‚Đ°ĐŒĐżŃ‹ ĐČ ĐżĐ°ŃĐżĐŸŃ€Ń‚Đ” - ŃŃ‚ĐŸ ĐœĐ” ĐŽĐŸĐșазыĐČаДт ĐœĐžŃ‡Đ”ĐłĐŸ ĐșŃ€ĐŸĐŒĐ” Ń‚ĐŸĐłĐŸ, Ń‡Ń‚ĐŸ ĐŸĐœ ĐżĐŸŃĐ”Ń‚ĐžĐ» ĐœĐ”ŃĐșĐŸĐ»ŃŒĐșĐŸ Đ°ŃŃ€ĐŸĐżĐŸŃ€Ń‚ĐŸĐČ Đž ĐŸŃ‚Đ”Đ»Đ”Đč. + +ĐąĐ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” ĐœĐ° ĐŸŃĐœĐŸĐČĐ” ĐŒŃƒŃ‚Đ°Ń†ĐžĐč прОзĐČĐ°ĐœĐŸ ĐżĐŸĐŒĐŸŃ‡ŃŒ ĐČ ŃŃ‚ĐŸĐŒ, ĐžĐ·ĐŒĐ”Ń€ŃŃ ĐșĐŸĐ»ĐžŃ‡Đ”ŃŃ‚ĐČĐŸ ĐșĐŸĐŽĐ°, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč был ĐŽĐ”ĐčстĐČĐžŃ‚Đ”Đ»ŃŒĐœĐŸ ПРОВЕРЕН, а ĐœĐ” ĐżŃ€ĐŸŃŃ‚ĐŸ ПОСМОбРЕН. [Stryker](https://stryker-mutator.io/) - ŃŃ‚ĐŸ JavaScript-Đ±ĐžĐ±Đ»ĐžĐŸŃ‚Đ”Đșа ĐŽĐ»Ń ĐŒŃƒŃ‚Đ°Ń†ĐžĐŸĐœĐœĐŸĐłĐŸ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ, Đž ДД Ń€Đ”Đ°Đ»ĐžĐ·Đ°Ń†ĐžŃ ĐŽĐ”ĐčстĐČĐžŃ‚Đ”Đ»ŃŒĐœĐŸ Ń…ĐŸŃ€ĐŸŃˆĐ°: + +((1) ĐŸĐœ ŃĐżĐ”Ń†ĐžĐ°Đ»ŃŒĐœĐŸ ĐžĐ·ĐŒĐ”ĐœŃĐ”Ń‚ ĐșĐŸĐŽ Đž "заĐșлаЎыĐČаДт ĐŸŃˆĐžĐ±ĐșĐž". ĐĐ°ĐżŃ€ĐžĐŒĐ”Ń€, ĐșĐŸĐŽ newOrder.price===0 ŃŃ‚Đ°ĐœĐŸĐČотся newOrder.price!=0. йаĐșОД "ĐŸŃˆĐžĐ±ĐșĐž" ĐœĐ°Đ·Ń‹ĐČаются ĐŒŃƒŃ‚Đ°Ń†ĐžŃĐŒĐž. + +(2) ĐŸĐœ запусĐșаДт тДсты, ДслО ĐČсД ĐŸĐœĐž ŃƒŃĐżĐ”ŃˆĐœŃ‹, Ń‚ĐŸ у ĐœĐ°Ń ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐ° - тДсты ĐœĐ” ĐČŃ‹ĐżĐŸĐ»ĐœĐžĐ»Đž сĐČĐŸŃŽ Đ·Đ°ĐŽĐ°Ń‡Ńƒ ĐżĐŸ ĐŸĐ±ĐœĐ°Ń€ŃƒĐ¶Đ”ĐœĐžŃŽ ĐŸŃˆĐžĐ±ĐŸĐș, ĐŒŃƒŃ‚Đ°Ń†ĐžĐž ĐČыжОлО. ЕслО тДсты ĐżŃ€ĐŸĐČĐ°Đ»ĐžĐ»ĐžŃŃŒ, Ń‚ĐŸ ĐŸŃ‚Đ»ĐžŃ‡ĐœĐŸ, ĐŒŃƒŃ‚Đ°Ń†ĐžĐž былО ŃƒŃŃ‚Ń€Đ°ĐœĐ”ĐœŃ‹. + +Đ—ĐœĐ°ĐœĐžĐ” Ń‚ĐŸĐłĐŸ, Ń‡Ń‚ĐŸ ĐČсД ОлО Đ±ĐŸĐ»ŃŒŃˆĐžĐœŃŃ‚ĐČĐŸ ĐŒŃƒŃ‚Đ°Ń†ĐžĐč былО ŃƒŃŃ‚Ń€Đ°ĐœĐ”ĐœŃ‹, ЎаДт ĐłĐŸŃ€Đ°Đ·ĐŽĐŸ Đ±ĐŸĐ»ŃŒŃˆŃƒŃŽ уĐČĐ”Ń€Đ”ĐœĐœĐŸŃŃ‚ŃŒ, Ń‡Đ”ĐŒ Ń‚Ń€Đ°ĐŽĐžŃ†ĐžĐŸĐœĐœĐŸĐ” ĐżĐŸĐșрытОД, а ĐČŃ€Đ”ĐŒŃ ĐœĐ°ŃŃ‚Ń€ĐŸĐčĐșĐž ĐżĐŸŃ‡Ń‚Đž ĐœĐ” ĐŸŃ‚Đ»ĐžŃ‡Đ°Đ”Ń‚ŃŃ +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** Вы Đ±ŃƒĐŽĐ”Ń‚Đ” ĐČĐČĐ”ĐŽĐ”ĐœŃ‹ ĐČ Đ·Đ°Đ±Đ»ŃƒĐ¶ĐŽĐ”ĐœĐžĐ”, ĐżĐŸĐ»Đ°ĐłĐ°Ń, Ń‡Ń‚ĐŸ 85% ĐżĐŸĐșрытОД ĐŸĐ·ĐœĐ°Ń‡Đ°Đ”Ń‚, Ń‡Ń‚ĐŸ ĐČаш тДст ĐŸĐ±ĐœĐ°Ń€ŃƒĐ¶ĐžŃ‚ ĐŸŃˆĐžĐ±ĐșĐž ĐČ 85% ĐșĐŸĐŽĐ°. + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :thumbsdown: ĐĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœĐŸ: 100% ĐżĐŸĐșрытОД, 0% ĐżŃ€ĐŸŃ‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐŸ + +![](https://img.shields.io/badge/🔹%20Example%20using%20Stryker-blue.svg "Using Stryker") + +```javascript +function addNewOrder(newOrder) { + logger.log(`Adding new order ${newOrder}`); + DB.save(newOrder); + Mailer.sendMail(newOrder.assignee, `A new order was places ${newOrder}`); + + return { approved: true }; +} + +it("Test addNewOrder, don't use such test names", () => { + addNewOrder({ assignee: "John@mailer.com", price: 120 }); +}); // ЗапусĐșаДт 100% ĐżĐŸĐșрытОД ĐșĐŸĐŽĐ°, ĐœĐŸ ĐœĐžŃ‡Đ”ĐłĐŸ ĐœĐ” ĐżŃ€ĐŸĐČĐ”Ń€ŃĐ”Ń‚ +``` + +
    + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: Stryker reports, ĐžĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚ ĐŽĐ»Ń Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ ĐŒŃƒŃ‚Đ°Ń†ĐžĐč, ĐŸĐ±ĐœĐ°Ń€ŃƒĐ¶ĐžĐČаДт Đž ĐżĐŸĐŽŃŃ‡ĐžŃ‚Ń‹ĐČаДт ĐșĐŸĐ»ĐžŃ‡Đ”ŃŃ‚ĐČĐŸ ĐșĐŸĐŽĐ°, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč ĐœĐ” ĐżŃ€ĐŸŃˆĐ”Đ» ĐżŃ€ĐŸĐČДрĐșу (Мутацоо) + +![alt text](assets/bp-20-yoni-goldberg-mutation-testing.jpeg "Stryker reports, a tool for mutation testing, detects and counts the amount of code that is not tested (Mutations)") + +
    + +

    + +## âšȘ 4.4 ĐŸĐŸĐžŃĐș ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒ с Ń‚Đ”ŃŃ‚Đ°ĐŒĐž про ĐżĐŸĐŒĐŸŃ‰Đž Test Linters + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** ĐĐ°Đ±ĐŸŃ€ ĐżĐ»Đ°ĐłĐžĐœĐŸĐČ ESLint был ŃĐŸĐ·ĐŽĐ°Đœ ŃĐżĐ”Ń†ĐžĐ°Đ»ŃŒĐœĐŸ ĐŽĐ»Ń ĐżŃ€ĐŸĐČДрĐșĐž ŃˆĐ°Đ±Đ»ĐŸĐœĐŸĐČ Ń‚Đ”ŃŃ‚ĐŸĐČĐŸĐłĐŸ ĐșĐŸĐŽĐ° Đž ĐŸĐ±ĐœĐ°Ń€ŃƒĐ¶Đ”ĐœĐžŃ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒ. ĐĐ°ĐżŃ€ĐžĐŒĐ”Ń€, [eslint-plugin-mocha](https://www.npmjs.com/package/eslint-plugin-mocha) ĐżŃ€Đ”ĐŽŃƒĐżŃ€Đ”ĐŽĐžŃ‚, ĐșĐŸĐłĐŽĐ° тДст ĐœĐ°ĐżĐžŃĐ°Đœ ĐœĐ° ĐłĐ»ĐŸĐ±Đ°Đ»ŃŒĐœĐŸĐŒ ŃƒŃ€ĐŸĐČĐœĐ” (ĐœĐ” ĐŽĐŸŃ‡Đ”Ń€ĐœĐžĐč ŃĐ»Đ”ĐŒĐ”ĐœŃ‚ describe()) ОлО ĐșĐŸĐłĐŽĐ° тДсты [ĐżŃ€ĐŸĐżŃƒŃ‰Đ”ĐœŃ‹](https://mochajs.org/#inclusive-tests) Ń‡Ń‚ĐŸ ĐŒĐŸĐ¶Đ”Ń‚ проĐČДстО Đș Đ»ĐŸĐ¶ĐœĐŸĐŒŃƒ ŃƒĐ±Đ”Đ¶ĐŽĐ”ĐœĐžŃŽ, Ń‡Ń‚ĐŸ ĐČсД тДсты ĐżŃ€ĐŸĐčĐŽĐ”ĐœŃ‹. ĐĐœĐ°Đ»ĐŸĐłĐžŃ‡ĐœĐŸ, [eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest) ĐŒĐŸĐ¶Đ”Ń‚, ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, ĐżŃ€Đ”ĐŽŃƒĐżŃ€Đ”ĐŽĐžŃ‚ŃŒ, ĐșĐŸĐłĐŽĐ° тДст ĐČĐŸĐŸĐ±Ń‰Đ” ĐœĐžŃ‡Đ”ĐłĐŸ ĐœĐ” ĐżŃ€ĐŸĐČĐ”Ń€ŃĐ”Ń‚. + +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** Про ĐČОЎД 90% ĐżĐŸĐșрытоя ĐșĐŸĐŽĐ° Đž 100% Đ·Đ”Đ»Đ”ĐœŃ‹Ń… Ń‚Đ”ŃŃ‚ĐŸĐČ ĐœĐ° ĐČĐ°ŃˆĐ”ĐŒ лОцД ĐżĐŸŃĐČотся ŃˆĐžŃ€ĐŸĐșая ŃƒĐ»Ń‹Đ±Đșа Ń‚ĐŸĐ»ŃŒĐșĐŸ ĐŽĐŸ тДх ĐżĐŸŃ€, ĐżĐŸĐșа ĐČы ĐœĐ” ĐżĐŸĐčĐŒĐ”Ń‚Đ”, Ń‡Ń‚ĐŸ ĐŒĐœĐŸĐłĐžĐ” тДсты ĐœĐžŃ‡Đ”ĐłĐŸ ĐœĐ” ĐżŃ€ĐŸĐČĐ”Ń€ŃŃŽŃ‚, а ĐŒĐœĐŸĐłĐžĐ” Ń‚Đ”ŃŃ‚ĐŸĐČыД ĐœĐ°Đ±ĐŸŃ€Ń‹ былО ĐżŃ€ĐŸŃŃ‚ĐŸ ĐżŃ€ĐŸĐżŃƒŃ‰Đ”ĐœŃ‹. ĐĐ°ĐŽĐ”ŃŽŃŃŒ, ĐČы ĐœĐžŃ‡Đ”ĐłĐŸ ĐœĐ” разĐČĐ”Ń€ĐœŃƒĐ»Đž, ĐŸŃĐœĐŸĐČыĐČаясь ĐœĐ° ŃŃ‚ĐŸĐŒ ĐŸŃˆĐžĐ±ĐŸŃ‡ĐœĐŸĐŒ ĐœĐ°Đ±Đ»ŃŽĐŽĐ”ĐœĐžĐž. + +
    +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :thumbsdown: ĐĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœĐŸ: ĐŸŃ€ĐžĐŒĐ”Ń€ тДста, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč ĐżĐŸĐ»ĐœŃ‹Đč ĐŸŃˆĐžĐ±ĐŸĐș Đž ĐČсД ĐŸĐœĐž ĐżĐŸĐčĐŒĐ°ĐœŃ‹ Đ»ĐžĐœŃ‚Đ”Ń€Đ°ĐŒĐž + +```javascript +describe("Too short description", () => { + const userToken = userService.getDefaultToken(); // ĐŸŃˆĐžĐ±Đșа:no-setup-in-describe, ĐČĐŒĐ”ŃŃ‚ĐŸ ŃŃ‚ĐŸĐłĐŸ ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒĐčтД hooks (эĐșĐŸĐœĐŸĐŒĐœĐŸ) + it("Some description", () => {}); // ĐŸŃˆĐžĐ±Đșа: valid-test-description. Đ”ĐŸĐ»Đ¶ĐœĐŸ ĐČĐșĐ»ŃŽŃ‡Đ°Ń‚ŃŒ "Should" + ĐœĐ” ĐŒĐ”ĐœĐ”Đ” 5 ŃĐ»ĐŸĐČ +}); + +it.skip("Test name", () => { + // ĐŸŃˆĐžĐ±Đșа:no-skipped-tests, ĐŸŃˆĐžĐ±Đșа:no-global-tests. ĐŸĐŸĐŒĐ”ŃŃ‚ĐžŃ‚Đ” тДсты Ń‚ĐŸĐ»ŃŒĐșĐŸ ĐČ describe ОлО suite + expect("somevalue"); // ĐŸŃˆĐžĐ±Đșа:no-assert +}); + +it("Test name", () => { + // ĐŸŃˆĐžĐ±Đșа:no-identical-title. ПросĐČаОĐČаĐčтД Ń‚Đ”ŃŃ‚Đ°ĐŒ ŃƒĐœĐžĐșĐ°Đ»ŃŒĐœŃ‹Đ” ĐœĐ°Đ·ĐČĐ°ĐœĐžŃ +}); +``` + +
    + +

    + +# РазЎДл 5ïžâƒŁ: CI and ĐŽŃ€ŃƒĐłĐžĐ” ĐżĐŸĐșазатДлО ĐșачДстĐČа + +

    + +## âšȘ  5.1 РасшоряĐčтД Đ»ĐžĐœŃ‚Đ”Ń€Ń‹ Đž ĐŸŃŃ‚Đ°ĐœĐ°ĐČлОĐČаĐčтД ŃĐ±ĐŸŃ€ĐșĐž, ĐČ ĐșĐŸŃ‚ĐŸŃ€Ń‹Ń… Đ”ŃŃ‚ŃŒ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒŃ‹ Đ»ĐžĐœŃ‚ĐžĐœĐłĐ° + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** Đ›ĐžĐœŃ‚ĐžĐœĐł - ŃŃ‚ĐŸ Đ±Đ”ŃĐżĐ»Đ°Ń‚ĐœŃ‹Đč ĐŸĐ±Đ”ĐŽ, за 5 ĐŒĐžĐœŃƒŃ‚ ĐœĐ°ŃŃ‚Ń€ĐŸĐčĐșĐž ĐČы ĐżĐŸĐ»ŃƒŃ‡Đ°Đ”Ń‚Đ” Đ±Đ”ŃĐżĐ»Đ°Ń‚ĐœŃ‹Đč аĐČŃ‚ĐŸĐżĐžĐ»ĐŸŃ‚, ĐŸŃ…Ń€Đ°ĐœŃŃŽŃ‰ĐžĐč ĐČаш ĐșĐŸĐŽ Đž ĐŸŃ‚Đ»Đ°ĐČлОĐČающоĐč ŃŃƒŃ‰Đ”ŃŃ‚ĐČĐ”ĐœĐœŃ‹Đ” ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒŃ‹ ĐżĐŸ ĐŒĐ”Ń€Đ” ĐœĐ°Đ±ĐŸŃ€Đ° тДĐșста. ĐŸŃ€ĐŸŃˆĐ»Đž тД ĐČŃ€Đ”ĐŒĐ”ĐœĐ°, ĐșĐŸĐłĐŽĐ° Đ»ĐžĐœŃ‚ĐžĐœĐł сĐČĐŸĐŽĐžĐ»ŃŃ Đș ĐșĐŸŃĐŒĐ”Ń‚ĐžĐșĐ” (ĐœĐžĐșаĐșох ĐżĐŸĐ»ŃƒŃ‚ĐŸĐœĐŸĐČ!). ĐĄĐ”ĐłĐŸĐŽĐœŃ Đ»ĐžĐœŃ‚Đ”Ń€Ń‹ ĐŒĐŸĐłŃƒŃ‚ ĐŸŃ‚Đ»Đ°ĐČлОĐČать ŃĐ”Ń€ŃŒĐ”Đ·ĐœŃ‹Đ” ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒŃ‹, таĐșОД ĐșаĐș ĐœĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœĐŸ Đ±Ń€ĐŸŃˆĐ”ĐœĐœŃ‹Đ” ĐŸŃˆĐžĐ±ĐșĐž Đž ĐżĐŸŃ‚Đ”Ń€Ń ĐžĐœŃ„ĐŸŃ€ĐŒĐ°Ń†ĐžĐž. ĐŸĐŸĐŒĐžĐŒĐŸ ĐŸŃĐœĐŸĐČĐœĐŸĐłĐŸ ĐœĐ°Đ±ĐŸŃ€Đ° праĐČОл (ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, [ESLint standard](https://www.npmjs.com/package/eslint-plugin-standard) ОлО [Airbnb style](https://www.npmjs.com/package/eslint-config-airbnb)), ĐČĐșлючОтД ĐœĐ”ŃĐșĐŸĐ»ŃŒĐșĐŸ ŃĐżĐ”Ń†ĐžĐ°Đ»ĐžĐ·ĐžŃ€ĐŸĐČĐ°ĐœĐœŃ‹Ń… Đ»ĐžĐœŃ‚Đ”Ń€ĐŸĐČ, ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€ [eslint-plugin-chai-expect](https://www.npmjs.com/package/eslint-plugin-chai-expect), ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč ĐŒĐŸĐ¶Đ”Ń‚ ĐŸĐ±ĐœĐ°Ń€ŃƒĐ¶ĐžŃ‚ŃŒ тДсты, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐœĐžŃ‡Đ”ĐłĐŸ ĐœĐ” ĐżŃ€ĐŸĐČĐ”Ń€ŃŃŽŃ‚, [eslint-plugin-promise](https://www.npmjs.com/package/eslint-plugin-promise?activeTab=readme) ĐŒĐŸĐ¶Đ”Ń‚ ĐŸĐ±ĐœĐ°Ń€ŃƒĐ¶ĐžŃ‚ŃŒ ĐżŃ€ĐŸĐŒĐžŃŃ‹ бДз Ń€Đ°Đ·Ń€Đ”ŃˆĐ”ĐœĐžŃ (your code will never continue), [eslint-plugin-security](https://www.npmjs.com/package/eslint-plugin-security?activeTab=readme) ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč ĐŒĐŸĐ¶Đ”Ń‚ ĐŸĐ±ĐœĐ°Ń€ŃƒĐ¶ĐžŃ‚ŃŒ Ń€Đ”ĐłŃƒĐ»ŃŃ€ĐœŃ‹Đ” ĐČŃ‹Ń€Đ°Đ¶Đ”ĐœĐžŃ, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐŒĐŸĐłŃƒŃ‚ Đ±Ń‹Ń‚ŃŒ ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°ĐœŃ‹ ĐŽĐ»Ń DOS атаĐș, Đž [eslint-plugin-you-dont-need-lodash-underscore](https://www.npmjs.com/package/eslint-plugin-you-dont-need-lodash-underscore), ŃĐżĐŸŃĐŸĐ±ĐœŃ‹Đč ĐżŃ€Đ”ĐŽŃƒĐżŃ€Đ”ĐŽĐžŃ‚ŃŒ, ĐșĐŸĐłĐŽĐ° ĐșĐŸĐŽ ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒĐ”Ń‚ ĐŒĐ”Ń‚ĐŸĐŽŃ‹ Đ±ĐžĐ±Đ»ĐžĐŸŃ‚Đ”ĐșĐž ŃƒŃ‚ĐžĐ»ĐžŃ‚, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” яĐČĐ»ŃŃŽŃ‚ŃŃ частью ĐŒĐ”Ń‚ĐŸĐŽĐŸĐČ ŃĐŽŃ€Đ° V8, ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€ Lodash.\_map(...). +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** ĐŸĐŸĐŽŃƒĐŒĐ°ĐčтД ĐŸ Ń‚ĐŸĐŒ ĐŽĐœĐ”, ĐșĐŸĐłĐŽĐ° ĐČаш ĐșĐŸĐŽ упал, ĐœĐŸ ĐČаш Đ»ĐŸĐł ĐœĐ” ĐŸŃ‚ĐŸĐ±Ń€Đ°Đ¶Đ°Đ”Ń‚ Ń‚Ń€Đ°ŃŃĐžŃ€ĐŸĐČĐșу стДĐșа ĐŸŃˆĐžĐ±ĐŸĐș. Đ§Ń‚ĐŸ ĐżŃ€ĐŸĐžĐ·ĐŸŃˆĐ»ĐŸ? Ваш ĐșĐŸĐŽ ĐżĐŸ ĐŸŃˆĐžĐ±ĐșĐ” ĐČŃ‹Đ±Ń€ĐŸŃĐžĐ» ĐŸĐ±ŃŠĐ”Đșт, ĐœĐ” яĐČĐ»ŃŃŽŃ‰ĐžĐčся ĐŸŃˆĐžĐ±ĐșĐŸĐč, Đž Ń‚Ń€Đ°ŃŃĐžŃ€ĐŸĐČĐșа стДĐșа была ĐżĐŸŃ‚Đ”Ń€ŃĐœĐ° - Ń…ĐŸŃ€ĐŸŃˆĐ°Ń ĐżŃ€ĐžŃ‡ĐžĐœĐ° ĐŽĐ»Ń Ń‚ĐŸĐłĐŸ, Ń‡Ń‚ĐŸĐ±Ń‹ Đ±ĐžŃ‚ŃŒŃŃ ĐłĐŸĐ»ĐŸĐČĐŸĐč ĐŸ ĐșĐžŃ€ĐżĐžŃ‡ĐœŃƒŃŽ ŃŃ‚Đ”ĐœŃƒ. 5-ĐŒĐžĐœŃƒŃ‚ĐœĐ°Ń ĐœĐ°ŃŃ‚Ń€ĐŸĐčĐșа Đ»ĐžĐœŃ‚Đ”Ń€Đ° ĐŒĐŸĐ¶Đ”Ń‚ ĐŸĐ±ĐœĐ°Ń€ŃƒĐ¶ĐžŃ‚ŃŒ эту ĐŸŃˆĐžĐ±Đșу Đž спасто ĐČаш ĐŽĐ”ĐœŃŒ + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :thumbsdown: ĐĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœĐŸ: ĐžŃˆĐžĐ±ĐŸŃ‡ĐœĐŸ ĐČыбрасыĐČĐ°Đ”Ń‚ŃŃ ĐœĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœŃ‹Đč ĐŸĐ±ŃŠĐ”Đșт Error, ĐŽĐ»Ń ŃŃ‚ĐŸĐč ĐŸŃˆĐžĐ±ĐșĐž ĐœĐ” ĐżĐŸŃĐČĐ»ŃĐ”Ń‚ŃŃ стДĐș-трДĐčс. К счастью, ESLint ДД ĐŸŃ‚Đ»Đ°ĐČлОĐČаДт + +![alt text](assets/bp-21-yoni-goldberg-eslint.jpeg "The wrong Error object is thrown mistakenly, no stack-trace will appear for this error. Luckily, ESLint catches the next production bug") + +
    + +

    + +## âšȘ  5.2 ĐĄĐŸĐșратОтД цоĐșĐ» ĐŸĐ±Ń€Đ°Ń‚ĐœĐŸĐč сĐČŃĐ·Đž с Đ»ĐŸĐșĐ°Đ»ŃŒĐœŃ‹ĐŒ Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐșĐŸĐŒ-CI + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** Đ’ĐœĐ”ĐŽŃ€Đ”ĐœĐžĐ” CI с Đ±Đ»Đ”ŃŃ‚ŃŃ‰ĐžĐŒĐž ĐżŃ€ĐŸĐČДрĐșĐ°ĐŒĐž ĐșачДстĐČа, таĐșĐžĐŒĐž ĐșаĐș Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ”, Đ»ĐžĐœŃ‚ĐžĐœĐł, ĐżŃ€ĐŸĐČДрĐșа ĐœĐ° ŃƒŃĐ·ĐČĐžĐŒĐŸŃŃ‚Đž Đž ĐŽŃ€ŃƒĐłĐŸĐ”. ĐŸĐŸĐŒĐŸĐłĐžŃ‚Đ” Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐșĐ°ĐŒ Đ·Đ°ĐżŃƒŃŃ‚ĐžŃ‚ŃŒ ŃŃ‚ĐŸŃ‚ ĐșĐŸĐœĐČĐ”ĐčДр таĐșжД Đ»ĐŸĐșĐ°Đ»ŃŒĐœĐŸ, Ń‡Ń‚ĐŸĐ±Ń‹ ĐżĐŸĐ»ŃƒŃ‡ĐžŃ‚ŃŒ ĐŒĐłĐœĐŸĐČĐ”ĐœĐœŃƒŃŽ ĐŸĐ±Ń€Đ°Ń‚ĐœŃƒŃŽ сĐČŃĐ·ŃŒ Đž ŃĐŸĐșратоть [пДтлю ĐŸĐ±Ń€Đ°Ń‚ĐœĐŸĐč сĐČŃĐ·Đž](https://www.gocd.org/2016/03/15/are-you-ready-for-continuous-delivery-part-2-feedback-loops/). ĐŸĐŸŃ‡Đ”ĐŒŃƒ? ЭффДĐșтоĐČĐœŃ‹Đč ĐżŃ€ĐŸŃ†Đ”ŃŃ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ ŃĐŸŃŃ‚ĐŸĐžŃ‚ Оз ĐŒĐœĐŸĐ¶Đ”ŃŃ‚ĐČа ОтДратОĐČĐœŃ‹Ń… цоĐșĐ»ĐŸĐČ: (1) ĐżŃ€ĐŸĐ±ĐœĐŸĐ” Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” -> (2) ĐŸĐ±Ń€Đ°Ń‚ĐœĐ°Ń сĐČŃĐ·ŃŒ -> (3) рДфаĐșŃ‚ĐŸŃ€ĐžĐœĐł. Đ§Đ”ĐŒ быстрДД ĐŸĐ±Ń€Đ°Ń‚ĐœĐ°Ń сĐČŃĐ·ŃŒ, Ń‚Đ”ĐŒ Đ±ĐŸĐ»ŃŒŃˆĐ” ОтДрацОĐč ŃƒĐ»ŃƒŃ‡ŃˆĐ”ĐœĐžŃ Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐș ĐŒĐŸĐ¶Đ”Ń‚ ĐżŃ€ĐŸĐČДстО ĐČ ĐșĐ°Đ¶ĐŽĐŸĐŒ ĐŒĐŸĐŽŃƒĐ»Đ” Đž ŃƒĐ»ŃƒŃ‡ŃˆĐžŃ‚ŃŒ Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚Ń‹. ĐĄ ĐŽŃ€ŃƒĐłĐŸĐč ŃŃ‚ĐŸŃ€ĐŸĐœŃ‹, ĐșĐŸĐłĐŽĐ° ĐŸĐ±Ń€Đ°Ń‚ĐœĐ°Ń сĐČŃĐ·ŃŒ ĐżŃ€ĐžŃ…ĐŸĐŽĐžŃ‚ с Đ·Đ°ĐżĐŸĐ·ĐŽĐ°ĐœĐžĐ”ĐŒ, Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐș ĐŒĐŸĐ¶Đ”Ń‚ пДрДĐčто ужД Đș ĐŽŃ€ŃƒĐłĐŸĐč Ń‚Đ”ĐŒĐ” ОлО заЎачД Đž ĐŸĐœ Đ±ŃƒĐŽĐ”Ń‚ ĐœĐ” ĐłĐŸŃ‚ĐŸĐČ Đș ĐŽĐŸŃ€Đ°Đ±ĐŸŃ‚ĐșĐ” ĐżŃ€Đ”ĐŽŃ‹ĐŽŃƒŃ‰Đ”ĐłĐŸ. + +ĐĐ”ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐșĐž CI (ĐżŃ€ĐžĐŒĐ”Ń€: [CircleCI local CLI](https://circleci.com/docs/2.0/local-cli/)) ĐżĐŸĐ·ĐČĐŸĐ»ŃŃŽŃ‚ запусĐșать ĐșĐŸĐœĐČĐ”ĐčДр Đ»ĐŸĐșĐ°Đ»ŃŒĐœĐŸ: ĐșĐŸĐŒĐŒĐ”Ń€Ń‡Đ”ŃĐșОД сДрĐČосы, таĐșОД ĐșаĐș [wallaby provide highly-valuable & testing insights](https://wallabyjs.com/) ĐČ ĐșачДстĐČĐ” ĐżŃ€ĐŸŃ‚ĐŸŃ‚ĐžĐżĐ° ĐŽĐ»Ń Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐșĐŸĐČ. В ĐșачДстĐČĐ” Đ°Đ»ŃŒŃ‚Đ”Ń€ĐœĐ°Ń‚ĐžĐČы ĐŒĐŸĐ¶ĐœĐŸ ĐżŃ€ĐŸŃŃ‚ĐŸ ĐŽĐŸĐ±Đ°ĐČоть ĐČ package.json сĐșропт npm, запусĐșающоĐč ĐČсД ĐșĐŸĐŒĐ°ĐœĐŽŃ‹ ĐœĐ° ĐżŃ€ĐŸĐČДрĐșу ĐșачДстĐČа (ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, test, lint, vulnerabilities), ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒĐčтД ĐžĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Ń‹ ĐČŃ€ĐŸĐŽĐ” [concurrently](https://www.npmjs.com/package/concurrently) ĐŽĐ»Ń распараллДлОĐČĐ°ĐœĐžŃ Đž ĐœĐ”ĐœŃƒĐ»Đ”ĐČĐŸĐłĐŸ ĐșĐŸĐŽĐ° ĐČŃ‹Ń…ĐŸĐŽĐ° ĐČ ŃĐ»ŃƒŃ‡Đ°Đ” ŃĐ±ĐŸŃ ĐŸĐŽĐœĐŸĐłĐŸ Оз ĐžĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚ĐŸĐČ. ĐąĐ”ĐżĐ”Ń€ŃŒ Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐșу ĐŽĐŸŃŃ‚Đ°Ń‚ĐŸŃ‡ĐœĐŸ ĐČызĐČать ĐŸĐŽĐœŃƒ ĐșĐŸĐŒĐ°ĐœĐŽŃƒ - ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, 'npm run quality' - Ń‡Ń‚ĐŸĐ±Ń‹ ĐżĐŸĐ»ŃƒŃ‡ĐžŃ‚ŃŒ ĐŒĐłĐœĐŸĐČĐ”ĐœĐœŃƒŃŽ ĐŸĐ±Ń€Đ°Ń‚ĐœŃƒŃŽ сĐČŃĐ·ŃŒ. йаĐșжД ŃŃƒŃ‰Đ”ŃŃ‚ĐČŃƒĐ”Ń‚ ĐČĐŸĐ·ĐŒĐŸĐ¶ĐœĐŸŃŃ‚ŃŒ прДрыĐČĐ°ĐœĐžŃ ĐșĐŸĐŒĐŒĐžŃ‚Đ°, ДслО ĐżŃ€ĐŸĐČДрĐșа ĐșачДстĐČа ĐœĐ” ŃƒĐŽĐ°Đ»Đ°ŃŃŒ, с ĐżĐŸĐŒĐŸŃ‰ŃŒŃŽ githook ([husky can help](https://github.com/typicode/husky)). +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** ĐšĐŸĐłĐŽĐ° Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚Ń‹ ĐżŃ€ĐžŃ…ĐŸĐŽŃŃ‚ ĐœĐ° ŃĐ»Đ”ĐŽŃƒŃŽŃ‰Đ”Đč ĐŽĐ”ĐœŃŒ ĐżĐŸŃĐ»Đ” ĐșĐŸĐŽĐ°, Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” ĐœĐ” ŃŃ‚Đ°ĐœĐŸĐČотся ĐœĐ”ĐŸŃ‚ŃŠĐ”ĐŒĐ»Đ”ĐŒĐŸĐč частью Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚ĐșĐž + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: ĐĄĐșропты npm, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐČŃ‹ĐżĐŸĐ»ĐœŃŃŽŃ‚ ĐżŃ€ĐŸĐČДрĐșу ĐșачДстĐČа ĐșĐŸĐŽĐ°, запусĐșаются ĐżĐ°Ń€Đ°Đ»Đ»Đ”Đ»ŃŒĐœĐŸ ĐżĐŸ Ń‚Ń€Đ”Đ±ĐŸĐČĐ°ĐœĐžŃŽ ОлО ĐșĐŸĐłĐŽĐ° Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐș ĐżŃ‹Ń‚Đ°Đ”Ń‚ŃŃ Đ·Đ°ĐżŃƒŃˆĐžŃ‚ŃŒ ĐœĐŸĐČыĐč ĐșĐŸĐŽ. + +```javascript +"scripts": { + "inspect:sanity-testing": "mocha **/**--test.js --grep \"sanity\"", + "inspect:lint": "eslint .", + "inspect:vulnerabilities": "npm audit", + "inspect:license": "license-checker --failOn GPLv2", + "inspect:complexity": "plato .", + + "inspect:all": "concurrently -c \"bgBlue.bold,bgMagenta.bold,yellow\" \"npm:inspect:quick-testing\" \"npm:inspect:lint\" \"npm:inspect:vulnerabilities\" \"npm:inspect:license\"" + }, + "husky": { + "hooks": { + "precommit": "npm run inspect:all", + "prepush": "npm run inspect:all" + } +} + +``` + +
    + +

    + +## âšȘ 5.3 Đ’Ń‹ĐżĐŸĐ»ĐœĐžŃ‚Đ” e2e Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” ĐČ ĐœĐ°ŃŃ‚ĐŸŃŃ‰Đ”Đč ĐżŃ€ĐŸĐŽĐ°ĐșŃˆĐœ-срДЎД + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** ĐšĐŸĐœĐ”Ń‡ĐœĐŸĐ” Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” (e2e) яĐČĐ»ŃĐ”Ń‚ŃŃ ĐŸŃĐœĐŸĐČĐœĐŸĐč ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐŸĐč Đ»ŃŽĐ±ĐŸĐłĐŸ CI-ĐșĐŸĐœĐČĐ”ĐčДра - ŃĐŸĐ·ĐŽĐ°ĐœĐžĐ” ĐżĐŸŃ…ĐŸĐ¶Đ”ĐłĐŸ ĐżŃ€ĐŸĐŽĐ°ĐșŃˆĐœ-зДрĐșала ŃĐŸ ĐČŃĐ”ĐŒĐž ŃĐŸĐżŃƒŃ‚ŃŃ‚ĐČŃƒŃŽŃ‰ĐžĐŒĐž ĐŸĐ±Đ»Đ°Ń‡ĐœŃ‹ĐŒĐž сДрĐČĐžŃĐ°ĐŒĐž ĐŒĐŸĐ¶Đ”Ń‚ Đ±Ń‹Ń‚ŃŒ ŃƒŃ‚ĐŸĐŒĐžŃ‚Đ”Đ»ŃŒĐœŃ‹ĐŒ. Ваша Ń†Đ”Đ»ŃŒ - ŃŃ‚ĐŸ ĐżĐŸĐžŃĐș ĐœĐ°ĐžĐ»ŃƒŃ‡ŃˆĐ”ĐłĐŸ ĐșĐŸĐŒĐżŃ€ĐŸĐŒĐžŃŃĐ°: [Docker-compose](https://serverless.com/) ĐżĐŸĐ·ĐČĐŸĐ»ŃĐ”Ń‚ ŃĐŸĐ·ĐŽĐ°Ń‚ŃŒ ĐžĐ·ĐŸĐ»ĐžŃ€ĐŸĐČĐ°ĐœĐœĐŸĐ” ĐŸĐșŃ€ŃƒĐ¶Đ”ĐœĐžĐ” с ĐžĐŽĐ”ĐœŃ‚ĐžŃ‡ĐœŃ‹ĐŒĐž ĐșĐŸĐœŃ‚Đ”ĐčĐœĐ”Ń€Đ°ĐŒĐž про ĐżĐŸĐŒĐŸŃ‰Đž ĐŸĐŽĐœĐŸĐłĐŸ тДĐșŃŃ‚ĐŸĐČĐŸĐłĐŸ фаĐčла, ĐŸĐŽĐœĐ°ĐșĐŸ Ń‚Đ”Ń…ĐœĐŸĐ»ĐŸĐłĐžŃ ĐżĐŸĐŽĐŽĐ”Ń€Đ¶ĐșĐž ĐŸŃ‚Đ»ĐžŃ‡Đ°Đ”Ń‚ŃŃ ĐŸŃ‚ Ń€Đ”Đ°Đ»ŃŒĐœĐŸĐč ĐżŃ€ĐŸĐŽĐ°ĐșŃˆĐœ-срДЎы. Вы ĐŒĐŸĐ¶Đ”Ń‚Đ” ĐșĐŸĐŒĐ±ĐžĐœĐžŃ€ĐŸĐČать Đ”ĐłĐŸ с ['AWS Local'](https://github.com/localstack/localstack) ĐŽĐ»Ń Ń€Đ°Đ±ĐŸŃ‚Ń‹ с заглушĐșĐŸĐč Ń€Đ”Đ°Đ»ŃŒĐœŃ‹Ń… сДрĐČĐžŃĐŸĐČ AWS. ЕслО ĐČы ĐżĐŸŃˆĐ»Đž ĐżĐŸ путо [serverless](https://serverless.com/), Ń‚ĐŸ ĐœĐ”ŃĐșĐŸĐ»ŃŒĐșĐŸ фрДĐčĐŒĐČĐŸŃ€ĐșĐŸĐČ Ń‚ĐžĐżĐ° serverless Đž [AWS SAM](https://docs.aws.amazon.com/lambda/latest/dg/serverless_app.html) ĐżĐŸĐ·ĐČĐŸĐ»ŃŃŽŃ‚ Đ»ĐŸĐșĐ°Đ»ŃŒĐœĐŸ ĐČызыĐČать ĐșĐŸĐŽ FaaS. +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** Đ˜ŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°ĐœĐžĐ” Ń€Đ°Đ·ĐœŃ‹Ń… Ń‚Đ”Ń…ĐœĐŸĐ»ĐŸĐłĐžĐč ĐŽĐ»Ń Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ Ń‚Ń€Đ”Đ±ŃƒĐ”Ń‚ ĐżĐŸĐŽĐŽĐ”Ń€Đ¶ĐșĐž 2-х ĐŒĐŸĐŽĐ”Đ»Đ”Đč разĐČДртыĐČĐ°ĐœĐžŃ, Ń€Đ°Đ·ĐŽĐ”Đ»ŃŃ Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐșĐŸĐČ Đž OC. + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :clap: ĐŸŃ€ĐžĐŒĐ”Ń€: CI-ĐșĐŸĐœĐČĐ”ĐčДр, ŃĐŸĐ·ĐŽĐ°ŃŽŃ‰ĐžĐč ĐșластДр Kubernetes ([Đ‘Đ»Đ°ĐłĐŸĐŽĐ°Ń€ĐœĐŸŃŃ‚ŃŒ: Dynamic-environments Kubernetes](https://container-solutions.com/dynamic-environments-kubernetes/)) + +
    deploy:
    stage: deploy
    image: registry.gitlab.com/gitlab-examples/kubernetes-deploy
    script:
    - ./configureCluster.sh $KUBE_CA_PEM_FILE $KUBE_URL $KUBE_TOKEN
    - kubectl create ns $NAMESPACE
    - kubectl create secret -n $NAMESPACE docker-registry gitlab-registry --docker-server="$CI_REGISTRY" --docker-username="$CI_REGISTRY_USER" --docker-password="$CI_REGISTRY_PASSWORD" --docker-email="$GITLAB_USER_EMAIL"
    - mkdir .generated
    - echo "$CI_BUILD_REF_NAME-$CI_BUILD_REF"
    - sed -e "s/TAG/$CI_BUILD_REF_NAME-$CI_BUILD_REF/g" templates/deals.yaml | tee ".generated/deals.yaml"
    - kubectl apply --namespace $NAMESPACE -f .generated/deals.yaml
    - kubectl apply --namespace $NAMESPACE -f templates/my-sock-shop.yaml
    environment:
    name: test-for-ci
    + +
    + +

    + +## âšȘ 5.4 ĐŸĐ°Ń€Đ°Đ»Đ»Đ”Đ»ŃŒĐœĐŸĐ” ĐČŃ‹ĐżĐŸĐ»ĐœĐ”ĐœĐžĐ” Ń‚Đ”ŃŃ‚ĐŸĐČ + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** ЕслО ĐČсД ŃĐŽĐ”Đ»Đ°ĐœĐŸ праĐČĐžĐ»ŃŒĐœĐŸ, Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” - ŃŃ‚ĐŸ ĐČаш Юруг 24/7, ĐŸĐ±Đ”ŃĐżĐ”Ń‡ĐžĐČающоĐč праĐșтОчДсĐșĐž ĐŒĐłĐœĐŸĐČĐ”ĐœĐœŃƒŃŽ ĐŸĐ±Ń€Đ°Ń‚ĐœŃƒŃŽ сĐČŃĐ·ŃŒ. На праĐșтоĐșĐ” ĐČŃ‹ĐżĐŸĐ»ĐœĐ”ĐœĐžĐ” 500 ŃŽĐœĐžŃ‚-Ń‚Đ”ŃŃ‚ĐŸĐČ ĐœĐ° ĐŸĐŽĐœĐŸĐŒ ĐżŃ€ĐŸŃ†Đ”ŃŃĐŸŃ€Đ” ĐČ ĐŸĐŽĐžĐœ ĐżĐŸŃ‚ĐŸĐș ĐŒĐŸĐ¶Đ”Ń‚ Đ·Đ°ĐœŃŃ‚ŃŒ слОшĐșĐŸĐŒ ĐŒĐœĐŸĐłĐŸ ĐČŃ€Đ”ĐŒĐ”ĐœĐž. К счастью, ŃĐŸĐČŃ€Đ”ĐŒĐ”ĐœĐœŃ‹Đ” ĐżŃ€ĐŸĐłŃ€Đ°ĐŒĐŒŃ‹ ĐŽĐ»Ń запусĐșа Ń‚Đ”ŃŃ‚ĐŸĐČ Đž CI-ĐżĐ»Đ°Ń‚Ń„ĐŸŃ€ĐŒŃ‹ (таĐșОД ĐșаĐș [Jest](https://github.com/facebook/jest), [AVA](https://github.com/avajs/ava) Đž [Mocha extensions](https://github.com/yandex/mocha-parallel-tests)) ĐŒĐŸĐłŃƒŃ‚ Ń€Đ°ŃĐżĐ°Ń€Đ°Đ»Đ»Đ”Đ»ĐžŃ‚ŃŒ тДст ĐœĐ° ĐœĐ”ŃĐșĐŸĐ»ŃŒĐșĐŸ ĐżŃ€ĐŸŃ†Đ”ŃŃĐŸĐČ Đž ĐŽĐŸĐ±ĐžŃ‚ŃŒŃŃ Đ·ĐœĐ°Ń‡ĐžŃ‚Đ”Đ»ŃŒĐœĐŸĐłĐŸ ŃƒĐ»ŃƒŃ‡ŃˆĐ”ĐœĐžŃ ĐČŃ€Đ”ĐŒĐ”ĐœĐž ĐŸĐ±Ń€Đ°Ń‚ĐœĐŸĐč сĐČŃĐ·Đž. ĐĐ”ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐżĐŸŃŃ‚Đ°ĐČщоĐșĐž CI таĐșжД распараллДлОĐČают тДсты ĐżĐŸ ĐșĐŸĐœŃ‚Đ”ĐčĐœĐ”Ń€Đ°ĐŒ (!), Ń‡Ń‚ĐŸ ДщД Đ±ĐŸĐ»ŃŒŃˆĐ” ŃĐŸĐșращаДт цоĐșĐ» ĐŸĐ±Ń€Đ°Ń‚ĐœĐŸĐč сĐČŃĐ·Đž. БуЮь Ń‚ĐŸ Đ»ĐŸĐșĐ°Đ»ŃŒĐœĐŸ ĐœĐ° ĐœĐ”ŃĐșĐŸĐ»ŃŒĐșох ĐżŃ€ĐŸŃ†Đ”ŃŃĐ°Ń… ОлО чДрДз ĐŸĐ±Đ»Đ°Ń‡ĐœŃ‹Đč CLI с ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°ĐœĐžĐ”ĐŒ ĐœĐ”ŃĐșĐŸĐ»ŃŒĐșох ĐŒĐ°ŃˆĐžĐœ - распараллДлОĐČĐ°ĐœĐžĐ” Ń‚Ń€Đ”Đ±ŃƒĐ”Ń‚ ŃĐŸŃ…Ń€Đ°ĐœĐ”ĐœĐžŃ аĐČŃ‚ĐŸĐœĐŸĐŒĐœĐŸŃŃ‚Đž Ń‚Đ”ŃŃ‚ĐŸĐČ, ĐżĐŸŃĐșĐŸĐ»ŃŒĐșу ĐșажЎыĐč Оз ĐœĐžŃ… ĐŒĐŸĐ¶Đ”Ń‚ ĐČŃ‹ĐżĐŸĐ»ĐœŃŃ‚ŃŒŃŃ ĐœĐ° Ń€Đ°Đ·ĐœŃ‹Ń… ĐżŃ€ĐŸŃ†Đ”ŃŃĐ°Ń…. + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** ĐŸĐŸĐ»ŃƒŃ‡Đ”ĐœĐžĐ” Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚ĐŸĐČ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ чДрДз час ĐżĐŸŃĐ»Đ” ĐČĐœĐ”ĐŽŃ€Đ”ĐœĐžŃ ĐœĐŸĐČĐŸĐłĐŸ ĐșĐŸĐŽĐ°, ĐșĐŸĐłĐŽĐ° ĐČы ужД ĐżĐžŃˆĐ”Ń‚Đ” ŃĐ»Đ”ĐŽŃƒŃŽŃ‰ĐžĐ” Ń„ŃƒĐœĐșцоо - ĐŸŃ‚Đ»ĐžŃ‡ĐœŃ‹Đč рДцДпт ĐŽĐ»Ń Ń‚ĐŸĐłĐŸ, Ń‡Ń‚ĐŸĐ±Ń‹ ŃĐŽĐ”Đ»Đ°Ń‚ŃŒ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” ĐŒĐ”ĐœĐ”Đ” аĐșŃ‚ŃƒĐ°Đ»ŃŒĐœŃ‹ĐŒ + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: Mocha parallel & Jest лДгĐșĐŸ ĐŸĐ±ĐłĐŸĐœŃŃŽŃ‚ Ń‚Ń€Đ°ĐŽĐžŃ†ĐžĐŸĐœĐœŃƒŃŽ Mocha Đ±Đ»Đ°ĐłĐŸĐŽĐ°Ń€Ń распараллДлОĐČĐ°ĐœĐžŃŽ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ ([Đ‘Đ»Đ°ĐłĐŸĐŽĐ°Ń€ĐœĐŸŃŃ‚ŃŒ: JavaScript Test-Runners Benchmark](https://medium.com/dailyjs/javascript-test-runners-benchmark-3a78d4117b4)) + +![alt text](assets/bp-24-yonigoldberg-jest-parallel.png "Mocha parallel & Jest easily outrun the traditional Mocha thanks to testing parallelization (Credit: JavaScript Test-Runners Benchmark)") + +
    + +

    + +## âšȘ 5.5 Đ”Đ”Ń€Đ¶ĐžŃ‚Đ”ŃŃŒ ĐżĐŸĐŽĐ°Đ»ŃŒŃˆĐ” ĐŸŃ‚ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒ, сĐČŃĐ·Đ°ĐœĐœŃ‹Ń… с праĐČĐŸĐČŃ‹ĐŒ ĐżĐŸĐ»Đ”ĐŒ, ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒŃ ĐżŃ€ĐŸĐČДрĐșу ĐœĐ° Đ»ĐžŃ†Đ”ĐœĐ·ĐžŃŽ Đž Đ°ĐœŃ‚ĐžĐżĐ»Đ°ĐłĐžĐ°Ń‚ + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** Đ’ĐŸĐżŃ€ĐŸŃŃ‹ Đ»ĐžŃ†Đ”ĐœĐ·ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ Đž плагОата, ĐČĐ”Ń€ĐŸŃŃ‚ĐœĐŸ, ĐœĐ” яĐČĐ»ŃŃŽŃ‚ŃŃ сДĐčчас ĐČашДĐč глаĐČĐœĐŸĐč Đ·Đ°Đ±ĐŸŃ‚ĐŸĐč, ĐœĐŸ ĐżĐŸŃ‡Đ”ĐŒŃƒ бы ĐœĐ” ĐżĐŸŃŃ‚Đ°ĐČоть ĐłĐ°Đ»ĐŸŃ‡Đșу Đž ĐČ ŃŃ‚ĐŸĐč графД за 10 ĐŒĐžĐœŃƒŃ‚? Куча паĐșĐ”Ń‚ĐŸĐČ npm, таĐșох ĐșаĐș [ĐżŃ€ĐŸĐČДрĐșа Đ»ĐžŃ†Đ”ĐœĐ·ĐžĐž](https://www.npmjs.com/package/license-checker) Đž [ĐżŃ€ĐŸĐČДрĐșа ĐœĐ° плагОат](https://www.npmjs.com/package/plagiarism-checker) (ĐșĐŸĐŒĐŒĐ”Ń€Ń‡Đ”ŃĐșĐžĐč Đž Đ±Đ”ŃĐżĐ»Đ°Ń‚ĐœŃ‹Đč ĐżĐ»Đ°Đœ), ĐŒĐŸĐłŃƒŃ‚ Đ±Ń‹Ń‚ŃŒ лДгĐșĐŸ ĐČŃŃ‚Ń€ĐŸĐ”ĐœŃ‹ ĐČ ĐČаш CI-ĐșĐŸĐœĐČĐ”ĐčДр Đž ĐżŃ€ĐŸĐČĐ”Ń€Đ”ĐœŃ‹ ĐœĐ° ĐœĐ°Đ»ĐžŃ‡ĐžĐ” таĐșох ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒ, ĐșаĐș заĐČĐžŃĐžĐŒĐŸŃŃ‚Đž с ĐŸĐłŃ€Đ°ĐœĐžŃ‡ĐžŃ‚Đ”Đ»ŃŒĐœŃ‹ĐŒĐž Đ»ĐžŃ†Đ”ĐœĐ·ĐžŃĐŒĐž ОлО ĐșĐŸĐŽ, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč был сĐșĐŸĐżĐžŃ€ĐŸĐČĐ°Đœ Оз Stack Overflow Đž, ĐŸŃ‡Đ”ĐČĐžĐŽĐœĐŸ, ĐœĐ°Ń€ŃƒŃˆĐ°Đ”Ń‚ аĐČŃ‚ĐŸŃ€ŃĐșОД праĐČа. + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** ĐĐ”ĐżŃ€Đ”ĐŽĐœĐ°ĐŒĐ”Ń€Đ”ĐœĐœĐŸ Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐșĐž ĐŒĐŸĐłŃƒŃ‚ ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČать паĐșДты с ĐœĐ”ŃĐŸĐŸŃ‚ĐČДтстĐČŃƒŃŽŃ‰ĐžĐŒĐž Đ»ĐžŃ†Đ”ĐœĐ·ĐžŃĐŒĐž ОлО ĐșĐŸĐżĐžŃ€ĐŸĐČать ĐșĐŸĐŒĐŒĐ”Ń€Ń‡Đ”ŃĐșĐžĐč ĐșĐŸĐŽ Đž ŃŃ‚ĐŸĐ»ĐșĐœŃƒŃ‚ŃŒŃŃ с юрОЎОчДсĐșĐžĐŒĐž ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐ°ĐŒĐž + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :clap: ПраĐČĐžĐ»ŃŒĐœĐŸ: + +```javascript +// ŃƒŃŃ‚Đ°ĐœĐŸĐČОтД license-checker ĐČ CI ОлО Đ»ĐŸĐșĐ°Đ»ŃŒĐœĐŸ +npm install -g license-checker + +// ĐżĐŸĐżŃ€ĐŸŃĐžŃ‚Đ” Đ”ĐłĐŸ ĐżŃ€ĐŸŃĐșĐ°ĐœĐžŃ€ĐŸĐČать ĐČсД Đ»ĐžŃ†Đ”ĐœĐ·ĐžĐž Đž ĐČыЮать ĐŸŃˆĐžĐ±Đșу с ĐșĐŸĐŽĐŸĐŒ ĐČŃ‹Ń…ĐŸĐŽĐ°, ĐŸŃ‚Đ»ĐžŃ‡ĐœŃ‹ĐŒ ĐŸŃ‚ 0, ĐČ ŃĐ»ŃƒŃ‡Đ°Đ” ĐŸĐ±ĐœĐ°Ń€ŃƒĐ¶Đ”ĐœĐžŃ ĐœĐ”Đ°ĐČŃ‚ĐŸŃ€ĐžĐ·ĐŸĐČĐ°ĐœĐœĐŸĐč Đ»ĐžŃ†Đ”ĐœĐ·ĐžĐž. ĐĄĐžŃŃ‚Đ”ĐŒĐ° CI ĐŽĐŸĐ»Đ¶ĐœĐ° ŃƒĐ»ĐŸĐČоть ŃŃ‚ĐŸŃ‚ ŃĐ±ĐŸĐč Đž ĐŸŃŃ‚Đ°ĐœĐŸĐČоть ŃĐ±ĐŸŃ€Đșу +license-checker --summary --failOn BSD + +``` + +
    + +![alt text](assets/bp-25-nodejs-licsense.png) + +
    + +

    + +## âšȘ 5.6 ĐŸĐŸŃŃ‚ĐŸŃĐœĐœĐ°Ń ĐżŃ€ĐŸĐČДрĐșа ĐœĐ° ĐœĐ°Đ»ĐžŃ‡ĐžĐ” ŃƒŃĐ·ĐČĐžĐŒŃ‹Ń… заĐČĐžŃĐžĐŒĐŸŃŃ‚Đ”Đč + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** ДажД ŃĐ°ĐŒŃ‹Đ” аĐČŃ‚ĐŸŃ€ĐžŃ‚Đ”Ń‚ĐœŃ‹Đ” заĐČĐžŃĐžĐŒĐŸŃŃ‚Đž, таĐșОД ĐșаĐș Express, ĐžĐŒĐ”ŃŽŃ‚ ОзĐČĐ”ŃŃ‚ĐœŃ‹Đ” ŃƒŃĐ·ĐČĐžĐŒĐŸŃŃ‚Đž. Đ­Ń‚ĐŸ ĐŒĐŸĐ¶ĐœĐŸ лДгĐșĐŸ ŃƒŃŃ‚Ń€Đ°ĐœĐžŃ‚ŃŒ с ĐżĐŸĐŒĐŸŃ‰ŃŒŃŽ ĐžĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚ĐŸĐČ ŃĐŸĐŸĐ±Ń‰Đ”ŃŃ‚ĐČа, таĐșох ĐșаĐș [npm audit](https://docs.npmjs.com/getting-started/running-a-security-audit), ОлО ĐșĐŸĐŒĐŒĐ”Ń€Ń‡Đ”ŃĐșох ĐžĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚ĐŸĐČ, таĐșох ĐșаĐș [snyk](https://snyk.io/) (ĐżŃ€Đ”ĐŽĐ»Đ°ĐłĐ°Đ”Ń‚ŃŃ таĐșжД Đ±Đ”ŃĐżĐ»Đ°Ń‚ĐœĐ°Ń ĐČĐ”Ń€ŃĐžŃ ĐŽĐ»Ń ŃĐŸĐŸĐ±Ń‰Đ”ŃŃ‚ĐČа). Оба ĐžĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Đ° ĐŒĐŸĐłŃƒŃ‚ Đ±Ń‹Ń‚ŃŒ ĐČызĐČĐ°ĐœŃ‹ Оз ĐČĐ°ŃˆĐ”ĐłĐŸ CI про ĐșĐ°Đ¶ĐŽĐŸĐč ŃĐ±ĐŸŃ€ĐșĐ” + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** Đ”Đ»Ń ĐżĐŸĐŽĐŽĐ”Ń€Đ¶Đ°ĐœĐžŃ ĐșĐŸĐŽĐ° Ń‡ĐžŃŃ‚Ń‹ĐŒ ĐŸŃ‚ ŃƒŃĐ·ĐČĐžĐŒĐŸŃŃ‚Đ”Đč бДз ŃĐżĐ”Ń†ĐžĐ°Đ»ŃŒĐœŃ‹Ń… ĐžĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚ĐŸĐČ ĐżŃ€ĐžĐŽĐ”Ń‚ŃŃ ĐżĐŸŃŃ‚ĐŸŃĐœĐœĐŸ ŃĐ»Đ”ĐŽĐžŃ‚ŃŒ за публОĐșĐ°Ń†ĐžŃĐŒĐž ĐČ Đ˜ĐœŃ‚Đ”Ń€ĐœĐ”Ń‚Đ” ĐŸ ĐœĐŸĐČых ŃƒĐłŃ€ĐŸĐ·Đ°Ń…. Đ”ĐŸĐČĐŸĐ»ŃŒĐœĐŸ ŃƒŃ‚ĐŸĐŒĐžŃ‚Đ”Đ»ŃŒĐœĐŸ + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :clap: Example: Đ Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚ NPM Audit + +![alt text](assets/bp-26-npm-audit-snyk.png "NPM Audit result") + +
    + +

    + +## âšȘ 5.7 АĐČŃ‚ĐŸĐŒĐ°Ń‚ĐžĐ·ĐžŃ€ŃƒĐčтД ĐŸĐ±ĐœĐŸĐČĐ»Đ”ĐœĐžĐ” заĐČĐžŃĐžĐŒĐŸŃŃ‚Đ”Đč + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** ĐŸĐŸŃĐ»Đ”ĐŽĐœĐ”Đ” ĐČĐœĐ”ĐŽŃ€Đ”ĐœĐžĐ” ĐČ Yarn Đž npm package-lock.json ŃĐŸĐ·ĐŽĐ°Đ»ĐŸ ŃĐ”Ń€ŃŒĐ”Đ·ĐœŃƒŃŽ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒŃƒ (ĐŽĐŸŃ€ĐŸĐłĐ° ĐČ Đ°ĐŽ ĐČŃ‹ĐŒĐŸŃ‰Đ”ĐœĐ° Đ±Đ»Đ°ĐłĐžĐŒĐž ĐœĐ°ĐŒĐ”Ń€Đ”ĐœĐžŃĐŒĐž) - ĐżĐŸ ŃƒĐŒĐŸĐ»Ń‡Đ°ĐœĐžŃŽ паĐșДты Đ±ĐŸĐ»ŃŒŃˆĐ” ĐœĐ” ĐżĐŸĐ»ŃƒŃ‡Đ°ŃŽŃ‚ ĐŸĐ±ĐœĐŸĐČĐ»Đ”ĐœĐžĐč. Đ Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐș, ĐČŃ‹ĐżĐŸĐ»ĐœŃŃŽŃ‰ĐžĐč ĐŒĐœĐŸĐ¶Đ”ŃŃ‚ĐČĐŸ сĐČДжОх разĐČДртыĐČĐ°ĐœĐžĐč с ĐżĐŸĐŒĐŸŃ‰ŃŒŃŽ 'npm install' Đž 'npm update', ĐœĐ” ĐżĐŸĐ»ŃƒŃ‡ĐžŃ‚ ĐœĐžĐșаĐșох ĐœĐŸĐČых ĐŸĐ±ĐœĐŸĐČĐ»Đ”ĐœĐžĐč. Đ­Ń‚ĐŸ проĐČĐŸĐŽĐžŃ‚ ĐČ Đ»ŃƒŃ‡ŃˆĐ”ĐŒ ŃĐ»ŃƒŃ‡Đ°Đ” Đș ĐœĐ”ĐșачДстĐČĐ”ĐœĐœŃ‹ĐŒ ĐČĐ”Ń€ŃĐžŃĐŒ заĐČĐžŃĐžĐŒŃ‹Ń… паĐșĐ”Ń‚ĐŸĐČ, а ĐČ Ń…ŃƒĐŽŃˆĐ”ĐŒ - Đș ŃƒŃĐ·ĐČĐžĐŒĐŸĐŒŃƒ ĐșĐŸĐŽŃƒ. ĐĄĐ”Đčчас ĐșĐŸĐŒĐ°ĐœĐŽŃ‹ Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐșĐŸĐČ ĐżĐŸĐ»Đ°ĐłĐ°ŃŽŃ‚ŃŃ ĐœĐ° ĐŽĐŸĐ±Ń€ŃƒŃŽ ĐČĐŸĐ»ŃŽ Đž ĐżĐ°ĐŒŃŃ‚ŃŒ Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐșĐŸĐČ, Ń‡Ń‚ĐŸĐ±Ń‹ ĐČŃ€ŃƒŃ‡ĐœŃƒŃŽ ĐŸĐ±ĐœĐŸĐČĐ»ŃŃ‚ŃŒ package.json ОлО ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČать ĐžĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Ń‹ [ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, ncu](https://www.npmjs.com/package/npm-check-updates) ĐČŃ€ŃƒŃ‡ĐœŃƒŃŽ. +Đ‘ĐŸĐ»Đ”Đ” ĐœĐ°ĐŽĐ”Đ¶ĐœŃ‹ĐŒ ŃĐżĐŸŃĐŸĐ±ĐŸĐŒ ĐŒĐŸĐ¶Đ”Ń‚ Đ±Ń‹Ń‚ŃŒ аĐČŃ‚ĐŸĐŒĐ°Ń‚ĐžĐ·Đ°Ń†ĐžŃ ĐżŃ€ĐŸŃ†Đ”ŃŃĐ° ĐżĐŸĐ»ŃƒŃ‡Đ”ĐœĐžŃ ĐœĐ°ĐžĐ±ĐŸĐ»Đ”Đ” ĐœĐ°ĐŽĐ”Đ¶ĐœŃ‹Ń… ĐČДрсОĐč заĐČĐžŃĐžĐŒŃ‹Ń… паĐșĐ”Ń‚ĐŸĐČ, Ń…ĐŸŃ‚Ń ĐżĐŸĐșа ĐœĐ” ŃŃƒŃ‰Đ”ŃŃ‚ĐČŃƒĐ”Ń‚ ĐžĐŽĐ”Đ°Đ»ŃŒĐœŃ‹Ń… Ń€Đ”ŃˆĐ”ĐœĐžĐč, Đ”ŃŃ‚ŃŒ ĐŽĐČа ĐČĐŸĐ·ĐŒĐŸĐ¶ĐœŃ‹Ń… путо аĐČŃ‚ĐŸĐŒĐ°Ń‚ĐžĐ·Đ°Ń†ĐžĐž: + +(1) CI ĐŒĐŸĐ¶Đ”Ń‚ ĐŸŃ‚ĐșĐ»ĐŸĐœŃŃ‚ŃŒ ŃĐ±ĐŸŃ€ĐșĐž с ŃƒŃŃ‚Đ°Ń€Đ”ĐČŃˆĐžĐŒĐž заĐČĐžŃĐžĐŒĐŸŃŃ‚ŃĐŒĐž - с ĐżĐŸĐŒĐŸŃ‰ŃŒŃŽ таĐșох ĐžĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚ĐŸĐČ, ĐșаĐș ['npm outdated'](https://docs.npmjs.com/cli/outdated) ОлО 'npm-check-updates (ncu)'. Đ­Ń‚ĐŸ застаĐČот Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐșĐŸĐČ ĐŸĐ±ĐœĐŸĐČоть заĐČĐžŃĐžĐŒĐŸŃŃ‚Đž. + +(2) Đ˜ŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČать ĐșĐŸĐŒĐŒĐ”Ń€Ń‡Đ”ŃĐșОД ĐžĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Ń‹, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” сĐșĐ°ĐœĐžŃ€ŃƒŃŽŃ‚ ĐșĐŸĐŽ Đž аĐČŃ‚ĐŸĐŒĐ°Ń‚ĐžŃ‡Đ”ŃĐșĐž ĐŸŃ‚ĐżŃ€Đ°ĐČĐ»ŃŃŽŃ‚ Đ·Đ°ĐżŃ€ĐŸŃŃ‹ ĐœĐ° ĐŸĐ±ĐœĐŸĐČĐ»Đ”ĐœĐžĐ” заĐČĐžŃĐžĐŒĐŸŃŃ‚Đ”Đč. ĐžŃŃ‚Đ°Đ”Ń‚ŃŃ ĐŸĐŽĐžĐœ ĐžĐœŃ‚Đ”Ń€Đ”ŃĐœŃ‹Đč ĐČĐŸĐżŃ€ĐŸŃ: ĐșаĐșĐŸĐč ĐŽĐŸĐ»Đ¶ĐœĐ° Đ±Ń‹Ń‚ŃŒ ĐżĐŸĐ»ĐžŃ‚ĐžĐșа ĐŸĐ±ĐœĐŸĐČĐ»Đ”ĐœĐžŃ заĐČĐžŃĐžĐŒĐŸŃŃ‚Đ”Đč - ĐŸĐ±ĐœĐŸĐČĐ»Đ”ĐœĐžĐ” про ĐșĐ°Đ¶ĐŽĐŸĐŒ патчД ŃĐŸĐ·ĐŽĐ°Đ”Ń‚ слОшĐșĐŸĐŒ ĐŒĐœĐŸĐłĐŸ ĐœĐ°ĐșĐ»Đ°ĐŽĐœŃ‹Ń… Ń€Đ°ŃŃ…ĐŸĐŽĐŸĐČ, ĐŸĐ±ĐœĐŸĐČĐ»Đ”ĐœĐžĐ” ŃŃ€Đ°Đ·Ńƒ ĐżĐŸŃĐ»Đ” ĐČŃ‹Ń…ĐŸĐŽĐ° ĐŒĐ°Đ¶ĐŸŃ€Đ° ĐŒĐŸĐ¶Đ”Ń‚ уĐșазыĐČать ĐœĐ° ĐœĐ”ŃŃ‚Đ°Đ±ĐžĐ»ŃŒĐœŃƒŃŽ ĐČДрсОю (ĐŒĐœĐŸĐłĐžĐ” паĐșДты ĐŸĐ±ĐœĐ°Ń€ŃƒĐ¶ĐžĐČают ŃƒŃĐ·ĐČĐžĐŒĐŸŃŃ‚ŃŒ ĐČ ĐżĐ”Ń€ĐČыД жД ĐŽĐœĐž ĐżĐŸŃĐ»Đ” ĐČŃ‹Ń…ĐŸĐŽĐ°, [ŃĐŒ. ĐžĐœŃ†ĐžĐŽĐ”ĐœŃ‚ с eslint-scope](https://nodesource.com/blog/a-high-level-post-mortem-of-the-eslint-scope-security-incident/)). + +ЭффДĐșтоĐČĐœĐ°Ń ĐżĐŸĐ»ĐžŃ‚ĐžĐșа ĐŸĐ±ĐœĐŸĐČĐ»Đ”ĐœĐžŃ ĐŒĐŸĐ¶Đ”Ń‚ ĐŽĐŸĐżŃƒŃĐșать ĐœĐ”ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč "ĐżĐ”Ń€ĐžĐŸĐŽ" - пусть ĐșĐŸĐŽ ĐŸŃ‚ŃŃ‚Đ°Đ”Ń‚ ĐŸŃ‚ @latest ĐœĐ° ĐœĐ”ĐșĐŸŃ‚ĐŸŃ€ĐŸĐ” ĐČŃ€Đ”ĐŒŃ Đž ĐČДрсОО, прДжЎД Ń‡Đ”ĐŒ счотать Đ»ĐŸĐșĐ°Đ»ŃŒĐœŃƒŃŽ ĐșĐŸĐżĐžŃŽ ŃƒŃŃ‚Đ°Ń€Đ”ĐČшДĐč (ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, Đ»ĐŸĐșĐ°Đ»ŃŒĐœĐ°Ń ĐČĐ”Ń€ŃĐžŃ - 1.3.1, а ĐČĐ”Ń€ŃĐžŃ Ń…Ń€Đ°ĐœĐžĐ»ĐžŃ‰Đ° - 1.3.8). +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** БуЮут запусĐșаться заĐČĐžŃĐžĐŒĐŸŃŃ‚Đž, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” былО ĐŸŃ‚ĐŒĐ”Ń‡Đ”ĐœŃ‹, ĐșаĐș росĐșĐŸĐČĐ°ĐœĐœŃ‹Đ” + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :clap: ĐŸŃ€ĐžĐŒĐ”Ń€: [ncu](https://www.npmjs.com/package/npm-check-updates) ĐŒĐŸĐ¶ĐœĐŸ ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČать ĐČŃ€ŃƒŃ‡ĐœŃƒŃŽ ОлО ĐČ Ń€Đ°ĐŒĐșах ĐșĐŸĐœĐČĐ”ĐčДра CI ĐŽĐ»Ń ĐŸĐżŃ€Đ”ĐŽĐ”Đ»Đ”ĐœĐžŃ ŃŃ‚Đ”ĐżĐ”ĐœĐž ĐŸŃ‚ŃŃ‚Đ°ĐČĐ°ĐœĐžŃ ĐșĐŸĐŽĐ° ĐŸŃ‚ ĐżĐŸŃĐ»Đ”ĐŽĐœĐžŃ… ĐČДрсОĐč + +![alt text](assets/bp-27-yoni-goldberg-npm.png "ncu can be used manually or within a CI pipeline to detect to which extent the code lag behind the latest versions") + +
    + +

    + +## âšȘ  5.8 Đ”Ń€ŃƒĐłĐžĐ”, ĐœĐ” сĐČŃĐ·Đ°ĐœĐœŃ‹Đ” с Node, ŃĐŸĐČДты ĐżĐŸ CI + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** Эта Đ·Đ°ĐŒĐ”Ń‚Đșа ĐżĐŸŃĐČŃŃ‰Đ”ĐœĐ° ŃĐŸĐČĐ”Ń‚Đ°ĐŒ ĐżĐŸ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃŽ, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” сĐČŃĐ·Đ°ĐœŃ‹ с Node JS ОлО, ĐżĐŸ ĐșраĐčĐœĐ”Đč ĐŒĐ”Ń€Đ”, ĐŒĐŸĐłŃƒŃ‚ Đ±Ń‹Ń‚ŃŒ ĐżŃ€ĐŸĐžĐ»Đ»ŃŽŃŃ‚Ń€ĐžŃ€ĐŸĐČĐ°ĐœŃ‹ ĐœĐ° Đ”ĐłĐŸ ĐżŃ€ĐžĐŒĐ”Ń€Đ”. ĐžĐŽĐœĐ°ĐșĐŸ ĐČ ŃŃ‚ĐŸĐŒ ĐżŃƒĐœĐșтД ŃĐłŃ€ŃƒĐżĐżĐžŃ€ĐŸĐČĐ°ĐœĐŸ ĐœĐ”ŃĐșĐŸĐ»ŃŒĐșĐŸ ŃĐŸĐČĐ”Ń‚ĐŸĐČ, ĐœĐ” сĐČŃĐ·Đ°ĐœĐœŃ‹Ń… с Node, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” Ń…ĐŸŃ€ĐŸŃˆĐŸ ОзĐČĐ”ŃŃ‚ĐœŃ‹ + +
    1. Đ˜ŃĐżĐŸĐ»ŃŒĐ·ŃƒĐčтД ĐŽĐ”ĐșларатОĐČĐœŃ‹Đč ŃĐžĐœŃ‚Đ°ĐșсОс. Đ­Ń‚ĐŸ Đ”ĐŽĐžĐœŃŃ‚ĐČĐ”ĐœĐœŃ‹Đč ĐČĐ°Ń€ĐžĐ°ĐœŃ‚ ĐŽĐ»Ń Đ±ĐŸĐ»ŃŒŃˆĐžĐœŃŃ‚ĐČа ĐżĐŸŃŃ‚Đ°ĐČщоĐșĐŸĐČ, ĐœĐŸ старыД ĐČДрсОО Jenkins ĐżĐŸĐ·ĐČĐŸĐ»ŃŃŽŃ‚ ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČать ĐșĐŸĐŽ ОлО UI
    2. ВыбОраĐčтД ĐżŃ€ĐŸĐŽŃƒĐșт, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč ĐžĐŒĐ”Đ”Ń‚ ĐČŃŃ‚Ń€ĐŸĐ”ĐœĐœŃƒŃŽ ĐżĐŸĐŽĐŽĐ”Ń€Đ¶Đșу Docker
    3. ЗапусĐșаĐčтД ŃĐœĐ°Ń‡Đ°Đ»Đ° ŃĐ°ĐŒŃ‹Đ” быстрыД тДсты. ĐĄĐŸĐ·ĐŽĐ°ĐčтД шаг/этап "Smoke testing", ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč ĐłŃ€ŃƒĐżĐżĐžŃ€ŃƒĐ”Ń‚ ĐœĐ”ŃĐșĐŸĐ»ŃŒĐșĐŸ быстрых ĐżŃ€ĐŸĐČĐ”Ń€ĐŸĐș (ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, Đ»ĐžĐœŃ‚ĐžĐœĐł, ĐŒĐŸĐŽŃƒĐ»ŃŒĐœŃ‹Đ” тДсты) Đž ĐŸĐ±Đ”ŃĐżĐ”Ń‡ĐžĐČаДт Đ±Ń‹ŃŃ‚Ń€ŃƒŃŽ ĐŸĐ±Ń€Đ°Ń‚ĐœŃƒŃŽ сĐČŃĐ·ŃŒ с ĐșĐŸĐŒĐŒĐžŃ‚Ń‚Đ”Ń€ĐŸĐŒ ĐșĐŸĐŽĐ°.
    4. ĐŁĐżŃ€ĐŸŃŃ‚ĐžŃ‚ŃŒ ĐżŃ€ĐŸŃĐŒĐŸŃ‚Ń€ ŃĐ±ĐŸŃ€ĐșĐž, ĐČĐșĐ»ŃŽŃ‡Đ°Ń ĐŸŃ‚Ń‡Đ”Ń‚Ń‹ ĐŸ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐž, ĐŸŃ‚Ń‡Đ”Ń‚Ń‹ ĐŸ ĐżĐŸĐșрытоо, ĐŸŃ‚Ń‡Đ”Ń‚Ń‹ ĐŸ ĐŒŃƒŃ‚Đ°Ń†ĐžŃŃ…, Đ¶ŃƒŃ€ĐœĐ°Đ»Ń‹ Đž т.ĐŽ.
    5. ĐĄĐŸĐ·ĐŽĐ°ĐčтД ĐœĐ”ŃĐșĐŸĐ»ŃŒĐșĐŸ Đ·Đ°ĐŽĐ°ĐœĐžĐč ĐŽĐ»Ń ĐșĐ°Đ¶ĐŽĐŸĐłĐŸ ŃĐŸĐ±Ń‹Ń‚ĐžŃ, ĐżĐŸĐČŃ‚ĐŸŃ€ĐœĐŸ ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒŃ шагО ĐŒĐ”Đ¶ĐŽŃƒ ĐœĐžĐŒĐž. ĐĐ°ĐżŃ€ĐžĐŒĐ”Ń€, ĐœĐ°ŃŃ‚Ń€ĐŸĐčтД ĐŸĐŽĐœĐŸ Đ·Đ°ĐŽĐ°ĐœĐžĐ” ĐŽĐ»Ń ĐșĐŸĐŒĐŒĐžŃ‚ĐŸĐČ ĐČДтĐșĐž Ń„ŃƒĐœĐșцоĐč Đž ĐŽŃ€ŃƒĐłĐŸĐ” - ĐŽĐ»Ń PR ĐŒĐ°ŃŃ‚Đ”Ń€Đ°. Пусть ĐșажЎыĐč Оз ĐœĐžŃ… ĐżĐŸĐČŃ‚ĐŸŃ€ĐœĐŸ ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒĐ”Ń‚ Đ»ĐŸĐłĐžĐșу, ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒŃ ĐŸĐ±Ń‰ĐžĐ” шагО (Đ±ĐŸĐ»ŃŒŃˆĐžĐœŃŃ‚ĐČĐŸ ĐżŃ€ĐŸĐŽŃƒĐșŃ‚ĐŸĐČ ĐżŃ€Đ”ĐŽĐŸŃŃ‚Đ°ĐČĐ»ŃŃŽŃ‚ ĐœĐ”ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐŒĐ”Ń…Đ°ĐœĐžĐ·ĐŒŃ‹ ĐŽĐ»Ń ĐżĐŸĐČŃ‚ĐŸŃ€ĐœĐŸĐłĐŸ ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°ĐœĐžŃ ĐșĐŸĐŽĐ°).
    6. НоĐșĐŸĐłĐŽĐ° ĐœĐ” ĐČстаĐČĐ»ŃĐčтД сДĐșрДты ĐČ ĐŸĐ±ŃŠŃĐČĐ»Đ”ĐœĐžĐ” Đ·Đ°ĐŽĐ°ĐœĐžŃ, бДрОтД ох Оз Ń…Ń€Đ°ĐœĐžĐ»ĐžŃ‰Đ° сДĐșŃ€Đ”Ń‚ĐŸĐČ ĐžĐ»Đž Оз ĐșĐŸĐœŃ„ĐžĐłŃƒŃ€Đ°Ń†ĐžĐž Đ·Đ°ĐŽĐ°ĐœĐžŃ
    7. ĐŻĐČĐœĐŸ ĐżĐŸĐČышаĐčтД ĐČДрсОю ĐČ ŃĐ±ĐŸŃ€ĐșĐ” рДлОза ОлО, ĐżĐŸ ĐșраĐčĐœĐ”Đč ĐŒĐ”Ń€Đ”, ŃƒĐ±Đ”ĐŽĐžŃ‚Đ”ŃŃŒ, Ń‡Ń‚ĐŸ Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐș ŃŃ‚ĐŸ сЎДлал
    8. ĐĄĐ±ĐŸŃ€Đșа Ń‚ĐŸĐ»ŃŒĐșĐŸ ĐŸĐŽĐžĐœ раз Đž ĐČŃ‹ĐżĐŸĐ»ĐœĐ”ĐœĐžĐ” ĐČсДх ĐżŃ€ĐŸĐČĐ”Ń€ĐŸĐș ĐœĐ°ĐŽ Đ”ĐŽĐžĐœŃŃ‚ĐČĐ”ĐœĐœŃ‹ĐŒ артДфаĐșŃ‚ĐŸĐŒ ŃĐ±ĐŸŃ€ĐșĐž (ĐœĐ°ĐżŃ€ĐžĐŒĐ”Ń€, ĐŸĐ±Ń€Đ°Đ·ĐŸĐŒ Docker)
    9. ĐąĐ”ŃŃ‚ĐžŃ€ŃƒĐčтД ĐČ ŃŃ„Đ”ĐŒĐ”Ń€ĐœĐŸĐč срДЎД, ĐșĐŸŃ‚ĐŸŃ€Đ°Ń ĐœĐ” ĐżĐ”Ń€Đ”ĐœĐŸŃĐžŃ‚ ŃĐŸŃŃ‚ĐŸŃĐœĐžĐ” ĐŒĐ”Đ¶ĐŽŃƒ ŃĐ±ĐŸŃ€ĐșĐ°ĐŒĐž. ĐšŃŃˆĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” ĐŒĐŸĐŽŃƒĐ»Đ”Đč node_modules ĐŒĐŸĐ¶Đ”Ń‚ Đ±Ń‹Ń‚ŃŒ Đ”ĐŽĐžĐœŃŃ‚ĐČĐ”ĐœĐœŃ‹ĐŒ ОсĐșĐ»ŃŽŃ‡Đ”ĐœĐžĐ”ĐŒ
    +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** Вы ĐŒĐŸĐ¶Đ”Ń‚Đ” ĐŒĐœĐŸĐłĐŸĐ” упустоть + +

    + +## âšȘ  5.9 Матроца ŃĐ±ĐŸŃ€ĐșĐž: Đ’Ń‹ĐżĐŸĐ»ĐœĐ”ĐœĐžĐ” ĐŸĐŽĐœĐžŃ… Đž тДх жД ŃˆĐ°ĐłĐŸĐČ CI с ĐžŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°ĐœĐžĐ”ĐŒ ĐœĐ”ŃĐșĐŸĐ»ŃŒĐșох ĐČДрсОĐč Node + +:white_check_mark: **ĐĄĐŽĐ”Đ»Đ°Ń‚ŃŒ:** ĐŸŃ€ĐŸĐČДрĐșа ĐșачДстĐČа - ŃŃ‚ĐŸ ŃĐ»ŃƒŃ‡Đ°ĐčĐœĐŸŃŃ‚ŃŒ, Ń‡Đ”ĐŒ Đ±ĐŸĐ»ŃŒŃˆĐ” ĐŒĐ”ŃŃ‚Đ° ĐČы ĐŸŃ…ĐČатыĐČаДтД Ń‚Đ”ĐŒ Đ±ĐŸĐ»ŃŒŃˆĐ” ŃˆĐ°ĐœŃ ĐŸĐ±ĐœĐ°Ń€ŃƒĐ¶ĐžŃ‚ŃŒ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒŃ‹ ĐœĐ° Ń€Đ°ĐœĐœĐ”Đč стаЮоо. Про Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚ĐșĐ” ĐŒĐœĐŸĐłĐŸĐșŃ€Đ°Ń‚ĐœĐŸ ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒĐ”ĐŒŃ‹Ń… паĐșĐ”Ń‚ĐŸĐČ ĐžĐ»Đž Ń€Đ°Đ±ĐŸŃ‚Đ” с ĐœĐ”ŃĐșĐŸĐ»ŃŒĐșĐžĐŒĐž ĐșĐ»ĐžĐ”ĐœŃ‚Đ°ĐŒĐž с Ń€Đ°Đ·Đ»ĐžŃ‡ĐœŃ‹ĐŒĐž ĐșĐŸĐœŃ„ĐžĐłŃƒŃ€Đ°Ń†ĐžĐž Đž ĐČĐ”Ń€ŃĐžŃĐŒĐž Node, CI ĐŽĐŸĐ»Đ¶Đ”Đœ ĐČŃ‹ĐżĐŸĐ»ĐœŃŃ‚ ĐșĐŸĐœĐČĐ”ĐčДр Ń‚Đ”ŃŃ‚ĐŸĐČ ĐŽĐ»Ń Ń€Đ°Đ·Đ»ĐžŃ‡ĐœŃ‹Ń… ĐżĐ”Ń€Đ”ŃŃ‚Đ°ĐœĐŸĐČĐŸĐș этох ĐșĐŸĐœŃ„ĐžĐłŃƒŃ€Đ°Ń†ĐžĐč. ĐĐ°ĐżŃ€ĐžĐŒĐ”Ń€, ДслО ĐŒŃ‹ ĐžŃĐżĐŸĐ»ŃŒĐ·ŃƒĐ”ĐŒ MySQL ĐŽĐ»Ń ĐŸĐŽĐœĐžŃ… ĐșĐ»ĐžĐ”ĐœŃ‚ĐŸĐČ Đž Postgres ĐŽĐ»Ń Юругох, ĐœĐ”ĐșĐŸŃ‚ĐŸŃ€Ń‹Đ” ĐżĐŸŃŃ‚Đ°ĐČщоĐșĐž ĐĄI ĐżĐŸĐŽĐŽŃ‘Ń€ĐłĐžĐČают Ń„ŃƒĐœĐșцою 'matrix', ĐșĐŸŃ‚ĐŸŃ€Đ°Ń ĐżĐŸĐ·ĐČĐŸĐ»ŃĐ”Ń‚ Đ·Đ°ĐżŃƒŃŃ‚ĐžŃ‚ŃŒ ĐșĐŸĐœĐČĐ”ĐčДр Ń‚Đ”ŃŃ‚ĐŸĐČ ĐŽĐ»Ń ĐČсДх ĐČĐ°Ń€ĐžĐ°ĐœŃ‚ĐŸĐČ MySQL, Postgres Đž ĐœĐ”ŃĐșĐŸĐ»ŃŒĐșох ĐČДрсОĐč Node (8, 9, 10). Đ­Ń‚ĐŸ ĐŽĐ”Đ»Đ°Đ”Ń‚ŃŃ Ń‚ĐŸĐ»ŃŒĐșĐŸ с ĐżĐŸĐŒĐŸŃ‰ŃŒŃŽ ĐșĐŸĐœŃ„ĐžĐłŃƒŃ€Đ°Ń†ĐžĐž бДз ĐșаĐșох-Đ»ĐžĐ±ĐŸ ĐŽĐŸĐżĐŸĐ»ĐœĐžŃ‚Đ”Đ»ŃŒĐœŃ‹Ń… усОлОĐč (про ŃƒŃĐ»ĐŸĐČОО, Ń‡Ń‚ĐŸ у ĐČас Đ”ŃŃ‚ŃŒ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžĐ” ОлО любыД ĐŽŃ€ŃƒĐłĐžĐ” ĐżŃ€ĐŸĐČДрĐșĐž ĐșачДстĐČа). Đ”Ń€ŃƒĐłĐžĐ” CI, ĐœĐ” ĐżĐŸĐŽĐŽĐ”Ń€Đ¶ĐžĐČающОД Matrix, ĐŒĐŸĐłŃƒŃ‚ ĐžĐŒĐ”Ń‚ŃŒ Ń€Đ°ŃŃˆĐžŃ€Đ”ĐœĐžŃ ĐŽĐ»Ń ŃŃ‚ĐŸĐłĐŸ. +
    + +❌ **Đ˜ĐœĐ°Ń‡Đ”:** йаĐș ĐœĐ”ŃƒĐ¶Đ”Đ»Đž ĐżĐŸŃĐ»Đ” ĐČсДĐč ŃŃ‚ĐŸĐč Ń‚ŃĐ¶Đ”Đ»ĐŸĐč Ń€Đ°Đ±ĐŸŃ‚Ń‹ ĐżĐŸ ĐœĐ°ĐżĐžŃĐ°ĐœĐžŃŽ Ń‚Đ”ŃŃ‚ĐŸĐČ ĐŒŃ‹ ĐżĐŸĐ·ĐČĐŸĐ»ĐžĐŒ ĐŸŃˆĐžĐ±ĐșĐ°ĐŒ ĐżŃ€ĐŸĐșрасться Ń‚ĐŸĐ»ŃŒĐșĐŸ Оз-за ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒ с ĐșĐŸĐœŃ„ĐžĐłŃƒŃ€Đ°Ń†ĐžĐ”Đč? + +
    + +
    ✏ ĐŸŃ€ĐžĐŒĐ”Ń€Ń‹ ĐșĐŸĐŽĐ° + +
    + +### :clap: ĐŸŃ€ĐžĐŒĐ”Ń€: Đ˜ŃĐżĐŸĐ»ŃŒĐ·ĐŸĐČĐ°ĐœĐžĐ” Travis (ŃĐŸĐŽĐ”Ń€Đ¶ĐžŃ‚ CI) ĐŽĐ»Ń запусĐșа ĐŸĐŽĐœĐŸĐłĐŸ Đž Ń‚ĐŸĐłĐŸ жД тДста ĐœĐ° ĐœĐ”ŃĐșĐŸĐ»ŃŒĐșох ĐČĐ”Ń€ŃĐžŃŃ… Node + +
    language: node_js
    node_js:
    - "7"
    - "6"
    - "5"
    - "4"
    install:
    - npm install
    script:
    - npm run test
    +
    + +

    + +# ĐšĐŸĐŒĐ°ĐœĐŽĐ° + +## Yoni Goldberg + +
    + +
    + +**Đ ĐŸĐ»ŃŒ:** АĐČŃ‚ĐŸŃ€ + +**Đ˜ĐœŃ„ĐŸŃ€ĐŒĐ°Ń†ĐžŃ:** ĐŻ ĐœĐ”Đ·Đ°ĐČĐžŃĐžĐŒŃ‹Đč ĐșĐŸĐœŃŃƒĐ»ŃŒŃ‚Đ°ĐœŃ‚, ĐșĐŸŃ‚ĐŸŃ€Ń‹Đč Ń€Đ°Đ±ĐŸŃ‚Đ°Đ”Ń‚ с ĐșĐŸĐŒĐżĐ°ĐœĐžŃĐŒĐž Fortune 500 Đž ĐłĐ°Ń€Đ°Đ¶ĐœŃ‹ĐŒĐž ŃŃ‚Đ°Ń€Ń‚Đ°ĐżĐ°ĐŒĐž ĐœĐ°ĐŽ ŃĐŸĐČĐ”Ń€ŃˆĐ”ĐœŃŃ‚ĐČĐŸĐČĐ°ĐœĐžĐ”ĐŒ ох JS Đž Node.js ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐč. Đ‘ĐŸĐ»ŃŒŃˆĐ” Юругох Ń‚Đ”ĐŒ я уĐČлДĐșаюсь Đž ŃŃ‚Ń€Đ”ĐŒĐ»ŃŽŃŃŒ ĐŸĐČĐ»Đ°ĐŽĐ”Ń‚ŃŒ ОсĐșусстĐČĐŸĐŒ Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ. ĐŻ таĐșжД яĐČĐ»ŃŃŽŃŃŒ аĐČŃ‚ĐŸŃ€ĐŸĐŒ ĐșĐœĐžĐłĐž [Node.js Best Practices] (https://github.com/goldbergyoni/nodebestpractices). + +**📗 ĐžĐœĐ»Đ°ĐčĐœ-Đșурс:** ĐŸĐŸĐœŃ€Đ°ĐČĐžĐ»ĐŸŃŃŒ ŃŃ‚ĐŸ руĐșĐŸĐČĐŸĐŽŃŃ‚ĐČĐŸ Đž ĐČы Ń…ĐŸŃ‚ĐžŃ‚Đ” ĐŽĐŸĐČДстО сĐČĐŸĐž ĐœĐ°ĐČыĐșĐž Ń‚Đ”ŃŃ‚ĐžŃ€ĐŸĐČĐ°ĐœĐžŃ ĐŽĐŸ ŃĐŸĐČĐ”Ń€ŃˆĐ”ĐœŃŃ‚ĐČа? ĐŸĐŸŃĐ”Ń‚ĐžŃ‚Đ” ĐŒĐŸĐč ĐșĐŸĐŒĐżĐ»Đ”ĐșŃĐœŃ‹Đč Đșурс [Testing Node.js & JavaScript From A To Z] (https://www.testjavascript.com). + +
    + +**Follow:** + +- [🐩 Twitter](https://twitter.com/goldbergyoni/) +- [📞 Contact](https://testjavascript.com/contact-2/) +- [✉ Newsletter](https://testjavascript.com/newsletter//) + +
    +
    +
    + +## [Bruno Scheufler](https://github.com/BrunoScheufler) + +**Đ ĐŸĐ»ŃŒ:** ĐšĐŸĐœŃŃƒĐ»ŃŒŃ‚Đ°ĐœŃ‚ Đž Ń‚Đ”Ń…ĐœĐžŃ‡Đ”ŃĐșĐžĐč Ń€Đ”Ń†Đ”ĐœĐ·Đ”ĐœŃ‚ + +ĐŸĐ”Ń€Đ”ŃĐŒĐŸŃ‚Ń€Đ”Ń‚ŃŒ, ŃƒĐ»ŃƒŃ‡ŃˆĐžŃ‚ŃŒ, Đž ĐŸŃ‚ŃˆĐ»ĐžŃ„ĐŸĐČать ĐČсД тДĐșсты. + +**Đ˜ĐœŃ„ĐŸŃ€ĐŒĐ°Ń†ĐžŃ:** full-stack web-Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐș, Node.js & GraphQL Đ»ŃŽĐ±ĐžŃ‚Đ”Đ»ŃŒ + +
    +
    + +## [Ido Richter](https://github.com/idori) + +**Đ ĐŸĐ»ŃŒ:** ĐšĐŸĐœŃ†Đ”ĐżŃ†ĐžŃ, ЎОзаĐčĐœ Đž ĐŸŃ‚Đ»ĐžŃ‡ĐœŃ‹Đ” ŃĐŸĐČДты + +**Đ˜ĐœŃ„ĐŸŃ€ĐŒĐ°Ń†ĐžŃ:** ĐœĐ°ŃŃ‚Đ”Ń€ŃĐșĐžĐč frontend-Ń€Đ°Đ·Ń€Đ°Đ±ĐŸŃ‚Ń‡ĐžĐș, CSS-эĐșспДрт Đž Đ»ŃŽĐ±ĐžŃ‚Đ”Đ»ŃŒ ŃĐŒĐŸĐŽĐ¶Đž + +## [Kyle Martin](https://github.com/js-kyle) + +**Đ ĐŸĐ»ŃŒ:** ĐŸĐŸĐŒĐŸĐłĐ°Đ”Ń‚ ĐżĐŸĐŽĐŽĐ”Ń€Đ¶ĐžĐČать ĐżŃ€ĐŸĐ”Đșт ĐČ Ń€Đ°Đ±ĐŸŃ‡Đ”ĐŒ ŃĐŸŃŃ‚ĐŸŃĐœĐžĐž Đž Đ°ĐœĐ°Đ»ĐžĐ·ĐžŃ€ŃƒĐ”Ń‚ ĐŒĐ”Ń‚ĐŸĐŽŃ‹, сĐČŃĐ·Đ°ĐœĐœŃ‹Đ” с Đ±Đ”Đ·ĐŸĐżĐ°ŃĐœĐŸŃŃ‚ŃŒŃŽ + +**About:** ЛюбОт Ń€Đ°Đ±ĐŸŃ‚Đ°Ń‚ŃŒ с Node.js ĐżŃ€ĐŸĐ”ĐșŃ‚Đ°ĐŒĐž Đž Đ±Đ”Đ·ĐŸĐżĐ°ŃĐœĐŸŃŃ‚ŃŒŃŽ ĐČДб-ĐżŃ€ĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐč. + +## Contributors ✹ + +Thanks goes to these wonderful people who have contributed to this repository! + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    Scott Davis

    🖋

    Adrien REDON

    🖋

    Stefano Magni

    🖋

    Yeoh Joer

    🖋

    Jhonny Moreira

    🖋

    Ian Germann

    🖋

    Hafez

    🖋

    Ruxandra Fediuc

    🖋

    Jack

    🖋

    Peter Carrero

    🖋

    Huhgawz

    🖋

    Haakon Borch

    🖋

    Jaime Mendoza

    🖋

    Cameron Dunford

    🖋

    John Gee

    🖋

    Aurelijus RoĆŸÄ—nas

    🖋

    Aaron

    🖋

    Tom Nagle

    🖋

    Yves yao

    🖋

    Userbit

    🖋

    Glaucia Lemos

    🚧

    koooge

    🖋

    Michal

    🖋

    roywalker

    🖋

    dangen

    🖋

    biesiadamich

    🖋

    Yanlin Jiang

    🖋

    sanguino

    🖋

    Morgan

    🖋

    Lukas Bischof

    ⚠ 🖋

    JuanMa Ruiz

    🖋

    Luís Ângelo Rodrigues Jr.

    🖋

    José Fernåndez

    🖋

    Alejandro Gutierrez Barcenilla

    🖋

    Jason

    🖋

    Otavio Araujo

    ⚠ 🖋

    Alex Ivanov

    🖋

    Yiqiao Xu

    🖋

    YuBin, Hsu

    🌍 đŸ’»
    + + + + + diff --git a/readme-ua.md b/readme-ua.md new file mode 100644 index 00000000..1f76d0e4 --- /dev/null +++ b/readme-ua.md @@ -0,0 +1,2135 @@ + + +# 👇 Đ§ĐŸĐŒŃƒ цДĐč ĐżĐŸŃŃ–Đ±ĐœĐžĐș ĐŒĐŸĐ¶Đ” ĐČĐžĐČДстО ĐČаші ĐœĐ°ĐČочĐșĐž Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐœĐ° ĐœĐŸĐČĐžĐč ріĐČĐ”ĐœŃŒ + +
    + +## 📗 50+ ĐœĐ°ĐčĐșращох праĐșтоĐș: ĐœĐ°ĐŽĐ·ĐČочаĐčĐœĐŸ ĐșĐŸĐŒĐżĐ»Đ”ĐșŃĐœĐžĐč і ĐČĐžŃ‡Đ”Ń€ĐżĐœĐžĐč + +ĐŠĐ” ĐżĐŸŃŃ–Đ±ĐœĐžĐș Ń–Đ· ĐœĐ°ĐŽŃ–ĐčĐœĐŸŃŃ‚Ń– JavaScript і Node.js ĐČіЮ А ĐŽĐŸ ĐŻ. Đ’Ń–Đœ ŃƒĐ·Đ°ĐłĐ°Đ»ŃŒĐœŃŽŃ” та Đșурує ĐŽĐ»Ń ĐČас ĐŽĐ”ŃŃŃ‚ĐșĐž ĐœĐ°ĐčĐșращох ĐżŃƒĐ±Đ»Ń–ĐșаціĐč у Đ±Đ»ĐŸĐłĐ°Ń…, ĐșĐœĐžĐł та Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Ń–ĐČ, яĐșі ĐŒĐŸĐ¶Đ” Đ·Đ°ĐżŃ€ĐŸĐżĐŸĐœŃƒĐČато Ń€ĐžĐœĐŸĐș + +## 🚱 ĐŸŃ€ĐŸŃŃƒĐœŃƒŃ‚ĐžĐč: ĐČĐžŃ…ĐŸĐŽĐžŃ‚ŃŒ ĐœĐ° 10 000 ĐŒĐžĐ»ŃŒ за ĐŒĐ”Đ¶Ń– ĐŸŃĐœĐŸĐČ + +ВорушаĐčтД ĐČ ĐżĐŸĐŽĐŸŃ€ĐŸĐ¶, яĐșа ĐČĐžŃ…ĐŸĐŽĐžŃ‚ŃŒ ЎалДĐșĐŸ за ĐŒĐ”Đ¶Ń– ĐŸŃĐœĐŸĐČ Ń– ĐżĐ”Ń€Đ”Ń…ĐŸĐŽĐžŃ‚ŃŒ ĐŽĐŸ сĐșĐ»Đ°ĐŽĐœĐžŃ… Ń‚Đ”ĐŒ, яĐș Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ у ĐČĐžŃ€ĐŸĐ±ĐœĐžŃ†Ń‚ĐČі, ĐŒŃƒŃ‚Đ°Ń†Ń–ĐčĐœĐ” Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ, Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐœĐ° ĐŸŃĐœĐŸĐČі ĐČластОĐČĐŸŃŃ‚Đ”Đč та Đ±Đ°ĐłĐ°Ń‚ĐŸ Ń–ĐœŃˆĐžŃ… ŃŃ‚Ń€Đ°Ń‚Đ”ĐłŃ–Ń‡ĐœĐžŃ… і ĐżŃ€ĐŸŃ„Đ”ŃŃ–ĐčĐœĐžŃ… Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Ń–ĐČ. ĐŻĐșŃ‰ĐŸ ĐČĐž ĐżŃ€ĐŸŃ‡ĐžŃ‚Đ°Ń”Ń‚Đ” ĐșĐŸĐ¶ĐœĐ” ŃĐ»ĐŸĐČĐŸ ĐČ Ń†ŃŒĐŸĐŒŃƒ ĐżĐŸŃŃ–Đ±ĐœĐžĐșу, ĐČаші ĐœĐ°ĐČочĐșĐž Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ, шĐČОЎшД за ĐČсД, Đ±ŃƒĐŽŃƒŃ‚ŃŒ ĐœĐ°Đ±Đ°ĐłĐ°Ń‚ĐŸ ĐČОщД ŃĐ”Ń€Đ”ĐŽĐœŃŒĐŸĐłĐŸ + +## 🌐 Full-stack: front, backend, CI, та Ń–ĐœŃˆĐ” + +ĐŸĐŸŃ‡ĐœŃ–Ń‚ŃŒ Ń–Đ· Ń€ĐŸĐ·ŃƒĐŒŃ–ĐœĐœŃ праĐșтоĐș Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ, яĐșі є ĐŸŃĐœĐŸĐČĐŸŃŽ ĐŽĐ»Ń Đ±ŃƒĐŽŃŒ-яĐșĐŸĐłĐŸ ріĐČĐœŃ ĐżŃ€ĐŸĐłŃ€Đ°ĐŒĐž. ĐŸĐŸŃ‚Ń–ĐŒ Đ·Đ°ĐłĐ»ĐžĐ±Ń‚Đ”ŃŃ ĐČ ĐŸĐ±Ń€Đ°ĐœŃƒ ĐŸĐ±Đ»Đ°ŃŃ‚ŃŒ: frontend + +
    + +### НапосаĐČ Yoni Goldberg + +- JavaScript & Node.js ĐșĐŸĐœŃŃƒĐ»ŃŒŃ‚Đ°ĐœŃ‚ +- 📗 [ĐąĐ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ Node.js і JavaScript ĐČіЮ А ĐŽĐŸ ĐŻ](https://www.testjavascript.com) - МіĐč ĐșĐŸĐŒĐżĐ»Đ”ĐșŃĐœĐžĐč ĐŸĐœĐ»Đ°ĐčĐœ-Đșурс Ń–Đ· Đ±Ń–Đ»ŃŒŃˆ ĐœŃ–Đ¶ [7 ĐłĐŸĐŽĐžĐœĐ°ĐŒĐž ĐČŃ–ĐŽĐ”ĐŸ](https://www.testjavascript.com), 14 топіĐČ Ń‚Đ”ŃŃ‚Ń–ĐČ Ń– Đ±Ń–Đ»ŃŒŃˆĐ” 40 Đșращох праĐșтоĐș +- [ХліЎĐșуĐčтД за ĐŒĐœĐŸŃŽ ĐČ Twitter](https://twitter.com/goldbergyoni/) + +
    + +### ĐŸĐ”Ń€Đ”ĐșлаЎО - чотаĐčтД сĐČĐŸŃ”ŃŽ ĐŒĐŸĐČĐŸŃŽ + +- 🇹🇳[Chinese](readme-zh-CN.md) - ЗаĐČЮяĐșĐž [Yves yao](https://github.com/yvesyao) +- đŸ‡°đŸ‡·[Korean](readme.kr.md) - ЗаĐČЮяĐșĐž [Rain Byun](https://github.com/ragubyun) +- đŸ‡”đŸ‡±[Polish](readme-pl.md) - ЗаĐČЮяĐșĐž [Michal Biesiada](https://github.com/mbiesiad) +- đŸ‡Ș🇾[Spanish](readme-es.md) - ЗаĐČЮяĐșĐž [Miguel G. Sanguino](https://github.com/sanguino) +- đŸ‡§đŸ‡·[Portuguese-BR](readme-pt-br.md) - ЗаĐČЮяĐșĐž [Iago Angelim Costa Cavalcante](https://github.com/iagocavalcante) , [Douglas Mariano Valero](https://github.com/DouglasMV) and [koooge](https://github.com/koooge) +- đŸ‡«đŸ‡·[French](readme-fr.md) - ЗаĐČЮяĐșĐž [Mathilde El Mouktafi](https://github.com/mel-mouk) +- đŸ‡ŻđŸ‡”[Japanese (draft)](https://github.com/yuichkun/javascript-testing-best-practices/blob/master/readme-jp.md) - ЗаĐČЮяĐșĐž [Yuichi Yogo](https://github.com/yuichkun) and [ryo](https://github.com/kawamataryo) +- đŸ‡čđŸ‡Œ[Traditional Chinese](readme-zh-TW.md) - ЗаĐČЮяĐșĐž [Yubin Hsu](https://github.com/yubinTW) +- đŸ‡ș🇩[Ukrainian](readme-ua.md) - ЗаĐČЮяĐșĐž [Serhii Shramko](https://github.com/Shramkoweb) +- Đ„ĐŸŃ‡Đ”Ń‚Đ” пДрДĐșластО ĐœĐ° сĐČĐŸŃŽ Ń€Ń–ĐŽĐœŃƒ ĐŒĐŸĐČу? Đ±ŃƒĐŽŃŒ ласĐșа, ĐČіЮĐșроĐčтД issue 💜 + +

    + +## `Đ—ĐŒŃ–ŃŃ‚` + +#### [`Đ ĐŸĐ·ĐŽŃ–Đ» 0: Đ—ĐŸĐ»ĐŸŃ‚Đ” праĐČĐžĐ»ĐŸ`](#section-0ïžâƒŁ-the-golden-rule) + +ĐžĐŽĐœĐ° ĐżĐŸŃ€Đ°ĐŽĐ°, яĐșа ĐœĐ°ĐŽĐžŃ…Đ°Ń” ĐČсіх Ń–ĐœŃˆĐžŃ… (1 ŃĐżĐ”Ń†Ń–Đ°Đ»ŃŒĐœĐžĐč ĐżŃƒĐœĐșт) + +#### [`Đ ĐŸĐ·ĐŽŃ–Đ» 1: ĐĐœĐ°Ń‚ĐŸĐŒŃ–Ń Ń‚Đ”ŃŃ‚Ńƒ`](#section-1-the-test-anatomy-1) + +ĐžŃĐœĐŸĐČа - струĐșтуруĐČĐ°ĐœĐœŃ чостох тДстіĐČ (12 ĐżŃƒĐœĐșт) + +#### [`Đ ĐŸĐ·ĐŽŃ–Đ» 2: Backend`](#section-2ïžâƒŁ-backend-testing) + +ЕфДĐșтоĐČĐœĐ” ĐœĐ°ĐżĐžŃĐ°ĐœĐœŃ backend-тДстіĐČ Ń– тДстіĐČ ĐŒŃ–ĐșŃ€ĐŸŃĐ”Ń€ĐČісіĐČ (13 ĐżŃƒĐœĐșтіĐČ) + +#### [`Đ ĐŸĐ·ĐŽŃ–Đ» 3: Frontend`](#section-3ïžâƒŁ-frontend-testing) + +ĐĐ°ĐżĐžŃĐ°ĐœĐœŃ тДстіĐČ ĐŽĐ»Ń ĐČДб-Ń–ĐœŃ‚Đ”Ń€Ń„Đ”Đčсу, ĐČĐșлючаючО тДстО ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Ń–ĐČ Ń– E2E (11 ĐżŃƒĐœĐșтіĐČ) + +#### [`Đ ĐŸĐ·ĐŽŃ–Đ» 4: Đ’ĐžĐŒŃ–Ń€ŃŽĐČĐ°ĐœĐœŃ ДфДĐșтоĐČĐœĐŸŃŃ‚Ń– тДстіĐČ`](#section-4ïžâƒŁ-measuring-test-effectiveness) + +ĐĄĐżĐŸŃŃ‚Đ”Ń€Đ”Đ¶Đ”ĐœĐœŃ за ŃŃ‚ĐŸŃ€ĐŸĐ¶Đ”ĐŒ - ĐČĐžĐŒŃ–Ń€ŃŽĐČĐ°ĐœĐœŃ яĐșĐŸŃŃ‚Ń– Ń‚Đ”ŃŃ‚Ńƒ (4 ĐżŃƒĐœĐșто) + +#### [`Đ ĐŸĐ·ĐŽŃ–Đ» 5: БДзпДрДрĐČĐœĐ° Ń–ĐœŃ‚Đ”ĐłŃ€Đ°Ń†Ń–Ń`](#section-5ïžâƒŁ-ci-and-other-quality-measures) + +Đ Đ”ĐșĐŸĐŒĐ”ĐœĐŽĐ°Ń†Ń–Ń— Ń‰ĐŸĐŽĐŸ CI у сĐČіті JS (9 ĐżŃƒĐœĐșтіĐČ) + +

    + +# Đ ĐŸĐ·ĐŽŃ–Đ» 0ïžâƒŁ: Đ—ĐŸĐ»ĐŸŃ‚Đ” праĐČĐžĐ»ĐŸ + +
    + +## âšȘ 0 Đ—ĐŸĐ»ĐŸŃ‚Đ” праĐČĐžĐ»ĐŸ: ЎОзаĐčĐœ ĐŽĐ»Ń ĐŸŃ‰Đ°ĐŽĐ»ĐžĐČĐŸĐłĐŸ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ + +:white_check_mark: **Đ ĐŸĐ±Đž:** +ĐąĐ”ŃŃ‚ĐŸĐČĐžĐč ĐșĐŸĐŽ – цД ĐœĐ” Ń€ĐŸĐ±ĐŸŃ‡ĐžĐč ĐșĐŸĐŽ. Đ ĐŸĐ·Ń€ĐŸĐ±Ń–Ń‚ŃŒ ĐčĐŸĐłĐŸ таĐșĐžĐŒ, Ń‰ĐŸĐ± ĐČŃ–Đœ буĐČ ĐșĐŸŃ€ĐŸŃ‚ĐșĐžĐŒ, ĐœĐ°ĐŽĐ·ĐČочаĐčĐœĐŸ ĐżŃ€ĐŸŃŃ‚ĐžĐŒ, Đ·Ń€ĐŸĐ·ŃƒĐŒŃ–Đ»ĐžĐŒ і ĐżŃ€ĐžŃ”ĐŒĐœĐžĐŒ ĐŽĐ»Ń Ń€ĐŸĐ±ĐŸŃ‚Đž. йрДба ĐżĐŸĐŽĐžĐČотося ĐœĐ° тДст і ĐŒĐžŃ‚Ń‚Ń”ĐČĐŸ Đ·Ń€ĐŸĐ·ŃƒĐŒŃ–Ń‚Đž ĐœĐ°ĐŒŃ–Ń€. + +Бачош, ĐœĐ°ŃˆŃ– ŃƒĐŒĐž ĐČжД заĐčĐœŃŃ‚Ń– ĐŸŃĐœĐŸĐČĐœĐŸŃŽ Ń€ĐŸĐ±ĐŸŃ‚ĐŸŃŽ – production-ĐșĐŸĐŽĐŸĐŒ. ĐĐ”ĐŒĐ°Ń” Â«ĐżŃ€ĐŸŃŃ‚ĐŸŃ€ŃƒÂ» ĐŽĐ»Ń ĐŽĐŸĐŽĐ°Ń‚ĐșĐŸĐČĐŸŃ— сĐșĐ»Đ°ĐŽĐœĐŸŃŃ‚Ń–. ĐŻĐșŃ‰ĐŸ ĐŒĐž ŃĐżŃ€ĐŸĐ±ŃƒŃ”ĐŒĐŸ ĐČŃ‚ĐžŃĐœŃƒŃ‚Đž ĐČ ĐœĐ°Ńˆ Đ±Ń–ĐŽĐŸĐ»Đ°ŃˆĐœĐžĐč ĐŒĐŸĐ·ĐŸĐș щД ĐŸĐŽĐœŃƒ ŃĐžŃŃ‚Đ”ĐŒŃƒ [sus](https://en.wikipedia.org/wiki/System_usability_scale), цД ŃĐżĐŸĐČŃ–Đ»ŃŒĐœĐžŃ‚ŃŒ Ń€ĐŸĐ±ĐŸŃ‚Ńƒ ĐșĐŸĐŒĐ°ĐœĐŽĐž, яĐșа працює ĐżŃ€ĐŸŃ‚Đž Ń‚ĐŸĐłĐŸ, Ń‡ĐŸĐŒŃƒ ĐŒĐž ĐżŃ€ĐŸĐČĐŸĐŽĐžĐŒĐŸ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ. ПраĐșŃ‚ĐžŃ‡ĐœĐŸ тут, Đ±Đ°ĐłĐ°Ń‚ĐŸ ĐșĐŸĐŒĐ°ĐœĐŽ ĐżŃ€ĐŸŃŃ‚ĐŸ ĐČŃ–ĐŽĐŒĐŸĐČĐ»ŃŃŽŃ‚ŃŒŃŃ ĐČіЮ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ. + +Đ’ĐžĐżŃ€ĐŸĐ±ŃƒĐČĐ°ĐœĐœŃ — цД ĐŒĐŸĐ¶Đ»ĐžĐČість ĐŽĐ»Ń Ń‡ĐŸĐłĐŸŃŃŒ Ń–ĐœŃˆĐŸĐłĐŸ — ĐŽĐŸĐ±Ń€ĐŸĐ·ĐžŃ‡Đ»ĐžĐČĐŸĐłĐŸ ĐżĐŸĐŒŃ–Ń‡ĐœĐžĐșа, ĐŽŃ€ŃƒĐłĐŸĐłĐŸ ĐżŃ–Đ»ĐŸŃ‚Đ°, яĐșĐžĐč Юає ĐČДлОĐșу Ń†Ń–ĐœĐœŃ–ŃŃ‚ŃŒ за ĐœĐ”ĐČДлОĐșі Ń–ĐœĐČДстОції. НауĐșа ĐłĐŸĐČĐŸŃ€ĐžŃ‚ŃŒ ĐœĐ°ĐŒ, Ń‰ĐŸ у ĐœĐ°Ń є ĐŽĐČі ŃĐžŃŃ‚Đ”ĐŒĐž ĐŒĐŸĐ·Đșу: ŃĐžŃŃ‚Đ”ĐŒĐ° 1 ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČується ĐŽĐ»Ń лДгĐșĐŸŃ— ĐŽŃ–ŃĐ»ŃŒĐœĐŸŃŃ‚Ń–, яĐș-ĐŸŃ‚ ĐČĐŸĐŽŃ–ĐœĐœŃ аĐČŃ‚ĐŸĐŒĐŸĐ±Ń–Đ»Ń ĐżĐŸ ĐżĐŸŃ€ĐŸĐ¶ĐœŃ–Đč ĐŽĐŸŃ€ĐŸĐ·Ń–, і ŃĐžŃŃ‚Đ”ĐŒĐ° 2, яĐșа ĐżŃ€ĐžĐ·ĐœĐ°Ń‡Đ”ĐœĐ° ĐŽĐ»Ń сĐșĐ»Đ°ĐŽĐœĐžŃ… і усĐČŃ–ĐŽĐŸĐŒĐ»Đ”ĐœĐžŃ… ĐŸĐżĐ”Ń€Đ°Ń†Ń–Đč, таĐșох яĐș Ń€ĐŸĐ·ĐČÊŒŃĐ·ŃƒĐČĐ°ĐœĐœŃ ĐŒĐ°Ń‚Đ”ĐŒĐ°Ń‚ĐžŃ‡ĐœĐŸĐłĐŸ ріĐČĐœŃĐœĐœŃ. Đ ĐŸĐ·Ń€ĐŸĐ±Ń–Ń‚ŃŒ сĐČіĐč тДст ĐŽĐ»Ń ŃĐžŃŃ‚Đ”ĐŒĐž 1. ДоĐČĐ»ŃŃ‡ĐžŃŃŒ ĐœĐ° Ń‚Đ”ŃŃ‚ĐŸĐČĐžĐč ĐșĐŸĐŽ, цД ĐżĐŸĐČĐžĐœĐœĐŸ _ĐČіЮчуĐČатося_ таĐșĐžĐŒ жД лДгĐșĐžĐŒ, яĐș Đ·ĐŒŃ–ĐœĐ° HTML-ĐŽĐŸĐșŃƒĐŒĐ”ĐœŃ‚Đ°, а ĐœĐ” Ń€ĐŸĐ·ĐČÊŒŃĐ·ŃƒĐČĐ°ĐœĐœŃ 2X(17 × 24). + +ĐŠŃŒĐŸĐłĐŸ ĐŒĐŸĐ¶ĐœĐ° ĐŽĐŸŃŃĐłŃ‚Đž ŃˆĐ»ŃŃ…ĐŸĐŒ ĐČОбірĐșĐŸĐČĐŸĐłĐŸ ĐČĐžĐ±ĐŸŃ€Ńƒ ĐŒĐ”Ń‚ĐŸĐŽŃ–ĐČ, Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Ń–ĐČ Ń– Ń‚Đ”ŃŃ‚ĐŸĐČох цілДĐč, яĐșі є Đ”ĐșĐŸĐœĐŸĐŒŃ–Ń‡ĐœĐŸ ДфДĐșтоĐČĐœĐžĐŒĐž та Đ·Đ°Đ±Đ”Đ·ĐżĐ”Ń‡ŃƒŃŽŃ‚ŃŒ [ĐČĐžŃĐŸĐșу Ń€Đ”ĐœŃ‚Đ°Đ±Đ”Đ»ŃŒĐœŃ–ŃŃ‚ŃŒ Ń–ĐœĐČДстОціĐč](https://en.wikipedia.org/wiki/Return_on_investment). ĐąĐ”ŃŃ‚ŃƒĐčтД лОшД ŃŃ‚Ń–Đ»ŃŒĐșĐž, сĐșŃ–Đ»ŃŒĐșĐž ĐżĐŸŃ‚Ń€Ń–Đ±ĐœĐŸ, ĐœĐ°ĐŒĐ°ĐłĐ°ĐčŃ‚Đ”ŃŃ ĐżŃ–ĐŽŃ‚Ń€ĐžĐŒŃƒĐČато ĐčĐŸĐłĐŸ ŃĐżŃ€ĐžŃ‚ĐœŃ–ŃŃ‚ŃŒ, Ń–ĐœĐșĐŸĐ»Đž ĐœĐ°ĐČіть ĐČĐ°Ń€Ń‚ĐŸ ĐČŃ–ĐŽĐŒĐŸĐČотося ĐČіЮ ĐŽĐ”ŃĐșох тДстіĐČ Ń– ĐżĐŸĐŒŃ–ĐœŃŃ‚Đž ĐœĐ°ĐŽŃ–ĐčĐœŃ–ŃŃ‚ŃŒ ĐœĐ° шĐČОЎĐșість і ĐżŃ€ĐŸŃŃ‚ĐŸŃ‚Ńƒ. + +![alt text](/assets/headspace.png "ĐŁ ĐœĐ°Ń ĐœĐ”ĐŒĐ°Ń” ĐŒŃ–ŃŃ†Ń ĐŽĐ»Ń ĐŽĐŸĐŽĐ°Ń‚ĐșĐŸĐČĐŸŃ— сĐșĐ»Đ°ĐŽĐœĐŸŃŃ‚Ń–") + +Đ‘Ń–Đ»ŃŒŃˆŃ–ŃŃ‚ŃŒ ĐœĐ°ĐČĐ”ĐŽĐ”ĐœĐžŃ… ĐœĐžĐ¶Ń‡Đ” ĐżĐŸŃ€Đ°ĐŽ є ĐżĐŸŃ…Ń–ĐŽĐœĐžĐŒĐž ĐČіЮ Ń†ŃŒĐŸĐłĐŸ ĐżŃ€ĐžĐœŃ†ĐžĐżŃƒ. + +### Đ“ĐŸŃ‚ĐŸĐČі ĐżĐŸŃ‡Đ°Ń‚Đž? + +

    + +# Đ ĐŸĐ·ĐŽŃ–Đ» 1: ĐĐœĐ°Ń‚ĐŸĐŒŃ–Ń Ń‚Đ”ŃŃ‚Ńƒ + +
    + +## âšȘ  1.1 Đ”ĐŸĐŽĐ°ĐčтД 3 Ń‡Đ°ŃŃ‚ĐžĐœĐž ĐŽĐŸ ĐșĐŸĐ¶ĐœĐŸŃ— ĐœĐ°Đ·ĐČĐž Ń‚Đ”ŃŃ‚Ńƒ + +:white_check_mark: **Đ ĐŸĐ±Đž:** ĐŁ Đ·ĐČіті ĐżŃ€ĐŸ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐŒĐ°Ń” Đ±ŃƒŃ‚Đž ĐČĐșĐ°Đ·Đ°ĐœĐŸ, чо Đ·Đ°ĐŽĐŸĐČĐŸĐ»ŃŒĐœŃŃ” ĐżĐŸŃ‚ĐŸŃ‡ĐœĐ° ĐČĐ”Ń€ŃŃ–Ń ĐżŃ€ĐŸĐłŃ€Đ°ĐŒĐž ĐČĐžĐŒĐŸĐłĐž ĐŽĐŸ люЎДĐč, яĐșі ĐœĐ” ĐŸĐ±ĐŸĐČâ€™ŃĐ·ĐșĐŸĐČĐŸ Đ·ĐœĐ°ĐčĐŸĐŒŃ– Đ· ĐșĐŸĐŽĐŸĐŒ: Ń‚Đ”ŃŃ‚ŃƒĐČĐ°Đ»ŃŒĐœĐžĐșа, Ń–ĐœĐ¶Đ”ĐœĐ”Ń€Đ° DevOps, яĐșĐžĐč Ń€ĐŸĐ·ĐłĐŸŃ€Ń‚Đ°Ń”, і ĐŒĐ°ĐčĐ±ŃƒŃ‚ĐœŃŒĐŸĐłĐŸ ĐČас чДрДз ĐŽĐČа Ń€ĐŸĐșĐž. ĐŠŃŒĐŸĐłĐŸ ĐŒĐŸĐ¶ĐœĐ° ĐŽĐŸŃŃĐłŃ‚Đž ĐœĐ°ĐčĐșращД, яĐșŃ‰ĐŸ тДстО сĐșĐ»Đ°ĐŽĐ°Ń‚ĐžĐŒŃƒŃ‚ŃŒŃŃ ĐœĐ° ріĐČĐœŃ– ĐČĐžĐŒĐŸĐł і ĐČĐșĐ»ŃŽŃ‡Đ°Ń‚ĐžĐŒŃƒŃ‚ŃŒ 3 Ń‡Đ°ŃŃ‚ĐžĐœĐž: + +(1) Đ©ĐŸ пДрДĐČіряється? НапроĐșлаЎ, ĐŒĐ”Ń‚ĐŸĐŽ ProductsService.addNewProduct + +(2) За яĐșох ĐŸĐ±ŃŃ‚Đ°ĐČĐžĐœ і ŃŃ†Đ”ĐœĐ°Ń€Ń–ŃŽ? НапроĐșлаЎ, у ĐŒĐ”Ń‚ĐŸĐŽ ĐœĐ” ĐżĐ”Ń€Đ”ĐŽĐ°Ń”Ń‚ŃŒŃŃ Ń†Ń–ĐœĐ° + +(3) ĐŻĐșĐžĐč Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚ ĐŸŃ‡Ń–Đșується? НапроĐșлаЎ, ĐœĐŸĐČĐžĐč ĐżŃ€ĐŸĐŽŃƒĐșт ĐœĐ” затĐČĐ”Ń€ĐŽĐ¶Đ”ĐœĐŸ + +
    + +❌ **Đ†ĐœĐ°ĐșшД:** Đ ĐŸĐ·ĐłĐŸŃ€Ń‚Đ°ĐœĐœŃ Ń‰ĐŸĐčĐœĐŸ ĐœĐ” ĐČĐŽĐ°Đ»ĐŸŃŃŒ, тДст Ń–Đ· ĐœĐ°Đ·ĐČĐŸŃŽ «Add product» ĐœĐ” ĐČЎаĐČся. ЧО цД ĐłĐŸĐČĐŸŃ€ĐžŃ‚ŃŒ ĐČĐ°ĐŒ ĐżŃ€ĐŸ тД, Ń‰ĐŸ ŃĐ°ĐŒĐ” ĐœĐ”ŃĐżŃ€Đ°ĐČĐœĐŸ? + +
    + +**👇 ĐŸŃ€ĐžĐŒŃ–Ń‚Đșа:** ĐšĐŸĐ¶Đ”Đœ ĐżŃƒĐœĐșт ĐŒĐ°Ń” проĐșлаЎО ĐșĐŸĐŽŃƒ, а Ń–ĐœĐșĐŸĐ»Đž таĐșĐŸĐ¶ Đ·ĐŸĐ±Ń€Đ°Đ¶Đ”ĐœĐœŃ. ĐĐ°Ń‚ĐžŃĐœŃ–Ń‚ŃŒ, Ń‰ĐŸĐ± Ń€ĐŸĐ·ĐłĐŸŃ€ĐœŃƒŃ‚Đž +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :clap: Đ ĐŸĐ±Đž цД праĐČĐžĐ»ŃŒĐœĐŸ. ПроĐșлаЎ: Ń–ĐŒâ€™Ń Ń‚Đ”ŃŃ‚Ńƒ, яĐșĐ” сĐșĐ»Đ°ĐŽĐ°Ń”Ń‚ŃŒŃŃ Đ· 3 Ń‡Đ°ŃŃ‚ĐžĐœ + +![](https://img.shields.io/badge/🔹%20Example%20using%20Mocha-blue.svg "ВоĐșĐŸŃ€ĐžŃŃ‚Đ°ĐœĐœŃ Mocha ĐŽĐ»Ń ілюстрації іЎДї") + +```javascript +//1. Đ±Đ»ĐŸĐș Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ +describe('Products Service', function() { + describe('Add new product', function() { + //2. ŃŃ†Đ”ĐœĐ°Ń€Ń–Đč і 3. ĐŸŃ‡Ń–ĐșуĐČĐ°ĐœĐœŃ + it('When no price is specified, then the product status is pending approval', ()=> { + const newProduct = new ProductService().add(...); + expect(newProduct.status).to.equal('pendingApproval'); + }); + }); +}); + +``` + +
    + +### :clap: Đ ĐŸĐ±Đž цД праĐČĐžĐ»ŃŒĐœĐŸ ПроĐșлаЎ: ĐœĐ°Đ·ĐČа Ń‚Đ”ŃŃ‚Ńƒ, Ń‰ĐŸ сĐșĐ»Đ°ĐŽĐ°Ń”Ń‚ŃŒŃŃ Đ· 3 Ń‡Đ°ŃŃ‚ĐžĐœ + +![alt text](/assets/bp-1-3-parts.jpeg "ĐĐ°Đ·ĐČа Ń‚Đ”ŃŃ‚Ńƒ, яĐșа сĐșĐ»Đ°ĐŽĐ°Ń”Ń‚ŃŒŃŃ Đ· 3 Ń‡Đ°ŃŃ‚ĐžĐœ") + +
    + +
    +
    © Credits & read-more + 1. Roy Osherove - Naming standards for unit tests +
    + +

    + +## âšȘ  1.2 СтруĐșŃ‚ŃƒŃ€ĐœŃ– тДстО за ŃŃ…Đ”ĐŒĐŸŃŽ ААА + +:white_check_mark: **Đ ĐŸĐ±Đž:** СтруĐșтуруĐčтД сĐČĐŸŃ— тДстО за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ 3 ĐŽĐŸĐ±Ń€Đ” ĐČŃ–ĐŽĐŸĐșŃ€Đ”ĐŒĐ»Đ”ĐœĐžŃ… Ń€ĐŸĐ·ĐŽŃ–Đ»Ń–ĐČ Â«ĐŁĐżĐŸŃ€ŃĐŽĐșуĐčтД, ЮіĐčтД та затĐČĐ”Ń€ĐŽĐ¶ŃƒĐčтД» (AAA). Đ”ĐŸŃ‚Ń€ĐžĐŒĐ°ĐœĐœŃ цієї струĐșтуро ĐłĐ°Ń€Đ°ĐœŃ‚ŃƒŃ”, Ń‰ĐŸ чотач ĐœĐ” ĐČĐžŃ‚Ń€Đ°Ń‡Đ°Ń‚ĐžĐŒĐ” ĐŒĐŸĐ·ĐŸĐș-CPU ĐœĐ° Ń€ĐŸĐ·ŃƒĐŒŃ–ĐœĐœŃ ĐżĐ»Đ°ĐœŃƒ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ: + +1st A - Arrange(ĐŁĐżĐŸŃ€ŃĐŽĐșуĐČато): Đ’Đ”ŃŃŒ ĐșĐŸĐŽ ĐœĐ°Đ»Đ°ŃˆŃ‚ŃƒĐČĐ°ĐœĐœŃ, Ń‰ĐŸĐ± проĐČДстО ŃĐžŃŃ‚Đ”ĐŒŃƒ ĐŽĐŸ ŃŃ†Đ”ĐœĐ°Ń€Ń–ŃŽ, яĐșĐžĐč ĐŒĐ°Ń” Ń–ĐŒŃ–Ń‚ŃƒĐČато тДст. ĐŠĐ” ĐŒĐŸĐ¶Đ” ĐČĐșлючатО стĐČĐŸŃ€Đ”ĐœĐœŃ Đ”ĐșĐ·Đ”ĐŒĐżĐ»ŃŃ€Đ° Ń‚Đ”ŃŃ‚ĐŸĐČĐŸĐłĐŸ ĐșĐŸĐœŃŃ‚Ń€ŃƒĐșŃ‚ĐŸŃ€Đ° Đ±Đ»ĐŸĐșу, ĐŽĐŸĐŽĐ°ĐČĐ°ĐœĐœŃ запОсіĐČ Đ‘Đ”, mocking/stubbing ĐŸĐ±ÊŒŃ”ĐșтіĐČ Ń‚Đ° Đ±ŃƒĐŽŃŒ-яĐșĐžĐč Ń–ĐœŃˆĐžĐč ĐșĐŸĐŽ ĐżŃ–ĐŽĐłĐŸŃ‚ĐŸĐČĐșĐž + +2nd A - Act(Дія): ВоĐșĐŸĐœĐ°ĐčтД Ń‚Đ”ŃŃ‚ĐŸĐČĐžĐč Đ±Đ»ĐŸĐș. ЗазĐČочаĐč 1 Ń€ŃĐŽĐŸĐș ĐșĐŸĐŽŃƒ + +3rd A - Assert(СтĐČĐ”Ń€ĐŽĐ¶ŃƒĐČĐ°ĐœĐœŃ): ĐŸĐ”Ń€Đ”ĐșĐŸĐœĐ°ĐčŃ‚Đ”ŃŃ, Ń‰ĐŸ ĐŸŃ‚Ń€ĐžĐŒĐ°ĐœĐ” Đ·ĐœĐ°Ń‡Đ”ĐœĐœŃ ĐČŃ–ĐŽĐżĐŸĐČіЮає ĐŸŃ‡Ń–ĐșуĐČĐ°ĐœĐœŃĐŒ. ЗазĐČочаĐč 1 Ń€ŃĐŽĐŸĐș ĐșĐŸĐŽŃƒ + +
    + +❌ **Đ†ĐœĐ°ĐșшД:** Во ĐœĐ” лОшД ĐČОтрачаєтД ĐłĐŸĐŽĐžĐœĐž ĐœĐ° Ń€ĐŸĐ·ŃƒĐŒŃ–ĐœĐœŃ ĐŸŃĐœĐŸĐČĐœĐŸĐłĐŸ ĐșĐŸĐŽŃƒ, алД тД, Ń‰ĐŸ ĐŒĐ°Đ»ĐŸ Đ±ŃƒŃ‚Đž ĐœĐ°ĐčĐżŃ€ĐŸŃŃ‚Ń–ŃˆĐŸŃŽ Ń‡Đ°ŃŃ‚ĐžĐœĐŸŃŽ ĐŽĐœŃ (Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ), ĐœĐ°ĐżŃ€ŃƒĐ¶ŃƒŃ” ĐČаш ĐŒĐŸĐ·ĐŸĐș + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :clap: Đ ĐŸĐ±Đž цД праĐČĐžĐ»ŃŒĐœĐŸ. ПроĐșлаЎ: тДст, струĐșŃ‚ŃƒŃ€ĐŸĐČĐ°ĐœĐžĐč за ŃˆĐ°Đ±Đ»ĐŸĐœĐŸĐŒ AAA + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "ПроĐșлаЎО Đ· Jest") ![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "ПроĐșлаЎО Đ· Mocha") + +```javascript +describe("Customer classifier", () => { + test("When customer spent more than 500$, should be classified as premium", () => { + //Arrange + const customerToClassify = { spent: 505, joined: new Date(), id: 1 }; + const DBStub = sinon.stub(dataAccess, "getCustomer").reply({ id: 1, classification: "regular" }); + + //Act + const receivedClassification = customerClassifier.classifyCustomer(customerToClassify); + + //Assert + expect(receivedClassification).toMatch("premium"); + }); +}); +``` + +
    + +### :thumbsdown: ПроĐșлаЎ Đ°ĐœŃ‚ĐžŃˆĐ°Đ±Đ»ĐŸĐœŃƒ: ĐœĐ”ĐŒĐ°Ń” ĐżĐŸĐŽŃ–Đ»Ńƒ, ĐŸĐŽĐœĐ° група, ĐČажчД Ń–ĐœŃ‚Đ”Ń€ĐżŃ€Đ”Ń‚ŃƒĐČато + +```javascript +test("Should be classified as premium", () => { + const customerToClassify = { spent: 505, joined: new Date(), id: 1 }; + const DBStub = sinon.stub(dataAccess, "getCustomer").reply({ id: 1, classification: "regular" }); + const receivedClassification = customerClassifier.classifyCustomer(customerToClassify); + expect(receivedClassification).toMatch("premium"); +}); +``` + +
    + +

    + +## âšȘ 1.3 Опошіть ĐŸŃ‡Ń–ĐșуĐČĐ°ĐœĐœŃ ĐŒĐŸĐČĐŸŃŽ ĐżŃ€ĐŸĐŽŃƒĐșту: ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČуĐčтД тĐČĐ”Ń€ĐŽĐ¶Đ”ĐœĐœŃ ĐČ ŃŃ‚ĐžĐ»Ń– BDD + +:white_check_mark: **Đ ĐŸĐ±Đž:** ĐĐ°ĐżĐžŃĐ°ĐœĐœŃ ĐČашох тДстіĐČ Ńƒ ĐŽĐ”ĐșларатОĐČĐœĐŸĐŒŃƒ стОлі Юає Đ·ĐŒĐŸĐłŃƒ чОтачДĐČі ĐŒĐžŃ‚Ń‚Ń”ĐČĐŸ ĐŸŃ‚Ń€ĐžĐŒĐ°Ń‚Đž пДрДĐČагО, ĐœĐ” ĐČотрачаючо ĐœĐ° цД Đ·ŃƒŃĐžĐ»Đ»Ń ĐŒĐŸĐ·Đșу. ĐšĐŸĐ»Đž ĐČĐž ĐżĐžŃˆĐ”Ń‚Đ” Ń–ĐŒĐżĐ”Ń€Đ°Ń‚ĐžĐČĐœĐžĐč ĐșĐŸĐŽ, ĐœĐ°ĐżĐŸĐČĐœĐ”ĐœĐžĐč ŃƒĐŒĐŸĐČĐœĐŸŃŽ Đ»ĐŸĐłŃ–ĐșĐŸŃŽ, чотач Đ·ĐŒŃƒŃˆĐ”ĐœĐžĐč ĐŽĐŸĐșлаЎатО Đ±Ń–Đ»ŃŒŃˆĐ” Đ·ŃƒŃĐžĐ»ŃŒ ĐŒĐŸĐ·Đșу. ĐŁ таĐșĐŸĐŒŃƒ ĐČОпаЎĐșу заĐșĐŸĐŽŃƒĐčтД ĐŸŃ‡Ń–ĐșуĐČĐ°ĐœĐœŃ ĐŒĐŸĐČĐŸŃŽ, ŃŃ…ĐŸĐ¶ĐŸŃŽ ĐœĐ° Đ»ŃŽĐŽŃŃŒĐșу, у ĐŽĐ”ĐșларатОĐČĐœĐŸĐŒŃƒ стОлі BDD, ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČуючо `expect` Đ°Đ±ĐŸ `should` і ĐœĐ” ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČуючо ŃĐżĐ”Ń†Ń–Đ°Đ»ŃŒĐœĐžĐč ĐșĐŸĐŽ. ĐŻĐșŃ‰ĐŸ Chai & Jest ĐœĐ” ĐŒŃ–ŃŃ‚ĐžŃ‚ŃŒ ĐżĐŸŃ‚Ń€Ń–Đ±ĐœĐŸĐłĐŸ тĐČĐ”Ń€ĐŽĐ¶Đ”ĐœĐœŃ, і ĐČĐŸĐœĐŸ ЎужД ĐżĐŸĐČŃ‚ĐŸŃ€ŃŽĐČĐ°ĐœĐ”, ĐżĐŸĐŽŃƒĐŒĐ°ĐčтД ĐœĐ°ĐŽ [extending Jest matcher (Jest)](https://jestjs.io/docs/en/expect#expectextendmatchers) Đ°Đ±ĐŸ стĐČĐŸŃ€Ń–Ń‚ŃŒ [custom Chai plugin](https://www.chaijs.com/guide/plugins/) +
    + +❌ **Đ†ĐœĐ°ĐșшД:** ĐšĐŸĐŒĐ°ĐœĐŽĐ° буЎД посато ĐŒĐ”ĐœŃˆĐ” тДстіĐČ Ń– проĐșрашато ĐœĐ°Đ±Ń€ĐžĐŽĐ»ĐžĐČі .skip() + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ
    + +![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "ПроĐșлаЎО Đ· Mocha & Chai") ![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "ПроĐșлаЎО Đ· Jest") + +### :thumbsdown: ПроĐșлаЎ Đ°ĐœŃ‚ĐžŃˆĐ°Đ±Đ»ĐŸĐœŃƒ: чотач ĐżĐŸĐČĐžĐœĐ”Đœ ĐżŃ€ĐŸĐłĐ»ŃĐœŃƒŃ‚Đž ĐŽĐŸĐČгОĐč ĐșĐŸĐŽ і Ń–ĐŒĐżĐ”Ń€Đ°Ń‚ĐžĐČĐœĐžĐč ĐșĐŸĐŽ, Ń‰ĐŸĐ± Đ·Ń€ĐŸĐ·ŃƒĐŒŃ–Ń‚Đž суть + +```javascript +test("When asking for an admin, ensure only ordered admins in results", () => { + // пропусĐșаючо, Ń‰ĐŸ ĐŒĐž ĐŽĐŸĐŽĐ°Đ»Đž сюЮо ĐŽĐČĐŸŃ… Đ°ĐŽĐŒŃ–ĐœŃ–ŃŃ‚Ń€Đ°Ń‚ĐŸŃ€Ń–ĐČ Â«admin1», «admin2» і юзДра «user1» + const allAdmins = getUsers({ adminOnly: true }); + + let admin1Found, + adming2Found = false; + + allAdmins.forEach(aSingleUser => { + if (aSingleUser === "user1") { + assert.notEqual(aSingleUser, "user1", "A user was found and not admin"); + } + if (aSingleUser === "admin1") { + admin1Found = true; + } + if (aSingleUser === "admin2") { + admin2Found = true; + } + }); + + if (!admin1Found || !admin2Found) { + throw new Error("Not all admins were returned"); + } +}); +``` + +
    + +### :clap: Đ ĐŸĐ±Đž цД праĐČĐžĐ»ŃŒĐœĐŸ. ПроĐșлаЎ. ĐŸĐ”Ń€Đ”ĐłĐ»ŃĐŽ ĐœĐ°ŃŃ‚ŃƒĐżĐœĐŸĐłĐŸ ĐŽĐ”ĐșларатОĐČĐœĐŸĐłĐŸ Ń‚Đ”ŃŃ‚Ńƒ – лДгĐșĐžĐč ĐČітДр + +```javascript +it("When asking for an admin, ensure only ordered admins in results", () => { + // пропусĐșаючо, Ń‰ĐŸ ĐŒĐž ĐŽĐŸĐŽĐ°Đ»Đž сюЮо ĐŽĐČĐŸŃ… Đ°ĐŽĐŒŃ–ĐœŃ–ŃŃ‚Ń€Đ°Ń‚ĐŸŃ€Ń–ĐČ Â«admin1», «admin2» і юзДра «user1» + const allAdmins = getUsers({ adminOnly: true }); + + expect(allAdmins) + .to.include.ordered.members(["admin1", "admin2"]) + .but.not.include.ordered.members(["user1"]); +}); +``` + +
    + +

    + +## âšȘ  1.4 Đ”ĐŸŃ‚Ń€ĐžĐŒŃƒĐčŃ‚Đ”ŃŃŒ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ Ń‡ĐŸŃ€ĐœĐŸŃ— сĐșŃ€ĐžĐœŃŒĐșĐž: пДрДĐČіряĐčтД лОшД ĐżŃƒĐ±Đ»Ń–Ń‡ĐœŃ– ĐŒĐ”Ń‚ĐŸĐŽĐž + +:white_check_mark: **Đ ĐŸĐ±Đž:** ĐąĐ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐČĐœŃƒŃ‚Ń€Ń–ŃˆĐœŃ–Ń… ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Ń–ĐČ ĐżŃ€ĐžĐœĐŸŃĐžŃ‚ŃŒ ĐČĐ”Đ»ĐžŃ‡Đ”Đ·ĐœŃ– ĐœĐ°ĐșĐ»Đ°ĐŽĐœŃ– ĐČотрато ĐŒĐ°ĐčжД за Đ±Đ”Đ·Ń†Ń–ĐœŃŒ. ĐŻĐșŃ‰ĐŸ ĐČаш ĐșĐŸĐŽ/API Юає ĐœĐ°Đ»Đ”Đ¶ĐœŃ– Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚Đž, чо ĐČĐ°Ń€Ń‚ĐŸ ĐČĐ°ĐŒ спраĐČЮі ĐČотратото ĐœĐ°ŃŃ‚ŃƒĐżĐœŃ– 3 ĐłĐŸĐŽĐžĐœĐž ĐœĐ° тД, Ń‰ĐŸĐ± пДрДĐČірото, ЯК ĐČŃ–Đœ працює ĐČŃĐ”Ń€Đ”ĐŽĐžĐœŃ–, а ĐżĐŸŃ‚Ń–ĐŒ ĐżŃ–ĐŽŃ‚Ń€ĐžĐŒŃƒĐČато ці ĐșрохĐșі тДстО? ĐšĐŸĐ¶ĐœĐŸĐłĐŸ Ń€Đ°Đ·Ńƒ, ĐșĐŸĐ»Đž пДрДĐČіряється Đ·Đ°ĐłĐ°Đ»ŃŒĐœĐŸĐŽĐŸŃŃ‚ŃƒĐżĐœĐ° ĐżĐŸĐČĐ”ĐŽŃ–ĐœĐșа, проĐČĐ°Ń‚ĐœĐ° Ń€Đ”Đ°Đ»Ń–Đ·Đ°Ń†Ń–Ń таĐșĐŸĐ¶ ĐœĐ”ŃĐČĐœĐŸ пДрДĐČіряється, і ĐČаші тДстО ĐœĐ” працюĐČĐ°Ń‚ĐžĐŒŃƒŃ‚ŃŒ, лОшД яĐșŃ‰ĐŸ є пДĐČĐœĐ° ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐ° (ĐœĐ°ĐżŃ€ĐžĐșлаЎ, ĐœĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœĐžĐč ĐČохіЮ). ĐŠĐ”Đč піЮхіЮ таĐșĐŸĐ¶ ĐœĐ°Đ·ĐžĐČають Â«ĐżĐŸĐČĐ”ĐŽŃ–ĐœĐșĐŸĐČĐžĐŒ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃĐŒÂ». З Ń–ĐœŃˆĐŸĐłĐŸ Đ±ĐŸĐșу, яĐșŃ‰ĐŸ ĐČĐž Ń‚Đ”ŃŃ‚ŃƒŃ”Ń‚Đ” ĐČĐœŃƒŃ‚Ń€Ń–ŃˆĐœŃ– Đ”Đ»Đ”ĐŒĐ”ĐœŃ‚Đž (піЮхіЮ Đ±Ń–Đ»ĐŸĐłĐŸ ящоĐșа) — ĐČаш Ń„ĐŸĐșус ĐżĐ”Ń€Đ”Ń…ĐŸĐŽĐžŃ‚ŃŒ ĐČіЮ ĐżĐ»Đ°ĐœŃƒĐČĐ°ĐœĐœŃ Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚Ńƒ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đ° ĐŽĐŸ ĐŽŃ€Ń–Đ±ĐœĐžŃ… ЎДталДĐč, і ĐČаш тДст ĐŒĐŸĐ¶Đ” Đ±ŃƒŃ‚Đž ĐœĐ”ĐČĐŽĐ°Đ»ĐžĐŒ чДрДз ĐœĐ”Đ·ĐœĐ°Ń‡ĐœŃ– ĐżĐ”Ń€Đ”ĐżĐžŃŃƒĐČĐ°ĐœĐœŃ ĐșĐŸĐŽŃƒ, Ń…ĐŸŃ‡Đ° Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚Đž Ń…ĐŸŃ€ĐŸŃˆŃ– — цД Đ·ĐœĐ°Ń‡ĐœĐŸ Đ·Đ±Ń–Đ»ŃŒŃˆŃƒŃ” Ń‚Đ”Ń…ĐœŃ–Ń‡ĐœĐ” ĐŸĐ±ŃĐ»ŃƒĐłĐŸĐČуĐČĐ°ĐœĐœŃ +
    + +❌ **Đ†ĐœĐ°ĐșшД:** Ваші тДстО ĐżĐŸĐČĐŸĐŽŃŃ‚ŃŒŃŃ яĐș [Ń…Đ»ĐŸĐżŃ‡ĐžĐș, яĐșĐžĐč ĐșрочаĐČ ĐČĐŸĐČĐș](https://en.wikipedia.org/wiki/The_Boy_Who_Cried_Wolf): ĐČОгуĐșĐž Ń…ĐžĐ±ĐœĐŸĐżĐŸĐ·ĐžŃ‚ĐžĐČĐœĐžŃ… ĐșроĐșіĐČ (ĐœĐ°ĐżŃ€ĐžĐșлаЎ, йДст ĐżŃ€ĐŸĐČалОĐČся чДрДз тД, Ń‰ĐŸ Ń–ĐŒâ€™Ń проĐČĐ°Ń‚ĐœĐŸŃ— Đ·ĐŒŃ–ĐœĐœĐŸŃ— Đ±ŃƒĐ»ĐŸ Đ·ĐŒŃ–ĐœĐ”ĐœĐŸ). ĐĐ” ĐŽĐžĐČĐœĐŸ, Ń‰ĐŸ ĐœĐ”Đ·Đ°Đ±Đ°Ń€ĐŸĐŒ люЎО ĐżĐŸŃ‡ĐœŃƒŃ‚ŃŒ Ń–ĐłĐœĐŸŃ€ŃƒĐČато ŃĐżĐŸĐČŃ–Ń‰Đ”ĐœĐœŃ CI, ĐżĐŸĐșĐž ĐŸĐŽĐœĐŸĐłĐŸ ĐŽĐœŃ ĐœĐ” ĐżŃ€ĐŸŃ–ĐłĐœĐŸŃ€ŃƒŃŽŃ‚ŃŒ спраĐČĐ¶ĐœŃŽ ĐżĐŸĐŒĐžĐ»Đșу... + +
    +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :thumbsdown: ПроĐșлаЎ Đ°ĐœŃ‚ĐžŃˆĐ°Đ±Đ»ĐŸĐœŃƒ: Ń‚Đ”ŃŃ‚ĐŸĐČĐžĐč проĐșлаЎ Ń‚Đ”ŃŃ‚ŃƒŃ” ĐČĐœŃƒŃ‚Ń€Ń–ŃˆĐœŃ– ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đž бДз ĐČĐ°ĐłĐŸĐŒĐŸŃ— ĐżŃ€ĐžŃ‡ĐžĐœĐž + +![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "ПроĐșлаЎО Đ· Mocha & Chai") + +```javascript +class ProductService { + // цДĐč ĐŒĐ”Ń‚ĐŸĐŽ ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČується Ń‚Ń–Đ»ŃŒĐșĐž ĐČĐœŃƒŃ‚Ń€Ń–ŃˆĐœŃŒĐŸ + // Đ·ĐŒŃ–ĐœĐ° цієї ĐœĐ°Đ·ĐČĐž прОзĐČДЎД ĐŽĐŸ ĐœĐ”ĐČЮачі тДстіĐČ + calculateVATAdd(priceWithoutVAT) { + return { finalPrice: priceWithoutVAT * 1.2 }; + // Đ—ĐŒŃ–ĐœĐ° Ń„ĐŸŃ€ĐŒĐ°Ń‚Ńƒ Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚Ńƒ Đ°Đ±ĐŸ ĐœĐ°Đ·ĐČĐž Đșлюча ĐČОщД прОзĐČДЎД ĐŽĐŸ ĐœĐ”ĐČЮачі тДстіĐČ + } + // ĐżŃƒĐ±Đ»Ń–Ń‡ĐœĐžĐč ĐŒĐ”Ń‚ĐŸĐŽ + getPrice(productId) { + const desiredProduct = DB.getProduct(productId); + finalPrice = this.calculateVATAdd(desiredProduct.price).finalPrice; + return finalPrice; + } +} + +it("White-box test: When the internal methods get 0 vat, it return 0 response", async () => { + // ĐĐ”ĐŒĐ°Ń” ĐČĐžĐŒĐŸĐłĐž ĐŽĐŸĐ·ĐČĐŸĐ»ŃŃ‚Đž ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČĐ°Ń‡Đ°ĐŒ Ń€ĐŸĐ·Ń€Đ°Ń…ĐŸĐČуĐČато ПДВ, ĐČŃ–ĐŽĐŸĐ±Ń€Đ°Đ¶Đ°Ń‚Đž лОшД ĐŸŃŃ‚Đ°Ń‚ĐŸŃ‡ĐœŃƒ Ń†Ń–ĐœŃƒ. ĐąĐžĐŒ ĐœĐ” ĐŒĐ”ĐœŃˆ, ĐŒĐž Ń…ĐžĐ±ĐœĐŸ ĐœĐ°ĐżĐŸĐ»ŃĐłĐ°Ń”ĐŒĐŸ тут ĐœĐ° пДрДĐČірці ĐČĐœŃƒŃ‚Ń€Ń–ŃˆĐœŃ–Ń… Đ”Đ»Đ”ĐŒĐ”ĐœŃ‚Ń–ĐČ Đșласу + expect(new ProductService().calculateVATAdd(0).finalPrice).to.equal(0); +}); +``` + +
    + +

    + +## âšȘ  1.5 ВОбОраĐčтД праĐČĐžĐ»ŃŒĐœŃ– Ń‚Đ”ŃŃ‚ĐŸĐČі ĐŽŃƒĐ±Đ»Ń–: ŃƒĐœĐžĐșаĐčтД mocks ĐœĐ° ĐșĐŸŃ€ĐžŃŃ‚ŃŒ stubs і spies + +:white_check_mark: **Đ ĐŸĐ±Đž:** ĐŸĐŸĐŽĐČіĐčĐœŃ– тДстО є ĐœĐ”ĐŸĐ±Ń…Ń–ĐŽĐœĐžĐŒ Đ·Đ»ĐŸĐŒ, Ń‚ĐŸĐŒŃƒ Ń‰ĐŸ ĐČĐŸĐœĐž ĐżĐŸĐČ'ŃĐ·Đ°ĐœŃ– Đ· ĐČĐœŃƒŃ‚Ń€Ń–ŃˆĐœŃ–ĐŒĐž Đ”Đ»Đ”ĐŒĐ”ĐœŃ‚Đ°ĐŒĐž ĐżŃ€ĐŸĐłŃ€Đ°ĐŒĐž, алД ĐŽĐ”ŃĐșі Đ· ĐœĐžŃ… ĐŒĐ°ŃŽŃ‚ŃŒ ĐČĐ”Đ»ĐžŃ‡Đ”Đ·ĐœŃƒ Ń†Ń–ĐœĐœŃ–ŃŃ‚ŃŒ ([ĐŸŃ€ĐŸŃ‡ĐžŃ‚Đ°ĐčтД тут ĐœĐ°ĐłĐ°ĐŽŃƒĐČĐ°ĐœĐœŃ: mocks vs stubs vs spies](https://martinfowler.com/articles/mocksArentStubs.html)). + +ĐŸĐ”Ń€Ńˆ ĐœŃ–Đ¶ ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČуĐČато ĐżĐŸĐŽĐČіĐčĐœŃ– тДстО, ĐżĐŸŃŃ‚Đ°ĐČтД ЎужД ĐżŃ€ĐŸŃŃ‚Đ” Đ·Đ°ĐżĐžŃ‚Đ°ĐœĐœŃ: чо ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČую я ĐčĐŸĐłĐŸ ĐŽĐ»Ń Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ Ń„ŃƒĐœĐșŃ†Ń–ĐŸĐœĐ°Đ»ŃŒĐœĐŸŃŃ‚Ń–, яĐșа ĐČŃ–ĐŽĐŸĐ±Ń€Đ°Đ¶Đ°Ń”Ń‚ŃŒŃŃ Đ°Đ±ĐŸ ĐŒĐŸĐ¶Đ” Đ·â€™ŃĐČотося ĐČ ĐŽĐŸĐșŃƒĐŒĐ”ĐœŃ‚Ń– Đ· ĐČĐžĐŒĐŸĐłĐ°ĐŒĐž? ĐŻĐșŃ‰ĐŸ ĐœŃ–, цД запах Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ Đ±Ń–Đ»ĐŸŃ— ĐșĐŸŃ€ĐŸĐ±ĐșĐž. + +НапроĐșлаЎ, яĐșŃ‰ĐŸ ĐČĐž Ń…ĐŸŃ‡Đ”Ń‚Đ” пДрДĐČірото, чо ĐČаша ĐżŃ€ĐŸĐłŃ€Đ°ĐŒĐ° ĐżĐŸĐČĐŸĐŽĐžŃ‚ŃŒŃŃ ĐœĐ°Đ»Đ”Đ¶ĐœĐžĐŒ Ń‡ĐžĐœĐŸĐŒ, ĐșĐŸĐ»Đž ĐżĐ»Đ°Ń‚Ń–Đ¶ĐœĐ° служба ĐœĐ” працює, ĐČĐž ĐŒĐŸĐ¶Đ”Ń‚Đ” Đ·Đ°Đ±Đ»ĐŸĐșуĐČато ĐżĐ»Đ°Ń‚Ń–Đ¶ĐœŃƒ службу та Đ·Đ°ĐżŃƒŃŃ‚ĐžŃ‚Đž ĐŽĐ”ŃĐșĐžĐč ĐżĐŸĐČĐ”Ń€ĐœĐ”ĐœĐœŃ Â«ĐĐ”ĐŒĐ°Ń” ĐČŃ–ĐŽĐżĐŸĐČіЎі», Ń‰ĐŸĐ± пДрДĐșĐŸĐœĐ°Ń‚ĐžŃŃ, Ń‰ĐŸ ĐŸĐŽĐžĐœĐžŃ†Ń, Ń‰ĐŸ Ń‚Đ”ŃŃ‚ŃƒŃ”Ń‚ŃŒŃŃ, ĐżĐŸĐČДртає праĐČĐžĐ»ŃŒĐœĐ” Đ·ĐœĐ°Ń‡Đ”ĐœĐœŃ. ĐŠĐ” пДрДĐČіряє ĐżĐŸĐČĐ”ĐŽŃ–ĐœĐșу/ĐČŃ–ĐŽĐżĐŸĐČіЮь/Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚ ĐœĐ°ŃˆĐŸŃ— ĐżŃ€ĐŸĐłŃ€Đ°ĐŒĐž за пДĐČĐœĐžŃ… ŃŃ†Đ”ĐœĐ°Ń€Ń–Ń—ĐČ. Во таĐșĐŸĐ¶ ĐŒĐŸĐ¶Đ”Ń‚Đ” ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČуĐČато ŃˆĐżĐžĐłŃƒĐœĐ°, Ń‰ĐŸĐ± піЮтĐČДрЎОтО, Ń‰ĐŸ ДлДĐșŃ‚Ń€ĐŸĐœĐœĐžĐč лОст Đ±ŃƒĐ»ĐŸ ĐœĐ°ĐŽŃ–ŃĐ»Đ°ĐœĐŸ, ĐșĐŸĐ»Đž ця служба ĐœĐ” працює — цД Đ·ĐœĐŸĐČу пДрДĐČірĐșа ĐżĐŸĐČĐ”ĐŽŃ–ĐœĐșĐž, яĐșа, ĐčĐŒĐŸĐČŃ–Ń€ĐœĐŸ, Đ·â€™ŃĐČоться ĐČ ĐŽĐŸĐșŃƒĐŒĐ”ĐœŃ‚Ń– ĐČĐžĐŒĐŸĐł (Â«ĐĐ°ĐŽŃ–ŃĐ»Đ°Ń‚Đž ДлДĐșŃ‚Ń€ĐŸĐœĐœĐžĐč лОст, яĐșŃ‰ĐŸ платіж ĐœĐ” ĐČĐŽĐ°Đ»ĐŸŃŃ збДрДгтО»). З Ń–ĐœŃˆĐŸĐłĐŸ Đ±ĐŸĐșу, яĐșŃ‰ĐŸ ĐČĐž Đ·ĐœŃƒŃ‰Đ°Ń”Ń‚Đ”ŃŃ ĐœĐ°ĐŽ ĐżĐ»Đ°Ń‚Ń–Đ¶ĐœĐŸŃŽ ŃĐ»ŃƒĐ¶Đ±ĐŸŃŽ та пДрДĐșĐŸĐœĐ°Ń”Ń‚Đ”ŃŃŒ, Ń‰ĐŸ її ĐČĐžĐșлОĐșалО Đ· праĐČĐžĐ»ŃŒĐœĐžĐŒĐž Ń‚ĐžĐżĐ°ĐŒĐž JavaScriptâ€Šâ€”â€ŠŃ‚ĐŸĐŽŃ– ĐČаш тДст Đ·ĐŸŃĐ”Ń€Đ”ĐŽĐ¶Đ”ĐœĐžĐč ĐœĐ° ĐČĐœŃƒŃ‚Ń€Ń–ŃˆĐœŃ–Ń… рДчах, яĐșі ĐœĐ” ĐŒĐ°ŃŽŃ‚ŃŒ ĐœŃ–Ń‡ĐŸĐłĐŸ ŃĐżŃ–Đ»ŃŒĐœĐŸĐłĐŸ Đ· Ń„ŃƒĐœĐșŃ†Ń–ŃĐŒĐž ĐżŃ€ĐŸĐłŃ€Đ°ĐŒĐž та, ĐčĐŒĐŸĐČŃ–Ń€ĐœĐŸ, Ń‡Đ°ŃŃ‚ĐŸ Đ·ĐŒŃ–ĐœŃŽĐČĐ°Ń‚ĐžĐŒŃƒŃ‚ŃŒŃŃ. +
    + +❌ **Đ†ĐœĐ°ĐșшД:** БуЮь-яĐșĐ” ĐżĐ”Ń€Đ”ĐżĐžŃŃƒĐČĐ°ĐœĐœŃ ĐșĐŸĐŽŃƒ ĐČĐžĐŒĐ°ĐłĐ°Ń” ĐżĐŸŃˆŃƒĐșу ĐČсіх ĐŒĐ°ĐșДтіĐČ Ńƒ ĐșĐŸĐŽŃ– та ĐČŃ–ĐŽĐżĐŸĐČŃ–ĐŽĐœĐŸĐłĐŸ ĐŸĐœĐŸĐČĐ»Đ”ĐœĐœŃ. йДстО стають Ń‚ŃĐłĐ°Ń€Đ”ĐŒ, а ĐœĐ” ĐșĐŸŃ€ĐžŃĐœĐžĐŒ ĐŽŃ€ŃƒĐłĐŸĐŒ + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :thumbsdown: ПроĐșлаЎ Đ°ĐœŃ‚ĐžŃˆĐ°Đ±Đ»ĐŸĐœŃƒ: Mocks Ń–ĐŒŃ–Ń‚ŃƒŃ” ĐČĐœŃƒŃ‚Ń€Ń–ŃˆĐœŃŽ рДалізацію + +![](https://img.shields.io/badge/🔧%20Example%20using%20Sinon-blue.svg "ПроĐșлаЎО Đ· Sinon") + +```javascript +it("When a valid product is about to be deleted, ensure data access DAL was called once, with the right product and right config", async () => { + // ĐŸŃ€ĐžĐżŃƒŃŃ‚ĐžĐŒĐŸ, ĐŒĐž ĐČжД ĐŽĐŸĐŽĐ°Đ»Đž ĐżŃ€ĐŸĐŽŃƒĐșт + const dataAccessMock = sinon.mock(DAL); + // ПОГАНО: Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐČĐœŃƒŃ‚Ń€ĐŸŃ‰Ń–ĐČ ĐœĐ°ŃĐżŃ€Đ°ĐČЮі є ĐœĐ°ŃˆĐŸŃŽ ĐłĐŸĐ»ĐŸĐČĐœĐŸŃŽ ĐŒĐ”Ń‚ĐŸŃŽ, а ĐœĐ” лОшД ĐżĐŸĐ±Ń–Ń‡ĐœĐžĐŒ ДфДĐșŃ‚ĐŸĐŒ + dataAccessMock + .expects("deleteProduct") + .once() + .withArgs(DBConfig, theProductWeJustAdded, true, false); + new ProductService().deletePrice(theProductWeJustAdded); + dataAccessMock.verify(); +}); +``` + +
    + +### :clap:Đ ĐŸĐ±Đž цД праĐČĐžĐ»ŃŒĐœĐŸ. ПроĐșлаЎ: (spy) ŃˆĐżĐžĐłŃƒĐœĐž Đ·ĐŸŃĐ”Ń€Đ”ĐŽĐ¶Đ”ĐœŃ– ĐœĐ° пДрДĐČірці ĐČĐžĐŒĐŸĐł, алД яĐș ĐżĐŸĐ±Ń–Ń‡ĐœĐžĐč ДфДĐșт ĐœĐ”ĐŒĐžĐœŃƒŃ‡Đ” Ń‚ĐŸŃ€Đșаються ĐČĐœŃƒŃ‚Ń€Ń–ŃˆĐœŃ–Ń… ĐŸŃ€ĐłĐ°ĐœŃ–ĐČ + +```javascript +it("When a valid product is about to be deleted, ensure an email is sent", async () => { + // ĐŸŃ€ĐžĐżŃƒŃŃ‚ĐžĐŒĐŸ, ĐŒĐž ĐČжД ĐŽĐŸĐŽĐ°Đ»Đž ĐżŃ€ĐŸĐŽŃƒĐșт + const spy = sinon.spy(Emailer.prototype, "sendEmail"); + new ProductService().deletePrice(theProductWeJustAdded); + // ĐŽĐŸĐ±Ń€Đ”: ĐŒĐž ĐŒĐ°Ń”ĐŒĐŸ спраĐČу Đ· ĐČĐœŃƒŃ‚Ń€Ń–ŃˆĐœŃ–ĐŒĐž ĐșĐŸĐŽĐŸĐŒ? йаĐș, алД яĐș ĐżĐŸĐ±Ń–Ń‡ĐœĐžĐč ДфДĐșт Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐČĐžĐŒĐŸĐł (ĐœĐ°ĐŽŃĐžĐ»Đ°ĐœĐœŃ ДлДĐșŃ‚Ń€ĐŸĐœĐœĐŸĐłĐŸ лОста) + expect(spy.calledOnce).to.be.true; +}); +``` + +
    + +

    + +## 📗 Đ„ĐŸŃ‡Đ”Ń‚Đ” ĐœĐ°ĐČчотося ĐČŃŃ–ĐŒ Ń†ĐžĐŒ праĐșтоĐșĐ°ĐŒ Ń–Đ· жОĐČĐžĐŒ ĐČŃ–ĐŽĐ”ĐŸ? + +### ВіЮĐČіЮаĐčтД ĐŒŃ–Đč ĐŸĐœĐ»Đ°ĐčĐœ-Đșурс [Testing Node.js & JavaScript From A To Z](https://www.testjavascript.com) + +

    + +## âšȘ 1.6 ВоĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČуĐčтД Ń€Đ”Đ°Đ»Ń–ŃŃ‚ĐžŃ‡ĐœŃ– ĐČŃ…Ń–ĐŽĐœŃ– ĐŽĐ°ĐœŃ– + +:white_check_mark: **Đ ĐŸĐ±Đž:** Đ§Đ°ŃŃ‚ĐŸ production ĐżĐŸĐŒĐžĐ»ĐșĐž ĐČояĐČĐ»ŃŃŽŃ‚ŃŒŃŃ піЮ час ЎужД ŃĐżĐ”Ń†ĐžŃ„Ń–Ń‡ĐœĐžŃ… і ĐœĐ”ŃĐżĐŸĐŽŃ–ĐČĐ°ĐœĐžŃ… ĐČŃ…Ń–ĐŽĐœĐžŃ… ĐŽĐ°ĐœĐžŃ… — Â«Â«Ń‡ĐžĐŒ Ń€Đ”Đ°Đ»Ń–ŃŃ‚ĐžŃ‡ĐœŃ–ŃˆĐžĐŒĐž є Ń‚Đ”ŃŃ‚ĐŸĐČі ĐČŃ…Ń–ĐŽĐœŃ– ĐŽĐ°ĐœŃ–, Ń‚ĐžĐŒ Đ±Ń–Đ»ŃŒŃˆŃ– ŃˆĐ°ĐœŃĐž ĐČояĐČото ĐżĐŸĐŒĐžĐ»ĐșĐž ĐœĐ° Ń€Đ°ĐœĐœŃ–Đč стаЮії. ВоĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČуĐčтД ŃĐżĐ”Ń†Ń–Đ°Đ»ŃŒĐœŃ– Đ±Ń–Đ±Đ»Ń–ĐŸŃ‚Đ”ĐșĐž, яĐș-ĐŸŃ‚ [Chance](https://github.com/chancejs/chancejs) Đ°Đ±ĐŸ [Faker](https://www.npmjs.com/package/faker), Ń‰ĐŸĐ± ĐłĐ”ĐœĐ”Ń€ŃƒĐČато псДĐČĐŽĐŸŃ€Đ”Đ°Đ»ŃŒĐœŃ– ĐŽĐ°ĐœŃ–. НапроĐșлаЎ, таĐșі Đ±Ń–Đ±Đ»Ń–ĐŸŃ‚Đ”ĐșĐž ĐŒĐŸĐ¶ŃƒŃ‚ŃŒ стĐČĐŸŃ€ŃŽĐČато Ń€Đ”Đ°Đ»Ń–ŃŃ‚ĐžŃ‡ĐœŃ– ĐœĐŸĐŒĐ”Ń€Đž Ń‚Đ”Đ»Đ”Ń„ĐŸĐœŃ–ĐČ, Ń–ĐŒĐ”ĐœĐ° ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČачіĐČ, ĐșŃ€Đ”ĐŽĐžŃ‚ĐœŃ– ĐșартĐșĐž, ĐœĐ°Đ·ĐČĐž ĐșĐŸĐŒĐżĐ°ĐœŃ–Đč і ĐœĐ°ĐČіть тДĐșст «lorem ipsum». Во таĐșĐŸĐ¶ ĐŒĐŸĐ¶Đ”Ń‚Đ” стĐČĐŸŃ€ĐžŃ‚Đž ĐŽĐ”ŃĐșі тДстО (ĐœĐ° ĐŽĐŸĐŽĐ°Ń‚ĐŸĐș ĐŽĐŸ ĐŒĐŸĐŽŃƒĐ»ŃŒĐœĐžŃ… тДстіĐČ, а ĐœĐ” яĐș Đ·Đ°ĐŒŃ–ĐœŃƒ), яĐșі Ń€Đ°ĐœĐŽĐŸĐŒŃ–Đ·ŃƒŃŽŃ‚ŃŒ ĐŽĐ°ĐœŃ– Ń„Đ°Đ»ŃŒŃĐžŃ„Ń–ĐșĐ°Ń‚ĐŸŃ€Ń–ĐČ, Ń‰ĐŸĐ± Ń€ĐŸĐ·ŃˆĐžŃ€ĐžŃ‚Đž Ń‚Đ”ŃŃ‚ĐŸĐČĐ°ĐœĐžĐč Đ±Đ»ĐŸĐș Đ°Đ±ĐŸ ĐœĐ°ĐČіть Ń–ĐŒĐżĐŸŃ€Ń‚ŃƒĐČато Ń€Đ”Đ°Đ»ŃŒĐœŃ– ĐŽĐ°ĐœŃ– Đ· ĐČĐ°ŃˆĐŸĐłĐŸ Ń€ĐŸĐ±ĐŸŃ‡ĐŸĐłĐŸ ŃĐ”Ń€Đ”ĐŽĐŸĐČоща. Đ„ĐŸŃ‡Đ”Ń‚Đ” пДрДĐčто ĐœĐ° ĐœĐŸĐČĐžĐč ріĐČĐ”ĐœŃŒ? ДоĐČіться ĐœĐ°ŃŃ‚ŃƒĐżĐœĐžĐč ĐżŃƒĐœĐșт (Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐœĐ° ĐŸŃĐœĐŸĐČі ĐČластОĐČĐŸŃŃ‚Đ”Đč). +
    + +❌ **Đ†ĐœĐ°ĐșшД:** УсД ĐČашД Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ Ń€ĐŸĐ·Ń€ĐŸĐ±ĐșĐž Ń…ĐžĐ±ĐœĐŸ ĐżĐŸĐșазуĐČĐ°Ń‚ĐžĐŒĐ” Đ·Đ”Đ»Đ”ĐœĐžĐč ĐșĐŸĐ»Ń–Ń€, яĐșŃ‰ĐŸ ĐČĐž ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČŃƒŃ”Ń‚Đ” ŃĐžĐœŃ‚Đ”Ń‚ĐžŃ‡ĐœŃ– ĐČŃ…Ń–ĐŽĐœŃ– ĐŽĐ°ĐœŃ–, яĐș-ĐŸŃ‚ «Foo», алД Ń‚ĐŸĐŽŃ– production ĐŒĐŸĐ¶Đ” стато чДрĐČĐŸĐœĐžĐŒ, ĐșĐŸĐ»Đž хаĐșДр ĐżĐ”Ń€Đ”ĐŽĐ°ŃŃ‚ŃŒ ĐœĐ”ĐżŃ€ĐžŃ”ĐŒĐœĐžĐč Ń€ŃĐŽĐŸĐș, яĐș-ĐŸŃ‚ «@3e2ddsf». ##’ 1 fdsfds . fds432 AAAA” + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :thumbsdown: ПроĐșлаЎ Đ°ĐœŃ‚ĐžŃˆĐ°Đ±Đ»ĐŸĐœŃƒ: ĐœĐ°Đ±Ń–Ń€ тДстіĐČ, яĐșĐžĐč ĐżŃ€ĐŸŃ…ĐŸĐŽĐžŃ‚ŃŒ чДрДз ĐœĐ”Ń€Đ”Đ°Đ»Ń–ŃŃ‚ĐžŃ‡ĐœŃ– ĐŽĐ°ĐœŃ– + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "ПроĐșлаЎО Đ· Jest") + +```javascript +const addProduct = (name, price) => { + const productNameRegexNoSpace = /^\S*$/; // white-space ĐœĐ” ĐŽĐŸĐżŃƒŃĐșаються + + if (!productNameRegexNoSpace.test(name)) return false; // цДĐč ŃˆĐ»ŃŃ… ĐœĐ” ĐŽĐŸŃŃĐłĐœŃƒŃ‚ĐŸ чДрДз Ń€Ń–ĐœĐœŃ–Đč ĐČохіЮ + + // Đ›ĐŸĐłŃ–Đșа тут + return true; +}; + +test("Wrong: When adding new product with valid properties, get successful confirmation", async () => { + // Đ ŃĐŽĐŸĐș "Foo", яĐșĐžĐč ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČується ĐČ ŃƒŃŃ–Ń… тДстах, ĐœŃ–ĐșĐŸĐ»Đž ĐœĐ” ĐČĐžĐșлОĐșає Ń…ĐžĐ±ĐœĐžĐč Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚ + const addProductResult = addProduct("Foo", 5); + expect(addProductResult).toBe(true); + // ĐŸĐŸĐ·ĐžŃ‚ĐžĐČĐœĐžĐč-Ń…ĐžĐ±ĐœĐžĐč: ĐŸĐżĐ”Ń€Đ°Ń†Ń–Ń ĐČĐŽĐ°Đ»Đ°ŃŃ, Ń‚ĐŸĐŒŃƒ Ń‰ĐŸ ĐŒĐž ĐœŃ–ĐșĐŸĐ»Đž ĐœĐ” ĐœĐ°ĐŒĐ°ĐłĐ°Đ»ĐžŃŃ ĐČĐžĐșĐŸŃ€ĐžŃŃ‚Đ°Ń‚Đž + // ĐœĐ°Đ·ĐČу ĐżŃ€ĐŸĐŽŃƒĐșту Đ· ĐżŃ€ĐŸĐ±Ń–Đ»Đ°ĐŒĐž +}); +``` + +
    + +### :clap:Đ ĐŸĐ±Đž цД праĐČĐžĐ»ŃŒĐœĐŸ. ПроĐșлаЎ: Ń€Đ°ĐœĐŽĐŸĐŒŃ–Đ·Đ°Ń†Ń–Ń Ń€Đ”Đ°Đ»Ń–ŃŃ‚ĐžŃ‡ĐœĐŸĐłĐŸ ĐČĐČĐ”ĐŽĐ”ĐœĐœŃ + +```javascript +it("Better: When adding new valid product, get successful confirmation", async () => { + const addProductResult = addProduct(faker.commerce.productName(), faker.random.number()); + // Đ—ĐłĐ”ĐœĐ”Ń€ĐŸĐČĐ°ĐœŃ– ĐČОпаЎĐșĐŸĐČі ĐŽĐ°ĐœĐœŃ–: {'Sleek Cotton Computer', 85481} + expect(addProductResult).to.be.true; + // йДст ĐżŃ€ĐŸĐČалОĐČся, ĐČОпаЎĐșĐŸĐČĐžĐč ĐČхіЮ Ń–ĐœŃ–Ń†Ń–ŃŽĐČаĐČ ĐżĐ”ĐČĐœĐžĐč ŃˆĐ»ŃŃ…, яĐșĐžĐč ĐŒĐž ĐœĐ” ĐżĐ»Đ°ĐœŃƒĐČалО. + // Мо Ń€Đ°ĐœĐŸ ĐČояĐČОлО ĐżĐŸĐŒĐžĐ»Đșу! +}); +``` + +
    + +

    + +## âšȘ  1.7 ĐŸĐ”Ń€Đ”ĐČіртД Đ±Đ°ĐłĐ°Ń‚ĐŸ ĐČŃ…Ń–ĐŽĐœĐžŃ… ĐșĐŸĐŒĐ±Ń–ĐœĐ°Ń†Ń–Đč за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐœĐ° ĐŸŃĐœĐŸĐČі ĐČластОĐČĐŸŃŃ‚Đ”Đč + +:white_check_mark: **Đ ĐŸĐ±Đž:** ЗазĐČочаĐč ĐŒĐž ĐŸĐ±ĐžŃ€Đ°Ń”ĐŒĐŸ ĐșŃ–Đ»ŃŒĐșа ĐČŃ…Ń–ĐŽĐœĐžŃ… зразĐșіĐČ ĐŽĐ»Ń ĐșĐŸĐ¶ĐœĐŸĐłĐŸ Ń‚Đ”ŃŃ‚Ńƒ. НаĐČіть яĐșŃ‰ĐŸ Ń„ĐŸŃ€ĐŒĐ°Ń‚ ĐČĐČĐ”ĐŽĐ”ĐœĐœŃ ĐœĐ°ĐłĐ°ĐŽŃƒŃ” Ń€Đ”Đ°Đ»ŃŒĐœŃ– ĐŽĐ°ĐœŃ– (ĐŽĐžĐČось [‘Don’t foo’](https://github.com/goldbergyoni/javascript-testing-best-practices#-%EF%B8%8F16-dont-foo-use-realistic-input-data)), ĐŒĐž Ń€ĐŸĐ·ĐłĐ»ŃĐŽĐ°Ń”ĐŒĐŸ лОшД ĐșŃ–Đ»ŃŒĐșа ĐșĐŸĐŒĐ±Ń–ĐœĐ°Ń†Ń–Đč ĐČĐČĐŸĐŽŃƒ (method(‘’, true, 1), method(“string” , false , 0)), ĐžĐŽĐœĐ°Đș production API, яĐșĐžĐč ĐČĐžĐșлОĐșається Đ· 5 ĐżĐ°Ń€Đ°ĐŒĐ”Ń‚Ń€Đ°ĐŒĐž, ĐŒĐŸĐ¶Đ” Đ±ŃƒŃ‚Đž ĐČĐžĐșлОĐșĐ°ĐœĐžĐč Ń–Đ· Ń‚ĐžŃŃŃ‡Đ°ĐŒĐž Ń€Ń–Đ·ĐœĐžŃ… ĐżĐ”Ń€Đ”ŃŃ‚Đ°ĐœĐŸĐČĐŸĐș, ĐŸĐŽĐœĐ° Đ· яĐșох ĐŒĐŸĐ¶Đ” прОзĐČДстО ĐŽĐŸ Đ·ŃƒĐżĐžĐœĐșĐž ĐœĐ°ŃˆĐŸĐłĐŸ ĐżŃ€ĐŸŃ†Đ”ŃŃƒ ([ĐŽĐžĐČось Fuzz Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ](https://en.wikipedia.org/wiki/Fuzzing)). Đ©ĐŸ, яĐșбО ĐČĐž ĐŒĐŸĐłĐ»Đž ĐœĐ°ĐżĐžŃĐ°Ń‚Đž ĐŸĐŽĐžĐœ тДст, яĐșĐžĐč аĐČŃ‚ĐŸĐŒĐ°Ń‚ĐžŃ‡ĐœĐŸ ĐœĐ°ĐŽŃĐžĐ»Đ°Ń” 1000 ĐżĐ”Ń€Đ”ŃŃ‚Đ°ĐœĐŸĐČĐŸĐș Ń€Ń–Đ·ĐœĐžŃ… ĐČŃ…Ń–ĐŽĐœĐžŃ… ĐŽĐ°ĐœĐžŃ… і ĐČояĐČĐ»ŃŃ”, ĐŽĐ»Ń яĐșĐŸĐłĐŸ ĐČŃ…Ń–ĐŽĐœĐŸĐłĐŸ ŃĐžĐłĐœĐ°Đ»Ńƒ ĐœĐ°Ńˆ ĐșĐŸĐŽ ĐœĐ” ĐżĐŸĐČДртає праĐČĐžĐ»ŃŒĐœŃƒ ĐČŃ–ĐŽĐżĐŸĐČіЮь? ĐąĐ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐœĐ° ĐŸŃĐœĐŸĐČі ĐČластОĐČĐŸŃŃ‚Đ”Đč — цД Ń‚Đ”Ń…ĐœŃ–Đșа, яĐșа Ń€ĐŸĐ±ĐžŃ‚ŃŒ ŃĐ°ĐŒĐ” цД: ĐœĐ°ĐŽŃĐžĐ»Đ°ŃŽŃ‡Đž ĐČсі ĐŒĐŸĐ¶Đ»ĐžĐČі ĐșĐŸĐŒĐ±Ń–ĐœĐ°Ń†Ń–Ń— ĐČŃ…Ń–ĐŽĐœĐžŃ… ĐŽĐ°ĐœĐžŃ… ĐŽĐŸ ĐČĐ°ŃˆĐŸĐłĐŸ Ń‚Đ”ŃŃ‚ĐŸĐČĐ°ĐœĐŸĐłĐŸ ĐżŃ€ĐžŃŃ‚Ń€ĐŸŃŽ, цД Đ·Đ±Ń–Đ»ŃŒŃˆŃƒŃ” ĐčĐŒĐŸĐČŃ–Ń€ĐœŃ–ŃŃ‚ŃŒ ĐČояĐČĐ»Đ”ĐœĐœŃ ĐżĐŸĐŒĐžĐ»ĐșĐž. НапроĐșлаЎ, Đ·Đ°ĐŽĐ°ĐœĐŸ ĐŒĐ”Ń‚ĐŸĐŽâ€Šâ€”â€ŠaddNewProduct(id, name, isDiscount)â€Šâ€”â€ŠĐ±Ń–Đ±Đ»Ń–ĐŸŃ‚Đ”ĐșĐž, Ń‰ĐŸ ĐżŃ–ĐŽŃ‚Ń€ĐžĐŒŃƒŃŽŃ‚ŃŒ, ĐČĐžĐșĐ»ĐžŃ‡ŃƒŃ‚ŃŒ цДĐč ĐŒĐ”Ń‚ĐŸĐŽ Ń–Đ· Đ±Đ°ĐłĐ°Ń‚ŃŒĐŒĐ° ĐșĐŸĐŒĐ±Ń–ĐœĐ°Ń†Ń–ŃĐŒĐž (Ń‡ĐžŃĐ»ĐŸ, Ń€ŃĐŽĐŸĐș, Đ»ĐŸĐłŃ–Ń‡ĐœĐžĐč ĐČОраз), ĐœĐ°ĐżŃ€ĐžĐșлаЎ (1, «iPhone», false), (2, «Galaxy» », праĐČЎа). Во ĐŒĐŸĐ¶Đ”Ń‚Đ” Đ·Đ°ĐżŃƒŃŃ‚ĐžŃ‚Đž Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐœĐ° ĐŸŃĐœĐŸĐČі ĐČластОĐČĐŸŃŃ‚Đ”Đč за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ ŃƒĐ»ŃŽĐ±Đ»Đ”ĐœĐŸĐłĐŸ Đ·Đ°ŃĐŸĐ±Ńƒ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ тДстіĐČ (Mocha, Jest Ń‚ĐŸŃ‰ĐŸ), ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČуючо таĐșі Đ±Ń–Đ±Đ»Ń–ĐŸŃ‚Đ”ĐșĐž, яĐș [js-verify](https://github.com/jsverify/jsverify) чо [testcheck](https://github.com/leebyron/testcheck-js) (Đ·ĐœĐ°Ń‡ĐœĐŸ Đșраща ĐŽĐŸĐșŃƒĐŒĐ”ĐœŃ‚Đ°Ń†Ń–Ń). ĐžĐœĐŸĐČĐ»Đ”ĐœĐœŃ: Nicolas Dubien ĐżŃ€ĐŸĐżĐŸĐœŃƒŃ” ĐČ ĐșĐŸĐŒĐ”ĐœŃ‚Đ°Ń€ŃŃ… ĐœĐžĐ¶Ń‡Đ” [checkout fast-check](https://github.com/dubzzz/fast-check#readme) яĐșĐžĐč, Đ·ĐŽĐ°Ń”Ń‚ŃŒŃŃ, ĐżŃ€ĐŸĐżĐŸĐœŃƒŃ” ĐŽĐ”ŃĐșі ĐŽĐŸĐŽĐ°Ń‚ĐșĐŸĐČі Ń„ŃƒĐœĐșції та таĐșĐŸĐ¶ аĐșтоĐČĐœĐŸ ĐżŃ–ĐŽŃ‚Ń€ĐžĐŒŃƒŃ”Ń‚ŃŒŃŃ +
    + +❌ **Đ†ĐœĐ°ĐșшД:** ĐĐ”ŃĐČŃ–ĐŽĐŸĐŒĐŸ ĐČĐž ĐŸĐ±ĐžŃ€Đ°Ń”Ń‚Đ” Ń‚Đ”ŃŃ‚ĐŸĐČі ĐČŃ…Ń–ĐŽĐœŃ– ĐŽĐ°ĐœŃ–, яĐșі ĐŸŃ…ĐŸĐżĐ»ŃŽŃŽŃ‚ŃŒ лОшД ŃˆĐ»ŃŃ…Đž ĐșĐŸĐŽŃƒ, яĐșі ĐŽĐŸĐ±Ń€Đ” працюють. На Đ¶Đ°Đ»ŃŒ, цД Đ·ĐœĐžĐ¶ŃƒŃ” ДфДĐșтоĐČĐœŃ–ŃŃ‚ŃŒ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ яĐș Đ·Đ°ŃĐŸĐ±Ńƒ ĐČояĐČĐ»Đ”ĐœĐœŃ ĐżĐŸĐŒĐžĐ»ĐŸĐș + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :clap: ПроĐșлаЎ праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: ĐąĐ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ Đ±Đ°ĐłĐ°Ń‚ŃŒĐŸŃ… ĐČŃ…Ń–ĐŽĐœĐžŃ… ĐżĐ”Ń€Đ”ŃŃ‚Đ°ĐœĐŸĐČĐŸĐș за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ “fast-check” + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "ПроĐșлаЎО Đ· Jest") + +```javascript +import fc from "fast-check"; + +describe("Product service", () => { + describe("Adding new", () => { + // буЎД ĐČĐžĐșĐŸĐœĐ°ĐœĐŸ 100 разіĐČ Đ· Ń€Ń–Đ·ĐœĐžĐŒĐž ĐČОпаЎĐșĐŸĐČĐžĐŒĐž ĐČластОĐČĐŸŃŃ‚ŃĐŒĐž + it("Add new product with random yet valid properties, always successful", () => + fc.assert( + fc.property(fc.integer(), fc.string(), (id, name) => { + expect(addNewProduct(id, name).status).toEqual("approved"); + }) + )); + }); +}); +``` + +
    + +

    + +## âšȘ  1.8 За ĐżĐŸŃ‚Ń€Đ”Đ±Đž ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČуĐčтД лОшД ĐșĐŸŃ€ĐŸŃ‚Đșі та ĐČĐ±ŃƒĐŽĐŸĐČĐ°ĐœŃ– Đ·ĐœŃ–ĐŒĐșĐž + +:white_check_mark: **Đ ĐŸĐ±Đž:** ĐšĐŸĐ»Đž є ĐżĐŸŃ‚Ń€Đ”Đ±Đ° ĐČ [ŃĐœĐ”ĐżŃˆĐŸŃ‚ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ–](https://jestjs.io/docs/en/snapshot-testing), ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČуĐčтД лОшД ĐșĐŸŃ€ĐŸŃ‚Đșі та Ń†Ń–Đ»Đ”ŃĐżŃ€ŃĐŒĐŸĐČĐ°ĐœŃ– ŃĐœĐ”ĐżŃˆĐŸŃ‚Đž (3-7 ряЮĐșіĐČ) яĐșі ĐČŃ…ĐŸĐŽŃŃ‚ŃŒ ĐŽĐŸ сĐșлаЎу Ń‚Đ”ŃŃ‚Ńƒ ([Inline Snapshot](https://jestjs.io/docs/en/snapshot-testing#inline-snapshots)) а ĐœĐ” ĐČ Đ·ĐŸĐČĐœŃ–ŃˆĐœŃ–Ń… фаĐčлах. Đ”ĐŸŃ‚Ń€ĐžĐŒŃƒŃŽŃ‡ĐžŃŃŒ цієї ĐČĐșазіĐČĐșĐž, ĐČаші тДстО Đ·Đ°Đ»ĐžŃˆĐ°Ń‚ĐžĐŒŃƒŃ‚ŃŒŃŃ Đ·Ń€ĐŸĐ·ŃƒĐŒŃ–Đ»ĐžĐŒĐž та ĐŒĐ”ĐœŃˆ ĐșрохĐșĐžĐŒĐž. + +З Ń–ĐœŃˆĐŸĐłĐŸ Đ±ĐŸĐșу, ĐœĐ°ĐČŃ‡Đ°Đ»ŃŒĐœŃ– ĐżĐŸŃŃ–Đ±ĐœĐžĐșĐž та Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Đž «ĐșĐ»Đ°ŃĐžŃ‡ĐœĐžŃ… Đ·ĐœŃ–ĐŒĐșіĐČ» Đ·Đ°ĐŸŃ…ĐŸŃ‡ŃƒŃŽŃ‚ŃŒ збДрігатО ĐČДлОĐșі фаĐčлО (ĐœĐ°ĐżŃ€ĐžĐșлаЎ, Ń€ĐŸĐ·ĐŒŃ–Ń‚Đșу ĐČŃ–Đ·ŃƒĐ°Đ»Ń–Đ·Đ°Ń†Ń–Ń— ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Ń–ĐČ, Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚ JSON API) ĐœĐ° Đ·ĐŸĐČĐœŃ–ŃˆĐœŃŒĐŸĐŒŃƒ ĐœĐŸŃŃ–Ń— та Đ·Đ°Đ±Đ”Đ·ĐżĐ”Ń‡ŃƒĐČато ĐżĐŸŃ€Ń–ĐČĐœŃĐœĐœŃ ĐŸŃ‚Ń€ĐžĐŒĐ°ĐœĐŸĐłĐŸ Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚Ńƒ Đ·Ń– Đ·Đ±Đ”Ń€Đ”Đ¶Đ”ĐœĐŸŃŽ ĐČДрсією піЮ час ĐșĐŸĐ¶ĐœĐŸĐłĐŸ Ń‚Đ”ŃŃ‚ĐŸĐČĐŸĐłĐŸ запусĐșу. ĐŠĐ”, ĐœĐ°ĐżŃ€ĐžĐșлаЎ, ĐŒĐŸĐ¶Đ” ĐœĐ”ŃĐČĐœĐŸ ĐżĐŸŃ”ĐŽĐœĐ°Ń‚Đž ĐœĐ°Ńˆ тДст Ń–Đ· 1000 ряЮĐșĐ°ĐŒĐž Đ· 3000 Đ·ĐœĐ°Ń‡Đ”ĐœĐœŃĐŒĐž ĐŽĐ°ĐœĐžŃ…, яĐșі аĐČŃ‚ĐŸŃ€ Ń‚Đ”ŃŃ‚Ńƒ ĐœŃ–ĐșĐŸĐ»Đž ĐœĐ” чотаĐČ Ń– ĐœĐ” ĐŒŃ–Ń€ĐșуĐČаĐČ. Đ§ĐŸĐŒŃƒ цД ĐœĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœĐŸ? йаĐșĐžĐŒ Ń‡ĐžĐœĐŸĐŒ, є 1000 ĐżŃ€ĐžŃ‡ĐžĐœ ĐœĐ”ĐČЮачі ĐČĐ°ŃˆĐŸĐłĐŸ Ń‚Đ”ŃŃ‚Ńƒ - ĐŽĐŸŃŃ‚Đ°Ń‚ĐœŃŒĐŸ Đ·ĐŒŃ–ĐœĐžŃ‚Đž ĐŸĐŽĐžĐœ Ń€ŃĐŽĐŸĐș, Ń‰ĐŸĐ± Đ·ĐœŃ–ĐŒĐŸĐș стаĐČ ĐœĐ”ĐŽŃ–ĐčŃĐœĐžĐŒ, і цД, ĐčĐŒĐŸĐČŃ–Ń€ĐœĐŸ, ŃŃ‚Đ°ĐœĐ”Ń‚ŃŒŃŃ Ń‡Đ°ŃŃ‚ĐŸ. ĐŻĐș Ń‡Đ°ŃŃ‚ĐŸ? ĐŽĐ»Ń ĐșĐŸĐ¶ĐœĐŸĐłĐŸ ĐżŃ€ĐŸĐ±Ń–Đ»Ńƒ, ĐșĐŸĐŒĐ”ĐœŃ‚Đ°Ń€Ń Đ°Đ±ĐŸ ĐœĐ”Đ·ĐœĐ°Ń‡ĐœĐŸŃ— Đ·ĐŒŃ–ĐœĐž CSS/HTML. ĐšŃ€Ń–ĐŒ Ń‚ĐŸĐłĐŸ, ĐœĐ°Đ·ĐČа Ń‚Đ”ŃŃ‚Ńƒ ĐœĐ” Юасть піЮĐșазĐșĐž ĐżŃ€ĐŸ ĐżĐŸĐŒĐžĐ»Đșу, ĐŸŃĐșŃ–Đ»ŃŒĐșĐž ĐČĐŸĐœĐ° лОшД пДрДĐČіряє, чо ĐœĐ” Đ·ĐŒŃ–ĐœĐžĐ»ĐžŃŃ 1000 ряЮĐșіĐČ, а таĐșĐŸĐ¶ Đ·Đ°ĐŸŃ…ĐŸŃ‡ŃƒŃ” аĐČŃ‚ĐŸŃ€Đ° Ń‚Đ”ŃŃ‚Ńƒ проĐčĐœŃŃ‚Đž за Đ±Đ°Đ¶Đ°ĐœĐžĐč Ń–ŃŃ‚ĐžĐœĐœĐžĐč ĐŽĐŸĐČгОĐč ĐŽĐŸĐșŃƒĐŒĐ”ĐœŃ‚, яĐșĐžĐč ĐČŃ–Đœ ĐœĐ” ĐŒŃ–Đł пДрДĐČірото та пДрДĐČірото. УсД цД ŃĐžĐŒĐżŃ‚ĐŸĐŒĐž ĐœĐ”Đ·Ń€ĐŸĐ·ŃƒĐŒŃ–Đ»ĐŸĐłĐŸ та Đ¶Đ°ĐŽŃ–Đ±ĐœĐŸĐłĐŸ ĐČĐžĐżŃ€ĐŸĐ±ŃƒĐČĐ°ĐœĐœŃ, яĐșĐ” ĐœĐ” є Đ·ĐŸŃĐ”Ń€Đ”ĐŽĐ¶Đ”ĐœĐžĐŒ і ŃĐżŃ€ŃĐŒĐŸĐČĐ°ĐœĐ” ĐœĐ° ĐŽĐŸŃŃĐłĐœĐ”ĐœĐœŃ Đ·Đ°ĐœĐ°ĐŽŃ‚ĐŸ Đ±Đ°ĐłĐ°Ń‚ĐŸ + +Đ’Đ°Ń€Ń‚ĐŸ Đ·Đ°Đ·ĐœĐ°Ń‡ĐžŃ‚Đž, Ń‰ĐŸ є ĐșŃ–Đ»ŃŒĐșа ĐČОпаЎĐșіĐČ, ĐșĐŸĐ»Đž ĐŽĐŸĐČгі та Đ·ĐŸĐČĐœŃ–ŃˆĐœŃ– ŃĐœĐ”ĐżŃˆĐŸŃ‚Đž є проĐčĐœŃŃ‚ĐœĐžĐŒĐž - ĐșĐŸĐ»Đž стĐČĐ”Ń€ĐŽĐ¶ŃƒŃ”Ń‚ŃŒŃŃ ĐœĐ° ŃŃ…Đ”ĐŒŃ–, а ĐœĐ” ĐœĐ° ĐŽĐ°ĐœĐžŃ… (ĐČĐžĐ»ŃƒŃ‡Đ”ĐœĐœŃ Đ·ĐœĐ°Ń‡Đ”ĐœŃŒ і Ń„ĐŸĐșусуĐČĐ°ĐœĐœŃ ĐœĐ° ĐżĐŸĐ»ŃŃ…) Đ°Đ±ĐŸ ĐșĐŸĐ»Đž ĐŸŃ‚Ń€ĐžĐŒĐ°ĐœĐžĐč ĐŽĐŸĐșŃƒĐŒĐ”ĐœŃ‚ ріЮĐșĐŸ Đ·ĐŒŃ–ĐœŃŽŃ”Ń‚ŃŒŃŃ
    + +❌ **Đ†ĐœĐ°ĐșшД:** UI тДстО ĐČпалО. ĐšĐŸĐŽ ĐČĐžĐłĐ»ŃĐŽĐ°Ń” праĐČĐžĐ»ŃŒĐœĐžĐŒ, Đ”ĐșŃ€Đ°Đœ ĐČŃ–ĐŽĐŸĐ±Ń€Đ°Đ¶Đ°Ń” Ń–ĐŽĐ”Đ°Đ»ŃŒĐœĐŸ ĐČсД, Ń‰ĐŸ ŃŃ‚Đ°Đ»ĐŸŃŃ? Đ’Đ°ŃˆĐ” Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ŃĐœĐ”ĐżŃˆĐŸŃ‚Ń–ĐČ Ń‰ĐŸĐčĐœĐŸ ĐČояĐČĐžĐ»ĐŸ Ń€Ń–Đ·ĐœĐžŃ†ŃŽ ĐŒŃ–Đ¶ ĐČĐžŃ…Ń–ĐŽĐœĐžĐŒ ĐŽĐŸĐșŃƒĐŒĐ”ĐœŃ‚ĐŸĐŒ і ĐżĐŸŃ‚ĐŸŃ‡ĐœĐžĐŒ ĐŸŃ‚Ń€ĐžĐŒĐ°ĐœĐžĐŒ ĐŽĐŸĐșŃƒĐŒĐ”ĐœŃ‚ĐŸĐŒ – ĐŽĐŸ Ń€ĐŸĐ·ĐŒŃ–Ń‚ĐșĐž ĐŽĐŸĐŽĐ°ĐœĐŸ ĐŸĐŽĐžĐœ ĐżŃ€ĐŸĐ±Ń–Đ»... + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :thumbsdown: ПроĐșлаЎ Đ°ĐœŃ‚ĐžŃˆĐ°Đ±Đ»ĐŸĐœŃƒ: ĐŸĐŸŃ”ĐŽĐœĐ°ĐœĐœŃ ĐœĐ°ŃˆĐŸĐłĐŸ Ń‚Đ”ŃŃ‚Ńƒ Đ· ĐœĐ”ĐČĐžĐŽĐžĐŒĐžĐŒĐž 2000 ряЮĐșĐ°ĐŒĐž ĐșĐŸĐŽŃƒ + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "ПроĐșлаЎО Đ· Jest") + +```javascript +it("TestJavaScript.com is renderd correctly", () => { + //Arrange + + //Act + const receivedPage = renderer + .create( Test JavaScript ) + .toJSON(); + + //Assert + expect(receivedPage).toMatchSnapshot(); + //йДпДр ĐŒĐž ĐœĐ”ŃĐČĐœĐŸ ĐżŃ–ĐŽŃ‚Ń€ĐžĐŒŃƒŃ”ĐŒĐŸ ĐŽĐŸĐșŃƒĐŒĐ”ĐœŃ‚ ĐŽĐŸĐČĐ¶ĐžĐœĐŸŃŽ 2000 ряЮĐșіĐČ + //ĐșĐŸĐ¶Đ”Đœ ĐŽĐŸĐŽĐ°Ń‚ĐșĐŸĐČĐžĐč Ń€ĐŸĐ·Ń€ĐžĐČ Ń€ŃĐŽĐșа Đ°Đ±ĐŸ ĐșĐŸĐŒĐ”ĐœŃ‚Đ°Ń€ - ĐżĐŸŃ€ŃƒŃˆĐžŃ‚ŃŒ цДĐč тДст +}); +``` + +
    + +### :clap: ПроĐșлаЎ праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: ОчіĐșуĐČĐ°ĐœĐœŃ ĐżĐŸĐŒŃ–Ń‚ĐœŃ– та Ń†Ń–Đ»Đ”ŃĐżŃ€ŃĐŒĐŸĐČĐ°ĐœŃ– + +```javascript +it("When visiting TestJavaScript.com home page, a menu is displayed", () => { + //Arrange + + //Act + const receivedPage = renderer + .create( Test JavaScript ) + .toJSON(); + + //Assert + + const menu = receivedPage.content.menu; + expect(menu).toMatchInlineSnapshot(` + +`); +}); +``` + +
    + +

    + +## âšȘ ïžĐĄĐșĐŸĐżŃ–ŃŽĐčтД ĐșĐŸĐŽ, алД Ń‚Ń–Đ»ŃŒĐșĐž ĐœĐ”ĐŸĐ±Ń…Ń–ĐŽĐœĐžĐč + +:white_check_mark: **Đ ĐŸĐ±Đž:** ВĐșĐ»ŃŽŃ‡Ń–Ń‚ŃŒ усі ĐœĐ”ĐŸĐ±Ń…Ń–ĐŽĐœŃ– ЎДталі, яĐșі ĐČплОĐČають ĐœĐ° Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚ Ń‚Đ”ŃŃ‚Ńƒ, алД ĐœĐ” Đ±Ń–Đ»ŃŒŃˆĐ” Ń‚ĐŸĐłĐŸ. ĐŻĐș проĐșлаЎ, Ń€ĐŸĐ·ĐłĐ»ŃĐœĐ”ĐŒĐŸ тДст, яĐșĐžĐč ĐŒĐ°Ń” Ń€ĐŸĐ·Ń€Đ°Ń…ĐŸĐČуĐČато 100 ряЮĐșіĐČ ĐČŃ…Ń–ĐŽĐœĐŸĐłĐŸ JSON - ВстаĐČĐ»ŃŃ‚Đž цД ĐČ ĐșĐŸĐ¶Đ”Đœ тДст ŃƒŃ‚ĐŸĐŒĐ»ĐžĐČĐŸ. ĐŻĐșŃ‰ĐŸ ĐČотягто ĐčĐŸĐłĐŸ за ĐŒĐ”Đ¶Ń– transferFactory.getJSON(), тДст Đ·Đ°Đ»ĐžŃˆĐžŃ‚ŃŒŃŃ ĐœĐ”ĐČĐžĐ·ĐœĐ°Ń‡Đ”ĐœĐžĐŒâ€Š- БДз ĐŽĐ°ĐœĐžŃ… ĐČажĐșĐŸ спіĐČĐČŃ–ĐŽĐœĐ”ŃŃ‚Đž Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚ Ń‚Đ”ŃŃ‚Ńƒ Đ· ĐżŃ€ĐžŃ‡ĐžĐœĐŸŃŽ (Â«Ń‡ĐŸĐŒŃƒ ĐČŃ–Đœ ĐŒĐ°Ń” ĐżĐŸĐČДртатО статус 400?»). ĐšĐ»Đ°ŃĐžŃ‡ĐœŃ– ĐșĐœĐžĐ¶ĐșĐŸĐČі ŃˆĐ°Đ±Đ»ĐŸĐœĐž x-unit ĐœĐ°Đ·ĐČалО цДĐč ŃˆĐ°Đ±Đ»ĐŸĐœ Â«Ń‚Đ°Ń”ĐŒĐœĐžŃ‡ĐžĐŒ ĐłĐŸŃŃ‚Đ”ĐŒÂ»â€Š-â€ŠĐ©ĐŸŃŃŒ ĐœĐ”ĐČĐžĐŽĐžĐŒĐ” ĐČĐżĐ»ĐžĐœŃƒĐ»ĐŸ ĐœĐ° ĐœĐ°ŃˆŃ– Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚Đž Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ, ĐŒĐž ĐœĐ” Đ·ĐœĐ°Ń”ĐŒĐŸ, Ń‰ĐŸ ŃĐ°ĐŒĐ”. Мо ĐŒĐŸĐ¶Đ”ĐŒĐŸ ĐŽĐŸŃŃĐłŃ‚Đž Đșращох Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚Ń–ĐČ, ĐČĐžŃ‚ŃĐłĐœŃƒĐČшО ĐżĐŸĐČŃ‚ĐŸŃ€ŃŽĐČĐ°ĐœŃ– ĐŽĐŸĐČгі Ń‡Đ°ŃŃ‚ĐžĐœĐž ĐœĐ°Đ·ĐŸĐČĐœŃ– І чітĐșĐŸ Đ·Đ°Đ·ĐœĐ°Ń‡ĐžĐČшО, яĐșі ĐșĐŸĐœĐșŃ€Đ”Ń‚ĐœŃ– ЎДталі ĐŒĐ°ŃŽŃ‚ŃŒ Đ·ĐœĐ°Ń‡Đ”ĐœĐœŃ ĐŽĐ»Ń Ń‚Đ”ŃŃ‚Ńƒ. ĐŸĐ”Ń€Đ”Ń…ĐŸĐŽŃŃ‡Đž ĐŽĐŸ ĐœĐ°ĐČĐ”ĐŽĐ”ĐœĐŸĐłĐŸ ĐČОщД проĐșлаЎу, тДст ĐŒĐŸĐ¶Đ” пДрДЎаĐČато ĐżĐ°Ń€Đ°ĐŒĐ”Ń‚Ń€Đž, яĐșі піЮĐșŃ€Đ”ŃĐ»ŃŽŃŽŃ‚ŃŒ ĐČажлОĐČість: transferFactory.getJSON({sender: undefined}). ĐŁ Ń†ŃŒĐŸĐŒŃƒ проĐșлаЎі чотач ĐżĐŸĐČĐžĐœĐ”Đœ ĐœĐ”ĐłĐ°ĐčĐœĐŸ Đ·Ń€ĐŸĐ±ĐžŃ‚Đž ĐČĐžŃĐœĐŸĐČĐŸĐș, Ń‰ĐŸ ĐżĐŸŃ€ĐŸĐ¶ĐœŃ” ĐżĐŸĐ»Đ” ĐČіЮпраĐČĐœĐžĐșа є ĐżŃ€ĐžŃ‡ĐžĐœĐŸŃŽ, Ń‡ĐŸĐŒŃƒ тДст ĐżĐŸĐČĐžĐœĐ”Đœ ĐŸŃ‡Ń–ĐșуĐČато ĐżĐŸĐŒĐžĐ»Đșу пДрДĐČірĐșĐž Đ°Đ±ĐŸ Đ±ŃƒĐŽŃŒ-яĐșĐžĐč Ń–ĐœŃˆĐžĐč ĐżĐŸĐŽŃ–Đ±ĐœĐžĐč аЎДĐșĐČĐ°Ń‚ĐœĐžĐč Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚. +
    + +❌ **Đ†ĐœĐ°ĐșшД:** ĐšĐŸĐżŃ–ŃŽĐČĐ°ĐœĐœŃ 500 ряЮĐșіĐČ JSON Đ·Ń€ĐŸĐ±ĐžŃ‚ŃŒ ĐČаші тДстО ĐœĐ”ĐżŃ€ĐžĐŽĐ°Ń‚ĐœĐžĐŒĐž ĐŽĐ»Ń ĐŸĐ±ŃĐ»ŃƒĐłĐŸĐČуĐČĐ°ĐœĐœŃ та Ń‡ĐžŃ‚Đ°ĐœĐœŃ. ĐŸĐ”Ń€Đ”ĐœĐ”ŃĐ”ĐœĐœŃ ĐČŃŃŒĐŸĐłĐŸ ĐœĐ°Đ·ĐŸĐČĐœŃ– заĐșŃ–ĐœŃ‡ĐžŃ‚ŃŒŃŃ ĐœĐ”Ń‡Ń–Ń‚ĐșĐžĐŒĐž Ń‚Đ”ŃŃ‚Đ°ĐŒĐž, яĐșі ĐČажĐșĐŸ Đ·Ń€ĐŸĐ·ŃƒĐŒŃ–Ń‚Đž + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :thumbsdown: ПроĐșлаЎ Đ°ĐœŃ‚ĐžŃˆĐ°Đ±Đ»ĐŸĐœŃƒ: ĐŸĐŸĐŒĐžĐ»Đșа Ń‚Đ”ŃŃ‚Ńƒ ĐœĐ”Đ·Ń€ĐŸĐ·ŃƒĐŒŃ–Đ»Đ°, ĐŸŃĐșŃ–Đ»ŃŒĐșĐž ĐČся ĐżŃ€ĐžŃ‡ĐžĐœĐ° Đ·ĐŸĐČĐœŃ–ŃˆĐœŃ і Ń…ĐŸĐČається у ĐČĐ”Đ»ĐžŃ‡Đ”Đ·ĐœĐŸĐŒŃƒ JSON + +![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "ПроĐșлаЎО Đ· Mocha") + +```javascript +test("When no credit, then the transfer is declined", async() => { + // Arrange + const transferRequest = testHelpers.factorMoneyTransfer() // ĐżĐŸĐČДртає 200 ряЮĐșіĐČ JSON; + const transferServiceUnderTest = new TransferService(); + + // Act + const transferResponse = await transferServiceUnderTest.transfer(transferRequest); + + // Assert + expect(transferResponse.status).toBe(409);// АлД Ń‡ĐŸĐŒŃƒ ĐŒĐž ĐŸŃ‡Ń–ĐșŃƒŃ”ĐŒĐŸ ĐœĐ”ĐČЮачі: у тДсті ĐČсД Đ·ĐŽĐ°Ń”Ń‚ŃŒŃŃ Đ°Đ±ŃĐŸĐ»ŃŽŃ‚ĐœĐŸ ЮіĐčŃĐœĐžĐŒ đŸ€” + }); +``` + +
    + +### :clap: ПроĐșлаЎ праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: йДст піЮĐșрДслює, Ń‰ĐŸ є ĐżŃ€ĐžŃ‡ĐžĐœĐŸŃŽ Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚Ńƒ Ń‚Đ”ŃŃ‚Ńƒ + +```javascript + +test("When no credit, then the transfer is declined ", async() => { + // Arrange + const transferRequest = testHelpers.factorMoneyTransfer({userCredit:100, transferAmount:200}) // ĐŸŃ‡Đ”ĐČĐžĐŽĐœĐŸ, браĐș ĐșрДЎОтіĐČ + const transferServiceUnderTest = new TransferService({disallowOvercharge:true}); + + // Act + const transferResponse = await transferServiceUnderTest.transfer(transferRequest); + + // Assert + expect(transferResponse.status).toBe(409); // ОчДĐČĐžĐŽĐœĐŸ, Ń‰ĐŸ яĐșŃ‰ĐŸ у ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČача ĐœĐ”ĐŒĐ°Ń” ĐșŃ€Đ”ĐŽĐžŃ‚Ńƒ, тДст ĐČпаЎД + }); + ``` + +
    + +

    + +## âšȘ  1.10 ĐĐ” Đ»ĐŸĐČіть ĐżĐŸĐŒĐžĐ»ĐșĐž, ĐŸŃ‡Ń–ĐșуĐčтД їх + +:white_check_mark: **Đ ĐŸĐ±Đž:** ПіЮ час ŃĐżŃ€ĐŸĐ±Đž стĐČĐ”Ń€ĐŽĐ¶ŃƒĐČато, Ń‰ĐŸ яĐșĐžĐčсь ĐČхіЮ ĐČĐžĐșлОĐșає ĐżĐŸĐŒĐžĐ»Đșу, ĐŒĐŸĐ¶Đ” ĐČĐžĐłĐ»ŃĐŽĐ°Ń‚Đž праĐČĐžĐ»ŃŒĐœĐžĐŒ ĐČĐžĐșĐŸŃ€ĐžŃŃ‚Đ°ĐœĐœŃ try-catch-finally і піЮтĐČĐ”Ń€ĐŽĐ¶ŃƒŃ”, Ń‰ĐŸ Đ±ŃƒĐ»ĐŸ ĐČĐČĐ”ĐŽĐ”ĐœĐŸ ĐżŃ€ĐŸĐżĐŸĐ·ĐžŃ†Ń–ŃŽ catch. Đ Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚ĐŸĐŒ є ĐœĐ”Đ·Ń€ŃƒŃ‡ĐœĐžĐč і Đ±Đ°ĐłĐ°Ń‚ĐŸŃĐ»Ń–ĐČĐœĐžĐč Ń‚Đ”ŃŃ‚ĐŸĐČĐžĐč проĐșлаЎ (проĐșлаЎ ĐœĐžĐ¶Ń‡Đ”), яĐșĐžĐč ĐżŃ€ĐžŃ…ĐŸĐČує ĐżŃ€ĐŸŃŃ‚ĐžĐč ĐœĐ°ĐŒŃ–Ń€ Ń‚Đ”ŃŃ‚Ńƒ та ĐŸŃ‡Ń–ĐșуĐČĐ°ĐœĐœŃ Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚Ńƒ + +Đ‘Ń–Đ»ŃŒŃˆ Đ”Đ»Đ”ĐłĐ°ĐœŃ‚ĐœĐŸŃŽ Đ°Đ»ŃŒŃ‚Đ”Ń€ĐœĐ°Ń‚ĐžĐČĐŸŃŽ є ĐČĐžĐșĐŸŃ€ĐžŃŃ‚Đ°ĐœĐœŃ ĐŸĐŽĐœĐŸŃ€ŃĐŽĐșĐŸĐČĐŸĐłĐŸ ĐČĐžĐŽŃ–Đ»Đ”ĐœĐŸĐłĐŸ тĐČĐ”Ń€ĐŽĐ¶Đ”ĐœĐœŃ Chai: expect(method).to.throw (Đ°Đ±ĐŸ Jest: expect(method).toThrow()). ĐĐ±ŃĐŸĐ»ŃŽŃ‚ĐœĐŸ ĐŸĐ±ĐŸĐČÊŒŃĐ·ĐșĐŸĐČĐŸ таĐșĐŸĐ¶ пДрДĐșĐŸĐœĐ°Ń‚ĐžŃŃ, Ń‰ĐŸ ĐČĐžĐœŃŃ‚ĐŸĐș ĐŒŃ–ŃŃ‚ĐžŃ‚ŃŒ ĐČластОĐČість, яĐșа ĐżĐŸĐČŃ–ĐŽĐŸĐŒĐ»ŃŃ” топ ĐżĐŸĐŒĐžĐ»ĐșĐž, Ń–ĐœĐ°ĐșшД, ĐČŃ€Đ°Ń…ĐŸĐČуючо лОшД Đ·Đ°ĐłĐ°Đ»ŃŒĐœŃƒ ĐżĐŸĐŒĐžĐ»Đșу і ĐżĐŸĐșажД ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČачДĐČі ĐœĐ”ĐČŃ‚Ń–ŃˆĐœĐ” ĐżĐŸĐČŃ–ĐŽĐŸĐŒĐ»Đ”ĐœĐœŃ +
    + +❌ **Đ†ĐœĐ°ĐșшД:** Đ‘ŃƒĐŽĐ” сĐșĐ»Đ°ĐŽĐœĐŸ Đ·Ń€ĐŸĐ±ĐžŃ‚Đž ĐČĐžŃĐœĐŸĐČĐŸĐș Đ·Ń– Đ·ĐČітіĐČ ĐżŃ€ĐŸ ĐČĐžĐżŃ€ĐŸĐ±ŃƒĐČĐ°ĐœĐœŃ (ĐœĐ°ĐżŃ€ĐžĐșлаЎ, Đ·ĐČітіĐČ CI), Ń‰ĐŸ ĐżŃ–ŃˆĐ»ĐŸ ĐœĐ” таĐș + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :thumbsdown: ПроĐșлаЎ Đ°ĐœŃ‚ĐžŃˆĐ°Đ±Đ»ĐŸĐœŃƒ: Đ”ĐŸĐČгОĐč Ń‚Đ”ŃŃ‚ĐŸĐČĐžĐč проĐșлаЎ, яĐșĐžĐč ĐœĐ°ĐŒĐ°ĐłĐ°Ń”Ń‚ŃŒŃŃ піЮтĐČДрЎОтО Ń–ŃĐœŃƒĐČĐ°ĐœĐœŃ ĐżĐŸĐŒĐžĐ»ĐșĐž за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ try-catch + +![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "ПроĐșлаЎО Đ· Mocha") + +```javascript +it("When no product name, it throws error 400", async () => { + let errorWeExceptFor = null; + try { + const result = await addNewProduct({}); + } catch (error) { + expect(error.code).to.equal("InvalidInput"); + errorWeExceptFor = error; + } + expect(errorWeExceptFor).not.to.be.null; + // яĐșŃ‰ĐŸ цД тĐČĐ”Ń€ĐŽĐ¶Đ”ĐœĐœŃ ĐœĐ” ĐČĐžĐșĐŸĐœŃƒŃ”Ń‚ŃŒŃŃ, ĐČŃ–ĐŽĐŸĐ±Ń€Đ°Đ¶Đ°Ń‚ĐžĐŒŃƒŃ‚ŃŒŃŃ лОшД Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚Đž тДстіĐČ/Đ·ĐČіто + // яĐșŃ‰ĐŸ яĐșĐ”ŃŃŒ Đ·ĐœĐ°Ń‡Đ”ĐœĐœŃ ĐŽĐŸŃ€Ń–ĐČĐœŃŽŃ” ĐœŃƒĐ»ŃŽ, ĐœĐ” буЎД Đ¶ĐŸĐŽĐœĐŸĐłĐŸ ŃĐ»ĐŸĐČа ĐżŃ€ĐŸ ĐČŃ–ĐŽŃŃƒŃ‚ĐœŃ–Đč ĐČĐžĐœŃŃ‚ĐŸĐș +}); +``` + +
    + +### :clap: ПроĐșлаЎ праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: ОчіĐșуĐČĐ°ĐœĐœŃ, Đ·Ń€ĐŸĐ·ŃƒĐŒŃ–Đ»Ń– Đ»ŃŽĐŽĐžĐœŃ–, яĐșі ĐŒĐŸĐ¶ŃƒŃ‚ŃŒ Đ±ŃƒŃ‚Đž лДгĐșĐŸ Đ·Ń€ĐŸĐ·ŃƒĐŒŃ–Đ»Ń–, ĐŒĐŸĐ¶Đ»ĐžĐČĐŸ, ĐœĐ°ĐČіть ŃĐżĐ”Ń†Ń–Đ°Đ»Ń–ŃŃ‚Đ°ĐŒ QA Đ°Đ±ĐŸ PM + +```javascript +it("When no product name, it throws error 400", async () => { + await expect(addNewProduct({})) + .to.eventually.throw(AppError) + .with.property("code", "InvalidInput"); +}); +``` + +
    + +

    + +## âšȘ  1.11 ĐŸĐŸĐ·ĐœĐ°Ń‡Ń‚Đ” сĐČĐŸŃ— тДстО + +:white_check_mark: **Đ ĐŸĐ±Đž:** Đ Ń–Đ·ĐœŃ– тДстО ĐżĐŸĐČĐžĐœĐœŃ– ĐČĐžĐșĐŸĐœŃƒĐČатося за Ń€Ń–Đ·ĐœĐžĐŒĐž ŃŃ†Đ”ĐœĐ°Ń€Ń–ŃĐŒĐž: quick smoke, бДз IO, тДстО ĐŒĐ°ŃŽŃ‚ŃŒ запусĐșатося, ĐșĐŸĐ»Đž Ń€ĐŸĐ·Ń€ĐŸĐ±ĐœĐžĐș збДрігає Đ°Đ±ĐŸ фіĐșсує фаĐčĐ», ĐżĐŸĐČĐœŃ– ĐœĐ°ŃĐșŃ€Ń–Đ·ĐœŃ– тДстО (e2e) зазĐČочаĐč запусĐșаються, ĐșĐŸĐ»Đž ĐœĐ°ĐŽŃĐžĐ»Đ°Ń”Ń‚ŃŒŃŃ ĐœĐŸĐČĐžĐč запОт ĐœĐ° ĐŸŃ‚Ń€ĐžĐŒĐ°ĐœĐœŃ Ń‚ĐŸŃ‰ĐŸ. ĐŠŃŒĐŸĐłĐŸ ĐŒĐŸĐ¶ĐœĐ° ĐŽĐŸŃŃĐłŃ‚Đž ĐżĐŸĐ·ĐœĐ°Ń‡Đ°ŃŽŃ‡Đž тДстО ĐșĐ»ŃŽŃ‡ĐŸĐČĐžĐŒĐž ŃĐ»ĐŸĐČĐ°ĐŒĐž, яĐș-ĐŸŃ‚ #cold #api #sanity, Ń‰ĐŸĐ± ĐČĐž ĐŒĐŸĐłĐ»Đž працюĐČато Đ·Ń– сĐČĐŸŃ—ĐŒ паĐșĐ”Ń‚ĐŸĐŒ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ та ĐČĐžĐșлОĐșато ĐżĐŸŃ‚Ń€Ń–Đ±ĐœŃƒ ĐżŃ–ĐŽĐŒĐœĐŸĐ¶ĐžĐœŃƒ. НапроĐșлаЎ, ĐŸŃŃŒ яĐș ĐČĐž ĐŒĐŸĐ¶Đ”Ń‚Đ” ĐČĐžĐșлОĐșато лОшД групу пДрДĐČірĐșĐž Ń€ĐŸĐ·ŃƒĐŒĐœĐŸŃŃ‚Ń– за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ Mocha: mocha — grep ‘sanity’ +
    + +❌ **Đ†ĐœĐ°ĐșшД:** ВоĐșĐŸĐœĐ°ĐœĐœŃ ĐČсіх тДстіĐČ, у Ń‚ĐŸĐŒŃƒ чОслі тДстіĐČ, яĐșі ĐČĐžĐșĐŸĐœŃƒŃŽŃ‚ŃŒ ĐŽĐ”ŃŃŃ‚ĐșĐž запОтіĐČ ĐŽĐŸ БД, Ń‰ĐŸŃ€Đ°Đ·Ńƒ, ĐșĐŸĐ»Đž Ń€ĐŸĐ·Ń€ĐŸĐ±ĐœĐžĐș ĐČĐœĐŸŃĐžŃ‚ŃŒ ĐœĐ”ĐČДлОĐșі Đ·ĐŒŃ–ĐœĐž, ĐŒĐŸĐ¶Đ” Đ±ŃƒŃ‚Đž ĐœĐ°ĐŽĐ·ĐČочаĐčĐœĐŸ ĐżĐŸĐČŃ–Đ»ŃŒĐœĐžĐŒ і ŃƒŃ‚Ń€ĐžĐŒŃƒĐČато Ń€ĐŸĐ·Ń€ĐŸĐ±ĐœĐžĐșіĐČ ĐČіЮ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ тДстіĐČВоĐșĐŸĐœĐ°ĐœĐœŃ ĐČсіх тДстіĐČ, у Ń‚ĐŸĐŒŃƒ чОслі тДстіĐČ, яĐșі ĐČĐžĐșĐŸĐœŃƒŃŽŃ‚ŃŒ ĐŽĐ”ŃŃŃ‚ĐșĐž запОтіĐČ ĐŽĐŸ БД, Ń‰ĐŸŃ€Đ°Đ·Ńƒ, ĐșĐŸĐ»Đž Ń€ĐŸĐ·Ń€ĐŸĐ±ĐœĐžĐș ĐČĐœĐŸŃĐžŃ‚ŃŒ ĐœĐ”ĐČДлОĐșі Đ·ĐŒŃ–ĐœĐž, ĐŒĐŸĐ¶Đ” Đ±ŃƒŃ‚Đž ĐœĐ°ĐŽĐ·ĐČочаĐčĐœĐŸ ĐżĐŸĐČŃ–Đ»ŃŒĐœĐžĐŒ і ŃƒŃ‚Ń€ĐžĐŒŃƒĐČато Ń€ĐŸĐ·Ń€ĐŸĐ±ĐœĐžĐșіĐČ ĐČіЮ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ тДстіĐČ + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :clap: ПроĐșлаЎ праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: ĐŸĐŸĐ·ĐœĐ°Ń‡Đ”ĐœĐœŃ тДстіĐČ ŃĐș «#cold-test» ĐŽĐŸĐ·ĐČĐŸĐ»ŃŃ” Ń‚Đ”ŃŃ‚ŃƒĐČĐ°Đ»ŃŒĐœĐžĐșу ĐČĐžĐșĐŸĐœŃƒĐČато лОшД шĐČОЎĐșі тДстО (Cold===шĐČОЎĐșі тДстО, яĐșі ĐœĐ” ĐČĐžĐșĐŸĐœŃƒŃŽŃ‚ŃŒ ĐČĐČĐ”ĐŽĐ”ĐœĐœŃ-ĐČĐžĐČĐ”ĐŽĐ”ĐœĐœŃ (IO) та ĐŒĐŸĐ¶ŃƒŃ‚ŃŒ ĐČĐžĐșĐŸĐœŃƒĐČатося Ń‡Đ°ŃŃ‚ĐŸ, ĐœĐ°ĐČіть ĐșĐŸĐ»Đž Ń€ĐŸĐ·Ń€ĐŸĐ±ĐœĐžĐș ĐČĐČĐŸĐŽĐžŃ‚ŃŒ тДĐșст) + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "ПроĐșлаЎО Đ· Jest") + +```javascript +// цДĐč тДст шĐČОЎĐșĐžĐč (бДз БД), і ĐŒĐž ĐżĐŸĐ·ĐœĐ°Ń‡Đ°Ń”ĐŒĐŸ ĐčĐŸĐłĐŸ ĐČŃ–ĐŽĐżĐŸĐČŃ–ĐŽĐœĐžĐŒ Ń‡ĐžĐœĐŸĐŒ +// тДпДр ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČач/CI ĐŒĐŸĐ¶Đ” запусĐșато ĐčĐŸĐłĐŸ Ń‡Đ°ŃŃ‚ĐŸ +describe("Order service", function() { + describe("Add new order #cold-test #sanity", function() { + test("Scenario - no currency was supplied. Expectation - Use the default currency #sanity", function() { + // Đ›ĐŸĐłŃ–Đșа тут + }); + }); +}); +``` + +
    + +

    + +## âšȘ  1.12 КласОфіĐșуĐčтД тДстО ĐżŃ€ĐžĐœĐ°ĐčĐŒĐœŃ– за 2 ріĐČĐœŃĐŒĐž + +:white_check_mark: **Đ ĐŸĐ±Đž:** Đ—Đ°ŃŃ‚ĐŸŃŃƒĐčтД пДĐČĐœŃƒ струĐșтуру ĐŽĐŸ сĐČĐŸĐłĐŸ ĐœĐ°Đ±ĐŸŃ€Ńƒ тДстіĐČ, Ń‰ĐŸĐ± ĐČОпаЎĐșĐŸĐČĐžĐč ĐČіЮĐČіЮуĐČач ĐŒŃ–Đł лДгĐșĐŸ Đ·Ń€ĐŸĐ·ŃƒĐŒŃ–Ń‚Đž ĐČĐžĐŒĐŸĐłĐž (тДстО — ĐœĐ°ĐčĐșраща ĐŽĐŸĐșŃƒĐŒĐ”ĐœŃ‚Đ°Ń†Ń–Ń) і Ń€Ń–Đ·ĐœŃ– ŃŃ†Đ”ĐœĐ°Ń€Ń–Ń—, яĐșі Ń‚Đ”ŃŃ‚ŃƒŃŽŃ‚ŃŒŃŃ. ЗĐČочаĐčĐœĐžĐŒ ĐŒĐ”Ń‚ĐŸĐŽĐŸĐŒ ĐŽĐ»Ń Ń†ŃŒĐŸĐłĐŸ є Ń€ĐŸĐ·ĐŒŃ–Ń‰Đ”ĐœĐœŃ ĐżŃ€ĐžĐœĐ°ĐčĐŒĐœŃ– 2 Đ±Đ»ĐŸĐșіĐČ Â«describe» ĐœĐ°ĐŽ ĐČĐ°ŃˆĐžĐŒĐž Ń‚Đ”ŃŃ‚Đ°ĐŒĐž: 1-Đč ĐżŃ€ĐžĐ·ĐœĐ°Ń‡Đ”ĐœĐžĐč ĐŽĐ»Ń ĐœĐ°Đ·ĐČĐž Ń‚Đ”ŃŃ‚ĐŸĐČĐ°ĐœĐŸĐłĐŸ Đ±Đ»ĐŸĐșу, а 2-Đč ĐŽĐ»Ń ĐŽĐŸĐŽĐ°Ń‚ĐșĐŸĐČĐŸĐłĐŸ ріĐČĐœŃ ĐșĐ°Ń‚Đ”ĐłĐŸŃ€ĐžĐ·Đ°Ń†Ń–Ń—, яĐș-ĐŸŃ‚ ŃŃ†Đ”ĐœĐ°Ń€Ń–Đč Đ°Đ±ĐŸ ŃĐżĐ”Ń†Ń–Đ°Đ»ŃŒĐœŃ– ĐșĐ°Ń‚Đ”ĐłĐŸŃ€Ń–Ń— (ĐŽĐžĐČось ПроĐșлаЎО ĐșĐŸĐŽŃƒ). ĐŠĐ” таĐșĐŸĐ¶ Đ·ĐœĐ°Ń‡ĐœĐŸ ĐżĐŸĐșращоть Đ·ĐČіто ĐżŃ€ĐŸ тДстО: чотач лДгĐșĐŸ ĐČĐžĐ·ĐœĐ°Ń‡ĐžŃ‚ŃŒ ĐșĐ°Ń‚Đ”ĐłĐŸŃ€Ń–Ń— тДстіĐČ, Đ·Đ°ĐłĐ»ĐžĐ±ĐžŃ‚ŃŒŃŃ ĐČ ĐżĐŸŃ‚Ń€Ń–Đ±ĐœĐžĐč Ń€ĐŸĐ·ĐŽŃ–Đ» і ĐżĐŸŃ€Ń–ĐČĐœŃŽŃ” ĐœĐ”ĐČЎалі тДстО. ĐšŃ€Ń–ĐŒ Ń‚ĐŸĐłĐŸ, Ń€ĐŸĐ·Ń€ĐŸĐ±ĐœĐžĐșу ŃŃ‚Đ°ĐœĐ” ĐœĐ°Đ±Đ°ĐłĐ°Ń‚ĐŸ лДгшД ĐŸŃ€Ń–Ń”ĐœŃ‚ŃƒĐČатося ĐČ ĐșĐŸĐŽŃ– ĐœĐ°Đ±ĐŸŃ€Ńƒ Đ· Đ±Đ°ĐłĐ°Ń‚ŃŒĐŒĐ° Ń‚Đ”ŃŃ‚Đ°ĐŒĐž. Đ†ŃĐœŃƒŃ” ĐșŃ–Đ»ŃŒĐșа Đ°Đ»ŃŒŃ‚Đ”Ń€ĐœĐ°Ń‚ĐžĐČĐœĐžŃ… струĐșтур ĐŽĐ»Ń ĐœĐ°Đ±ĐŸŃ€Ńƒ тДстіĐČ, яĐșі ĐČĐž ĐŒĐŸĐ¶Đ”Ń‚Đ” Ń€ĐŸĐ·ĐłĐ»ŃĐœŃƒŃ‚Đž, ĐœĐ°ĐżŃ€ĐžĐșлаЎ [given-when-then](https://github.com/searls/jasmine-given) і [RITE](https://github.com/ericelliott/riteway) + +
    + +❌ **Đ†ĐœĐ°ĐșшД:** ДоĐČĐ»ŃŃ‡ĐžŃŃŒ ĐœĐ° Đ·ĐČіт Ń–Đ· ĐżĐ»ĐŸŃĐșĐžĐŒ і ĐŽĐŸĐČĐłĐžĐŒ спОсĐșĐŸĐŒ тДстіĐČ, чотач ĐżĐŸĐČĐžĐœĐ”Đœ ĐżĐŸĐ±Ń–Đ¶ĐœĐŸ ĐżŃ€ĐŸŃ‡ĐžŃ‚Đ°Ń‚Đž ĐŽĐŸĐČгі тДĐșсто, Ń‰ĐŸĐ± Đ·Ń€ĐŸĐ±ĐžŃ‚Đž ĐČĐžŃĐœĐŸĐČĐŸĐș ĐżŃ€ĐŸ ĐŸŃĐœĐŸĐČĐœŃ– ŃŃ†Đ”ĐœĐ°Ń€Ń–Ń— та спіĐČĐČŃ–ĐŽĐœĐ”ŃŃ‚Đž Đ·Đ°ĐłĐ°Đ»ŃŒĐœŃ–ŃŃ‚ŃŒ ĐœĐ”ĐČЎалОх тДстіĐČ. Đ ĐŸĐ·ĐłĐ»ŃĐœĐ”ĐŒĐŸ ĐœĐ°ŃŃ‚ŃƒĐżĐœĐžĐč ĐČĐžĐżĐ°ĐŽĐŸĐș: ĐșĐŸĐ»Đž 7/100 тДстіĐČ ĐœĐ” ĐČЮаються, ĐżĐ”Ń€Đ”ĐłĐ»ŃĐŽ ĐżĐ»ĐŸŃĐșĐŸĐłĐŸ спОсĐșу ĐżĐŸŃ‚Ń€Đ”Đ±ŃƒŃ” ĐżŃ€ĐŸŃ‡ĐžŃ‚Đ°ĐœĐœŃ тДĐșсту ĐœĐ”ĐČЎалОх тДстіĐČ, Ń‰ĐŸĐ± ĐżĐŸĐ±Đ°Ń‡ĐžŃ‚Đž, яĐș ĐČĐŸĐœĐž ĐżĐŸĐČâ€™ŃĐ·Đ°ĐœŃ– ĐŸĐŽĐžĐœ Đ· ĐŸĐŽĐœĐžĐŒ. ĐžĐŽĐœĐ°Đș ĐČ Ń–Ń”Ń€Đ°Ń€Ń…Ń–Ń‡ĐœĐŸĐŒŃƒ Đ·ĐČіті ĐČсі ĐČĐŸĐœĐž ĐŒĐŸĐ¶ŃƒŃ‚ŃŒ ĐČŃ…ĐŸĐŽĐžŃ‚Đž ĐŽĐŸ ĐŸĐŽĐœĐŸĐłĐŸ ĐżĐŸŃ‚ĐŸĐșу Đ°Đ±ĐŸ ĐșĐ°Ń‚Đ”ĐłĐŸŃ€Ń–Ń—, і чотач шĐČОЎĐșĐŸ Đ·Ń€ĐŸĐ·ŃƒĐŒŃ–Ń”, Ń‰ĐŸ Đ°Đ±ĐŸ ĐżŃ€ĐžĐœĐ°ĐčĐŒĐœŃ– ĐŽĐ” є ĐŸŃĐœĐŸĐČĐœĐ° ĐżŃ€ĐžŃ‡ĐžĐœĐ° Đ·Đ±ĐŸŃŽ + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :clap: ПроĐșлаЎ праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: СтруĐșтуруĐČĐ°ĐœĐœŃ ĐœĐ°Đ±ĐŸŃ€Ńƒ Đ· ĐœĐ°Đ·ĐČĐŸŃŽ Đ±Đ»ĐŸĐșу, Ń‰ĐŸ Ń‚Đ”ŃŃ‚ŃƒŃ”Ń‚ŃŒŃŃ, і ŃŃ†Đ”ĐœĐ°Ń€Ń–ŃĐŒĐž прОзĐČДЎД ĐŽĐŸ Đ·Ń€ŃƒŃ‡ĐœĐŸĐłĐŸ Đ·ĐČіту, яĐșĐžĐč ĐżĐŸĐșĐ°Đ·Đ°ĐœĐŸ ĐœĐžĐ¶Ń‡Đ” + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "ПроĐșлаЎО Đ· Jest") + +```javascript +// ĐąĐ”ŃŃ‚ŃƒŃ”ĐŒĐ° ĐŸĐŽĐžĐœĐžŃ†Ń +describe("Transfer service", () => { + // ĐĄŃ†Đ”ĐœĐ°Ń€Ń–Đč + describe("When no credit", () => { + // ОчіĐșуĐČĐ°ĐœĐœŃ + test("Then the response status should decline", () => {}); + + // ОчіĐșуĐČĐ°ĐœĐœŃ + test("Then it should send email to admin", () => {}); + }); +}); +``` + +![alt text](assets/hierarchical-report.png) + +
    + +### :thumbsdown: ПроĐșлаЎ Đ°ĐœŃ‚ĐžŃˆĐ°Đ±Đ»ĐŸĐœŃƒ: ĐŸĐ»ĐŸŃĐșĐžĐč ŃĐżĐžŃĐŸĐș тДстіĐČ ŃƒŃĐșĐ»Đ°ĐŽĐœĐžŃ‚ŃŒ чОтачДĐČі Ń–ĐŽĐ”ĐœŃ‚ĐžŃ„Ń–Đșацію Ń–ŃŃ‚ĐŸŃ€Ń–Đč ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČачіĐČ Ń– спіĐČĐČŃ–ĐŽĐœĐ”ŃĐ”ĐœĐœŃ ĐœĐ”ĐČЎалОх тДстіĐČ + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "ПроĐșлаЎО Đ· Mocha") + +```javascript +test("Then the response status should decline", () => {}); + +test("Then it should send email", () => {}); + +test("Then there should not be a new transfer record", () => {}); +``` + +![alt text](assets/flat-report.png) + +
    + +
    + +

    + +## âšȘ 1.13 Đ†ĐœŃˆĐ° Đ·Đ°ĐłĐ°Đ»ŃŒĐœĐ° ĐłŃ–ĐłŃ–Ń”ĐœĐ° Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ + +:white_check_mark: **Đ ĐŸĐ±Đž:** Щя ĐżŃƒĐ±Đ»Ń–Đșація Đ·ĐŸŃĐ”Ń€Đ”ĐŽĐ¶Đ”ĐœĐ° ĐœĐ° ĐżĐŸŃ€Đ°ĐŽĐ°Ń… Ń‰ĐŸĐŽĐŸ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ, яĐșі ĐżĐŸĐČâ€™ŃĐ·Đ°ĐœŃ– Đ· Node JS Đ°Đ±ĐŸ, ĐżŃ€ĐžĐœĐ°ĐčĐŒĐœŃ–, ĐŒĐŸĐ¶ŃƒŃ‚ŃŒ Đ±ŃƒŃ‚Đž прДЎстаĐČĐ»Đ”ĐœŃ– ĐœĐ° проĐșлаЎі Node JS. ĐžĐŽĐœĐ°Đș у Ń†ŃŒĐŸĐŒŃƒ Ń€ĐŸĐ·ĐŽŃ–Đ»Ń– Đ·ĐłŃ€ŃƒĐżĐŸĐČĐ°ĐœĐŸ ĐșŃ–Đ»ŃŒĐșа ĐŽĐŸĐ±Ń€Đ” ĐČŃ–ĐŽĐŸĐŒĐžŃ… ĐżĐŸŃ€Đ°ĐŽ, ĐœĐ” ĐżĐŸĐČÊŒŃĐ·Đ°ĐœĐžŃ… Đ· Node + +НаĐČчаĐčŃ‚Đ”ŃŃ і праĐșтоĐșуĐčŃ‚Đ”ŃŃ [TDD ĐżŃ€ĐžĐœŃ†ĐžĐżĐž](https://www.sm-cloud.com/book-review-test-driven-development-by-example-a-tldr/) — ĐČĐŸĐœĐž ĐœĐ°ĐŽĐ·ĐČочаĐčĐœĐŸ Ń†Ń–ĐœĐœŃ– ĐŽĐ»Ń Đ±Đ°ĐłĐ°Ń‚ŃŒĐŸŃ…, алД ĐœĐ” Đ»ŃĐșаĐčŃ‚Đ”ŃŃ, яĐșŃ‰ĐŸ ĐČĐŸĐœĐž ĐœĐ” ĐČŃ–ĐŽĐżĐŸĐČіЮають ĐČĐ°ŃˆĐŸĐŒŃƒ стОлю, ĐČĐž ĐœĐ” Ń”ĐŽĐžĐœŃ–. Đ ĐŸĐ·ĐłĐ»ŃĐœŃŒŃ‚Đ” ĐŒĐŸĐ¶Đ»ĐžĐČість ĐœĐ°ĐżĐžŃĐ°ĐœĐœŃ тДстіĐČ ĐżĐ”Ń€Đ”ĐŽ ĐșĐŸĐŽĐŸĐŒ у [red-green-refactor style](https://blog.cleancoder.com/uncle-bob/2014/12/17/TheCyclesOfTDD.html), пДрДĐșĐŸĐœĐ°ĐčŃ‚Đ”ŃŃ, Ń‰ĐŸ ĐșĐŸĐ¶Đ”Đœ тДст пДрДĐČіряє ŃĐ°ĐŒĐ” ĐŸĐŽĐœŃƒ річ, яĐșŃ‰ĐŸ ĐČĐž Đ·ĐœĐ°ĐčЎДтД ĐżĐŸĐŒĐžĐ»ĐșŃƒâ€Šâ€”â€ŠĐżĐ”Ń€Đ”ĐŽ ĐČопраĐČĐ»Đ”ĐœĐœŃĐŒ ĐœĐ°ĐżĐžŃˆŃ–Ń‚ŃŒ тДст, яĐșĐžĐč ĐČояĐČоть цю ĐżĐŸĐŒĐžĐ»Đșу ĐČ ĐŒĐ°ĐčĐ±ŃƒŃ‚ĐœŃŒĐŸĐŒŃƒ, ЎаĐčтД ĐșĐŸĐ¶ĐœĐŸĐŒŃƒ Ń‚Đ”ŃŃ‚Ńƒ ĐżŃ€ĐžĐœĐ°ĐčĐŒĐœŃ– ĐŸĐŽĐžĐœ раз ĐżĐŸĐŒĐžĐ»ĐžŃ‚ĐžŃŃ, ĐżĐ”Ń€Ńˆ ĐœŃ–Đ¶ стато Đ·Đ”Đ»Đ”ĐœĐžĐŒ, Đ·Đ°ĐżŃƒŃŃ‚Ń–Ń‚ŃŒ ĐŒĐŸĐŽŃƒĐ»ŃŒ, ĐœĐ°ĐżĐžŃĐ°ĐČшО шĐČОЎĐșĐžĐč і ŃĐżŃ€ĐŸŃ‰Đ”ĐœĐžĐč ĐșĐŸĐŽ, яĐșĐžĐč Đ·Đ°ĐŽĐŸĐČĐŸĐ»ŃŒĐœŃŃ” тДст - ĐżĐŸŃ‚Ń–ĐŒ ĐżĐŸŃŃ‚ŃƒĐżĐŸĐČĐŸ рДфаĐșŃ‚ĐŸŃ€ĐžĐœĐł і пДрДĐČĐ”ĐŽŃ–Ń‚ŃŒ ĐčĐŸĐłĐŸ ĐŽĐŸ ріĐČĐœŃ ĐČĐžŃ€ĐŸĐ±ĐœĐžŃ†Ń‚ĐČа, ŃƒĐœĐžĐșаĐčтД Đ±ŃƒĐŽŃŒ-яĐșĐŸŃ— Đ·Đ°Đ»Đ”Đ¶ĐœĐŸŃŃ‚Ń– ĐČіЮ ŃĐ”Ń€Đ”ĐŽĐŸĐČоща (ŃˆĐ»ŃŃ…Đž, ОС Ń‚ĐŸŃ‰ĐŸ) +
    + +❌ **Đ†ĐœĐ°ĐșшД:** Во ĐżŃ€ĐŸĐżŃƒŃŃ‚ĐžŃ‚Đ” ĐżĐ”Ń€Đ»ĐžĐœĐž ĐŒŃƒĐŽŃ€ĐŸŃŃ‚Ń–, яĐșі Đ·Đ±ĐžŃ€Đ°Đ»ĐžŃŃ ĐŽĐ”ŃŃŃ‚ĐžĐ»Ń–Ń‚Ń‚ŃĐŒĐž + +

    + +# Section 2ïžâƒŁ: Backend Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ + +## âšȘ 2.1 Đ—Đ±Đ°ĐłĐ°Ń‡ŃƒĐčтД сĐČĐŸŃ” ĐżĐŸŃ€Ń‚Ń„ĐŸĐ»Ń–ĐŸ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ: ĐżĐŸĐŽĐžĐČіться за ĐŒĐ”Đ¶Ń– ĐŒĐŸĐŽŃƒĐ»ŃŒĐœĐžŃ… тДстіĐČ Ń– ĐżŃ–Ń€Đ°ĐŒŃ–ĐŽĐž + +:white_check_mark: **Đ ĐŸĐ±Đž:** [ĐŸŃ–Ń€Đ°ĐŒŃ–ĐŽĐ° Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ](https://martinfowler.com/bliki/TestPyramid.html), Đ„ĐŸŃ‡Đ° їĐč ĐżĐŸĐœĐ°ĐŽ 10 Ń€ĐŸĐșіĐČ, цД Ń‡ŃƒĐŽĐŸĐČа та рДлДĐČĐ°ĐœŃ‚ĐœĐ° ĐŒĐŸĐŽĐ”Đ»ŃŒ, яĐșа ĐżŃ€ĐŸĐżĐŸĐœŃƒŃ” тро топо Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ та ĐČплОĐČає ĐœĐ° стратДгію Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ Đ±Ń–Đ»ŃŒŃˆĐŸŃŃ‚Ń– Ń€ĐŸĐ·Ń€ĐŸĐ±ĐœĐžĐșіĐČ. Đ’ĐŸĐŽĐœĐŸŃ‡Đ°Ń Đ·â€™ŃĐČĐžĐ»ĐŸŃŃ Đ±Ń–Đ»ŃŒŃˆĐ” ĐșŃ–Đ»ŃŒĐșĐŸŃ… блОсĐșучох ĐœĐŸĐČох ĐŒĐ”Ń‚ĐŸĐŽŃ–ĐČ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ, яĐșі Ń…ĐŸĐČаються ĐČ Ń‚Ń–ĐœŃ– ĐżŃ–Ń€Đ°ĐŒŃ–ĐŽĐž Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ. Đ’Ń€Đ°Ń…ĐŸĐČуючо ĐČсі ĐŽŃ€Đ°ĐŒĐ°Ń‚ĐžŃ‡ĐœŃ– Đ·ĐŒŃ–ĐœĐž, яĐșі ĐŒĐž ŃĐżĐŸŃŃ‚Đ”Ń€Ń–ĐłĐ°Ń”ĐŒĐŸ за ĐŸŃŃ‚Đ°ĐœĐœŃ– 10 Ń€ĐŸĐșіĐČ (ĐŒŃ–ĐșŃ€ĐŸŃĐ”Ń€ĐČісо, Ń…ĐŒĐ°Ń€ĐœŃ– сДрĐČісо, бДзсДрĐČĐ”Ń€ĐœĐžĐč ĐŽĐŸŃŃ‚ŃƒĐż), чо ĐŒĐŸĐ¶Đ»ĐžĐČĐŸ, Ń‰ĐŸ ĐŸĐŽĐœĐ° ĐŽĐŸŃĐžŃ‚ŃŒ стара ĐŒĐŸĐŽĐ”Đ»ŃŒ піЮіĐčĐŽĐ” ĐŽĐ»Ń *ĐČсіх* топіĐČ ĐżŃ€ĐŸĐłŃ€Đ°ĐŒ? ЧО ĐœĐ” ĐČĐ°Ń€Ń‚ĐŸ сĐČіту Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ Ń€ĐŸĐ·ĐłĐ»ŃĐœŃƒŃ‚Đž ĐŒĐŸĐ¶Đ»ĐžĐČість ĐČітато ĐœĐŸĐČі ĐŒĐ”Ń‚ĐŸĐŽĐž Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ? + +ĐĐ” Đ·Ń€ĐŸĐ·ŃƒĐŒŃ–ĐčтД ĐŒĐ”ĐœĐ” ĐœĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœĐŸ, у 2019 Ń€ĐŸŃ†Ń– ĐżŃ–Ń€Đ°ĐŒŃ–ĐŽĐ° Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ, TDD і ĐŒĐŸĐŽŃƒĐ»ŃŒĐœŃ– тДстО ĐČсД щД Đ·Đ°Đ»ĐžŃˆĐ°ŃŽŃ‚ŃŒŃŃ ĐżĐŸŃ‚ŃƒĐ¶ĐœĐŸŃŽ Ń‚Đ”Ń…ĐœŃ–ĐșĐŸŃŽ та, ĐŒĐ°Đ±ŃƒŃ‚ŃŒ, ĐœĐ°ĐčĐșращД ĐżŃ–ĐŽŃ…ĐŸĐŽŃŃ‚ŃŒ ĐŽĐ»Ń Đ±Đ°ĐłĐ°Ń‚ŃŒĐŸŃ… ĐżŃ€ĐŸĐłŃ€Đ°ĐŒ. ĐąŃ–Đ»ŃŒĐșĐž, яĐș і Đ±ŃƒĐŽŃŒ-яĐșа Ń–ĐœŃˆĐ° ĐŒĐŸĐŽĐ”Đ»ŃŒ, ĐżĐŸĐżŃ€Đž її ĐșĐŸŃ€ĐžŃĐœŃ–ŃŃ‚ŃŒ, [Ń–ĐœĐșĐŸĐ»Đž ĐČĐŸĐœĐ° ĐŒĐŸĐ¶Đ” ĐżĐŸĐŒĐžĐ»ŃŃ‚ĐžŃŃ](https://en.wikipedia.org/wiki/All_models_are_wrong). НапроĐșлаЎ, Ń€ĐŸĐ·ĐłĐ»ŃĐœĐ”ĐŒĐŸ IoT-ĐŽĐŸĐŽĐ°Ń‚ĐŸĐș, яĐșĐžĐč проĐčĐŒĐ°Ń” Đ±Đ°ĐłĐ°Ń‚ĐŸ ĐżĐŸĐŽŃ–Đč у ŃˆĐžĐœŃƒ ĐżĐŸĐČŃ–ĐŽĐŸĐŒĐ»Đ”ĐœŃŒ, яĐș-ĐŸŃ‚ Kafka/RabbitMQ, яĐșі ĐżĐŸŃ‚Ń–ĐŒ ĐœĐ°ĐŽŃ…ĐŸĐŽŃŃ‚ŃŒ у яĐșĐ”ŃŃŒ ŃŃ…ĐŸĐČОщД ĐŽĐ°ĐœĐžŃ… і ĐČ ĐșŃ–ĐœŃ†Đ”ĐČĐŸĐŒŃƒ ĐżŃ–ĐŽŃŃƒĐŒĐșу Đ·Đ°ĐżĐžŃ‚ŃƒŃŽŃ‚ŃŒŃŃ ĐŽĐ”ŃĐșĐžĐŒ Ń–ĐœŃ‚Đ”Ń€Ń„Đ”ĐčŃĐŸĐŒ ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČача Đ°ĐœĐ°Đ»Ń–Ń‚ĐžĐșĐž. ЧО спраĐČЮі ĐŒĐž ĐżĐŸĐČĐžĐœĐœŃ– ĐČотрачато 50% ĐœĐ°ŃˆĐŸĐłĐŸ Đ±ŃŽĐŽĐ¶Đ”Ń‚Ńƒ ĐœĐ° Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐœĐ° ĐœĐ°ĐżĐžŃĐ°ĐœĐœŃ ĐŒĐŸĐŽŃƒĐ»ŃŒĐœĐžŃ… тДстіĐČ ĐŽĐ»Ń ĐżŃ€ĐŸĐłŃ€Đ°ĐŒĐž, яĐșа ĐŸŃ€Ń–Ń”ĐœŃ‚ĐŸĐČĐ°ĐœĐ° ĐœĐ° Ń–ĐœŃ‚Đ”ĐłŃ€Đ°Ń†Ń–ŃŽ та ĐŒĐ°ĐčжД ĐœĐ” ĐŒŃ–ŃŃ‚ĐžŃ‚ŃŒ Đ»ĐŸĐłŃ–ĐșĐž? Зі Đ·Đ±Ń–Đ»ŃŒŃˆĐ”ĐœĐœŃĐŒ Ń€Ń–Đ·ĐœĐŸĐŒĐ°ĐœŃ–Ń‚ĐœĐŸŃŃ‚Ń– топіĐČ ĐŽĐŸĐŽĐ°Ń‚ĐșіĐČ (Đ±ĐŸŃ‚Ń–ĐČ, ĐșŃ€ĐžĐżŃ‚ĐŸ, ĐœĐ°ĐČĐžŃ‡ĐŸĐș Alexa) Đ·Ń€ĐŸŃŃ‚Đ°Ń” ĐčĐŒĐŸĐČŃ–Ń€ĐœŃ–ŃŃ‚ŃŒ Đ·ĐœĐ°Đčто ŃŃ†Đ”ĐœĐ°Ń€Ń–Ń—, ĐŽĐ” ĐżŃ–Ń€Đ°ĐŒŃ–ĐŽĐ° Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐœĐ” ĐœĐ°ĐčĐșращД ĐżŃ–ĐŽŃ…ĐŸĐŽĐžŃ‚ŃŒ. + +НастаĐČ Ń‡Đ°Ń збагатОтО ĐČашД ĐżĐŸŃ€Ń‚Ń„ĐŸĐ»Ń–ĐŸ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ та ĐŸĐ·ĐœĐ°ĐčĐŸĐŒĐžŃ‚ĐžŃŃ Đ· Đ±Ń–Đ»ŃŒŃˆĐŸŃŽ ĐșŃ–Đ»ŃŒĐșістю топіĐČ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ (ĐœĐ°ŃŃ‚ŃƒĐżĐœŃ– ĐżŃƒĐœĐșто ĐżŃ€ĐŸĐżĐŸĐœŃƒŃŽŃ‚ŃŒ ĐșŃ–Đ»ŃŒĐșа Ń–ĐŽĐ”Đč), ĐŒĐŸĐŽĐ”Đ»ŃĐŒĐž Ń€ĐŸĐ·ŃƒĐŒŃƒ, яĐș-ĐŸŃ‚ ĐżŃ–Ń€Đ°ĐŒŃ–ĐŽĐ° Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ, а таĐșĐŸĐ¶ зістаĐČото топо Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ Đ· Ń€Đ”Đ°Đ»ŃŒĐœĐžĐŒĐž ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐ°ĐŒĐž, Đ· яĐșĐžĐŒĐž ĐČĐž стоĐșĐ°Ń”Ń‚Đ”ŃŃŒ («ГДĐč, ĐœĐ°Ńˆ API Đ·Đ»Đ°ĐŒĐ°ĐœĐŸ, ĐœĐ°ĐżĐžŃˆŃ–ĐŒĐŸ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐșĐŸĐœŃ‚Ń€Đ°ĐșтіĐČ, ĐŸŃ€Ń–Ń”ĐœŃ‚ĐŸĐČĐ°ĐœĐ” ĐœĐ° ŃĐżĐŸĐ¶ĐžĐČача!'), ĐŽĐžĐČДрсОфіĐșуĐčтД сĐČĐŸŃ— тДстО, яĐș Ń–ĐœĐČĐ”ŃŃ‚ĐŸŃ€, яĐșĐžĐč стĐČĐŸŃ€ŃŽŃ” ĐżĐŸŃ€Ń‚Ń„Đ”Đ»ŃŒ ĐœĐ° ĐŸŃĐœĐŸĐČі Đ°ĐœĐ°Đ»Ń–Đ·Ńƒ рОзОĐșіĐČâ€Šâ€”â€ŠĐŸŃ†Ń–ĐœŃ–Ń‚ŃŒ, ĐŽĐ” ĐŒĐŸĐ¶ŃƒŃ‚ŃŒ ĐČĐžĐœĐžĐșĐœŃƒŃ‚Đž ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐž, і ĐżŃ–ĐŽĐ±Đ”Ń€Ń–Ń‚ŃŒ ĐŽĐ”ŃĐșі Đ·Đ°ĐżĐŸĐ±Ń–Đ¶ĐœŃ– Đ·Đ°Ń…ĐŸĐŽĐž ĐŽĐ»Ń ĐżĐŸĐŒÊŒŃĐșŃˆĐ”ĐœĐœŃ цох ĐżĐŸŃ‚Đ”ĐœŃ†Ń–ĐčĐœĐžŃ… рОзОĐșіĐČ + +Đ—Đ°ŃŃ‚Đ”Ń€Đ”Đ¶Đ”ĐœĐœŃ: Đ°Ń€ĐłŃƒĐŒĐ”ĐœŃ‚ TDD у сĐČіті ĐżŃ€ĐŸĐłŃ€Đ°ĐŒĐœĐŸĐłĐŸ Đ·Đ°Đ±Đ”Đ·ĐżĐ”Ń‡Đ”ĐœĐœŃ проĐčĐŒĐ°Ń” Ń‚ĐžĐżĐŸĐČĐ” ĐŸĐ±Đ»ĐžŃ‡Ń‡Ń Ń…ĐžĐ±ĐœĐŸŃ— ĐŽĐžŃ…ĐŸŃ‚ĐŸĐŒŃ–Ń—, ĐŽĐ”ŃĐșі ĐżŃ€ĐŸĐżĐŸĐČіЮують ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČуĐČато ĐčĐŸĐłĐŸ ĐČсюЮо, Ń–ĐœŃˆŃ– ĐČĐČĐ°Đ¶Đ°ŃŽŃ‚ŃŒ, Ń‰ĐŸ цД ЮояĐČĐŸĐ». ĐšĐŸĐ¶Đ”Đœ, Ń…Ń‚ĐŸ ĐłĐŸĐČĐŸŃ€ĐžŃ‚ŃŒ ĐČ Đ°Đ±ŃĐŸĐ»ŃŽŃ‚Ń–, ĐżĐŸĐŒĐžĐ»ŃŃ”Ń‚ŃŒŃŃ :] +
    + +❌ **Đ†ĐœĐ°ĐșшД:** Во ĐżŃ€ĐŸĐżŃƒŃŃ‚ĐžŃ‚Đ” ĐŽĐ”ŃĐșі Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Đž Đ· ĐœĐ”ĐčĐŒĐŸĐČŃ–Ń€ĐœĐŸŃŽ Ń€Đ”ĐœŃ‚Đ°Đ±Đ”Đ»ŃŒĐœŃ–ŃŃ‚ŃŽ Ń–ĐœĐČДстОціĐč, ĐŽĐ”ŃĐșі, яĐș-ĐŸŃ‚ Fuzz, lint і mutation, ĐŒĐŸĐ¶ŃƒŃ‚ŃŒ ĐœĐ°ĐŽĐ°Ń‚Đž Ń†Ń–ĐœĐœŃ–ŃŃ‚ŃŒ за 10 хĐČĐžĐ»ĐžĐœ + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :clap: ПроĐșлаЎ праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: Cindy Sridharan ĐżŃ€ĐŸĐżĐŸĐœŃƒŃ” багатД ĐżĐŸŃ€Ń‚Ń„ĐŸĐ»Ń–ĐŸ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ у сĐČĐŸŃ—Đč ĐŽĐžĐČĐŸĐČĐžĐ¶ĐœŃ–Đč ĐżŃƒĐ±Đ»Ń–Đșації Â«ĐąĐ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐŒŃ–ĐșŃ€ĐŸŃĐ”Ń€ĐČісіĐČ â€” таĐșĐžĐŒ жД Ń‡ĐžĐœĐŸĐŒÂ» + +![alt text](assets/bp-12-rich-testing.jpeg "Cindy Sridharan ĐżŃ€ĐŸĐżĐŸĐœŃƒŃ” багатД ĐżĐŸŃ€Ń‚Ń„ĐŸĐ»Ń–ĐŸ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ у сĐČĐŸŃ—Đč ĐŽĐžĐČĐŸĐČĐžĐ¶ĐœŃ–Đč ĐżŃƒĐ±Đ»Ń–Đșації Â«ĐąĐ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐŒŃ–ĐșŃ€ĐŸŃĐ”Ń€ĐČісіĐČ â€” таĐșĐžĐŒ жД Ń‡ĐžĐœĐŸĐŒÂ»") + +â˜șïžĐŸŃ€ĐžĐșлаЎ: [YouTube: “За ĐŒĐ”Đ¶Đ°ĐŒĐž ĐŒĐŸĐŽŃƒĐ»ŃŒĐœĐžŃ… тДстіĐČ: 5 блОсĐșучох топіĐČ Ń‚Đ”ŃŃ‚Ń–ĐČ Node.JS (2018)” (Yoni Goldberg)](https://www.youtube.com/watch?v=-2zP494wdUY&feature=youtu.be) + +
    + +![alt text](assets/bp-12-Yoni-Goldberg-Testing.jpeg "ĐĐ°Đ·ĐČа Ń‚Đ”ŃŃ‚Ńƒ, яĐșа сĐșĐ»Đ°ĐŽĐ°Ń”Ń‚ŃŒŃŃ Đ· 3 Ń‡Đ°ŃŃ‚ĐžĐœ") + +
    + +

    + +## âšȘ 2.2 ĐąĐ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Ń–ĐČ ĐŒĐŸĐ¶Đ” Đ±ŃƒŃ‚Đž ĐČĐ°ŃˆĐžĐŒ ĐœĐ°ĐčĐșŃ€Đ°Ń‰ĐžĐŒ Đ·Đ°Ń…ĐŸĐŽĐŸĐŒ + +:white_check_mark: **Đ ĐŸĐ±Đž:** ĐšĐŸĐ¶Đ”Đœ ĐŒĐŸĐŽŃƒĐ»ŃŒĐœĐžĐč тДст ĐŸŃ…ĐŸĐżĐ»ŃŽŃ” ĐœĐ”ĐČДлОĐșу Ń‡Đ°ŃŃ‚ĐžĐœŃƒ ĐżŃ€ĐŸĐłŃ€Đ°ĐŒĐž, і цД ĐŽĐŸŃ€ĐŸĐłĐŸ, Ń‰ĐŸĐ± ĐŸŃ…ĐŸĐżĐžŃ‚Đž Ń†Ń–Đ»Ńƒ, Ń‚ĐŸĐŽŃ– яĐș ĐœĐ°ŃĐșŃ€Ń–Đ·ĐœĐ”(end-to-end) Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ лДгĐșĐŸ ĐŸŃ…ĐŸĐżĐ»ŃŽŃ” ĐČДлОĐșу Ń‡Đ°ŃŃ‚ĐžĐœŃƒ, алД є ĐœĐ”ŃŃ‚Đ°Đ±Ń–Đ»ŃŒĐœĐžĐŒ і ĐżĐŸĐČŃ–Đ»ŃŒĐœĐžĐŒ, Ń‡ĐŸĐŒŃƒ б ĐœĐ” Đ·Đ°ŃŃ‚ĐŸŃŃƒĐČато Đ·Đ±Đ°Đ»Đ°ĐœŃĐŸĐČĐ°ĐœĐžĐč піЮхіЮ і ĐœĐ°ĐżĐžŃĐ°Ń‚Đž тДстО, Đ±Ń–Đ»ŃŒŃˆŃ– за ĐŒĐŸĐŽŃƒĐ»ŃŒĐœŃ– тДстО, алД ĐŒĐ”ĐœŃˆŃ–, ĐœŃ–Đ¶ ĐœĐ°ŃĐșŃ€Ń–Đ·ĐœĐ” Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ? ĐąĐ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Ń–ĐČ â€“ цД ĐœĐ”ĐŸŃĐżŃ–ĐČĐ°ĐœĐ° ĐżŃ–ŃĐœŃ сĐČіту Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃâ€Šâ€”â€ŠĐČĐŸĐœĐž Đ·Đ°Đ±Đ”Đ·ĐżĐ”Ń‡ŃƒŃŽŃ‚ŃŒ ĐœĐ°ĐčĐșращД Đ· ĐŸĐ±ĐŸŃ… сĐČітіĐČ: проĐčĐœŃŃ‚ĐœŃƒ ĐżŃ€ĐŸĐŽŃƒĐșтоĐČĐœŃ–ŃŃ‚ŃŒ і ĐŒĐŸĐ¶Đ»ĐžĐČість Đ·Đ°ŃŃ‚ĐŸŃĐŸĐČуĐČато ŃˆĐ°Đ±Đ»ĐŸĐœĐž TDD + Ń€Đ”Đ°Đ»Ń–ŃŃ‚ĐžŃ‡ĐœĐ” та ĐČДлОĐșĐ” ĐżĐŸĐșроття. + +йДстО ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Ń–ĐČ Đ·ĐŸŃĐ”Ń€Đ”ĐŽĐ¶ŃƒŃŽŃ‚ŃŒŃŃ ĐœĐ° Â«Đ±Đ»ĐŸĐșу» ĐŒŃ–ĐșŃ€ĐŸŃĐ”Ń€ĐČісу, ĐČĐŸĐœĐž працюють ĐżŃ€ĐŸŃ‚Đž API, ĐœĐ” ĐŒĐŸĐșають(mock) ĐœŃ–Ń‡ĐŸĐłĐŸ, Ń‰ĐŸ ĐœĐ°Đ»Đ”Đ¶ĐžŃ‚ŃŒ ŃĐ°ĐŒĐŸĐŒŃƒ ĐŒŃ–ĐșŃ€ĐŸŃĐ”Ń€ĐČісу (ĐœĐ°ĐżŃ€ĐžĐșлаЎ, спраĐČĐ¶ĐœŃŽ БД Đ°Đ±ĐŸ ĐżŃ€ĐžĐœĐ°ĐčĐŒĐœŃ– ĐČДрсію цієї БД у ĐżĐ°ĐŒâ€™ŃŃ‚Ń–), алД Đ·Đ°ĐłĐ»ŃƒŃˆŃƒŃŽŃ‚ŃŒ усД, Ń‰ĐŸ є Đ·ĐŸĐČĐœŃ–ŃˆĐœŃ–ĐŒ яĐș ĐČĐžĐșлОĐșĐž ĐŽĐŸ Ń–ĐœŃˆĐžŃ… ĐŒŃ–ĐșŃ€ĐŸŃĐ”Ń€ĐČісіĐČ. Đ ĐŸĐ±Đ»ŃŃ‡Đž цД, ĐŒĐž пДрДĐČŃ–Ń€ŃŃ”ĐŒĐŸ тД, Ń‰ĐŸ Ń€ĐŸĐ·ĐłĐŸŃ€Ń‚Đ°Ń”ĐŒĐŸ, ĐżŃ–ĐŽŃ…ĐŸĐŽĐžĐŒĐŸ ĐŽĐŸ ĐżŃ€ĐŸĐłŃ€Đ°ĐŒĐž Đ·Đ·ĐŸĐČĐœŃ– ĐČŃĐ”Ń€Đ”ĐŽĐžĐœŃƒ та ĐŸŃ‚Ń€ĐžĐŒŃƒŃ”ĐŒĐŸ ĐČДлОĐșу ĐČпДĐČĐœĐ”ĐœŃ–ŃŃ‚ŃŒ за проĐčĐœŃŃ‚ĐœĐžĐč ĐżŃ€ĐŸĐŒŃ–Đ¶ĐŸĐș часу. + +[ĐŁ ĐœĐ°Ń є ĐżĐŸĐČĐœĐžĐč ĐżĐŸŃŃ–Đ±ĐœĐžĐș, просĐČŃŃ‡Đ”ĐœĐžĐč ĐČĐžĐșĐ»ŃŽŃ‡ĐœĐŸ праĐČĐžĐ»ŃŒĐœĐŸĐŒŃƒ ĐœĐ°ĐżĐžŃĐ°ĐœĐœŃŽ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ĐœĐžŃ… тДстіĐČ](https://github.com/testjavascript/nodejs-integration-tests-best-practices) + +
    + +❌ **Đ†ĐœĐ°ĐșшД:** Во ĐŒĐŸĐ¶Đ”Ń‚Đ” ĐČотратото ĐŽĐŸĐČгі ĐŽĐœŃ– ĐœĐ° ĐœĐ°ĐżĐžŃĐ°ĐœĐœŃ ĐŒĐŸĐŽŃƒĐ»ŃŒĐœĐžŃ… тДстіĐČ, Ń‰ĐŸĐ± ĐŽŃ–Đ·ĐœĐ°Ń‚ĐžŃŃ, Ń‰ĐŸ ĐČĐž ĐŸŃ‚Ń€ĐžĐŒĐ°Đ»Đž лОшД 20% ĐżĐŸĐșроття ŃĐžŃŃ‚Đ”ĐŒĐž + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :clap: ПроĐșлаЎ праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: Supertest ĐŽĐŸĐ·ĐČĐŸĐ»ŃŃ” ĐżŃ–ĐŽŃ…ĐŸĐŽĐžŃ‚Đž ĐŽĐŸ Express API ĐČ ĐżŃ€ĐŸŃ†Đ”ŃŃ– (шĐČОЎĐșĐŸ та ĐŸŃ…ĐŸĐżĐ»ŃŽŃ” Đ±Đ°ĐłĐ°Ń‚ĐŸ ріĐČĐœŃ–ĐČ) + +![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "ПроĐșлаЎО Đ· Mocha") + +![alt text](assets/bp-13-component-test-yoni-goldberg.png " [Supertest](https://www.npmjs.com/package/supertest) ĐŽĐŸĐ·ĐČĐŸĐ»ŃŃ” ĐżŃ–ĐŽŃ…ĐŸĐŽĐžŃ‚Đž ĐŽĐŸ Express API ĐČ ĐżŃ€ĐŸŃ†Đ”ŃŃ– (шĐČОЎĐșĐŸ та ĐŸŃ…ĐŸĐżĐ»ŃŽŃ” Đ±Đ°ĐłĐ°Ń‚ĐŸ ріĐČĐœŃ–ĐČ)") + +
    + +

    + +## âšȘ 2.3 ĐŸĐ”Ń€Đ”ĐșĐŸĐœĐ°ĐčŃ‚Đ”ŃŃ, Ń‰ĐŸ ĐœĐŸĐČі рДлізО ĐœĐ” ĐżĐŸŃ€ŃƒŃˆŃƒŃŽŃ‚ŃŒ API, ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČуючо тДстО ĐșĐŸĐœŃ‚Ń€Đ°Đșту + +:white_check_mark: **Đ ĐŸĐ±Đž:** ОтжД, ĐČаш ĐŒŃ–ĐșŃ€ĐŸŃĐ”Ń€ĐČіс ĐŒĐ°Ń” ĐșŃ–Đ»ŃŒĐșа ĐșĐ»Ń–Ń”ĐœŃ‚Ń–ĐČ, і ĐČĐž запусĐșаєтД ĐșŃ–Đ»ŃŒĐșа ĐČДрсіĐč сДрĐČісу Đ· ĐŒŃ–Ń€ĐșуĐČĐ°ĐœŃŒ ŃŃƒĐŒŃ–ŃĐœĐŸŃŃ‚Ń– (Ń‰ĐŸĐ± усі булО Đ·Đ°ĐŽĐŸĐČĐŸĐ»Đ”ĐœŃ–). ĐŸĐŸŃ‚Ń–ĐŒ ĐČĐž Đ·ĐŒŃ–ĐœŃŽŃ”Ń‚Đ” яĐșĐ”ŃŃŒ ĐżĐŸĐ»Đ” і Â«Đ±ŃƒĐŒ!», яĐșĐžĐčсь ĐČажлОĐČĐžĐč ĐșĐ»Ń–Ń”ĐœŃ‚, яĐșĐžĐč ĐżĐŸĐșĐ»Đ°ĐŽĐ°Ń”Ń‚ŃŒŃŃ ĐœĐ° цД ĐżĐŸĐ»Đ”, ŃĐ”Ń€ĐŽĐžŃ‚ŃŒŃŃ. ĐŠĐ” 22-Đč піЮступ сĐČіту Ń–ĐœŃ‚Đ”ĐłŃ€Đ°Ń†Ń–Ń—: сДрĐČĐ”Ń€ĐœŃ–Đč ŃŃ‚ĐŸŃ€ĐŸĐœŃ– ЎужД сĐșĐ»Đ°ĐŽĐœĐŸ ĐČрахуĐČато ĐČсі Ń‡ĐžŃĐ»Đ”ĐœĐœŃ– ĐŸŃ‡Ń–ĐșуĐČĐ°ĐœĐœŃ ĐșĐ»Ń–Ń”ĐœŃ‚Ń–ĐČ — З Ń–ĐœŃˆĐŸĐłĐŸ Đ±ĐŸĐșу, ĐșĐ»Ń–Ń”ĐœŃ‚Đž ĐœĐ” ĐŒĐŸĐ¶ŃƒŃ‚ŃŒ ĐżŃ€ĐŸĐČĐŸĐŽĐžŃ‚Đž Đ¶ĐŸĐŽĐœĐŸĐłĐŸ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ, ĐŸŃĐșŃ–Đ»ŃŒĐșĐž сДрĐČДр ĐșĐŸĐœŃ‚Ń€ĐŸĐ»ŃŽŃ” Юато ĐČОпусĐșу. Đ†ŃĐœŃƒŃ” цілОĐč спДĐșтр ĐŒĐ”Ń‚ĐŸĐŽŃ–ĐČ, яĐșі ĐŒĐŸĐ¶ŃƒŃ‚ŃŒ ĐżĐŸĐŒÊŒŃĐșшото ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒŃƒ ĐșĐŸĐœŃ‚Ń€Đ°Đșту, ĐŽĐ”ŃĐșі Đ· ĐœĐžŃ… ĐżŃ€ĐŸŃŃ‚Ń–, Ń–ĐœŃˆŃ– є Đ±Đ°ĐłĐ°Ń‚ŃˆĐžĐŒĐž ĐœĐ° Ń„ŃƒĐœĐșції та ĐČĐžĐŒĐ°ĐłĐ°ŃŽŃ‚ŃŒ ĐșŃ€ŃƒŃ‚Ń–ŃˆĐŸŃ— ĐșроĐČĐŸŃ— ĐœĐ°ĐČŃ‡Đ°ĐœĐœŃ. ĐŁ ĐżŃ€ĐŸŃŃ‚ĐŸĐŒŃƒ та рДĐșĐŸĐŒĐ”ĐœĐŽĐŸĐČĐ°ĐœĐŸĐŒŃƒ ĐżŃ–ĐŽŃ…ĐŸĐŽŃ– ĐżĐŸŃŃ‚Đ°Ń‡Đ°Đ»ŃŒĐœĐžĐș API ĐżŃƒĐ±Đ»Ń–Đșує паĐșДт npm Ń–Đ· Ń‚ĐžĐżĐŸĐŒ API (ĐœĐ°ĐżŃ€ĐžĐșлаЎ, JSDoc, TypeScript). ĐąĐŸĐŽŃ– ŃĐżĐŸĐ¶ĐžĐČачі Đ·ĐŒĐŸĐ¶ŃƒŃ‚ŃŒ ĐŸŃ‚Ń€ĐžĐŒĐ°Ń‚Đž цю Đ±Ń–Đ±Đ»Ń–ĐŸŃ‚Đ”Đșу та ĐŸŃ‚Ń€ĐžĐŒĐ°Ń‚Đž ĐČĐžĐłĐŸĐŽŃƒ ĐČіЮ Đ°ĐœĐ°Đ»Ń–Đ·Ńƒ часу ĐșĐŸĐŽŃƒĐČĐ°ĐœĐœŃ та пДрДĐČірĐșĐž. ВОгаЎлОĐČішоĐč піЮхіЮ — ĐČĐžĐșĐŸŃ€ĐžŃŃ‚Đ°ĐœĐœŃ [PACT](https://docs.pact.io/), стĐČĐŸŃ€Đ”ĐœĐŸĐłĐŸ ĐŽĐ»Ń Ń„ĐŸŃ€ĐŒĐ°Đ»Ń–Đ·Đ°Ń†Ń–Ń— Ń†ŃŒĐŸĐłĐŸ ĐżŃ€ĐŸŃ†Đ”ŃŃƒ за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ ЎужД руĐčĐœŃ–ĐČĐœĐŸĐłĐŸ ĐżŃ–ĐŽŃ…ĐŸĐŽŃƒâ€Šâ€”Â«ĐœĐ” сДрĐČДр ŃĐ°ĐŒ ĐČĐžĐ·ĐœĐ°Ń‡Đ°Ń” ĐżĐ»Đ°Đœ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ, а ĐșĐ»Ń–Ń”ĐœŃ‚ ĐČĐžĐ·ĐœĐ°Ń‡Đ°Ń” тДстО. PACT ĐŒĐŸĐ¶Đ” запОсуĐČато ĐŸŃ‡Ń–ĐșуĐČĐ°ĐœĐœŃ ĐșĐ»Ń–Ń”ĐœŃ‚Đ° та Ń€ĐŸĐ·ĐŒŃ–Ń‰ŃƒĐČато ĐČ ŃĐżŃ–Đ»ŃŒĐœĐŸĐŒŃƒ ĐŒŃ–ŃŃ†Ń–, Â«ĐżĐŸŃĐ”Ń€Đ”ĐŽĐœĐžĐșу», Ń‰ĐŸĐ± сДрĐČДр ĐŒŃ–Đł ĐŸŃ‚Ń€ĐžĐŒĐ°Ń‚Đž ĐŸŃ‡Ń–ĐșуĐČĐ°ĐœĐœŃ та запусĐșато ĐșĐŸĐ¶ĐœŃƒ збірĐșу за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ Đ±Ń–Đ±Đ»Ń–ĐŸŃ‚Đ”ĐșĐž PACT ĐŽĐ»Ń ĐČояĐČĐ»Đ”ĐœĐœŃ ĐżĐŸŃ€ŃƒŃˆĐ”ĐœĐžŃ… ĐșĐŸĐœŃ‚Ń€Đ°ĐșтіĐČâ€Šâ€”â€ŠĐŸŃ‡Ń–ĐșуĐČĐ°ĐœĐœŃ ĐșĐ»Ń–Ń”ĐœŃ‚Đ°, яĐșĐ” ĐœĐ” ĐČŃ–ĐŽĐżĐŸĐČіЮають. ЗаĐČЮяĐșĐž Ń†ŃŒĐŸĐŒŃƒ ĐČсі ĐœĐ”ĐČŃ–ĐŽĐżĐŸĐČŃ–ĐŽĐœĐŸŃŃ‚Ń– API сДрĐČДра та ĐșĐ»Ń–Ń”ĐœŃ‚Đ° ĐČояĐČĐ»ŃŃŽŃ‚ŃŒŃŃ ĐœĐ° Ń€Đ°ĐœĐœŃ–Đč стаЮії Đ·Đ±ĐžŃ€Đ°ĐœĐœŃ/CI, Ń‰ĐŸ ĐŒĐŸĐ¶Đ” ĐżĐŸĐ·Đ±Đ°ĐČото ĐČас ĐČіЮ Ń€ĐŸĐ·Ń‡Đ°Ń€ŃƒĐČĐ°ĐœĐœŃ +
    + +❌ **Đ†ĐœĐ°ĐșшД:** ĐĐ»ŃŒŃ‚Đ”Ń€ĐœĐ°Ń‚ĐžĐČĐ°ĐŒĐž є ĐČĐžŃĐœĐ°Đ¶Đ»ĐžĐČĐ” Ń€ŃƒŃ‡ĐœĐ” Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ Đ°Đ±ĐŸ страх Ń€ĐŸĐ·ĐłĐŸŃ€Ń‚Đ°ĐœĐœŃ + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :clap: ПроĐșлаЎ праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: + +![](https://img.shields.io/badge/🔧%20Example%20using%20PACT-blue.svg "ПроĐșлаЎ Đ· PACT") + +![alt text](assets/bp-14-testing-best-practices-contract-flow.png) + +
    + +

    + +## âšȘ  2.4 ĐąĐ”ŃŃ‚ŃƒĐčтД ĐŒŃ–ĐŽĐ»ĐČаро(middlewares) ĐŸĐșŃ€Đ”ĐŒĐŸ + +:white_check_mark: **Đ ĐŸĐ±Đž:** Đ‘Đ°ĐłĐ°Ń‚ĐŸ Ń…Ń‚ĐŸ ŃƒĐœĐžĐșає ĐŒŃ–ĐŽĐ»ĐČаро, ĐŸŃĐșŃ–Đ»ŃŒĐșĐž ĐČĐŸĐœĐŸ прДЎстаĐČĐ»ŃŃ” ĐœĐ”ĐČДлОĐșу Ń‡Đ°ŃŃ‚ĐžĐœŃƒ ŃĐžŃŃ‚Đ”ĐŒĐž та ĐżĐŸŃ‚Ń€Đ”Đ±ŃƒŃ” аĐșтоĐČĐœĐŸĐłĐŸ сДрĐČДра Express. ОбОЎĐČі ĐżŃ€ĐžŃ‡ĐžĐœĐž ĐœĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœŃ–â€Šâ€”â€ŠĐŸŃ€ĐŸĐŒŃ–Đ¶ĐœŃ– ĐżŃ€ĐŸĐłŃ€Đ°ĐŒĐž ĐœĐ”ĐČДлОĐșі, алД ĐČплОĐČають ĐœĐ° ĐČсі Đ°Đ±ĐŸ Đ±Ń–Đ»ŃŒŃˆŃ–ŃŃ‚ŃŒ запОтіĐČ Ń– ĐŒĐŸĐ¶ŃƒŃ‚ŃŒ Đ±ŃƒŃ‚Đž лДгĐșĐŸ пДрДĐČŃ–Ń€Đ”ĐœŃ– яĐș чості Ń„ŃƒĐœĐșції, яĐșі ĐŸŃ‚Ń€ĐžĐŒŃƒŃŽŃ‚ŃŒ {req,res} ĐŸĐ±ÊŒŃ”Đșто JS. Đ©ĐŸĐ± пДрДĐČірото Ń„ŃƒĐœĐșцію ĐŒŃ–ĐŽĐ»ĐČаро, ĐżĐŸŃ‚Ń€Ń–Đ±ĐœĐŸ ĐżŃ€ĐŸŃŃ‚ĐŸ ĐČĐžĐșлОĐșато її та пДрДĐČірото ([ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČуючо, ĐœĐ°ĐżŃ€ĐžĐșлаЎ, Sinon](https://www.npmjs.com/package/sinon)) ĐČĐ·Đ°Ń”ĐŒĐŸĐŽŃ–ŃŽ Đ· ĐŸĐ±â€™Ń”ĐșŃ‚Đ°ĐŒĐž {req,res}, Ń‰ĐŸĐ± пДрДĐșĐŸĐœĐ°Ń‚ĐžŃŃ, Ń‰ĐŸ Ń„ŃƒĐœĐșція ĐČĐžĐșĐŸĐœĐ°Đ»Đ° праĐČĐžĐ»ŃŒĐœŃƒ Юію. Đ‘Ń–Đ±Đ»Ń–ĐŸŃ‚Đ”Đșа [node-mock-http](https://www.npmjs.com/package/node-mocks-http) ĐčĐŽĐ” щД Ўалі Đč ĐČŃ€Đ°Ń…ĐŸĐČує ĐŸĐ±â€™Ń”Đșто {req,res} Ń€Đ°Đ·ĐŸĐŒ Đ·Ń– ŃĐżĐŸŃŃ‚Đ”Ń€Đ”Đ¶Đ”ĐœĐœŃĐŒ за Ń—Ń…ĐœŃŒĐŸŃŽ ĐżĐŸĐČĐ”ĐŽŃ–ĐœĐșĐŸŃŽ. НапроĐșлаЎ, ĐČŃ–Đœ ĐŒĐŸĐ¶Đ” стĐČĐ”Ń€ĐŽĐ¶ŃƒĐČато, чо статус http, ĐČŃŃ‚Đ°ĐœĐŸĐČĐ»Đ”ĐœĐžĐč ĐŽĐ»Ń ĐŸĐ±â€™Ń”Đșта res, ĐČŃ–ĐŽĐżĐŸĐČіЮає ĐŸŃ‡Ń–ĐșуĐČĐ°ĐœĐœŃĐŒ (ĐŽĐžĐČ. проĐșлаЎ ĐœĐžĐ¶Ń‡Đ”) +
    + +❌ **Đ†ĐœĐ°ĐșшД:** ĐŸĐŸĐŒĐžĐ»Đșа ĐČ ĐŒŃ–ĐŽĐ»ĐČарі Express === ĐżĐŸĐŒĐžĐ»Đșа ĐČ ŃƒŃŃ–Ń… Đ°Đ±ĐŸ Đ±Ń–Đ»ŃŒŃˆĐŸŃŃ‚Ń– запОтіĐČ + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :clap:ПроĐșлаЎ праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: ĐąĐ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐŒŃ–ĐŽĐ»ĐČаро ĐČ Ń–Đ·ĐŸĐ»ŃŃ†Ń–Ń— бДз Đ·ĐŽŃ–ĐčŃĐœĐ”ĐœĐœŃ ĐŒĐ”Ń€Đ”Đ¶Đ”ĐČох ĐČĐžĐșлОĐșіĐČ Ń– ĐżŃ€ĐŸĐ±ŃƒĐŽĐ¶Đ”ĐœĐœŃ ĐČсієї ĐŒĐ°ŃˆĐžĐœĐž Express + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "ПроĐșлаЎО Đ· Jest") + +```javascript +//the middleware we want to test +const unitUnderTest = require("./middleware"); +const httpMocks = require("node-mocks-http"); +// ĐĄĐžĐœŃ‚Đ°ĐșсОс Jest, Đ”ĐșĐČіĐČĐ°Đ»Đ”ĐœŃ‚ĐœĐžĐč describe() і it() у Mocha +test("A request without authentication header, should return http status 403", () => { + const request = httpMocks.createRequest({ + method: "GET", + url: "/user/42", + headers: { + authentication: "" + } + }); + const response = httpMocks.createResponse(); + unitUnderTest(request, response); + expect(response.statusCode).toBe(403); +}); +``` + +
    + +

    + +## âšȘ 2.5 Đ’ĐžĐŒŃ–Ń€ŃŽĐČĐ°ĐœĐœŃ та рД фаĐșŃ‚ĐŸŃ€ĐžĐœĐł за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Ń–ĐČ ŃŃ‚Đ°Ń‚ĐžŃ‡ĐœĐŸĐłĐŸ Đ°ĐœĐ°Đ»Ń–Đ·Ńƒ + +:white_check_mark: **Đ ĐŸĐ±Đž:** ВоĐșĐŸŃ€ĐžŃŃ‚Đ°ĐœĐœŃ Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Ń–ĐČ ŃŃ‚Đ°Ń‚ĐžŃ‡ĐœĐŸĐłĐŸ Đ°ĐœĐ°Đ»Ń–Đ·Ńƒ ĐŽĐŸĐżĐŸĐŒĐ°ĐłĐ°Ń”, ĐœĐ°ĐŽĐ°ŃŽŃ‡Đž ĐŸĐ±â€™Ń”ĐșтоĐČĐœŃ– ŃĐżĐŸŃĐŸĐ±Đž ĐżĐŸĐșращото яĐșість ĐșĐŸĐŽŃƒ та збДрДгтО ĐČаш ĐșĐŸĐŽ ĐżŃ€ĐžĐŽĐ°Ń‚ĐœĐžĐŒ ĐŽĐ»Ń ĐŸĐ±ŃĐ»ŃƒĐłĐŸĐČуĐČĐ°ĐœĐœŃ. Во ĐŒĐŸĐ¶Đ”Ń‚Đ” ĐŽĐŸĐŽĐ°Ń‚Đž Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Đž ŃŃ‚Đ°Ń‚ĐžŃ‡ĐœĐŸĐłĐŸ Đ°ĐœĐ°Đ»Ń–Đ·Ńƒ ĐŽĐŸ збірĐșĐž CI, Ń‰ĐŸĐ± пДрДрĐČато її, ĐșĐŸĐ»Đž ĐČĐŸĐœĐ° ĐČояĐČоть запах ĐșĐŸĐŽŃƒ. Đ™ĐŸĐłĐŸ ĐłĐŸĐ»ĐŸĐČĐœŃ– пДрДĐČагО ĐœĐ°ĐŽ Đ·ĐČочаĐčĐœĐžĐŒ Đ»Ń–ĐœŃ‚ŃƒĐČĐ°ĐœĐœŃĐŒ — цД ĐŒĐŸĐ¶Đ»ĐžĐČість пДрДĐČірято яĐșість у ĐșĐŸĐœŃ‚Đ”Đșсті ĐșŃ–Đ»ŃŒĐșĐŸŃ… фаĐčліĐČ (ĐœĐ°ĐżŃ€ĐžĐșлаЎ, ĐČояĐČĐ»ŃŃ‚Đž ĐŽŃƒĐ±Đ»Ń–Đșато), ĐČĐžĐșĐŸĐœŃƒĐČато Ń€ĐŸĐ·ŃˆĐžŃ€Đ”ĐœĐžĐč Đ°ĐœĐ°Đ»Ń–Đ· (ĐœĐ°ĐżŃ€ĐžĐșлаЎ, сĐșĐ»Đ°ĐŽĐœĐŸŃŃ‚Ń– ĐșĐŸĐŽŃƒ) і стДжОтО за Ń–ŃŃ‚ĐŸŃ€Ń–Ń”ŃŽ та ĐżŃ€ĐŸĐłŃ€Đ”ŃĐŸĐŒ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒ Ń–Đ· ĐșĐŸĐŽĐŸĐŒ. ДĐČа проĐșлаЎО Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Ń–ĐČ, яĐșі ĐČĐž ĐŒĐŸĐ¶Đ”Ń‚Đ” ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČуĐČато, цД [SonarQube](https://www.sonarqube.org/) (4900+ [Đ·Ń–Ń€ĐŸŃ‡ĐŸĐș](https://github.com/SonarSource/sonarqube)) і [Code Climate](https ://codeclimate.com/) (2000+ [Đ·Ń–Ń€ĐŸŃ‡ĐŸĐș](https://github.com/codeclimate/codeclimate)) + +Credit: [Keith Holliday](https://github.com/TheHollidayInn) + +
    + +❌ **Đ†ĐœĐ°ĐșшД:** З ĐœĐžĐ·ŃŒĐșĐŸŃŽ яĐșістю ĐșĐŸĐŽŃƒ ĐżĐŸĐŒĐžĐ»ĐșĐž та ĐżŃ€ĐŸĐŽŃƒĐșтоĐČĐœŃ–ŃŃ‚ŃŒ заĐČжЎО Đ±ŃƒĐŽŃƒŃ‚ŃŒ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐŸŃŽ, яĐșу ĐœĐ” Đ·ĐŒĐŸĐ¶Đ” ĐČорішото Đ¶ĐŸĐŽĐœĐ° блОсĐșуча ĐœĐŸĐČа Đ±Ń–Đ±Đ»Ń–ĐŸŃ‚Đ”Đșа чо ŃŃƒŃ‡Đ°ŃĐœŃ– Ń„ŃƒĐœĐșції + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :clap: ПроĐșлаЎ праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: CodeClimate, ĐșĐŸĐŒĐ”Ń€Ń†Ń–ĐčĐœĐžĐč Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚, яĐșĐžĐč ĐŒĐŸĐ¶Đ” Ń–ĐŽĐ”ĐœŃ‚ĐžŃ„Ń–ĐșуĐČато сĐșĐ»Đ°ĐŽĐœŃ– ĐŒĐ”Ń‚ĐŸĐŽĐž: + +![](https://img.shields.io/badge/🔧%20Example%20using%20Code%20Climate-blue.svg "ПроĐșлаЎ Đ· CodeClimate") + +![alt text](assets/bp-16-yoni-goldberg-quality.png "CodeClimate, ĐșĐŸĐŒĐ”Ń€Ń†Ń–ĐčĐœĐžĐč Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚, яĐșĐžĐč ĐŒĐŸĐ¶Đ” Ń–ĐŽĐ”ĐœŃ‚ĐžŃ„Ń–ĐșуĐČато сĐșĐ»Đ°ĐŽĐœŃ– ĐŒĐ”Ń‚ĐŸĐŽĐž:") + +
    + +

    + +## âšȘ  2.6 ĐŸĐ”Ń€Đ”ĐČіртД сĐČĐŸŃŽ ĐłĐŸŃ‚ĐŸĐČĐœŃ–ŃŃ‚ŃŒ ĐŽĐŸ Ń…Đ°ĐŸŃŃƒ, ĐżĐŸĐČâ€™ŃĐ·Đ°ĐœĐŸĐłĐŸ Đ· Node + +:white_check_mark: **Đ ĐŸĐ±Đž:** ДоĐČĐœĐŸ, алД Đ±Ń–Đ»ŃŒŃˆŃ–ŃŃ‚ŃŒ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœŃŒ ĐżŃ€ĐŸĐłŃ€Đ°ĐŒĐœĐŸĐłĐŸ Đ·Đ°Đ±Đ”Đ·ĐżĐ”Ń‡Đ”ĐœĐœŃ ŃŃ‚ĐŸŃŃƒŃŽŃ‚ŃŒŃŃ лОшД Đ»ĐŸĐłŃ–ĐșĐž та ĐŽĐ°ĐœĐžŃ…, алД ĐŽĐ”ŃĐșі Đ· ĐœĐ°Đčгіршох рДчДĐč, яĐșі Ń‚Ń€Đ°ĐżĐ»ŃŃŽŃ‚ŃŒŃŃ (і їх спраĐČЮі ĐČажĐșĐŸ ĐŒŃ–ĐłŃ€ŃƒĐČато), — цД ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐž Đ· Ń–ĐœŃ„Ń€Đ°ŃŃ‚Ń€ŃƒĐșŃ‚ŃƒŃ€ĐŸŃŽ. НапроĐșлаЎ, ĐČĐž ĐșĐŸĐ»Đž-ĐœĐ”Đ±ŃƒĐŽŃŒ пДрДĐČŃ–Ń€ŃĐ»Đž, Ń‰ĐŸ ĐČŃ–ĐŽĐ±ŃƒĐČається, ĐșĐŸĐ»Đž ĐżĐ°ĐŒâ€™ŃŃ‚ŃŒ ĐČĐ°ŃˆĐŸĐłĐŸ ĐżŃ€ĐŸŃ†Đ”ŃŃƒ пДрДĐČĐ°ĐœŃ‚Đ°Đ¶ŃƒŃ”Ń‚ŃŒŃŃ, Đ°Đ±ĐŸ ĐșĐŸĐ»Đž сДрĐČДр/ĐżŃ€ĐŸŃ†Đ”Ń ĐČĐŒĐžŃ€Đ°Ń”, чо ĐČаша ŃĐžŃŃ‚Đ”ĐŒĐ° ĐŒĐŸĐœŃ–Ń‚ĐŸŃ€ĐžĐœĐłŃƒ Ń€ĐŸĐ·ŃƒĐŒŃ–Ń”, ĐșĐŸĐ»Đž API стає ĐœĐ° 50% ĐżĐŸĐČŃ–Đ»ŃŒĐœŃ–ŃˆĐžĐŒ?. Đ©ĐŸĐ± пДрДĐČірото та ĐżĐŸŃ„Ń–Đșсото таĐșі ĐżĐŸĐłĐ°ĐœŃ– рДчі — [Đ†ĐœĐ¶Đ”ĐœĐ”Ń€Ń–Ń Ń…Đ°ĐŸŃŃƒ](https://principlesofchaos.org/ua/) стĐČĐŸŃ€Đ”ĐœĐ° Netflix. Đ’Ń–Đœ ĐŒĐ°Ń” ĐœĐ° ĐŒĐ”Ń‚Ń– забДзпДчОтО ĐŸĐ±Ń–Đ·ĐœĐ°ĐœŃ–ŃŃ‚ŃŒ, струĐșтуру та Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Đž ĐŽĐ»Ń пДрДĐČірĐșĐž стіĐčĐșĐŸŃŃ‚Ń– ĐœĐ°ŃˆĐŸŃ— ĐżŃ€ĐŸĐłŃ€Đ°ĐŒĐž ĐŽĐŸ Ń…Đ°ĐŸŃ‚ĐžŃ‡ĐœĐžŃ… ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒ. НапроĐșлаЎ, ĐŸĐŽĐžĐœ Ń–Đ· ĐčĐŸĐłĐŸ ĐČŃ–ĐŽĐŸĐŒĐžŃ… Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Ń–ĐČ, [ĐŒĐ°ĐČпа Ń…Đ°ĐŸŃŃƒ](https://github.com/Netflix/chaosmonkey), ĐČОпаЎĐșĐŸĐČĐŸ ĐČĐžĐŒĐžĐșає сДрĐČДрО, Ń‰ĐŸĐ± ĐłĐ°Ń€Đ°ĐœŃ‚ŃƒĐČато, Ń‰ĐŸ ĐœĐ°Ńˆ сДрĐČіс усД щД ĐŒĐŸĐ¶Đ” ĐŸĐ±ŃĐ»ŃƒĐłĐŸĐČуĐČато ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČачіĐČ Ń– ĐœĐ” ĐżĐŸĐșĐ»Đ°ĐŽĐ°Ń‚ĐžŃŃ ĐœĐ° ĐŸĐŽĐžĐœ сДрĐČДр (є таĐșĐŸĐ¶ ĐČĐ”Ń€ŃŃ–Ń Kubernetes, [kube-monkey](https://github.com/asobti/kube-monkey), яĐșа ĐČбОĐČає ĐŒĐŸĐŽŃƒĐ»Ń–). Усі ці Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Đž працюють ĐœĐ° ріĐČĐœŃ– Ń…ĐŸŃŃ‚ĐžĐœĐłŃƒ/ĐżĐ»Đ°Ń‚Ń„ĐŸŃ€ĐŒĐž, алД Ń‰ĐŸ, яĐșŃ‰ĐŸ ĐČĐž Ń…ĐŸŃ‡Đ”Ń‚Đ” пДрДĐČірото та стĐČĐŸŃ€ĐžŃ‚Đž чостоĐč Ń…Đ°ĐŸŃ Node, ĐœĐ°ĐżŃ€ĐžĐșлаЎ пДрДĐČірото, яĐș ĐČаш ĐżŃ€ĐŸŃ†Đ”Ń Node спраĐČĐ»ŃŃ”Ń‚ŃŒŃŃ Đ· ĐœĐ”ĐżĐ”Ń€Đ”Ń…ĐŸĐżĐ»Đ”ĐœĐžĐŒĐž ĐżĐŸĐŒĐžĐ»ĐșĐ°ĐŒĐž, ĐœĐ”ĐŸĐ±Ń€ĐŸĐ±Đ»Đ”ĐœĐžĐŒ ĐČŃ–ĐŽŃ…ĐžĐ»Đ”ĐœĐœŃĐŒ ĐŸĐ±Ń–Ń†ŃĐœĐŸĐș, пДрДĐČĐ°ĐœŃ‚Đ°Đ¶Đ”ĐœĐœŃĐŒ ĐżĐ°ĐŒâ€™ŃŃ‚Ń– v8 Ń–Đ· ĐŒĐ°ĐșŃĐžĐŒĐ°Đ»ŃŒĐœĐŸ ĐŽĐŸĐżŃƒŃŃ‚ĐžĐŒĐžĐŒ 1,7 ГБ чо чо ĐČаш UX Đ·Đ°Đ»ĐžŃˆĐ°Ń”Ń‚ŃŒŃŃ Đ·Đ°ĐŽĐŸĐČŃ–Đ»ŃŒĐœĐžĐŒ, ĐșĐŸĐ»Đž цоĐșĐ» ĐżĐŸĐŽŃ–Đč Ń‡Đ°ŃŃ‚ĐŸ Đ±Đ»ĐŸĐșується? Ń‰ĐŸĐ± ĐČорішото цю ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒŃƒ, я ĐœĐ°ĐżĐžŃĐ°ĐČ [node-chaos](https://github.com/i0natan/node-chaos-monkey) (Đ°Đ»ŃŒŃ„Đ°), яĐșĐžĐč ĐœĐ°ĐŽĐ°Ń” ĐČŃŃ–Đ»ŃĐșі Ń…Đ°ĐŸŃ‚ĐžŃ‡ĐœŃ– Юії, ĐżĐŸĐČâ€™ŃĐ·Đ°ĐœŃ– Đ· Node +
    + +❌ **Đ†ĐœĐ°ĐșшД:** бут ĐœĐ”ĐŒĐ°Ń” ĐČĐžŃ…ĐŸĐŽŃƒ, заĐșĐŸĐœ ĐœĐ”Ń€Ń„Ń– ĐČЮароть ĐżĐŸ ĐČĐ°ŃˆĐŸĐŒŃƒ ĐČĐžŃ€ĐŸĐ±ĐœĐžŃ†Ń‚ĐČу бДз жалю + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :clap: ПроĐșлаЎ праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: : Node-chaos ĐŒĐŸĐ¶Đ” ĐłĐ”ĐœĐ”Ń€ŃƒĐČато Ń€Ń–Đ·ĐœĐŸĐŒĐ°ĐœŃ–Ń‚ĐœŃ– Ń€ĐŸĐ·Ń–ĐłŃ€Đ°ŃˆŃ– Node.js, Ń‰ĐŸĐ± ĐČĐž ĐŒĐŸĐłĐ»Đž пДрДĐČірото, ĐœĐ°ŃĐșŃ–Đ»ŃŒĐșĐž ĐČаша ĐżŃ€ĐŸĐłŃ€Đ°ĐŒĐ° стіĐčĐșа ĐŽĐŸ Ń…Đ°ĐŸŃŃƒ + +![alt text](assets/bp-17-yoni-goldberg-chaos-monkey-nodejs.png "Node-chaos ĐŒĐŸĐ¶Đ” ĐłĐ”ĐœĐ”Ń€ŃƒĐČато Ń€Ń–Đ·ĐœĐŸĐŒĐ°ĐœŃ–Ń‚ĐœŃ– Ń€ĐŸĐ·Ń–ĐłŃ€Đ°ŃˆŃ– Node.js, Ń‰ĐŸĐ± ĐČĐž ĐŒĐŸĐłĐ»Đž пДрДĐČірото, ĐœĐ°ŃĐșŃ–Đ»ŃŒĐșĐž ĐČаша ĐżŃ€ĐŸĐłŃ€Đ°ĐŒĐ° стіĐčĐșа ĐŽĐŸ Ń…Đ°ĐŸŃŃƒ") + +
    + +
    + +## âšȘ 2.7 ĐŁĐœĐžĐșаĐčтД ĐłĐ»ĐŸĐ±Đ°Đ»ŃŒĐœĐžŃ… фіĐșстур сіЮіĐČ, ĐŽĐŸĐŽĐ°ĐČаĐčтД ĐŽĐ°ĐœĐœŃ– ĐŽĐ»Ń ĐșĐŸĐ¶ĐœĐŸĐłĐŸ Ń‚Đ”ŃŃ‚Ńƒ + +:white_check_mark: **Đ ĐŸĐ±Đž:** Đ”ĐŸŃ‚Ń€ĐžĐŒŃƒŃŽŃ‡ĐžŃŃŒ Đ·ĐŸĐ»ĐŸŃ‚ĐŸĐłĐŸ праĐČОла (Đ ĐŸĐ·ĐŽŃ–Đ» 0), ĐșĐŸĐ¶Đ”Đœ тДст ĐżĐŸĐČĐžĐœĐ”Đœ ĐŽĐŸĐŽĐ°ĐČато ĐČĐ»Đ°ŃĐœĐžĐč ĐœĐ°Đ±Ń–Ń€ ряЮĐșіĐČ Đ‘Đ” і Юіято ĐœĐ° ĐœĐžŃ…, Ń‰ĐŸĐ± Đ·Đ°ĐżĐŸĐ±Ń–ĐłŃ‚Đž Đ·Ń‡Đ”ĐżĐ»Đ”ĐœĐœŃŽ та лДгĐșĐŸ ĐŸĐ±Ò‘Ń€ŃƒĐœŃ‚ŃƒĐČато ĐżĐŸŃ‚Ń–Đș Ń‚Đ”ŃŃ‚Ńƒ. НаспраĐČЮі цД Ń‡Đ°ŃŃ‚ĐŸ ĐżĐŸŃ€ŃƒŃˆŃƒŃ”Ń‚ŃŒŃŃ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°Đ»ŃŒĐœĐžĐșĐ°ĐŒĐž, яĐșі Đ·Đ°ĐżĐŸĐČĐœŃŽŃŽŃ‚ŃŒ БД ĐŽĐ°ĐœĐžĐŒĐž пДрДЎ запусĐșĐŸĐŒ тДстіĐČ (таĐșĐŸĐ¶ ĐČŃ–ĐŽĐŸĐŒĐžŃ… яĐș «test fixture») зараЎО піЮĐČĐžŃ‰Đ”ĐœĐœŃ ĐżŃ€ĐŸĐŽŃƒĐșтоĐČĐœĐŸŃŃ‚Ń–. Đ„ĐŸŃ‡Đ° ĐżŃ€ĐŸĐŽŃƒĐșтоĐČĐœŃ–ŃŃ‚ŃŒ спраĐČЮі є ĐŸĐ±Ò‘Ń€ŃƒĐœŃ‚ĐŸĐČĐ°ĐœĐŸŃŽ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐŸŃŽâ€Šâ€”Â«Ń—Ń— ĐŒĐŸĐ¶ĐœĐ° ĐżĐŸŃ„Ń–Đșсото (ĐŽĐžĐČ. ĐżŃƒĐœĐșт Â«ĐąĐ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Ń–ĐČ»), ĐŸĐŽĐœĐ°Đș сĐșĐ»Đ°ĐŽĐœŃ–ŃŃ‚ŃŒ Ń‚Đ”ŃŃ‚Ńƒ є ЎужД Đ±ĐŸĐ»Ń–ŃĐœĐžĐŒ ŃŃƒĐŒĐŸĐŒ, яĐșĐžĐč у Đ±Ń–Đ»ŃŒŃˆĐŸŃŃ‚Ń– ĐČОпаЎĐșіĐČ ĐżĐŸĐČĐžĐœĐ”Đœ ĐșĐ”Ń€ŃƒĐČато Ń–ĐœŃˆĐžĐŒĐž ĐŒŃ–Ń€ĐșуĐČĐ°ĐœĐœŃĐŒĐž. На праĐșтоці Đ·Ń€ĐŸĐ±Ń–Ń‚ŃŒ таĐș, Ń‰ĐŸĐ± ĐșĐŸĐ¶Đ”Đœ Ń‚Đ”ŃŃ‚ĐŸĐČĐžĐč проĐșлаЎ яĐČĐœĐŸ ĐŽĐŸĐŽĐ°ĐČаĐČ ĐœĐ”ĐŸĐ±Ń…Ń–ĐŽĐœŃ– запОсО БД і ЮіяĐČ Đ»ĐžŃˆĐ” Đ· Ń†ĐžĐŒĐž Đ·Đ°ĐżĐžŃĐ°ĐŒĐž. ĐŻĐșŃ‰ĐŸ ĐżŃ€ĐŸĐŽŃƒĐșтоĐČĐœŃ–ŃŃ‚ŃŒ стає ĐșŃ€ĐžŃ‚ĐžŃ‡ĐœĐŸŃŽ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐŸŃŽâ€Šâ€”â€ŠĐ·Đ±Đ°Đ»Đ°ĐœŃĐŸĐČĐ°ĐœĐžĐč ĐșĐŸĐŒĐżŃ€ĐŸĐŒŃ–Ń ĐŒĐŸĐ¶Đ” проĐčто у Ń„ĐŸŃ€ĐŒŃ– Đ·Đ°ĐżĐŸĐČĐœĐ”ĐœĐœŃ Ń”ĐŽĐžĐœĐŸĐłĐŸ ĐœĐ°Đ±ĐŸŃ€Ńƒ тДстіĐČ, яĐșі ĐœĐ” Đ·ĐŒŃ–ĐœŃŽŃŽŃ‚ŃŒ ĐŽĐ°ĐœŃ– (ĐœĐ°ĐżŃ€ĐžĐșлаЎ, запОтО) +
    + +❌ **Đ†ĐœĐ°ĐșшД:** ĐšŃ–Đ»ŃŒĐșа тДстіĐČ Đ·Đ°Đ·ĐœĐ°ŃŽŃ‚ŃŒ ĐœĐ”ĐČЮач, Ń€ĐŸĐ·ĐłĐŸŃ€Ń‚Đ°ĐœĐœŃ пДрДрĐČĐ°ĐœĐŸ, ĐœĐ°ŃˆĐ° ĐșĐŸĐŒĐ°ĐœĐŽĐ° ĐČĐžŃ‚Ń€Đ°Ń‡Đ°Ń‚ĐžĐŒĐ” ĐŽĐŸŃ€ĐŸĐłĐŸŃ†Ń–ĐœĐœĐžĐč час зараз, у ĐœĐ°Ń є ĐżĐŸĐŒĐžĐ»Đșа? ĐŽĐŸŃĐ»Ń–ĐŽŃ–ĐŒ, ĐŸ ĐœŃ–â€Šâ€”â€ŠŃŃ…ĐŸĐ¶Đ”, Ń‰ĐŸ ĐŽĐČа тДстО ĐŒŃƒŃ‚ŃƒĐČалО ĐŸĐŽĐœĐ°ĐșĐŸĐČі ĐČĐžŃ…Ń–ĐŽĐœŃ– ĐŽĐ°ĐœŃ– + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :thumbsdown: ПроĐșлаЎ Đ°ĐœŃ‚ĐžŃˆĐ°Đ±Đ»ĐŸĐœŃƒ: тДстО ĐœĐ” є ĐœĐ”Đ·Đ°Đ»Đ”Đ¶ĐœĐžĐŒĐž і ĐżĐŸĐșĐ»Đ°ĐŽĐ°ŃŽŃ‚ŃŒŃŃ ĐœĐ° яĐșĐžĐčсь ĐłĐ»ĐŸĐ±Đ°Đ»ŃŒĐœĐžĐč хуĐș ĐŽĐ»Ń пДрДЎачі ĐłĐ»ĐŸĐ±Đ°Đ»ŃŒĐœĐžŃ… ĐŽĐ°ĐœĐžŃ… БД + +![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "ПроĐșлаЎО Đ· Mocha") + +```javascript +before(async () => { + //ĐŽĐŸĐŽĐ°ĐČĐ°ĐœĐœŃ ĐŽĐ°ĐœĐžŃ… ĐżŃ€ĐŸ саĐčто та Đ°ĐŽĐŒŃ–ĐœŃ–ĐČ ĐŽĐŸ ĐœĐ°ŃˆĐŸŃ— БД. ДД ĐŽĐ°ĐœŃ–? ĐœĐ°Đ·ĐŸĐČĐœŃ–. ĐŁ яĐșĐŸĐŒŃƒŃŃŒ Đ·ĐŸĐČĐœŃ–ŃˆĐœŃŒĐŸĐŒŃƒ фрДĐčĐŒĐČĐŸŃ€Đșу json Đ°Đ±ĐŸ ĐŒŃ–ĐłŃ€Đ°Ń†Ń–Ń— + await DB.AddSeedDataFromJson('seed.json'); +}); +it("When updating site name, get successful confirmation", async () => { + //ĐŻ Đ·ĐœĐ°ŃŽ, Ń‰ĐŸ саĐčт Đ· ĐœĐ°Đ·ĐČĐŸŃŽ "ĐżĐŸŃ€Ń‚Đ°Đ»" Ń–ŃĐœŃƒŃ” - я бачОĐČ Ń†Đ” ĐČ ĐżĐŸŃ‡Đ°Ń‚ĐșĐŸĐČох фаĐčлах + const siteToUpdate = await SiteService.getSiteByName("Portal"); + const updateNameResult = await SiteService.changeName(siteToUpdate, "newName"); + expect(updateNameResult).to.be(true); +}); +it("When querying by site name, get the right site", async () => { + //ĐŻ Đ·ĐœĐ°ŃŽ, Ń‰ĐŸ саĐčт Đ· ĐœĐ°Đ·ĐČĐŸŃŽ "ĐżĐŸŃ€Ń‚Đ°Đ»" Ń–ŃĐœŃƒŃ” - я бачОĐČ Ń†Đ” ĐČ ĐżĐŸŃ‡Đ°Ń‚ĐșĐŸĐČох фаĐčлах + const siteToCheck = await SiteService.getSiteByName("Portal"); + expect(siteToCheck.name).to.be.equal("Portal"); //ĐĐ”ĐČЮача! ĐŸĐŸĐżĐ”Ń€Đ”ĐŽĐœŃ–Đč тДст Đ·ĐŒŃ–ĐœĐžĐČ ĐœĐ°Đ·ĐČу :[ +}); + +``` + +
    + +### :clap: ПроĐșлаЎ праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: Мо ĐŒĐŸĐ¶Đ”ĐŒĐŸ Đ·Đ°Đ»ĐžŃˆĐ°Ń‚ĐžŃŃ ĐČ ĐŒĐ”Đ¶Đ°Ń… Ń‚Đ”ŃŃ‚Ńƒ, ĐșĐŸĐ¶Đ”Đœ тДст Юіє ĐœĐ° ĐČĐ»Đ°ŃĐœĐžĐč ĐœĐ°Đ±Ń–Ń€ ĐŽĐ°ĐœĐžŃ… + +```javascript +it("When updating site name, get successful confirmation", async () => { + //тДст ĐŽĐŸĐŽĐ°Ń” сĐČіжі ĐœĐŸĐČі запОсО та Юіє лОшД ĐœĐ° ĐŸŃĐœĐŸĐČі запОсіĐČ + const siteUnderTest = await SiteService.addSite({ + name: "siteForUpdateTest" + }); + const updateNameResult = await SiteService.changeName(siteUnderTest, "newName"); + expect(updateNameResult).to.be(true); +}); +``` + +
    + +
    + +## âšȘ 2.8 Đ’ĐžĐ±Đ”Ń€Ń–Ń‚ŃŒ чітĐșу стратДгію ĐŸŃ‡ĐžŃ‰Đ”ĐœĐœŃ ĐŽĐ°ĐœĐžŃ…: ĐżŃ–ŃĐ»Ń ĐČŃŃŒĐŸĐłĐŸ (рДĐșĐŸĐŒĐ”ĐœĐŽĐŸĐČĐ°ĐœĐŸ) Đ°Đ±ĐŸ ĐżŃ–ŃĐ»Ń ĐșĐŸĐ¶ĐœĐŸĐłĐŸ + +:white_check_mark: **Đ ĐŸĐ±Đž:** Час, ĐșĐŸĐ»Đž тДстО ĐŸŃ‡ĐžŃ‰Đ°ŃŽŃ‚ŃŒ базу ĐŽĐ°ĐœĐžŃ…, ĐČĐžĐ·ĐœĐ°Ń‡Đ°Ń” ŃĐżĐŸŃŃ–Đ± ĐœĐ°ĐżĐžŃĐ°ĐœĐœŃ тДстіĐČ. ДĐČа ĐœĐ°ĐčĐ±Ń–Đ»ŃŒŃˆ Đ¶ĐžŃ‚Ń‚Ń”Đ·ĐŽĐ°Ń‚ĐœĐžŃ… ĐČĐ°Ń€Ń–Đ°ĐœŃ‚Đž: ĐŸŃ‡ĐžŃ‰Đ”ĐœĐœŃ ĐżŃ–ŃĐ»Ń ĐČсіх тДстіĐČ Ń– ĐŸŃ‡ĐžŃ‰Đ”ĐœĐœŃ ĐżŃ–ŃĐ»Ń ĐșĐŸĐ¶ĐœĐŸĐłĐŸ Ń‚Đ”ŃŃ‚Ńƒ. ВОбОраючО ĐŸŃŃ‚Đ°ĐœĐœŃ–Đč ĐČĐ°Ń€Ń–Đ°ĐœŃ‚, ĐŸŃ‡ĐžŃ‰Đ”ĐœĐœŃ ĐżŃ–ŃĐ»Ń ĐșĐŸĐ¶ĐœĐŸĐłĐŸ ĐŸĐșŃ€Đ”ĐŒĐŸĐłĐŸ Ń‚Đ”ŃŃ‚Ńƒ ĐłĐ°Ń€Đ°ĐœŃ‚ŃƒŃ” чості таблОці та стĐČĐŸŃ€ŃŽŃ” Đ·Ń€ŃƒŃ‡ĐœŃ– пДрДĐČагО Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐŽĐ»Ń Ń€ĐŸĐ·Ń€ĐŸĐ±ĐœĐžĐșа. На ĐŒĐŸĐŒĐ”ĐœŃ‚ ĐżĐŸŃ‡Đ°Ń‚Đșу Ń‚Đ”ŃŃ‚Ńƒ ĐœĐ” Ń–ŃĐœŃƒŃ” Ń–ĐœŃˆĐžŃ… запОсіĐČ, ĐŒĐŸĐ¶ĐœĐ° Đ±ŃƒŃ‚Đž ĐČпДĐČĐœĐ”ĐœĐžĐŒ, яĐșі ĐŽĐ°ĐœŃ– Đ·Đ°ĐżĐžŃ‚ŃƒŃŽŃ‚ŃŒŃŃ, і ĐœĐ°ĐČіть ĐŒĐŸĐ¶Đ” ĐČĐžĐœĐžĐșĐœŃƒŃ‚Đž ŃĐżĐŸĐșуса піЮрахуĐČато ряЮĐșĐž піЮ час тĐČĐ”Ń€ĐŽĐ¶Đ”ĐœŃŒ. ĐŠĐ” ĐŒĐ°Ń” сДрĐčĐŸĐ·ĐœŃ– ĐœĐ”ĐŽĐŸĐ»Ń–ĐșĐž: піЮ час Ń€ĐŸĐ±ĐŸŃ‚Đž ĐČ Đ±Đ°ĐłĐ°Ń‚ĐŸĐżŃ€ĐŸŃ†Đ”ŃĐœĐŸĐŒŃƒ Ń€Đ”Đ¶ĐžĐŒŃ– тДстО ĐŒĐŸĐ¶ŃƒŃ‚ŃŒ заĐČажатО ĐŸĐŽĐžĐœ ĐŸĐŽĐœĐŸĐŒŃƒ. ĐŸĐŸĐșĐž ĐżŃ€ĐŸŃ†Đ”Ń-1 ĐŸŃ‡ĐžŃ‰Đ°Ń” таблОці, у Ń‚ĐŸĐč ŃĐ°ĐŒĐžĐč ĐŒĐŸĐŒĐ”ĐœŃ‚ ĐżŃ€ĐŸŃ†Đ”Ń-2 Đ·Đ°ĐżĐžŃ‚ŃƒŃ” ĐŽĐ°ĐœŃ– та Đ·Đ°Đ·ĐœĐ°Ń” ĐżĐŸĐŒĐžĐ»ĐșĐž (ĐŸŃĐșŃ–Đ»ŃŒĐșĐž БД Ń€Đ°ĐżŃ‚ĐŸĐČĐŸ ĐČĐžĐŽĐ°Đ»Đ”ĐœĐŸ ĐżŃ€ĐŸŃ†Đ”ŃĐŸĐŒ-1). ĐšŃ€Ń–ĐŒ Ń‚ĐŸĐłĐŸ, ĐČажчД ŃƒŃŃƒĐœŃƒŃ‚Đž ĐœĐ”ĐČЎалі тДстО – ĐČіЮĐČіЮуĐČĐ°ĐœĐœŃ БД ĐœĐ” ĐżĐŸĐșажД Đ¶ĐŸĐŽĐœĐžŃ… запОсіĐČ. + +ДругоĐč ĐČĐ°Ń€Ń–Đ°ĐœŃ‚ — ĐŸŃ‡ĐžŃ‰Đ”ĐœĐœŃ ĐżŃ–ŃĐ»Ń заĐČĐ”Ń€ŃˆĐ”ĐœĐœŃ ĐČсіх Ń‚Đ”ŃŃ‚ĐŸĐČох фаĐčліĐČ (Đ°Đ±ĐŸ ĐœĐ°ĐČіть Ń‰ĐŸĐŽĐœŃ!). йаĐșĐžĐč піЮхіЮ ĐŸĐ·ĐœĐ°Ń‡Đ°Ń”, Ń‰ĐŸ та ŃĐ°ĐŒĐ° БД Đ· ĐœĐ°ŃĐČĐœĐžĐŒĐž Đ·Đ°ĐżĐžŃĐ°ĐŒĐž ĐŸĐ±ŃĐ»ŃƒĐłĐŸĐČує ĐČсі тДстО та ĐżŃ€ĐŸŃ†Đ”ŃĐž. Đ©ĐŸĐ± ĐœĐ” ĐœĐ°ŃŃ‚ŃƒĐżĐ°Ń‚Đž ĐŸĐŽĐžĐœ ĐŸĐŽĐœĐŸĐŒŃƒ ĐœĐ° ĐœĐŸĐłĐž, тДстО ĐżĐŸĐČĐžĐœĐœŃ– ĐŽĐŸĐŽĐ°ĐČато та Юіято ĐœĐ° ĐŸŃĐœĐŸĐČі ĐșĐŸĐœĐșŃ€Đ”Ń‚ĐœĐžŃ… запОсіĐČ, яĐșі ĐČĐŸĐœĐž ĐŽĐŸĐŽĐ°Đ»Đž. ĐŸĐŸŃ‚Ń€Ń–Đ±ĐœĐŸ пДрДĐČірото, чо ĐŽĐŸĐŽĐ°ĐœĐŸ яĐșĐžĐčсь запОс? ĐŸŃ€ĐžĐżŃƒŃŃ‚Ń–ĐŒĐŸ, Ń‰ĐŸ є Ń–ĐœŃˆŃ– тосячі запОсіĐČ Ń– запОт ĐœĐ° запОсО, яĐșі булО ĐŽĐŸĐŽĐ°ĐœŃ– яĐČĐœĐŸ. ĐŸĐŸŃ‚Ń€Ń–Đ±ĐœĐŸ пДрДĐČірото, чо ĐČĐžĐŽĐ°Đ»Đ”ĐœĐŸ запОс? ĐĐ”ĐŒĐŸĐ¶Đ»ĐžĐČĐŸ пропустото, Ń‰ĐŸ Ń‚Đ°Đ±Đ»ĐžŃ†Ń ĐżĐŸŃ€ĐŸĐ¶ĐœŃ, пДрДĐČіртД, чо ĐœĐ”ĐŒĐ°Ń” Ń†ŃŒĐŸĐłĐŸ ĐșĐŸĐœĐșŃ€Đ”Ń‚ĐœĐŸĐłĐŸ запОсу. Щя Ń‚Đ”Ń…ĐœŃ–Đșа Юає ĐșŃ–Đ»ŃŒĐșа Đ·ĐœĐ°Ń‡ĐœĐžŃ… пДрДĐČаг: ĐČĐŸĐœĐ° працює ĐœĐ°Ń‚ĐžĐČĐœĐŸ ĐČ Đ±Đ°ĐłĐ°Ń‚ĐŸĐżŃ€ĐŸŃ†Đ”ŃĐœĐŸĐŒŃƒ Ń€Đ”Đ¶ĐžĐŒŃ–, ĐșĐŸĐ»Đž Ń€ĐŸĐ·Ń€ĐŸĐ±ĐœĐžĐș Ń…ĐŸŃ‡Đ” Đ·Ń€ĐŸĐ·ŃƒĐŒŃ–Ń‚Đž, Ń‰ĐŸ ŃŃ‚Đ°Đ»ĐŸŃŃ – ĐŽĐ°ĐœŃ– є, а ĐœĐ” ĐČĐžĐŽĐ°Đ»ŃŃŽŃ‚ŃŒŃŃ. ĐŠĐ” таĐșĐŸĐ¶ Đ·Đ±Ń–Đ»ŃŒŃˆŃƒŃ” ĐčĐŒĐŸĐČŃ–Ń€ĐœŃ–ŃŃ‚ŃŒ ĐČояĐČĐ»Đ”ĐœĐœŃ ĐżĐŸĐŒĐžĐ»ĐŸĐș, ĐŸŃĐșŃ–Đ»ŃŒĐșĐž БД Đ·Đ°ĐżĐŸĐČĐœĐ”ĐœĐ° Đ·Đ°ĐżĐžŃĐ°ĐŒĐž, а ĐœĐ” ŃˆŃ‚ŃƒŃ‡ĐœĐŸ ĐżĐŸŃ€ĐŸĐ¶ĐœŃ. [ĐŸĐ”Ń€Đ”ĐłĐ»ŃĐœŃŒŃ‚Đ” ĐżĐŸĐČĐœŃƒ ĐżĐŸŃ€Ń–ĐČĐœŃĐ»ŃŒĐœŃƒ таблОцю тут](https://github.com/testjavascript/nodejs-integration-tests-best-practices/blob/master/graphics/db-clean-options.png). +
    + +❌ **Đ†ĐœĐ°ĐșшД:** БДз стратДгії Ń€ĐŸĐ·ĐŽŃ–Đ»Đ”ĐœĐœŃ запОсіĐČ Đ°Đ±ĐŸ ĐŸŃ‡ĐžŃ‰Đ”ĐœĐœŃ - йДстО Đ±ŃƒĐŽŃƒŃ‚ŃŒ ĐœĐ°ŃŃ‚ŃƒĐżĐ°Ń‚Đž ĐŸĐŽĐžĐœ ĐœĐ° ĐŸĐŽĐœĐŸĐłĐŸ; ВоĐșĐŸŃ€ĐžŃŃ‚Đ°ĐœĐœŃ Ń‚Ń€Đ°ĐœĐ·Đ°ĐșціĐč працюĐČĐ°Ń‚ĐžĐŒĐ” лОшД ĐŽĐ»Ń Ń€Đ”Đ»ŃŃ†Ń–ĐčĐœĐŸŃ— БД і, ĐčĐŒĐŸĐČŃ–Ń€ĐœĐŸ, усĐșĐ»Đ°ĐŽĐœĐžŃ‚ŃŒŃŃ, ĐșĐŸĐ»Đž Đ·â€™ŃĐČĐ»ŃŃ‚ŃŒŃŃ ĐČĐœŃƒŃ‚Ń€Ń–ŃˆĐœŃ– Ń‚Ń€Đ°ĐœĐ·Đ°Đșції + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :clap: ĐŸŃ€ĐžĐ±ĐžŃ€Đ°ĐœĐœŃ ĐżŃ–ŃĐ»Ń ВХІЄ пДрДĐČŃ–Ń€ĐŸĐș. ĐĐ” ĐŸĐ±ĐŸĐČ'ŃĐ·ĐșĐŸĐČĐŸ ĐżŃ–ŃĐ»Ń ĐșĐŸĐ¶ĐœĐŸĐłĐŸ запусĐșу. Đ§ĐžĐŒ Đ±Ń–Đ»ŃŒŃˆĐ” ĐŽĐ°ĐœĐžŃ… ĐŒĐž ĐŒĐ°Ń”ĐŒĐŸ піЮ час ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ тДстіĐČ, Ń‚ĐžĐŒ Đ±Ń–Đ»ŃŒŃˆĐ” цД ĐœĐ°ĐłĐ°ĐŽŃƒŃ” пДрДĐČагО ĐČĐžŃ€ĐŸĐ±ĐœĐžŃ†Ń‚ĐČа + +```javascript + // ĐŸŃ–ŃĐ»Ń ĐČŃŃŒĐŸĐłĐŸ прОбратО (рДĐșĐŸĐŒĐ”ĐœĐŽĐŸĐČĐ°ĐœĐŸ) + // global-teardown.js +module.exports = async () => { + // ... + if (Math.ceil(Math.random() * 10) === 10) { + await new OrderRepository().cleanup(); + } +}; +``` + +
    + +
    + +## âšȘ 2.9 Đ†Đ·ĐŸĐ»ŃŽĐčтД ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ ĐČіЮ сĐČіту за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ ĐżĐ”Ń€Đ”Ń…ĐŸĐżĐ»ŃŽĐČача HTTP + +:white_check_mark: **Đ ĐŸĐ±Đž:** Đ†Đ·ĐŸĐ»ŃŽĐčтД Ń‚Đ”ŃŃ‚ĐŸĐČĐ°ĐœĐžĐč ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚, ĐżĐ”Ń€Đ”Ń…ĐŸĐżĐ»ŃŽŃŽŃ‡Đž Đ±ŃƒĐŽŃŒ-яĐșĐžĐč ĐČĐžŃ…Ń–ĐŽĐœĐžĐč HTTP-запОт і ĐœĐ°ĐŽĐ°ŃŽŃ‡Đž ĐżĐŸŃ‚Ń€Ń–Đ±ĐœŃƒ ĐČŃ–ĐŽĐżĐŸĐČіЮь, Ń‰ĐŸĐ± HTTP API спіĐČаĐČŃ‚ĐŸŃ€Đ° ĐœĐ” ĐżĐŸŃŃ‚Ń€Đ°Đ¶ĐŽĐ°ĐČ. Nock є Ń‡ŃƒĐŽĐŸĐČĐžĐŒ Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚ĐŸĐŒ ĐŽĐ»Ń цієї ĐŒŃ–ŃŃ–Ń—, ĐŸŃĐșŃ–Đ»ŃŒĐșĐž ĐČŃ–Đœ Đ·Đ°Đ±Đ”Đ·ĐżĐ”Ń‡ŃƒŃ” Đ·Ń€ŃƒŃ‡ĐœĐžĐč ŃĐžĐœŃ‚Đ°ĐșсОс ĐŽĐ»Ń ĐČĐžĐ·ĐœĐ°Ń‡Đ”ĐœĐœŃ ĐżĐŸĐČĐ”ĐŽŃ–ĐœĐșĐž Đ·ĐŸĐČĐœŃ–ŃˆĐœŃ–Ń… служб. Đ†Đ·ĐŸĐ»ŃŃ†Ń–Ń є ĐŸĐ±ĐŸĐČâ€™ŃĐ·ĐșĐŸĐČĐŸŃŽ ĐŽĐ»Ń Đ·Đ°ĐżĐŸĐ±Ń–ĐłĐ°ĐœĐœŃ ŃˆŃƒĐŒŃƒ та ĐżĐŸĐČŃ–Đ»ŃŒĐœĐŸŃ— ĐżŃ€ĐŸĐŽŃƒĐșтоĐČĐœĐŸŃŃ‚Ń–, алД Đ·ĐŽĐ”Đ±Ń–Đ»ŃŒŃˆĐŸĐłĐŸ ĐŽĐ»Ń Ń–ĐŒŃ–Ń‚Đ°Ń†Ń–Ń— Ń€Ń–Đ·ĐœĐžŃ… ŃŃ†Đ”ĐœĐ°Ń€Ń–Ń—ĐČ Ń– рДаĐșціĐč. Đ„ĐŸŃ€ĐŸŃˆĐžĐč ŃĐžĐŒŃƒĐ»ŃŃ‚ĐŸŃ€ ĐżĐŸĐ»ŃŒĐŸŃ‚Ńƒ — цД ĐœĐ” ĐŒĐ°Đ»ŃŽĐČĐ°ĐœĐœŃ Ń‡ĐžŃŃ‚ĐŸĐłĐŸ блаĐșĐžŃ‚ĐœĐŸĐłĐŸ ĐœĐ”Đ±Đ°, а стĐČĐŸŃ€Đ”ĐœĐœŃ Đ±Đ”Đ·ĐżĐ”Ń‡ĐœĐžŃ… ŃˆŃ‚ĐŸŃ€ĐŒŃ–ĐČ Ń– Ń…Đ°ĐŸŃŃƒ. ĐŠĐ” ĐżĐŸŃĐžĐ»ŃŽŃ”Ń‚ŃŒŃŃ ĐČ Đ°Ń€Ń…Ń–Ń‚Đ”Đșтурі ĐŒŃ–ĐșŃ€ĐŸŃĐ”Ń€ĐČісу, ĐŽĐ” Ń„ĐŸĐșус заĐČжЎО ĐŒĐ°Ń” Đ±ŃƒŃ‚Đž Đ·ĐŸŃĐ”Ń€Đ”ĐŽĐ¶Đ”ĐœĐžĐč ĐœĐ° ĐŸĐŽĐœĐŸĐŒŃƒ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Ń– бДз Đ·Đ°Đ»ŃƒŃ‡Đ”ĐœĐœŃ Ń€Đ”ŃˆŃ‚Đž сĐČіту. Đ„ĐŸŃ‡Đ° ĐŒĐŸĐ¶ĐœĐ° Ń–ĐŒŃ–Ń‚ŃƒĐČато ĐżĐŸĐČĐ”ĐŽŃ–ĐœĐșу Đ·ĐŸĐČĐœŃ–ŃˆĐœŃŒĐŸĐłĐŸ сДрĐČісу за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ ĐżĐŸĐČŃ‚ĐŸŃ€ŃŽĐČĐ°ĐœĐžŃ… тДстіĐČ (mocking), Đ±Đ°Đ¶Đ°ĐœĐŸ ĐœĐ” Ń‚ĐŸŃ€Đșатося Ń€ĐŸĐ·ĐłĐŸŃ€ĐœŃƒŃ‚ĐŸĐłĐŸ ĐșĐŸĐŽŃƒ та Юіято ĐœĐ° ріĐČĐœŃ– ĐŒĐ”Ń€Đ”Đ¶Ń–, Ń‰ĐŸĐ± тДстО Đ·Đ°Đ»ĐžŃˆĐ°Đ»ĐžŃŃ Ń‡ĐžŃŃ‚ĐŸ Ń‡ĐŸŃ€ĐœĐŸŃŽ сĐșŃ€ĐžĐœŃŒĐșĐŸŃŽ. ĐĐ”ĐłĐ°Ń‚ĐžĐČĐœĐŸŃŽ ŃŃ‚ĐŸŃ€ĐŸĐœĐŸŃŽ Ń–Đ·ĐŸĐ»ŃŃ†Ń–Ń— є ĐČŃ–ĐŽŃŃƒŃ‚ĐœŃ–ŃŃ‚ŃŒ ĐČояĐČĐ»Đ”ĐœĐœŃ Đ·ĐŒŃ–Đœ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đ° спіĐČаĐČŃ‚ĐŸŃ€Đ° та ĐœĐ”ĐżĐŸŃ€ĐŸĐ·ŃƒĐŒŃ–ĐœĐœŃ ĐŒŃ–Đ¶ ĐŽĐČĐŸĐŒĐ° ŃĐ»ŃƒĐ¶Đ±Đ°ĐŒĐž. ĐžĐ±ĐŸĐČâ€™ŃĐ·ĐșĐŸĐČĐŸ ĐșĐŸĐŒĐżĐ”ĐœŃŃƒĐčтД цД за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ ĐșŃ–Đ»ŃŒĐșĐŸŃ… ĐșĐŸĐœŃ‚Ń€Đ°ĐșтіĐČ Đ°Đ±ĐŸ тДстіĐČ E2E +
    + +❌ **Đ†ĐœĐ°ĐșшД:** Đ”Đ”ŃĐșі службО ĐœĐ°ĐŽĐ°ŃŽŃ‚ŃŒ Ń„Đ°Đ»ŃŒŃˆĐžĐČу ĐČДрсію, яĐșу Đ°Đ±ĐŸĐœĐ”ĐœŃ‚ ĐŒĐŸĐ¶Đ” Ń€ĐŸĐ·ĐłĐŸŃ€ĐœŃƒŃ‚Đž Đ»ĐŸĐșĐ°Đ»ŃŒĐœĐŸ, зазĐČочаĐč за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ Docker. ĐŠĐ” ĐżĐŸĐ»Đ”ĐłŃˆĐžŃ‚ŃŒ ĐœĐ°Đ»Đ°ŃˆŃ‚ŃƒĐČĐ°ĐœĐœŃ та піЮĐČощоть ĐżŃ€ĐŸĐŽŃƒĐșтоĐČĐœŃ–ŃŃ‚ŃŒ, алД ĐœĐ” ĐŽĐŸĐżĐŸĐŒĐŸĐ¶Đ” Đ· ŃĐžĐŒŃƒĐ»ŃŃ†Ń–Ń”ŃŽ Ń€Ń–Đ·ĐœĐžŃ… ĐČŃ–ĐŽĐżĐŸĐČŃ–ĐŽĐ”Đč; Đ”Đ”ŃĐșі службО ĐœĐ°ĐŽĐ°ŃŽŃ‚ŃŒ ŃĐ”Ń€Đ”ĐŽĐŸĐČОщД Â«ĐżŃ–ŃĐŸŃ‡ĐœĐžŃ†Ń–Â», Ń‚ĐŸĐŒŃƒ спраĐČĐ¶ĐœŃ служба Đ·Đ°Đ·ĐœĐ°Ń” уЮару, алД ĐœĐ” ŃĐżŃ€ĐžŃ‡ĐžĐœŃŃ” Đ¶ĐŸĐŽĐœĐžŃ… ĐČотрат Đ°Đ±ĐŸ ĐżĐŸĐ±Ń–Ń‡ĐœĐžŃ… ДфДĐșтіĐČ. ĐŠĐ” Đ·ĐŒĐ”ĐœŃˆĐžŃ‚ŃŒ ŃˆŃƒĐŒ піЮ час ĐœĐ°Đ»Đ°ŃˆŃ‚ŃƒĐČĐ°ĐœĐœŃ ŃŃ‚ĐŸŃ€ĐŸĐœĐœŃŒĐŸŃ— службО, алД таĐșĐŸĐ¶ ĐœĐ” ĐŽĐŸĐ·ĐČĐŸĐ»ĐžŃ‚ŃŒ Ń–ĐŒŃ–Ń‚ŃƒĐČато ŃŃ†Đ”ĐœĐ°Ń€Ń–Ń— + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :clap: Đ—Đ°ĐżĐŸĐ±Ń–ĐłĐ°ĐœĐœŃ ĐŒĐ”Ń€Đ”Đ¶Đ”ĐČĐžĐŒ ĐČĐžĐșлОĐșĐ°ĐŒ Đ·ĐŸĐČĐœŃ–ŃˆĐœŃ–Ń… ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Ń–ĐČ ĐŽĐŸĐ·ĐČĐŸĐ»ŃŃ” Ń–ĐŒŃ–Ń‚ŃƒĐČато ŃŃ†Đ”ĐœĐ°Ń€Ń–Ń— та ĐŒŃ–ĐœŃ–ĐŒŃ–Đ·ŃƒĐČато ŃˆŃƒĐŒ + +```javascript +// ĐŸĐ”Ń€Đ”Ń…ĐŸĐżĐ»ŃŽĐČато запОтО ĐŽĐ»Ń ŃŃ‚ĐŸŃ€ĐŸĐœĐœŃ–Ń… API і ĐżĐŸĐČДртатО ĐżĐŸĐżĐ”Ń€Đ”ĐŽĐœŃŒĐŸ ĐČĐžĐ·ĐœĐ°Ń‡Đ”ĐœŃƒ ĐČŃ–ĐŽĐżĐŸĐČіЮь +beforeEach(() => { + nock('http://localhost/user/').get(`/1`).reply(200, { + id: 1, + name: 'John', + }); +}); +``` + +
    + +## âšȘ 2.10 ĐŸĐ”Ń€Đ”ĐČіртД ŃŃ…Đ”ĐŒŃƒ ĐČŃ–ĐŽĐżĐŸĐČіЮі, ĐșĐŸĐ»Đž є аĐČŃ‚ĐŸĐŒĐ°Ń‚ĐžŃ‡ĐœĐŸ Đ·ĐłĐ”ĐœĐ”Ń€ĐŸĐČĐ°ĐœŃ– ĐżĐŸĐ»Ń + +:white_check_mark: **Đ ĐŸĐ±Đž:** ĐŻĐșŃ‰ĐŸ ĐœĐ”ĐŒĐŸĐ¶Đ»ĐžĐČĐŸ піЮтĐČДрЎОтО пДĐČĐœŃ– ĐŽĐ°ĐœŃ–, пДрДĐČіртД ĐœĐ°ŃĐČĐœŃ–ŃŃ‚ŃŒ і топо ĐŸĐ±ĐŸĐČâ€™ŃĐ·ĐșĐŸĐČох ĐżĐŸĐ»Ń–ĐČ. Đ†ĐœĐŸĐŽŃ– ĐČŃ–ĐŽĐżĐŸĐČіЮь ĐŒŃ–ŃŃ‚ĐžŃ‚ŃŒ ĐČажлОĐČі ĐżĐŸĐ»Ń Đ· ĐŽĐžĐœĐ°ĐŒŃ–Ń‡ĐœĐžĐŒĐž ĐŽĐ°ĐœĐžĐŒĐž, яĐșі ĐœĐ”ĐŒĐŸĐ¶Đ»ĐžĐČĐŸ пДрДЎбачОтО піЮ час ĐœĐ°ĐżĐžŃĐ°ĐœĐœŃ Ń‚Đ”ŃŃ‚Ńƒ, яĐș-ĐŸŃ‚ Юато та чОсла, Ń‰ĐŸ Đ·Đ±Ń–Đ»ŃŒŃˆŃƒŃŽŃ‚ŃŒŃŃ. ĐŻĐșŃ‰ĐŸ ĐŽĐŸĐłĐŸĐČір API ĐŸĐ±Ń–Ń†ŃŃ”, Ń‰ĐŸ ці ĐżĐŸĐ»Ń ĐœĐ” Đ±ŃƒĐŽŃƒŃ‚ŃŒ ĐœŃƒĐ»ŃŒĐŸĐČĐžĐŒĐž і ĐŒŃ–ŃŃ‚ŃŃ‚ŃŒ праĐČĐžĐ»ŃŒĐœŃ– топо, ĐŸĐ±ĐŸĐČâ€™ŃĐ·ĐșĐŸĐČĐŸ пДрДĐČіртД цД. Đ‘Ń–Đ»ŃŒŃˆŃ–ŃŃ‚ŃŒ Đ±Ń–Đ±Đ»Ń–ĐŸŃ‚Đ”Đș тĐČĐ”Ń€ĐŽĐ¶Đ”ĐœŃŒ ĐżŃ–ĐŽŃ‚Ń€ĐžĐŒŃƒŃŽŃ‚ŃŒ топо пДрДĐČірĐșĐž. ĐŻĐșŃ‰ĐŸ ĐČŃ–ĐŽĐżĐŸĐČіЮь ĐœĐ”ĐČДлОĐșа, пДрДĐČіртД ĐŽĐ°ĐœŃ–, Ń‰ĐŸ ĐżĐŸĐČĐ”Ń€Ń‚Đ°ŃŽŃ‚ŃŒŃŃ, і ĐČĐČĐ”ĐŽŃ–Ń‚ŃŒ Ń€Đ°Đ·ĐŸĐŒ ĐČ ĐŸĐŽĐœĐŸĐŒŃƒ тĐČĐ”Ń€ĐŽĐ¶Đ”ĐœĐœŃ– (ĐŽĐžĐČ. проĐșлаЎ ĐșĐŸĐŽŃƒ). ЩД ĐŸĐŽĐžĐœ ĐČĐ°Ń€Ń–Đ°ĐœŃ‚ — пДрДĐČірото ĐČсю ĐČŃ–ĐŽĐżĐŸĐČіЮь за ĐŽĐŸĐșŃƒĐŒĐ”ĐœŃ‚ĐŸĐŒ OpenAPI (Swagger). Đ‘Ń–Đ»ŃŒŃˆŃ–ŃŃ‚ŃŒ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°Đ»ŃŒĐœĐžĐșіĐČ ĐŒĐ°ŃŽŃ‚ŃŒ Ń€ĐŸĐ·ŃˆĐžŃ€Đ”ĐœĐœŃ ŃĐżŃ–Đ»ŃŒĐœĐŸŃ‚Đž, яĐșі пДрДĐČіряють ĐČŃ–ĐŽĐżĐŸĐČіЮі API ĐœĐ° Ń—Ń…ĐœŃŽ ĐŽĐŸĐșŃƒĐŒĐ”ĐœŃ‚Đ°Ń†Ń–ŃŽ. + + +
    + +❌ **Đ†ĐœĐ°ĐșшД:** Đ„ĐŸŃ‡Đ° ĐżŃ€ĐŸĐłŃ€Đ°ĐŒĐ° ĐČĐžĐșлОĐșу ĐșĐŸĐŽŃƒ/API ĐżĐŸĐșĐ»Đ°ĐŽĐ°Ń”Ń‚ŃŒŃŃ ĐœĐ° ĐŽĐ”ŃĐșі ĐżĐŸĐ»Ń Đ· ĐŽĐžĐœĐ°ĐŒŃ–Ń‡ĐœĐžĐŒĐž ĐŽĐ°ĐœĐžĐŒĐž (ĐœĐ°ĐżŃ€ĐžĐșлаЎ, Ń–ĐŽĐ”ĐœŃ‚ĐžŃ„Ń–ĐșĐ°Ń‚ĐŸŃ€, Юата), ĐČĐŸĐœĐ° ĐœĐ” проĐčĐŽĐ” у ĐČŃ–ĐŽĐżĐŸĐČіЮь і ĐœĐ” ĐżĐŸŃ€ŃƒŃˆĐžŃ‚ŃŒ ĐșĐŸĐœŃ‚Ń€Đ°Đșт + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :clap: СтĐČĐ”Ń€ĐŽĐ¶ŃƒŃŽŃ‡Đž, Ń‰ĐŸ ĐżĐŸĐ»Ń Đ· ĐŽĐžĐœĐ°ĐŒŃ–Ń‡ĐœĐžĐŒ Đ·ĐœĐ°Ń‡Đ”ĐœĐœŃĐŒ Ń–ŃĐœŃƒŃŽŃ‚ŃŒ і ĐŒĐ°ŃŽŃ‚ŃŒ праĐČĐžĐ»ŃŒĐœĐžĐč топ + +```javascript + test('When adding a new valid order, Then should get back approval with 200 response', async () => { + // ... + // СтĐČĐ”Ń€ĐŽĐ¶ŃƒĐČĐ°ĐœĐœŃ + expect(receivedAPIResponse).toMatchObject({ + status: 200, + data: { + id: expect.any(Number), // БуЮь-яĐșĐ” Ń‡ĐžŃĐ»ĐŸ Đ·Đ°ĐŽĐŸĐČĐŸĐ»ŃŒĐœŃŃ” цДĐč тДст + mode: 'approved', + }, + }); +}); +``` + +
    + +
    + +## âšȘ 2.12 ĐŸĐ”Ń€Đ”ĐČіртД ĐșраĐčĐœŃ– ĐČОпаЎĐșĐž Ń–ĐœŃ‚Đ”ĐłŃ€Đ°Ń†Ń–Ń— та Ń…Đ°ĐŸŃ + +:white_check_mark: **Đ ĐŸĐ±Đž:** ĐŸĐ”Ń€Đ”ĐČіряючо Ń–ĐœŃ‚Đ”ĐłŃ€Đ°Ń†Ń–Ń—, ĐČĐžŃ…ĐŸĐŽŃŒŃ‚Đ” за Ń€Đ°ĐŒĐșĐž щаслОĐČох і ŃŃƒĐŒĐœĐžŃ… ŃˆĐ»ŃŃ…Ń–ĐČ. ĐŸĐ”Ń€Đ”ĐČіряĐčтД ĐœĐ” лОшД ĐČŃ–ĐŽĐżĐŸĐČіЮі Đ· ĐżĐŸĐŒĐžĐ»ĐșĐ°ĐŒĐž (ĐœĐ°ĐżŃ€ĐžĐșлаЎ, ĐżĐŸĐŒĐžĐ»Đșа HTTP 500), а Đč Đ°ĐœĐŸĐŒĐ°Đ»Ń–Ń— ĐœĐ° ріĐČĐœŃ– ĐŒĐ”Ń€Đ”Đ¶Ń–, яĐș-ĐŸŃ‚ ĐżĐŸĐČŃ–Đ»ŃŒĐœŃ– ĐČŃ–ĐŽĐżĐŸĐČіЮі та ĐČŃ–ĐŽĐżĐŸĐČіЮі, Ń‰ĐŸ заĐșŃ–ĐœŃ‡ĐžĐ»ĐžŃŃ. ĐŠĐ” ĐŽĐŸĐČДЎД, Ń‰ĐŸ ĐșĐŸĐŽ стіĐčĐșĐžĐč і ĐŒĐŸĐ¶Đ” ĐŸĐ±Ń€ĐŸĐ±Đ»ŃŃ‚Đž Ń€Ń–Đ·ĐœŃ– ĐŒĐ”Ń€Đ”Đ¶Đ”ĐČі ŃŃ†Đ”ĐœĐ°Ń€Ń–Ń—, яĐș-ĐŸŃ‚ ĐČОбір праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ŃˆĐ»ŃŃ…Ńƒ ĐżŃ–ŃĐ»Ń таĐčĐŒ-ауту, ĐČŃ–ĐŽŃŃƒŃ‚ĐœŃ–ŃŃ‚ŃŒ ĐșрохĐșох ŃƒĐŒĐŸĐČ Đ·ĐŒĐ°ĐłĐ°ĐœĐœŃ та ĐŒŃ–ŃŃ‚ĐžŃ‚ŃŒ аĐČŃ‚ĐŸĐŒĐ°Ń‚ĐžŃ‡ĐœĐžĐč ĐČĐžĐŒĐžĐșач ĐŽĐ»Ń ĐżĐŸĐČŃ‚ĐŸŃ€ĐœĐžŃ… ŃĐżŃ€ĐŸĐ±. Đ†ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Đž ĐżĐ”Ń€Đ”Ń…ĐŸĐżĐ»Đ”ĐœĐœŃ Đ· аĐČŃ‚ĐŸŃ€ĐžŃ‚Đ”Ń‚ĐœĐžĐŒ ĐŽĐŸŃĐČŃ–ĐŽĐŸĐŒ ĐŒĐŸĐ¶ŃƒŃ‚ŃŒ лДгĐșĐŸ Ń–ĐŒŃ–Ń‚ŃƒĐČато Ń€Ń–Đ·ĐœŃ– ĐżĐŸĐČĐ”ĐŽŃ–ĐœĐșĐž ĐŒĐ”Ń€Đ”Đ¶Ń–, яĐș-ĐŸŃ‚ ĐœĐ”ŃĐżĐŸĐșіĐčĐœĐ” ĐŸĐ±ŃĐ»ŃƒĐłĐŸĐČуĐČĐ°ĐœĐœŃ, яĐșĐ” час ĐČіЮ часу Юає збіĐč. Đ’Ń–Đœ ĐœĐ°ĐČіть ĐŒĐŸĐ¶Đ” Đ·Ń€ĐŸĐ·ŃƒĐŒŃ–Ń‚Đž, ĐșĐŸĐ»Đž Đ·ĐœĐ°Ń‡Đ”ĐœĐœŃ таĐčĐŒ-ауту HTTP-ĐșĐ»Ń–Ń”ĐœŃ‚Đ° за Đ·Đ°ĐŒĐŸĐČчуĐČĐ°ĐœĐœŃĐŒ пДрДĐČощує Đ·ĐŒĐŸĐŽĐ”Đ»ŃŒĐŸĐČĐ°ĐœĐžĐč час ĐČŃ–ĐŽĐżĐŸĐČіЮі, і ĐČŃ–ĐŽŃ€Đ°Đ·Ńƒ, ĐœĐ” чДĐșаючо, ĐČĐžĐșлОĐșато ĐČĐžĐœŃŃ‚ĐŸĐș часу ĐŸŃ‡Ń–ĐșуĐČĐ°ĐœĐœŃ + + +
    + +❌ **Đ†ĐœĐ°ĐșшД:** Усі ĐČаші тДстО ĐżŃ€ĐŸŃ…ĐŸĐŽŃŃ‚ŃŒ ŃƒŃĐżŃ–ŃˆĐœĐŸ, Ń‚Ń–Đ»ŃŒĐșĐž ĐČĐžŃ€ĐŸĐ±ĐœĐžŃ†Ń‚ĐČĐŸ буЎД аĐČаріĐčĐœĐŸ Đ°Đ±ĐŸ ĐœĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœĐŸ ĐżĐŸĐČŃ–ĐŽĐŸĐŒĐ»ŃŃ‚Đž ĐżŃ€ĐŸ ĐżĐŸĐŒĐžĐ»ĐșĐž, ĐșĐŸĐ»Đž трДті ŃŃ‚ĐŸŃ€ĐŸĐœĐž ĐœĐ°ĐŽŃĐžĐ»Đ°Ń‚ĐžĐŒŃƒŃ‚ŃŒ ĐČĐžĐœŃŃ‚ĐșĐŸĐČі ĐČŃ–ĐŽĐżĐŸĐČіЮі + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :clap: Đ—Đ°Đ±Đ”Đ·ĐżĐ”Ń‡Đ”ĐœĐœŃ Ń‚ĐŸĐłĐŸ, Ń‰ĐŸ ĐČ Ń€Đ°Đ·Ń– Đ·Đ±ĐŸŃ—ĐČ Ńƒ ĐŒĐ”Ń€Đ”Đ¶Ń– аĐČŃ‚ĐŸĐŒĐ°Ń‚ĐžŃ‡ĐœĐžĐč ĐČĐžĐŒĐžĐșач ĐŒĐŸĐ¶Đ” ĐČрятуĐČато сотуацію + +```javascript + test('When users service replies with 503 once and retry mechanism is applied, then an order is added successfully', async () => { + // ĐŁĐżĐŸŃ€ŃĐŽĐșуĐČĐ°ĐœĐœŃ + nock.removeInterceptor(userServiceNock.interceptors[0]) + nock('http://localhost/user/') + .get('/1') + .reply(503, undefined, { 'Retry-After': 100 }); + nock('http://localhost/user/') + .get('/1') + .reply(200); + const orderToAdd = { + userId: 1, + productId: 2, + mode: 'approved', + }; + + // Дія + const response = await axiosAPIClient.post('/order', orderToAdd); + + // СтĐČĐ”Ń€ĐŽĐ¶ŃƒĐČĐ°ĐœĐœŃ + expect(response.status).toBe(200); +}); +``` + +
    + +
    + + +## âšȘ 2.13 ĐŸĐ”Ń€Đ”ĐČіртД п’ять ĐżĐŸŃ‚Đ”ĐœŃ†Ń–ĐčĐœĐžŃ… Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚Ń–ĐČ + +:white_check_mark: **Đ ĐŸĐ±Đž:** ĐŸĐ»Đ°ĐœŃƒŃŽŃ‡Đž сĐČĐŸŃ— тДстО, ĐżĐŸĐŽŃƒĐŒĐ°ĐčтД ĐżŃ€ĐŸ ĐŸŃ…ĐŸĐżĐ»Đ”ĐœĐœŃ п’ято Ń‚ĐžĐżĐŸĐČох ĐČĐžŃ…ĐŸĐŽŃ–ĐČ ĐżĐŸŃ‚ĐŸĐșу. ĐšĐŸĐ»Đž ĐČаш тДст запусĐșає пДĐČĐœŃƒ Юію (ĐœĐ°ĐżŃ€ĐžĐșлаЎ, ĐČĐžĐșлОĐș API), ĐČŃ–ĐŽĐ±ŃƒĐČається рДаĐșція, ĐČŃ–ĐŽĐ±ŃƒĐČається Ń‰ĐŸŃŃŒ Đ·ĐœĐ°Ń‡ŃƒŃ‰Đ” та ĐČĐžĐŒĐ°ĐłĐ°Ń” Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ. ЗауĐČажтД, Ń‰ĐŸ ĐœĐ°ĐŒ баĐčЎужД, яĐș ĐČсД працює. Мо Đ·ĐŸŃĐ”Ń€Đ”ĐŽĐ¶Đ”ĐœŃ– ĐœĐ° Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚Đ°Ń…, рДчах, яĐșі ĐżĐŸĐŒŃ–Ń‚ĐœŃ– Đ·Đ·ĐŸĐČĐœŃ– та ĐŒĐŸĐ¶ŃƒŃ‚ŃŒ ĐČĐżĐ»ĐžĐœŃƒŃ‚Đž ĐœĐ° ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČача. Щі Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚Đž/рДаĐșції ĐŒĐŸĐ¶ĐœĐ° Ń€ĐŸĐ·ĐŽŃ–Đ»ĐžŃ‚Đž ĐœĐ° 5 ĐșĐ°Ń‚Đ”ĐłĐŸŃ€Ń–Đč: + +‱ Đ’Ń–ĐŽĐżĐŸĐČіЮь - йДст ĐČĐžĐșлОĐșає Юію (ĐœĐ°ĐżŃ€ĐžĐșлаЎ, чДрДз API) і ĐŸŃ‚Ń€ĐžĐŒŃƒŃ” ĐČŃ–ĐŽĐżĐŸĐČіЮь. йДпДр ĐČŃ–Đœ заĐčĐŒĐ°Ń”Ń‚ŃŒŃŃ пДрДĐČірĐșĐŸŃŽ праĐČĐžĐ»ŃŒĐœĐŸŃŃ‚Ń– ĐŽĐ°ĐœĐžŃ… ĐČŃ–ĐŽĐżĐŸĐČіЮі, ŃŃ…Đ”ĐŒĐž та статусу HTTP + +‱ ĐĐŸĐČĐžĐč ŃŃ‚Đ°Đœ - ĐŸŃ–ŃĐ»Ń ĐČĐžĐșлОĐșу Юії ĐŽĐ”ŃĐșі **Đ·Đ°ĐłĐ°Đ»ŃŒĐœĐŸĐŽĐŸŃŃ‚ŃƒĐżĐœŃ–** ĐŽĐ°ĐœŃ–, ĐčĐŒĐŸĐČŃ–Ń€ĐœĐŸ, Đ·ĐŒŃ–ĐœŃŽŃŽŃ‚ŃŒŃŃ + +‱ Đ—ĐŸĐČĐœŃ–ŃˆĐœŃ– ĐČĐžĐșлОĐșĐž - ĐŸŃ–ŃĐ»Ń ĐČĐžĐșлОĐșу Юії ĐżŃ€ĐŸĐłŃ€Đ°ĐŒĐ° ĐŒĐŸĐ¶Đ” ĐČĐžĐșлОĐșато Đ·ĐŸĐČĐœŃ–ŃˆĐœŃ–Đč ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ чДрДз HTTP Đ°Đ±ĐŸ Đ±ŃƒĐŽŃŒ-яĐșĐžĐč Ń–ĐœŃˆĐžĐč Ń‚Ń€Đ°ĐœŃĐżĐŸŃ€Ń‚. НапроĐșлаЎ, ĐŽĐ·ĐČŃ–ĐœĐŸĐș ĐŽĐ»Ń ĐČіЮпраĐČĐșĐž SMS, ДлДĐșŃ‚Ń€ĐŸĐœĐœĐŸŃ— ĐżĐŸŃˆŃ‚Đž Đ°Đ±ĐŸ ŃŃ‚ŃĐłĐœĐ”ĐœĐœŃ ĐșĐŸŃˆŃ‚Ń–ĐČ Đ· ĐșŃ€Đ”ĐŽĐžŃ‚ĐœĐŸŃ— ĐșартĐșĐž + +‱ Message queues - Đ Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚ĐŸĐŒ ĐżĐŸŃ‚ĐŸĐșу ĐŒĐŸĐ¶Đ” Đ±ŃƒŃ‚Đž ĐżĐŸĐČŃ–ĐŽĐŸĐŒĐ»Đ”ĐœĐœŃ ĐČ Ń‡Đ”Ń€Đ·Ń– + +‱ ĐĄĐżĐŸŃŃ‚Đ”Ń€Đ”Đ¶Đ»ĐžĐČість - Đ”Đ”ŃĐșі рДчі ĐœĐ”ĐŸĐ±Ń…Ń–ĐŽĐœĐŸ ĐČŃ–ĐŽŃŃ‚Đ”Đ¶ŃƒĐČато, яĐș-ĐŸŃ‚ ĐżĐŸĐŒĐžĐ»ĐșĐž чо Đ·ĐœĐ°Ń‡ĐœŃ– ĐŽŃ–Đ»ĐŸĐČі ĐżĐŸĐŽŃ–Ń—. ĐšĐŸĐ»Đž Ń‚Ń€Đ°ĐœĐ·Đ°Đșція Đ·Đ°Đ·ĐœĐ°Ń” ĐœĐ”ĐČЮачі, ĐŒĐž ĐŸŃ‡Ń–ĐșŃƒŃ”ĐŒĐŸ ĐœĐ” лОшД праĐČĐžĐ»ŃŒĐœĐŸŃ— ĐČŃ–ĐŽĐżĐŸĐČіЮі, алД Đč праĐČĐžĐ»ŃŒĐœĐŸŃ— ĐŸĐ±Ń€ĐŸĐ±ĐșĐž ĐżĐŸĐŒĐžĐ»ĐŸĐș і ĐœĐ°Đ»Đ”Đ¶ĐœĐŸĐłĐŸ Đ¶ŃƒŃ€ĐœĐ°Đ»ŃŽĐČĐ°ĐœĐœŃ/ĐŒĐ”Ń‚Ń€ĐžĐș. Щя Ń–ĐœŃ„ĐŸŃ€ĐŒĐ°Ń†Ń–Ń ĐœĐ°ĐŽŃ…ĐŸĐŽĐžŃ‚ŃŒ Đ±Đ”Đ·ĐżĐŸŃĐ”Ń€Đ”ĐŽĐœŃŒĐŸ ĐŽĐŸ ЎужД ĐČажлОĐČĐŸĐłĐŸ ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČача – ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČача ops (SRE/Đ°ĐŽĐŒŃ–ĐœŃ–ŃŃ‚Ń€Đ°Ń‚ĐŸŃ€Đ°) + + + +

    + +# Section 3ïžâƒŁ: Frontend ĐąĐ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ + +## âšȘ  3.1 Đ’Ń–ĐŽĐŸĐșŃ€Đ”ĐŒŃ‚Đ” Ń–ĐœŃ‚Đ”Ń€Ń„Đ”Đčс ĐČіЮ Ń„ŃƒĐœĐșŃ†Ń–ĐŸĐœĐ°Đ»ŃŒĐœĐŸŃŃ‚Ń– + +:white_check_mark: **Đ ĐŸĐ±Đž:** ĐšĐŸĐ»Đž ĐČĐž Đ·ĐŸŃĐ”Ń€Đ”ĐŽĐ¶ŃƒŃ”Ń‚Đ”ŃŃ ĐœĐ° Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ– Đ»ĐŸĐłŃ–ĐșĐž ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Ń–ĐČ, ЎДталі Ń–ĐœŃ‚Đ”Ń€Ń„Đ”Đčсу ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČача стають ŃˆŃƒĐŒĐŸĐŒ, яĐșĐžĐč сліЎ ĐČОЎілОтО, Ń‰ĐŸĐ± ĐČаші тДстО ĐŒĐŸĐłĐ»Đž Đ·ĐŸŃĐ”Ń€Đ”ĐŽĐžŃ‚ĐžŃŃ ĐœĐ° чостох ĐŽĐ°ĐœĐžŃ…. На праĐșтоці ĐČотягуĐčтД ĐżĐŸŃ‚Ń€Ń–Đ±ĐœŃ– ĐŽĐ°ĐœŃ– Đ· Ń€ĐŸĐ·ĐŒŃ–Ń‚ĐșĐž абстраĐșŃ‚ĐœĐžĐŒ ŃĐżĐŸŃĐŸĐ±ĐŸĐŒ, яĐșĐžĐč ĐœĐ” ĐœĐ°ĐŽŃ‚ĐŸ ĐżĐŸĐČâ€™ŃĐ·Đ°ĐœĐžĐč Ń–Đ· ĐłŃ€Đ°Ń„Ń–Ń‡ĐœĐŸŃŽ рДалізацією, затĐČĐ”Ń€ĐŽĐ¶ŃƒĐčтД лОшД чості ĐŽĐ°ĐœŃ– (ĐœĐ° ĐČŃ–ĐŽĐŒŃ–ĐœŃƒ ĐČіЮ ĐłŃ€Đ°Ń„Ń–Ń‡ĐœĐžŃ… ЎДталДĐč HTML/CSS) і ĐČĐžĐŒĐžĐșаĐčтД Đ°ĐœŃ–ĐŒĐ°Ń†Ń–ŃŽ, яĐșа ŃĐżĐŸĐČŃ–Đ»ŃŒĐœŃŽŃ”Ń‚ŃŒŃŃ. ĐŁ ĐČас ĐŒĐŸĐ¶Đ” ĐČĐžĐœĐžĐșĐœŃƒŃ‚Đž ŃĐżĐŸĐșуса ŃƒĐœĐžĐșĐœŃƒŃ‚Đž ĐČŃ–Đ·ŃƒĐ°Đ»Ń–Đ·Đ°Ń†Ń–Ń— та ĐżŃ€ĐŸŃ‚Đ”ŃŃ‚ŃƒĐČато лОшД Đ·Đ°ĐŽĐœŃŽ Ń‡Đ°ŃŃ‚ĐžĐœŃƒ Ń–ĐœŃ‚Đ”Ń€Ń„Đ”Đčсу ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČача (ĐœĐ°ĐżŃ€ĐžĐșлаЎ, службО, Юії, ĐŒĐ°ĐłĐ°Đ·ĐžĐœ), алД цД прОзĐČДЎД ĐŽĐŸ ĐČĐžĐłĐ°ĐŽĐ°ĐœĐžŃ… тДстіĐČ, яĐșі ĐœĐ” ŃŃ…ĐŸĐ¶Ń– ĐœĐ° Ń€Đ”Đ°Đ»ŃŒĐœŃ–ŃŃ‚ŃŒ, і ĐœĐ” ĐČояĐČĐ»ŃŃ‚ŃŒ ĐČОпаЎĐșіĐČ, ĐșĐŸĐ»Đž праĐČĐžĐ»ŃŒĐœŃ– ĐŽĐ°ĐœŃ– ĐœĐ°ĐČіть ĐœĐ” ĐżĐŸŃ‚Ń€Đ°ĐżĐ»ŃŃ” ĐČ Ń–ĐœŃ‚Đ”Ń€Ń„Đ”Đčс ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČача + +
    + +❌ **Đ†ĐœĐ°ĐșшД:** Чості ĐŸĐ±Ń‡ĐžŃĐ»Đ”ĐœŃ– ĐŽĐ°ĐœŃ– ĐČĐ°ŃˆĐŸĐłĐŸ Ń‚Đ”ŃŃ‚Ńƒ ĐŒĐŸĐ¶ŃƒŃ‚ŃŒ Đ±ŃƒŃ‚Đž ĐłĐŸŃ‚ĐŸĐČі чДрДз 10 ĐŒŃ, алД Ń‚ĐŸĐŽŃ– ĐČĐ”ŃŃŒ тДст троĐČĐ°Ń‚ĐžĐŒĐ” 500 ĐŒŃ (100 тДстіĐČ = 1 хĐČ) чДрДз ĐŽĐ”ŃĐșу Ń„Đ°ĐœŃ‚Đ°ŃŃ‚ĐžŃ‡ĐœŃƒ та ĐœĐ”Ń€Đ”Đ»Đ”ĐČĐ°ĐœŃ‚ĐœŃƒ Đ°ĐœŃ–ĐŒĐ°Ń†Ń–ŃŽ + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :clap: ПроĐșлаЎ праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: Đ’Ń–ĐŽĐŸĐșŃ€Đ”ĐŒĐ»Đ”ĐœĐœŃ ЎДталДĐč Ń–ĐœŃ‚Đ”Ń€Ń„Đ”Đčсу + +![](https://img.shields.io/badge/🔧%20Example%20using%20React-blue.svg "ПроĐșлаЎ Đ· React") ![](https://img.shields.io/badge/🔧%20Example%20using%20React%20Testing%20Library-blue.svg "ПроĐșлаЎ Đ· react-testing-library") + +```javascript +test("When users-list is flagged to show only VIP, should display only VIP members", () => { + // Arrange + const allUsers = [{ id: 1, name: "Yoni Goldberg", vip: false }, { id: 2, name: "John Doe", vip: true }]; + + // Act + const { getAllByTestId } = render(); + + // Assert - ĐĄĐżĐŸŃ‡Đ°Ń‚Đșу ĐČĐžŃ‚ŃĐłĐœŃ–Ń‚ŃŒ ĐŽĐ°ĐœŃ– Đ· Ń–ĐœŃ‚Đ”Ń€Ń„Đ”Đčсу ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČача + const allRenderedUsers = getAllByTestId("user").map(uiElement => uiElement.textContent); + const allRealVIPUsers = allUsers.filter(user => user.vip).map(user => user.name); + expect(allRenderedUsers).toEqual(allRealVIPUsers); //compare data with data, no UI here +}); +``` + +
    + +### :thumbsdown: ПроĐșлаЎ Đ°ĐœŃ‚ĐžŃˆĐ°Đ±Đ»ĐŸĐœŃƒ: ДДталі та ĐŽĐ°ĐœŃ– Ń–ĐœŃ‚Đ”Ń€Ń„Đ”Đčсу ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČача Đ·ĐŒŃ–ŃˆĐ°ĐœŃ– + +```javascript +test("When flagging to show only VIP, should display only VIP members", () => { + // Arrange + const allUsers = [{ id: 1, name: "Yoni Goldberg", vip: false }, { id: 2, name: "John Doe", vip: true }]; + + // Act + const { getAllByTestId } = render(); + + // Assert - Đ—ĐŒŃ–ŃˆŃƒĐčтД Ń–ĐœŃ‚Đ”Ń€Ń„Đ”Đčс і ĐŽĐ°ĐœŃ– ĐČ Ń‚ĐČĐ”Ń€ĐŽĐ¶Đ”ĐœĐœŃ– + expect(getAllByTestId("user")).toEqual('[
  • John Doe
  • ]'); +}); +``` + +
    + +

    + +## âšȘ  3.2 Запот Đ”Đ»Đ”ĐŒĐ”ĐœŃ‚Ń–ĐČ HTML ĐœĐ° ĐŸŃĐœĐŸĐČі Đ°Ń‚Ń€ĐžĐ±ŃƒŃ‚Ń–ĐČ, яĐșі ĐœĐ°ĐČряЮ чо Đ·ĐŒŃ–ĐœŃŃ‚ŃŒŃŃ + +:white_check_mark: **Đ ĐŸĐ±Đž:** ВоĐșĐŸĐœŃƒĐčтД запОт ĐŽĐŸ HTML-Đ”Đ»Đ”ĐŒĐ”ĐœŃ‚Ń–ĐČ ĐœĐ° ĐŸŃĐœĐŸĐČі Đ°Ń‚Ń€ĐžĐ±ŃƒŃ‚Ń–ĐČ, яĐșі, ĐčĐŒĐŸĐČŃ–Ń€ĐœĐŸ, пДрДжОĐČуть ĐłŃ€Đ°Ń„Ń–Ń‡ĐœŃ– Đ·ĐŒŃ–ĐœĐž, ĐœĐ° ĐČŃ–ĐŽĐŒŃ–ĐœŃƒ ĐČіЮ сДлДĐșŃ‚ĐŸŃ€Ń–ĐČ CSS і ĐŒŃ–Ń‚ĐŸĐș Ń„ĐŸŃ€ĐŒ. ĐŻĐșŃ‰ĐŸ ĐżŃ€ĐžĐ·ĐœĐ°Ń‡Đ”ĐœĐžĐč Đ”Đ»Đ”ĐŒĐ”ĐœŃ‚ ĐœĐ” ĐŒĐ°Ń” таĐșох Đ°Ń‚Ń€ĐžĐ±ŃƒŃ‚Ń–ĐČ, стĐČĐŸŃ€Ń–Ń‚ŃŒ ŃĐżĐ”Ń†Ń–Đ°Đ»ŃŒĐœĐžĐč Ń‚Đ”ŃŃ‚ĐŸĐČĐžĐč Đ°Ń‚Ń€ĐžĐ±ŃƒŃ‚, ĐœĐ°ĐżŃ€ĐžĐșлаЎ «test-id-submit-button». ВоĐșĐŸĐœĐ°ĐœĐœŃ Ń†ŃŒĐŸĐłĐŸ ŃˆĐ»ŃŃ…Ńƒ ĐœĐ” Ń‚Ń–Đ»ŃŒĐșĐž ĐłĐ°Ń€Đ°ĐœŃ‚ŃƒŃ”, Ń‰ĐŸ ĐČаші Ń„ŃƒĐœĐșŃ†Ń–ĐŸĐœĐ°Đ»ŃŒĐœŃ–/Đ»ĐŸĐłŃ–Ń‡ĐœŃ– тДстО ĐœŃ–ĐșĐŸĐ»Đž ĐœĐ” Đ·Đ»Đ°ĐŒĐ°ŃŽŃ‚ŃŒŃŃ чДрДз Đ·ĐŒŃ–ĐœĐž Đ·ĐŸĐČĐœŃ–ŃˆĐœŃŒĐŸĐłĐŸ ĐČĐžĐłĐ»ŃĐŽŃƒ, алД таĐșĐŸĐ¶ стає Đ·Ń€ĐŸĐ·ŃƒĐŒŃ–Đ»ĐžĐŒ ĐŽĐ»Ń ĐČсієї ĐșĐŸĐŒĐ°ĐœĐŽĐž, Ń‰ĐŸ цДĐč Đ”Đ»Đ”ĐŒĐ”ĐœŃ‚ і Đ°Ń‚Ń€ĐžĐ±ŃƒŃ‚ ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČуються Ń‚Đ”ŃŃ‚Đ°ĐŒĐž, і їх ĐœĐ” сліЎ ĐČĐžĐŽĐ°Đ»ŃŃ‚Đž + +
    + +❌ **Đ†ĐœĐ°ĐșшД:** Во Ń…ĐŸŃ‡Đ”Ń‚Đ” пДрДĐČірото Ń„ŃƒĐœĐșŃ†Ń–ĐŸĐœĐ°Đ»ŃŒĐœŃ–ŃŃ‚ŃŒ ĐČŃ…ĐŸĐŽŃƒ, яĐșа ĐŸŃ…ĐŸĐżĐ»ŃŽŃ” Đ±Đ°ĐłĐ°Ń‚ĐŸ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Ń–ĐČ, Đ»ĐŸĐłŃ–Đșу та службО, ĐČсД ĐœĐ°Đ»Đ°ŃˆŃ‚ĐŸĐČĐ°ĐœĐŸ Ń–ĐŽĐ”Đ°Đ»ŃŒĐœĐŸ – заглушĐșĐž, ŃˆĐżĐžĐłŃƒĐœĐž, ĐČĐžĐșлОĐșĐž Ajax Ń–Đ·ĐŸĐ»ŃŒĐŸĐČĐ°ĐœŃ–. ВсД Đ·ĐŽĐ°Ń”Ń‚ŃŒŃŃ Ń–ĐŽĐ”Đ°Đ»ŃŒĐœĐžĐŒ. ĐąĐŸĐŽŃ– тДст ĐœĐ” ĐČЮається, Ń‚ĐŸĐŒŃƒ Ń‰ĐŸ ЎОзаĐčĐœĐ”Ń€ Đ·ĐŒŃ–ĐœĐžĐČ Đșлас CSS div Đ· 'thick-border' ĐœĐ° 'thin-border' + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :clap: ПроĐșлаЎ праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: Запот Đ”Đ»Đ”ĐŒĐ”ĐœŃ‚Đ° за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ ŃĐżĐ”Ń†Ń–Đ°Đ»ŃŒĐœĐŸĐłĐŸ Đ°Ń‚Ń€ĐžĐ±ŃƒŃ‚Đ° ĐŽĐ»Ń Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ + +![](https://img.shields.io/badge/🔧%20Example%20using%20React-blue.svg "ПроĐșлаЎ Đ· React") + +```html +// ĐšĐŸĐŽ Ń€ĐŸĐ·ĐŒŃ–Ń‚ĐșĐž (React component) +

    + + {value} + + +

    +``` + +```javascript +// у Ń†ŃŒĐŸĐŒŃƒ проĐșлаЎі ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČується Đ±Ń–Đ±Đ»Ń–ĐŸŃ‚Đ”Đșа react-testing-library +test("Whenever no data is passed to metric, show 0 as default", () => { + // Arrange + const metricValue = undefined; + + // Act + const { getByTestId } = render(); + + expect(getByTestId("errorsLabel").text()).toBe("0"); +}); +``` + +
    + +### :thumbsdown: ПроĐșлаЎ Đ°ĐœŃ‚ĐžŃˆĐ°Đ±Đ»ĐŸĐœŃƒ: ĐŸĐŸĐșĐ»Đ°ĐŽĐ°ŃŽŃ‡ĐžŃŃŒ ĐœĐ° Đ°Ń‚Ń€ĐžĐ±ŃƒŃ‚Đž + +```html + +{value} + +``` + +```javascript +// ПроĐșлаЎ Đ· Еnzyme +test("Whenever no data is passed, error metric shows zero", () => { + // ... + + expect(wrapper.find("[className='d-flex-column']").text()).toBe("0"); +}); +``` + +
    + +
    + +## âšȘ  3.3 За ĐŒĐŸĐ¶Đ»ĐžĐČĐŸŃŃ‚Ń– Ń‚Đ”ŃŃ‚ŃƒĐčтД Đ· Ń€Đ”Đ°Đ»Ń–ŃŃ‚ĐžŃ‡ĐœĐžĐŒ і ĐżĐŸĐČĐœŃ–ŃŃ‚ŃŽ ĐČіЮтĐČĐŸŃ€Đ”ĐœĐžĐŒ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ĐŸĐŒ + +:white_check_mark: **Đ ĐŸĐ±Đž:** Đ©ĐŸŃ€Đ°Đ·Ńƒ, ĐșĐŸĐ»Đž ĐČаш ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ ĐŒĐ°Ń” проĐčĐœŃŃ‚ĐœĐžĐč Ń€ĐŸĐ·ĐŒŃ–Ń€, Ń‚Đ”ŃŃ‚ŃƒĐčтД сĐČіĐč ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ Đ·Đ·ĐŸĐČĐœŃ–, яĐș цД Ń€ĐŸĐ±Đ»ŃŃ‚ŃŒ ĐČаші ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČачі, ĐżĐŸĐČĐœŃ–ŃŃ‚ŃŽ ĐČŃ–Đ·ŃƒĐ°Đ»Ń–Đ·ŃƒĐčтД ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČĐ°Đ»ŃŒĐœĐžŃ†ŃŒĐșĐžĐč Ń–ĐœŃ‚Đ”Ń€Ń„Đ”Đčс, Ń€Đ”Đ°ĐłŃƒĐčтД ĐœĐ° ĐœŃŒĐŸĐłĐŸ та стĐČĐ”Ń€ĐŽĐ¶ŃƒĐčтД, Ń‰ĐŸ ĐŸĐ±Ń€ĐŸĐ±Đ»Đ”ĐœĐžĐč ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČĐ°Đ»ŃŒĐœĐžŃ†ŃŒĐșĐžĐč Ń–ĐœŃ‚Đ”Ń€Ń„Đ”Đčс ĐżĐŸĐČĐŸĐŽĐžŃ‚ŃŒŃŃ ĐœĐ°Đ»Đ”Đ¶ĐœĐžĐŒ Ń‡ĐžĐœĐŸĐŒ. ĐŁĐœĐžĐșаĐčтД Đ±ŃƒĐŽŃŒ-яĐșĐŸĐłĐŸ Đ·ĐœŃƒŃ‰Đ°Đ»ŃŒĐœĐŸĐłĐŸ, частĐșĐŸĐČĐŸĐłĐŸ та ĐżĐŸĐČĐ”Ń€Ń…ĐœĐ”ĐČĐŸĐłĐŸ Ń€Đ”ĐœĐŽĐ”Ń€ĐžĐœĐłŃƒ — таĐșĐžĐč піЮхіЮ ĐŒĐŸĐ¶Đ” прОзĐČДстО ĐŽĐŸ ĐœĐ”ĐČĐ»ĐŸĐČĐ»Đ”ĐœĐžŃ… ĐżĐŸĐŒĐžĐ»ĐŸĐș чДрДз браĐș ЎДталДĐč і ĐżĐŸŃĐžĐ»ĐžŃ‚Đž Ń‚Đ”Ń…ĐœŃ–Ń‡ĐœĐ” ĐŸĐ±ŃĐ»ŃƒĐłĐŸĐČуĐČĐ°ĐœĐœŃ, ĐŸŃĐșŃ–Đ»ŃŒĐșĐž тДстО Đ·Ń–ĐżŃŃƒŃŽŃ‚ŃŒŃŃ Đ· ĐČĐœŃƒŃ‚Ń€Ń–ŃˆĐœŃ–ĐŒĐž Đ”Đ»Đ”ĐŒĐ”ĐœŃ‚Đ°ĐŒĐž (ĐŽĐžĐČ. ĐŒĐ°Ń€ĐșĐŸĐČĐ°ĐœĐžĐč ĐżŃƒĐœĐșт ['Favour blackbox testing'](https://github). .com/goldbergyoni/javascript-testing-best-practices#-%EF%B8%8F-14-stick-to-black-box-testing-test-only-public-methods)). ĐŻĐșŃ‰ĐŸ ĐŸĐŽĐžĐœ Ń–Đ· ĐŽĐŸŃ‡Ń–Ń€ĐœŃ–Ń… ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Ń–ĐČ Đ·ĐœĐ°Ń‡ĐœĐŸ ŃƒĐżĐŸĐČŃ–Đ»ŃŒĐœŃŽŃ” (ĐœĐ°ĐżŃ€ĐžĐșлаЎ, Đ°ĐœŃ–ĐŒĐ°Ń†Ń–Ń) Đ°Đ±ĐŸ усĐșĐ»Đ°ĐŽĐœŃŽŃ” ĐœĐ°Đ»Đ°ŃˆŃ‚ŃƒĐČĐ°ĐœĐœŃ, ĐżĐŸĐŽŃƒĐŒĐ°ĐčтД ĐżŃ€ĐŸ яĐČĐœŃƒ Đ·Đ°ĐŒŃ–ĐœŃƒ ĐčĐŸĐłĐŸ ĐœĐ° ĐżŃ–ĐŽŃ€ĐŸĐ±Đșу + +З ĐŸĐłĐ»ŃĐŽŃƒ ĐœĐ° ĐČсД сĐșĐ°Đ·Đ°ĐœĐ”, ĐČĐ°Ń€Ń‚ĐŸ Đ·Ń€ĐŸĐ±ĐžŃ‚Đž ĐŸĐŽĐœĐ” Đ·Đ°ŃŃ‚Đ”Ń€Đ”Đ¶Đ”ĐœĐœŃ: ця Ń‚Đ”Ń…ĐœŃ–Đșа працює ĐŽĐ»Ń ĐŒĐ°Đ»ĐžŃ…/ŃĐ”Ń€Đ”ĐŽĐœŃ–Ń… ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Ń–ĐČ, яĐșі ĐŒŃ–ŃŃ‚ŃŃ‚ŃŒ ĐŽĐŸŃ‡Ń–Ń€ĐœŃ– ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đž Ń€ĐŸĐ·ŃƒĐŒĐœĐŸĐłĐŸ Ń€ĐŸĐ·ĐŒŃ–Ń€Ńƒ. ĐŸĐŸĐČĐœĐ” ĐČіЮтĐČĐŸŃ€Đ”ĐœĐœŃ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đ° Ń–Đ· Đ·Đ°ĐœĐ°ĐŽŃ‚ĐŸ ĐČДлОĐșĐŸŃŽ ĐșŃ–Đ»ŃŒĐșістю ĐŽĐŸŃ‡Ń–Ń€ĐœŃ–Ń… ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Ń–ĐČ ŃƒŃĐșĐ»Đ°ĐŽĐœĐžŃ‚ŃŒ ĐŸĐ±Ò‘Ń€ŃƒĐœŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐżĐŸĐŒĐžĐ»ĐŸĐș Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ (Đ°ĐœĐ°Đ»Ń–Đ· ĐżĐ”Ń€ŃˆĐŸĐżŃ€ĐžŃ‡ĐžĐœĐž) і ĐŒĐŸĐ¶Đ” працюĐČато ĐœĐ°ĐŽŃ‚ĐŸ ĐżĐŸĐČŃ–Đ»ŃŒĐœĐŸ. ĐŁ таĐșох ĐČОпаЎĐșах ĐœĐ°ĐżĐžŃˆŃ–Ń‚ŃŒ лОшД ĐșŃ–Đ»ŃŒĐșа тДстіĐČ ĐżŃ€ĐŸŃ‚Đž Ń†ŃŒĐŸĐłĐŸ Đ¶ĐžŃ€ĐœĐŸĐłĐŸ Đ±Đ°Ń‚ŃŒĐșіĐČсьĐșĐŸĐłĐŸ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đ° та Đ±Ń–Đ»ŃŒŃˆĐ” тДстіĐČ Ń‰ĐŸĐŽĐŸ ĐčĐŸĐłĐŸ ĐŽĐŸŃ‡Ń–Ń€ĐœŃ–Ń… ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Ń–ĐČ + +
    + +❌ **Đ†ĐœĐ°ĐșшД:** ПіЮ час ĐżĐ”Ń€Đ”ĐłĐ»ŃĐŽŃƒ ĐČĐœŃƒŃ‚Ń€Ń–ŃˆĐœŃ–Ń… ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Ń–ĐČ, ĐČĐžĐșлОĐșаючо ĐčĐŸĐłĐŸ проĐČĐ°Ń‚ĐœŃ– ĐŒĐ”Ń‚ĐŸĐŽĐž та пДрДĐČіряючо ĐČĐœŃƒŃ‚Ń€Ń–ŃˆĐœŃ–Đč ŃŃ‚Đ°Đœ, ĐČĐ°ĐŒ ĐŽĐŸĐČĐ”ĐŽĐ”Ń‚ŃŒŃŃ ĐČĐžĐșĐŸĐœĐ°Ń‚Đž рДфаĐșŃ‚ĐŸŃ€ĐžĐœĐł усіх тДстіĐČ ĐżŃ–ĐŽ час рДфаĐșŃ‚ĐŸŃ€ĐžĐœĐłŃƒ рДалізації ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Ń–ĐČ. ЧО спраĐČЮі у ĐČас є ĐŒĐŸĐ¶Đ»ĐžĐČĐŸŃŃ‚Ń– ĐŽĐ»Ń таĐșĐŸĐłĐŸ ріĐČĐœŃ ĐŸĐ±ŃĐ»ŃƒĐłĐŸĐČуĐČĐ°ĐœĐœŃ? + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :clap: ПроĐșлаЎ праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: Đ Đ”Đ°Đ»Ń–ŃŃ‚ĐžŃ‡ĐœĐ° Ń€ĐŸĐ±ĐŸŃ‚Đ° Đ· ĐżĐŸĐČĐœŃ–ŃŃ‚ŃŽ ĐČŃ–ĐŽŃ€Đ”ĐœĐŽĐ”Ń€Đ”ĐœĐžĐŒ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ĐŸĐŒ + +![](https://img.shields.io/badge/🔧%20Example%20using%20React-blue.svg "ПроĐșлаЎ Đ· React") ![](https://img.shields.io/badge/🔧%20Example%20using%20Enzyme-blue.svg "ПроĐșлаЎ Đ· Enzyme") + +```javascript +class Calendar extends React.Component { + static defaultProps = { showFilters: false }; + + render() { + return ( +
    + /* ĐŸĐ°ĐœĐ”Đ»ŃŒ Ń„Ń–Đ»ŃŒŃ‚Ń€Ń–ĐČ Ń–Đ· ĐșĐœĐŸĐżĐșĐŸŃŽ, Ń‰ĐŸĐ± ĐżŃ€ĐžŃ…ĐŸĐČато/ĐżĐŸĐșазатО Ń„Ń–Đ»ŃŒŃ‚Ń€Đž */ + +
    + ); + } +} + +// ПроĐșлаЎ Đ· React & Enzyme +test("Realistic approach: When clicked to show filters, filters are displayed", () => { + // Arrange + const wrapper = mount(); + + // Act + wrapper.find("button").simulate("click"); + + // Assert + expect(wrapper.text().includes("Choose Filter")); + // Ось яĐș ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČач піЮіĐčĐŽĐ” ĐŽĐŸ Ń†ŃŒĐŸĐłĐŸ Đ”Đ»Đ”ĐŒĐ”ĐœŃ‚Đ°: за тДĐșŃŃ‚ĐŸĐŒ +}); +``` + +### :thumbsdown: ПроĐșлаЎ Đ°ĐœŃ‚ĐžŃˆĐ°Đ±Đ»ĐŸĐœŃƒ: ĐœĐŸĐșŃ–ĐœĐł за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ ĐœĐ”ĐłĐ»ĐžĐ±ĐŸĐșĐŸĐłĐŸ(shallow) Ń€Đ”ĐœĐŽĐ”Ń€Ń–ĐœĐłŃƒ + +```javascript +test("Shallow/mocked approach: When clicked to show filters, filters are displayed", () => { + // Arrange + const wrapper = shallow(); + + // Act + wrapper + .find("filtersPanel") + .instance() + .showFilters(); + // Đ”ĐŸŃ‚ĐŸŃ€ĐșĐœŃ–Ń‚ŃŒŃŃ ĐŽĐŸ ĐČĐœŃƒŃ‚Ń€Ń–ŃˆĐœŃ–Ń… Đ”Đ»Đ”ĐŒĐ”ĐœŃ‚Ń–ĐČ, ĐŸĐ±Ń–ĐčЮіть Ń–ĐœŃ‚Đ”Ń€Ń„Đ”Đčс і ĐČĐžĐșлОĐșаĐčтД ĐŒĐ”Ń‚ĐŸĐŽ. ПіЮхіЮ Đ±Ń–Đ»ĐŸĐłĐŸ ящоĐșа + + // Assert + expect(wrapper.find("Filter").props()).toEqual({ title: "Choose Filter" }); + // Ń‰ĐŸ, яĐșŃ‰ĐŸ ĐŒĐž Đ·ĐŒŃ–ĐœĐžĐŒĐŸ Ń–ĐŒâ€™Ń ĐżŃ€ĐŸĐżŃƒ Đ°Đ±ĐŸ ĐœĐ” ĐżĐ”Ń€Đ”ĐŽĐ°ĐŒĐŸ ĐœŃ–Ń‡ĐŸĐłĐŸ ĐČŃ–ĐŽĐżĐŸĐČŃ–ĐŽĐœĐŸĐłĐŸ? +}); +``` + +
    + +
    + +## âšȘ  3.4 ĐĐ” спіть, ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČуĐčтД ĐČĐ±ŃƒĐŽĐŸĐČĐ°ĐœŃƒ ĐżŃ–ĐŽŃ‚Ń€ĐžĐŒĐșу фрДĐčĐŒĐČĐŸŃ€ĐșіĐČ ĐŽĐ»Ń Đ°ŃĐžĐœŃ…Ń€ĐŸĐœĐœĐžŃ… ĐżĐŸĐŽŃ–Đč. йаĐșĐŸĐ¶ ŃĐżŃ€ĐŸĐ±ŃƒĐčтД просĐșĐŸŃ€ĐžŃ‚Đž ĐżŃ€ĐŸŃ†Đ”Ń + +:white_check_mark: **Đ ĐŸĐ±Đž:** ĐŁ Đ±Đ°ĐłĐ°Ń‚ŃŒĐŸŃ… ĐČОпаЎĐșах час заĐČĐ”Ń€ŃˆĐ”ĐœĐœŃ Đ±Đ»ĐŸĐșу, Ń‰ĐŸ Ń‚Đ”ŃŃ‚ŃƒŃ”Ń‚ŃŒŃŃ, ĐżŃ€ĐŸŃŃ‚ĐŸ ĐœĐ”ĐČŃ–ĐŽĐŸĐŒĐžĐč (ĐœĐ°ĐżŃ€ĐžĐșлаЎ, Đ°ĐœŃ–ĐŒĐ°Ń†Ń–Ń ĐżŃ€ĐžĐ·ŃƒĐżĐžĐœŃŃ” ĐżĐŸŃĐČу Đ”Đ»Đ”ĐŒĐ”ĐœŃ‚Đ°) - у таĐșĐŸĐŒŃƒ ĐČОпаЎĐșу ŃƒĐœĐžĐșаĐčтД ŃĐżĐ»ŃŃ‡ĐŸĐłĐŸ Ń€Đ”Đ¶ĐžĐŒŃƒ (ĐœĐ°ĐżŃ€ĐžĐșлаЎ, setTimeOut) і ĐČіЮЮаĐčтД пДрДĐČагу Đ±Ń–Đ»ŃŒŃˆ ĐŽĐ”Ń‚Đ”Ń€ĐŒŃ–ĐœĐŸĐČĐ°ĐœĐžĐŒ ĐŒĐ”Ń‚ĐŸĐŽĐ°ĐŒ, яĐșі ĐżŃ€ĐŸĐżĐŸĐœŃƒŃŽŃ‚ŃŒ Đ±Ń–Đ»ŃŒŃˆŃ–ŃŃ‚ŃŒ ĐżĐ»Đ°Ń‚Ń„ĐŸŃ€ĐŒ. Đ”Đ”ŃĐșі Đ±Ń–Đ±Đ»Ń–ĐŸŃ‚Đ”ĐșĐž ĐŽĐŸĐ·ĐČĐŸĐ»ŃŃŽŃ‚ŃŒ ĐŸŃ‡Ń–ĐșуĐČато ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ ĐŸĐżĐ”Ń€Đ°Ń†Ń–Đč (ĐœĐ°ĐżŃ€ĐžĐșлаЎ, [Cypress cy.request('url')](https://docs.cypress.io/guides/references/best-practices.html#Unnecessary-Waiting)), Ń–ĐœŃˆŃ– ĐœĐ°ĐŽĐ°ŃŽŃ‚ŃŒ API ĐŽĐ»Ń ĐŸŃ‡Ń–ĐșуĐČĐ°ĐœĐœŃ, яĐș [@testing-library/dom method wait(expect(element))](https://testing-library.com/docs/guide-disappearance). Đ†ĐœĐŸĐŽŃ– Đ±Ń–Đ»ŃŒŃˆ Đ”Đ»Đ”ĐłĐ°ĐœŃ‚ĐœĐžĐŒ ŃĐżĐŸŃĐŸĐ±ĐŸĐŒ є заглушĐșа ĐżĐŸĐČŃ–Đ»ŃŒĐœĐŸĐłĐŸ Ń€Đ”ŃŃƒŃ€ŃŃƒ, ĐœĐ°ĐżŃ€ĐžĐșлаЎ API, а ĐżĐŸŃ‚Ń–ĐŒ, ĐșĐŸĐ»Đž ĐŒĐŸĐŒĐ”ĐœŃ‚ ĐČŃ–ĐŽĐżĐŸĐČіЮі ŃŃ‚Đ°ĐœĐ” ĐŽĐ”Ń‚Đ”Ń€ĐŒŃ–ĐœĐŸĐČĐ°ĐœĐžĐŒ, ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ ĐŒĐŸĐ¶Đ” Đ±ŃƒŃ‚Đž яĐČĐœĐŸ ĐżĐŸĐČŃ‚ĐŸŃ€ĐœĐŸ ĐČіЮтĐČĐŸŃ€Đ”ĐœĐžĐč. ĐŻĐșŃ‰ĐŸ Đ·Đ°Đ»Đ”Đ¶ĐœĐŸ ĐČіЮ Đ·ĐŸĐČĐœŃ–ŃˆĐœŃŒĐŸĐłĐŸ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đ°, яĐșĐžĐč споть, ĐŒĐŸĐ¶Đ” ĐČояĐČотося ĐșĐŸŃ€ĐžŃĐœĐžĐŒ [ĐżĐŸŃĐżŃ–ŃˆĐžŃ‚Đž ĐłĐŸĐŽĐžĐœĐœĐžĐș](https://jestjs.io/docs/en/timer-mocks). ĐĄĐŸĐœ — цД ŃˆĐ°Đ±Đ»ĐŸĐœ, яĐșĐŸĐłĐŸ сліЎ ŃƒĐœĐžĐșато, ĐŸŃĐșŃ–Đ»ŃŒĐșĐž ĐČŃ–Đœ Đ·ĐŒŃƒŃˆŃƒŃ” ĐČаш тДст Đ±ŃƒŃ‚Đž ĐżĐŸĐČŃ–Đ»ŃŒĐœĐžĐŒ Đ°Đ±ĐŸ рОзОĐșĐŸĐČĐ°ĐœĐžĐŒ (яĐșŃ‰ĐŸ ĐČĐž чДĐșаєтД Đ·Đ°ĐœĐ°ĐŽŃ‚ĐŸ ĐșĐŸŃ€ĐŸŃ‚ĐșĐžĐč ĐżĐ”Ń€Ń–ĐŸĐŽ). Đ©ĐŸŃ€Đ°Đ·Ńƒ, ĐșĐŸĐ»Đž ŃĐżĐ»ŃŃ‡ĐžĐč Ń€Đ”Đ¶ĐžĐŒ і ĐŸĐżĐžŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐœĐ”ĐŒĐžĐœŃƒŃ‡Ń–, а ĐżĐ»Đ°Ń‚Ń„ĐŸŃ€ĐŒĐ° Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐœĐ” ĐżŃ–ĐŽŃ‚Ń€ĐžĐŒŃƒŃ”, ĐŽĐ”ŃĐșі Đ±Ń–Đ±Đ»Ń–ĐŸŃ‚Đ”ĐșĐž npm, яĐș-ĐŸŃ‚ [wait-for-expect](https://www.npmjs.com/package/wait-for-expect), ĐŒĐŸĐ¶ŃƒŃ‚ŃŒ ĐŽĐŸĐżĐŸĐŒĐŸĐłŃ‚Đž Đ· ĐœĐ°ĐżŃ–ĐČ - ĐŽĐ”Ń‚Đ”Ń€ĐŒŃ–ĐœĐŸĐČĐ°ĐœĐ” Ń€Ń–ŃˆĐ”ĐœĐœŃ +
    + +❌ **Đ†ĐœĐ°ĐșшД:** Про троĐČĐ°Đ»ĐŸĐŒŃƒ ŃĐœŃ– тДстО Đ±ŃƒĐŽŃƒŃ‚ŃŒ ĐœĐ° ĐżĐŸŃ€ŃĐŽĐŸĐș ĐżĐŸĐČŃ–Đ»ŃŒĐœŃ–ŃˆĐ”. ПіЮ час ŃĐżŃ€ĐŸĐ±Đž ĐżĐ”Ń€Đ”Ń…ĐŸĐŽŃƒ ĐČ Ń€Đ”Đ¶ĐžĐŒ ŃĐœŃƒ ĐŽĐ»Ń ĐœĐ”ĐČДлОĐșох чОсДл тДст ĐœĐ” ĐČЮасться, яĐșŃ‰ĐŸ Ń‚Đ”ŃŃ‚ĐŸĐČĐ°ĐœĐžĐč простріĐč ĐœĐ” ĐČŃ–ĐŽĐżĐŸĐČіĐČ ŃĐČĐŸŃ”Ń‡Đ°ŃĐœĐŸ. ОтжД, цД Đ·ĐČĐŸĐŽĐžŃ‚ŃŒŃŃ ĐŽĐŸ ĐșĐŸĐŒĐżŃ€ĐŸĐŒŃ–ŃŃƒ ĐŒŃ–Đ¶ ĐœĐ”ŃŃ‚Đ°Đ±Ń–Đ»ŃŒĐœŃ–ŃŃ‚ŃŽ та ĐżĐŸĐłĐ°ĐœĐŸŃŽ ĐżŃ€ĐŸĐŽŃƒĐșтоĐČĐœŃ–ŃŃ‚ŃŽ + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :clap: ПроĐșлаЎ праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: API E2E, яĐșĐžĐč ĐČорішується лОшД ĐżŃ–ŃĐ»Ń ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ Đ°ŃĐžĐœŃ…Ń€ĐŸĐœĐœĐžŃ… ĐŸĐżĐ”Ń€Đ°Ń†Ń–Đč (Cypress) + +![](https://img.shields.io/badge/🔹%20Example%20using%20Cypress-blue.svg "ВоĐșĐŸŃ€ĐžŃŃ‚Đ°ĐœĐœŃ Cypress ĐŽĐ»Ń ілюстрації іЎДї") +![](https://img.shields.io/badge/🔧%20Example%20using%20React%20Testing%20Library-blue.svg "ПроĐșлаЎ Đ· react-testing-library") + +```javascript +// ВоĐșĐŸŃ€ĐžŃŃ‚Đ°ĐœĐœŃ Cypress +cy.get("#show-products").click(); // НаĐČігація +cy.wait("@products"); // зачДĐșаĐčтД, ĐżĐŸĐșĐž Đ·'яĐČоться ĐŒĐ°Ń€ŃˆŃ€ŃƒŃ‚ +// цДĐč Ń€ŃĐŽĐŸĐș буЎД ĐČĐžĐșĐŸĐœĐ°ĐœĐŸ лОшД Ń‚ĐŸĐŽŃ–, ĐșĐŸĐ»Đž ĐŒĐ°Ń€ŃˆŃ€ŃƒŃ‚ буЎД ĐłĐŸŃ‚ĐŸĐČĐžĐč +``` + +### :clap: ПроĐșлаЎ праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: ĐąĐ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ Đ±Ń–Đ±Đ»Ń–ĐŸŃ‚Đ”ĐșĐž, яĐșа ĐŸŃ‡Ń–Đșує Đ”Đ»Đ”ĐŒĐ”ĐœŃ‚Đž DOM + +```javascript +// @testing-library/dom +test("movie title appears", async () => { + // Đ”Đ»Đ”ĐŒĐ”ĐœŃ‚ ŃĐżĐŸŃ‡Đ°Ń‚Đșу ĐČŃ–ĐŽŃŃƒŃ‚ĐœŃ–Đč... + + // чДĐșато ĐżĐŸŃĐČĐž + await wait(() => { + expect(getByText("the lion king")).toBeInTheDocument(); + }); + + // ĐŽĐŸŃ‡Đ”Đșатося ĐżĐŸŃĐČĐž і ĐżĐŸĐČĐ”Ń€ĐœŃƒŃ‚Đž Đ”Đ»Đ”ĐŒĐ”ĐœŃ‚ + const movie = await waitForElement(() => getByText("the lion king")); +}); +``` + +### :thumbsdown: ПроĐșлаЎ Đ°ĐœŃ‚ĐžŃˆĐ°Đ±Đ»ĐŸĐœŃƒ: ŃĐżĐ”Ń†Ń–Đ°Đ»ŃŒĐœĐžĐč ĐșĐŸĐŽ ŃĐœŃƒ + +```javascript +test("movie title appears", async () => { + // Đ”Đ»Đ”ĐŒĐ”ĐœŃ‚ ŃĐżĐŸŃ‡Đ°Ń‚Đșу ĐČŃ–ĐŽŃŃƒŃ‚ĐœŃ–Đč... + + // ŃĐżĐ”Ń†Ń–Đ°Đ»ŃŒĐœĐ° Đ»ĐŸĐłŃ–Đșа ĐŸŃ‡Ń–ĐșуĐČĐ°ĐœĐœŃ (Đ·Đ°ŃŃ‚Đ”Ń€Đ”Đ¶Đ”ĐœĐœŃ: ŃĐżŃ€ĐŸŃ‰Đ”ĐœĐŸ, бДз часу ĐŸŃ‡Ń–ĐșуĐČĐ°ĐœĐœŃ) + const interval = setInterval(() => { + const found = getByText("the lion king"); + if (found) { + clearInterval(interval); + expect(getByText("the lion king")).toBeInTheDocument(); + } + }, 100); + + // ĐŽĐŸŃ‡Đ”Đșатося ĐżĐŸŃĐČĐž і ĐżĐŸĐČĐ”Ń€ĐœŃƒŃ‚Đž Đ”Đ»Đ”ĐŒĐ”ĐœŃ‚ + const movie = await waitForElement(() => getByText("the lion king")); +}); +``` + +
    + +
    + +## âšȘ  3.5 ĐŸĐŸĐŽĐžĐČіться, яĐș ĐČĐŒŃ–ŃŃ‚ ĐżĐŸĐŽĐ°Ń”Ń‚ŃŒŃŃ ĐČ ĐŒĐ”Ń€Đ”Đ¶Ń– + +![](https://img.shields.io/badge/🔧%20Example%20using%20Google%20LightHouse-blue.svg "ПроĐșлаЎ Đ· Lighthouse") + +✅ **Đ ĐŸĐ±Đž:** Đ—Đ°ŃŃ‚ĐŸŃŃƒĐčтД ĐŽĐ”ŃĐșĐžĐč аĐșтоĐČĐœĐžĐč ĐŒĐŸĐœŃ–Ń‚ĐŸŃ€, яĐșĐžĐč ĐłĐ°Ń€Đ°ĐœŃ‚ŃƒŃ” ĐŸĐżŃ‚ĐžĐŒŃ–Đ·Đ°Ń†Ń–ŃŽ заĐČĐ°ĐœŃ‚Đ°Đ¶Đ”ĐœĐœŃ ŃŃ‚ĐŸŃ€Ń–ĐœĐșĐž ĐČ Ń€Đ”Đ°Đ»ŃŒĐœŃ–Đč ĐŒĐ”Ń€Đ”Đ¶Ń– - цД ŃŃ‚ĐŸŃŃƒŃ”Ń‚ŃŒŃŃ Đ±ŃƒĐŽŃŒ-яĐșох ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒ Đ· UX, яĐș-ĐŸŃ‚ ĐżĐŸĐČŃ–Đ»ŃŒĐœĐ” заĐČĐ°ĐœŃ‚Đ°Đ¶Đ”ĐœĐœŃ ŃŃ‚ĐŸŃ€Ń–ĐœĐșĐž Đ°Đ±ĐŸ ĐœĐ”ĐŒŃ–ĐœŃ–Ń„Ń–ĐșĐŸĐČĐ°ĐœĐžĐč паĐșДт. Đ ĐžĐœĐŸĐș Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Ń–ĐČ ĐżĐ”Ń€Đ”ĐČірĐșĐž ĐœĐ” ĐșĐŸŃ€ĐŸŃ‚ĐșĐžĐč: таĐșі Đ±Đ°Đ·ĐŸĐČі Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Đž, яĐș [pingdom](https://www.pingdom.com/), AWS CloudWatch, [gcp StackDriver](https://cloud.google.com/monitoring/uptime-checks) /) ĐŒĐŸĐ¶ĐœĐ° лДгĐșĐŸ ĐœĐ°Đ»Đ°ŃˆŃ‚ŃƒĐČато, Ń‰ĐŸĐ± стДжОтО за Ń‚ĐžĐŒ, чо працює сДрĐČДр і чо ĐČŃ–ĐŽĐżĐŸĐČіЮає ĐČŃ–Đœ ĐČŃ–ĐŽĐżĐŸĐČŃ–ĐŽĐœĐŸ ĐŽĐŸ проĐčĐœŃŃ‚ĐœĐŸŃ— ŃƒĐłĐŸĐŽĐž ĐżŃ€ĐŸ ріĐČĐ”ĐœŃŒ ĐŸĐ±ŃĐ»ŃƒĐłĐŸĐČуĐČĐ°ĐœĐœŃ. ĐŠĐ” лОшД Юряпає ĐżĐŸĐČĐ”Ń€Ń…ĐœŃŽ Ń‚ĐŸĐłĐŸ, Ń‰ĐŸ ĐŒĐŸĐ¶Đ” піто ĐœĐ” таĐș, Ń‚ĐŸĐŒŃƒ ĐșращД ĐČОбратО Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Đž, яĐșі ŃĐżĐ”Ń†Ń–Đ°Đ»Ń–Đ·ŃƒŃŽŃ‚ŃŒŃŃ ĐœĐ° Ń–ĐœŃ‚Đ”Ń€Ń„Đ”Đčсі (ĐœĐ°ĐżŃ€ĐžĐșлаЎ, [lighthouse](https://developers.google.com/web/tools/lighthouse/), [pagespeed]( https://developers.google.com/speed/pagespeed/insights/)) і ĐČĐžĐșĐŸĐœŃƒĐČато ĐŽĐ”Ń‚Đ°Đ»ŃŒĐœŃ–ŃˆĐžĐč Đ°ĐœĐ°Đ»Ń–Đ·. ХліЎ Đ·ĐŸŃĐ”Ń€Đ”ĐŽĐžŃ‚ĐžŃŃ ĐœĐ° ŃĐžĐŒĐżŃ‚ĐŸĐŒĐ°Ń…, ĐżĐŸĐșĐ°Đ·ĐœĐžĐșах, яĐșі Đ±Đ”Đ·ĐżĐŸŃĐ”Ń€Đ”ĐŽĐœŃŒĐŸ ĐČплОĐČають ĐœĐ° UX, яĐș-ĐŸŃ‚ час заĐČĐ°ĐœŃ‚Đ°Đ¶Đ”ĐœĐœŃ ŃŃ‚ĐŸŃ€Ń–ĐœĐșĐž, [ĐČажлОĐČĐžĐč ĐŒĐ°Đ»ŃŽĐœĐŸĐș](https://scotch.io/courses/10-web-performance-audit-tips-for-your-next- billion-users-in-2018/fmp-first-meaningful-paint), [час, ĐżĐŸĐșĐž ŃŃ‚ĐŸŃ€Ń–ĐœĐșа ĐœĐ” ŃŃ‚Đ°ĐœĐ” Ń–ĐœŃ‚Đ”Ń€Đ°ĐșтоĐČĐœĐŸŃŽ (TTI)](https://calibreapp.com/blog/time-to-interactive/). ĐšŃ€Ń–ĐŒ Ń‚ĐŸĐłĐŸ, ĐŒĐŸĐ¶ĐœĐ° таĐșĐŸĐ¶ ŃĐżĐŸŃŃ‚Đ”Ń€Ń–ĐłĐ°Ń‚Đž за Ń‚Đ”Ń…ĐœŃ–Ń‡ĐœĐžĐŒĐž ĐżŃ€ĐžŃ‡ĐžĐœĐ°ĐŒĐž, таĐșĐžĐŒĐž яĐș Đ·Đ°Đ±Đ”Đ·ĐżĐ”Ń‡Đ”ĐœĐœŃ ŃŃ‚ĐžŃĐœĐ”ĐœĐœŃ ĐČĐŒŃ–ŃŃ‚Ńƒ, час ĐŽĐŸ ĐżĐ”Ń€ŃˆĐŸĐłĐŸ баĐčта, ĐŸĐżŃ‚ĐžĐŒŃ–Đ·Đ°Ń†Ń–Ń Đ·ĐŸĐ±Ń€Đ°Đ¶Đ”ĐœŃŒ, Đ·Đ°Đ±Đ”Đ·ĐżĐ”Ń‡Đ”ĐœĐœŃ Ń€ĐŸĐ·ŃƒĐŒĐœĐŸĐłĐŸ Ń€ĐŸĐ·ĐŒŃ–Ń€Ńƒ DOM, SSL та Đ±Đ°ĐłĐ°Ń‚ĐŸ Ń–ĐœŃˆĐžŃ…. Đ‘Đ°Đ¶Đ°ĐœĐŸ ĐŒĐ°Ń‚Đž ці багаті ĐŒĐŸĐœŃ–Ń‚ĐŸŃ€Đž яĐș піЮ час Ń€ĐŸĐ·Ń€ĐŸĐ±ĐșĐž, яĐș Ń‡Đ°ŃŃ‚ĐžĐœŃƒ CI, таĐș і, Ń‰ĐŸ ĐœĐ°ĐčĐłĐŸĐ»ĐŸĐČĐœŃ–ŃˆĐ”, - 24x7 ĐœĐ° Ń€ĐŸĐ±ĐŸŃ‡ĐžŃ… сДрĐČДрах/CDN + +
    + +❌ **Đ†ĐœĐ°ĐșшД:** ĐœĐ°Đ±ŃƒŃ‚ŃŒ, проĐșŃ€ĐŸ усĐČŃ–ĐŽĐŸĐŒĐ»ŃŽĐČато, Ń‰ĐŸ ĐżŃ–ŃĐ»Ń таĐșĐŸŃ— Ń€Đ”Ń‚Đ”Đ»ŃŒĐœĐŸŃ— Ń€ĐŸĐ±ĐŸŃ‚Đž ĐœĐ°ĐŽ стĐČĐŸŃ€Đ”ĐœĐœŃĐŒ Ń–ĐœŃ‚Đ”Ń€Ń„Đ”Đčсу ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČача, ĐżŃ€ĐŸŃ…ĐŸĐŽĐ¶Đ”ĐœĐœŃ 100% Ń„ŃƒĐœĐșŃ†Ń–ĐŸĐœĐ°Đ»ŃŒĐœĐžŃ… тДстіĐČ Ń– сĐșĐ»Đ°ĐŽĐœĐŸĐłĐŸ ĐŸĐ±â€™Ń”ĐŽĐœĐ°ĐœĐœŃ – UX жахлОĐČĐžĐč і ĐżĐŸĐČŃ–Đ»ŃŒĐœĐžĐč чДрДз ĐœĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœŃƒ ĐșĐŸĐœŃ„Ń–ĐłŃƒŃ€Đ°Ń†Ń–ŃŽ CDN + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +### :clap: ПроĐșлаЎ праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: ЗĐČіт ĐżŃ€ĐŸ пДрДĐČірĐșу заĐČĐ°ĐœŃ‚Đ°Đ¶Đ”ĐœĐœŃ ŃŃ‚ĐŸŃ€Ń–ĐœĐșĐž Lighthouse + +![](/assets/lighthouse2.png "Lighthouse Đ·ĐČіт заĐČĐ°ĐœŃ‚Đ°Đ¶Đ”ĐœĐœŃ ŃŃ‚ĐŸŃ€Ń–ĐœĐșĐž") + +
    + +
    + +## âšȘ  3.6 ĐĐ”ŃŃ‚Đ°Đ±Ń–Đ»ŃŒĐœŃ– та ĐżĐŸĐČŃ–Đ»ŃŒĐœŃ– Ń€Đ”ŃŃƒŃ€ŃĐž, яĐș-ĐŸŃ‚ сДрĐČĐ”Ń€ĐœŃ– API + +:white_check_mark: **Đ ĐŸĐ±Đž:** ПіЮ час ĐșĐŸĐŽŃƒĐČĐ°ĐœĐœŃ ĐŸŃĐœĐŸĐČĐœĐžŃ… тДстіĐČ (ĐœĐ” тДстіĐČ E2E) ŃƒĐœĐžĐșаĐčтД Đ·Đ°Đ»ŃƒŃ‡Đ”ĐœĐœŃ Đ±ŃƒĐŽŃŒ-яĐșох Ń€Đ”ŃŃƒŃ€ŃŃ–ĐČ, яĐșі Đ·ĐœĐ°Ń…ĐŸĐŽŃŃ‚ŃŒŃŃ ĐżĐŸĐ·Đ° ĐŒĐ”Đ¶Đ°ĐŒĐž ĐČĐ°ŃˆĐŸŃ— ĐČŃ–ĐŽĐżĐŸĐČŃ–ĐŽĐ°Đ»ŃŒĐœĐŸŃŃ‚Ń– та ĐșĐŸĐœŃ‚Ń€ĐŸĐ»ŃŽ, яĐș-ĐŸŃ‚ сДрĐČĐ”Ń€ĐœĐžĐč API, і ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČуĐčтД ĐœĐ°Ń‚ĐŸĐŒŃ–ŃŃ‚ŃŒ заглушĐșĐž (Ń‚ĐŸĐ±Ń‚ĐŸ test double). На праĐșтоці Đ·Đ°ĐŒŃ–ŃŃ‚ŃŒ Ń€Đ”Đ°Đ»ŃŒĐœĐžŃ… ĐŒĐ”Ń€Đ”Đ¶Đ”ĐČох ĐČĐžĐșлОĐșіĐČ API ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČуĐčтД яĐșусь Ń‚Đ”ŃŃ‚ĐŸĐČу ĐżĐŸĐŽĐČіĐčĐœŃƒ Đ±Ń–Đ±Đ»Ń–ĐŸŃ‚Đ”Đșу (ĐœĐ°ĐżŃ€ĐžĐșлаЎ, [Sinon](https://sinonjs.org/), [Test doubles](https://www.npmjs.com/package/testdouble) Ń‚ĐŸŃ‰ĐŸ) ĐŽĐ»Ń заглушĐșĐž ĐČŃ–ĐŽĐżĐŸĐČіЮі API. ĐžŃĐœĐŸĐČĐœĐŸŃŽ пДрДĐČĐ°ĐłĐŸŃŽ є Đ·Đ°ĐżĐŸĐ±Ń–ĐłĐ°ĐœĐœŃ ĐœĐ”ŃŃ‚Đ°Đ±Ń–Đ»ŃŒĐœĐŸŃŃ‚Ń– — API Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ Đ°Đ±ĐŸ ĐżŃ€ĐŸĐŒŃ–Đ¶ĐœĐŸŃ— ĐŸĐ±Ń€ĐŸĐ±ĐșĐž за ĐČĐžĐ·ĐœĐ°Ń‡Đ”ĐœĐœŃĐŒ ĐœĐ” є ЎужД ŃŃ‚Đ°Đ±Ń–Đ»ŃŒĐœĐžĐŒĐž і час ĐČіЮ часу ĐœĐ” ĐżŃ€ĐŸŃ…ĐŸĐŽŃŃ‚ŃŒ ĐČаші тДстО, Ń…ĐŸŃ‡Đ° ВАй ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ ĐżĐŸĐČĐŸĐŽĐžŃ‚ŃŒŃŃ ĐœĐŸŃ€ĐŒĐ°Đ»ŃŒĐœĐŸ (ĐČĐžŃ€ĐŸĐ±ĐœĐžŃ‡Đ” ŃĐ”Ń€Đ”ĐŽĐŸĐČОщД ĐœĐ” ĐżŃ€ĐžĐ·ĐœĐ°Ń‡Đ”ĐœĐ” ĐŽĐ»Ń Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ, і зазĐČочаĐč ĐČĐŸĐœĐŸ ĐłĐ°Đ»ŃŒĐŒŃƒŃ” запОтО). ĐŠĐ” ĐŽĐŸĐ·ĐČĐŸĐ»ĐžŃ‚ŃŒ ŃĐžĐŒŃƒĐ»ŃŽĐČато Ń€Ń–Đ·ĐœŃ– Юії API, яĐșі ĐżĐŸĐČĐžĐœĐœŃ– ĐșĐ”Ń€ŃƒĐČато ĐżĐŸĐČĐ”ĐŽŃ–ĐœĐșĐŸŃŽ ĐČĐ°ŃˆĐŸĐłĐŸ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đ°, ĐœĐ°ĐżŃ€ĐžĐșлаЎ, ĐșĐŸĐ»Đž ĐŽĐ°ĐœŃ– ĐœĐ” Đ·ĐœĐ°ĐčĐŽĐ”ĐœĐŸ Đ°Đ±ĐŸ ĐČĐžĐżĐ°ĐŽĐŸĐș, ĐșĐŸĐ»Đž API ĐČоЮає ĐżĐŸĐŒĐžĐ»Đșу. І ĐŸŃŃ‚Đ°ĐœĐœŃ”, алД ĐœĐ” ĐŒĐ”ĐœŃˆ ĐČажлОĐČĐ”, ĐŒĐ”Ń€Đ”Đ¶Đ”ĐČі ĐČĐžĐșлОĐșĐž Đ·ĐœĐ°Ń‡ĐœĐŸ ŃĐżĐŸĐČŃ–Đ»ŃŒĐœŃŽŃŽŃ‚ŃŒ тДстО + +
    + +❌ **Đ†ĐœĐ°ĐșшД:** The average test runs no longer than few ms, a typical API call last 100ms>, this makes each test ~20x slower + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :clap: ПроĐșлаЎ праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: Stubbing or intercepting API calls + +![](https://img.shields.io/badge/🔧%20Example%20using%20React-blue.svg "ПроĐșлаЎ Đ· React") ![](https://img.shields.io/badge/🔧%20Example%20using%20React%20Testing%20Library-blue.svg "ПроĐșлаЎ Đ· react-testing-library") + +```javascript +// unit under test +export default function ProductsList() { + const [products, setProducts] = useState(false); + + const fetchProducts = async () => { + const products = await axios.get("api/products"); + setProducts(products); + }; + + useEffect(() => { + fetchProducts(); + }, []); + + return products ?
    {products}
    :
    No products
    ; +} + +// test +test("When no products exist, show the appropriate message", () => { + // Arrange + nock("api") + .get(`/products`) + .reply(404); + + // Act + const { getByTestId } = render(); + + // Assert + expect(getByTestId("no-products-message")).toBeTruthy(); +}); +``` + +
    + +
    + +## âšȘ  3.7 МаĐčтД ЎужД ĐŒĐ°Đ»ĐŸ ĐœĐ°ŃĐșŃ€Ń–Đ·ĐœĐžŃ… тДстіĐČ(e2e), яĐșі ĐŸŃ…ĐŸĐżĐ»ŃŽŃŽŃ‚ŃŒ ĐČсю ŃĐžŃŃ‚Đ”ĐŒŃƒ + +:white_check_mark: **Đ ĐŸĐ±Đž:** AĐ„ĐŸŃ‡Đ° E2E (ĐœĐ°ŃĐșŃ€Ń–Đ·ĐœĐ”) зазĐČочаĐč ĐŸĐ·ĐœĐ°Ń‡Đ°Ń” Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ лОшД Ń–ĐœŃ‚Đ”Ń€Ń„Đ”Đčсу ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČача за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ спраĐČĐ¶ĐœŃŒĐŸĐłĐŸ Đ±Ń€Đ°ŃƒĐ·Đ”Ń€Đ° (ĐŽĐžĐČ. [ĐżŃƒĐœĐșт 3.6](https://github.com/goldbergyoni/javascript-testing-best-practices#-%EF%B8% 8F-36-stub-flaky-and-slow-resources-like-backend-apis)), ĐŽĐ»Ń Ń–ĐœŃˆĐžŃ… ĐČĐŸĐœĐž ĐŸĐ·ĐœĐ°Ń‡Đ°ŃŽŃ‚ŃŒ тДстО, яĐșі Ń€ĐŸĐ·Ń‚ŃĐłŃƒŃŽŃ‚ŃŒ ĐČсю ŃĐžŃŃ‚Đ”ĐŒŃƒ, ĐČĐșлючаючО спраĐČĐ¶ĐœŃ–Đč сДрĐČДр. ĐžŃŃ‚Đ°ĐœĐœŃ–Đč топ тДстіĐČ Ń” ЎужД Ń†Ń–ĐœĐœĐžĐŒ, ĐŸŃĐșŃ–Đ»ŃŒĐșĐž ĐČĐŸĐœĐž ĐŸŃ…ĐŸĐżĐ»ŃŽŃŽŃ‚ŃŒ ĐżĐŸĐŒĐžĐ»ĐșĐž Ń–ĐœŃ‚Đ”ĐłŃ€Đ°Ń†Ń–Ń— ĐŒŃ–Đ¶ Ń–ĐœŃ‚Đ”Ń€Ń„Đ”ĐčŃĐŸĐŒ і сДрĐČĐ”Ń€ĐŸĐŒ, яĐșі ĐŒĐŸĐ¶ŃƒŃ‚ŃŒ ĐČĐžĐœĐžĐșĐœŃƒŃ‚Đž чДрДз ĐœĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœĐ” Ń€ĐŸĐ·ŃƒĐŒŃ–ĐœĐœŃ ŃŃ…Đ”ĐŒĐž ĐŸĐ±ĐŒŃ–ĐœŃƒ. Đ’ĐŸĐœĐž таĐșĐŸĐ¶ є ДфДĐșтоĐČĐœĐžĐŒ ĐŒĐ”Ń‚ĐŸĐŽĐŸĐŒ ĐŽĐ»Ń ĐČояĐČĐ»Đ”ĐœĐœŃ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒ ĐŒŃ–Đ¶ŃĐ”Ń€ĐČŃ–ŃĐœĐŸŃ— Ń–ĐœŃ‚Đ”ĐłŃ€Đ°Ń†Ń–Ń— (ĐœĐ°ĐżŃ€ĐžĐșлаЎ, ĐŒŃ–ĐșŃ€ĐŸŃĐ”Ń€ĐČіс A ĐœĐ°ĐŽŃĐžĐ»Đ°Ń” ĐœĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœĐ” ĐżĐŸĐČŃ–ĐŽĐŸĐŒĐ»Đ”ĐœĐœŃ ĐŒŃ–ĐșŃ€ĐŸŃĐ”Ń€ĐČісу B) і ĐœĐ°ĐČіть ĐŽĐ»Ń ĐČояĐČĐ»Đ”ĐœĐœŃ Đ·Đ±ĐŸŃ—ĐČ Ń€ĐŸĐ·ĐłĐŸŃ€Ń‚Đ°ĐœĐœŃ – ĐŽĐ»Ń Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ E2E ĐœĐ” Ń–ŃĐœŃƒŃ” таĐșох ĐŽŃ€ŃƒĐ¶ĐœŃ–Ń… і зрілОх Ń–ĐœŃ‚Đ”Ń€Ń„Đ”ĐčсіĐČ, яĐș UI. таĐșі фрДĐčĐŒĐČĐŸŃ€ĐșĐž, яĐș [Cypress](https://www.cypress.io/) і [Puppeteer](https://github.com/GoogleChrome/puppeteer). ĐĐ”ĐŽĐŸĐ»Ń–ĐșĐŸĐŒ таĐșох тДстіĐČ Ń” ĐČĐžŃĐŸĐșа ĐČартість ĐœĐ°Đ»Đ°ŃˆŃ‚ŃƒĐČĐ°ĐœĐœŃ ŃĐ”Ń€Đ”ĐŽĐŸĐČоща Đ· таĐșĐŸŃŽ ĐșŃ–Đ»ŃŒĐșістю ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Ń–ĐČ Ń–, Đ·ĐŽĐ”Đ±Ń–Đ»ŃŒŃˆĐŸĐłĐŸ, Ń—Ń…ĐœŃ ĐșрохĐșість — Đ· ĐŸĐłĐ»ŃĐŽŃƒ ĐœĐ° 50 ĐŒŃ–ĐșŃ€ĐŸŃĐ”Ń€ĐČісіĐČ, ĐœĐ°ĐČіть яĐșŃ‰ĐŸ ĐŸĐŽĐžĐœ Đ·Đ°Đ·ĐœĐ°Ń” ĐœĐ”ĐČЮачі, Ń‚ĐŸĐŽŃ– Đ·Đ°Đ·ĐœĐ°Ń” ĐœĐ”ĐČЮачі ĐČĐ”ŃŃŒ E2E. З цієї ĐżŃ€ĐžŃ‡ĐžĐœĐž ĐŒĐž ĐżĐŸĐČĐžĐœĐœŃ– ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČуĐČато цю Ń‚Đ”Ń…ĐœŃ–Đșу Đ”ĐșĐŸĐœĐŸĐŒĐœĐŸ і, ĐŒĐŸĐ¶Đ»ĐžĐČĐŸ, ĐŒĐ°Ń‚Đž 1-10 таĐșох і ĐœĐ” Đ±Ń–Đ»ŃŒŃˆĐ”. ĐąĐžĐŒ ĐœĐ” ĐŒĐ”ĐœŃˆ, ĐœĐ°ĐČіть ĐœĐ”ĐČДлОĐșа ĐșŃ–Đ»ŃŒĐșість тДстіĐČ E2E, шĐČОЎшД за ĐČсД, ĐČояĐČоть топ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒ, ĐœĐ° яĐșі ĐČĐŸĐœĐž ĐœĐ°Ń†Ń–Đ»Đ”ĐœŃ– – ĐżĐŸĐŒĐžĐ»ĐșĐž Ń€ĐŸĐ·ĐłĐŸŃ€Ń‚Đ°ĐœĐœŃ та Ń–ĐœŃ‚Đ”ĐłŃ€Đ°Ń†Ń–Ń—. Đ‘Đ°Đ¶Đ°ĐœĐŸ запусĐșато їх у ŃĐ”Ń€Đ”ĐŽĐŸĐČощі, ŃŃ…ĐŸĐ¶ĐŸĐŒŃƒ ĐœĐ° ĐČĐžŃ€ĐŸĐ±ĐœĐžŃ†Ń‚ĐČĐŸ + +
    + +❌ **Đ†ĐœĐ°ĐșшД:** Đ†ĐœŃ‚Đ”Ń€Ń„Đ”Đčс ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČача ĐŒĐŸĐ¶Đ” Đ±Đ°ĐłĐ°Ń‚ĐŸ ĐČĐșлаЎатО ĐČ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ сĐČĐŸŃ”Ń— Ń„ŃƒĐœĐșŃ†Ń–ĐŸĐœĐ°Đ»ŃŒĐœĐŸŃŃ‚Ń–, алД ЎужД ĐżŃ–Đ·ĐœĐŸ Đ·Ń€ĐŸĐ·ŃƒĐŒŃ–Ń”, Ń‰ĐŸ ĐșĐŸŃ€ĐžŃĐœĐ” ĐœĐ°ĐČĐ°ĐœŃ‚Đ°Đ¶Đ”ĐœĐœŃ (ŃŃ…Đ”ĐŒĐ° ĐŽĐ°ĐœĐžŃ…, Đ· яĐșĐŸŃŽ ĐŒĐ°Ń” працюĐČато Ń–ĐœŃ‚Đ”Ń€Ń„Đ”Đčс ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČача) ŃĐžĐ»ŃŒĐœĐŸ ĐČŃ–ĐŽŃ€Ń–Đ·ĐœŃŃ”Ń‚ŃŒŃŃ ĐČіЮ ĐŸŃ‡Ń–ĐșуĐČĐ°ĐœĐŸĐłĐŸ. + +
    + +## âšȘ  3.8 ПросĐșĐŸŃ€Ń–Ń‚ŃŒ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ E2E, ĐżĐŸĐČŃ‚ĐŸŃ€ĐœĐŸ ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČуючо ĐŸĐ±Đ»Ń–ĐșĐŸĐČі ĐŽĐ°ĐœŃ– ĐŽĐ»Ń ĐČŃ…ĐŸĐŽŃƒ + +:white_check_mark: **Đ ĐŸĐ±Đž:** ĐŁ тДстах E2E, яĐșі ĐČĐșĐ»ŃŽŃ‡Đ°ŃŽŃ‚ŃŒ спраĐČĐ¶ĐœŃŽ сДрĐČĐ”Ń€ĐœŃƒ Ń‡Đ°ŃŃ‚ĐžĐœŃƒ та ĐżĐŸĐșĐ»Đ°ĐŽĐ°ŃŽŃ‚ŃŒŃŃ ĐœĐ° ЮіĐčŃĐœĐžĐč ĐŒĐ°Ń€ĐșДр ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČача ĐŽĐ»Ń ĐČĐžĐșлОĐșіĐČ API, Ń–Đ·ĐŸĐ»ŃŽĐČато тДст ĐŽĐŸ ріĐČĐœŃ, ĐœĐ° яĐșĐŸĐŒŃƒ ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČач стĐČĐŸŃ€ŃŽŃ”Ń‚ŃŒŃŃ та ĐČŃ…ĐŸĐŽĐžŃ‚ŃŒ у ŃĐžŃŃ‚Đ”ĐŒŃƒ піЮ час ĐșĐŸĐ¶ĐœĐŸĐłĐŸ Đ·Đ°ĐżĐžŃ‚Ńƒ, ĐœĐ” ĐČопраĐČĐŽĐŸĐČується. Đ—Đ°ĐŒŃ–ŃŃ‚ŃŒ Ń†ŃŒĐŸĐłĐŸ уĐČіĐčЮіть лОшД ĐŸĐŽĐžĐœ раз пДрДЎ ĐżĐŸŃ‡Đ°Ń‚ĐșĐŸĐŒ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ тДстіĐČ (Ń‚ĐŸĐ±Ń‚ĐŸ хуĐș before-all), Đ·Đ±Đ”Ń€Đ”Đ¶Ń–Ń‚ŃŒ ĐŒĐ°Ń€ĐșДр у Đ»ĐŸĐșĐ°Đ»ŃŒĐœĐŸĐŒŃƒ ŃŃ…ĐŸĐČощі та ĐżĐŸĐČŃ‚ĐŸŃ€ĐœĐŸ ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČуĐčтД ĐčĐŸĐłĐŸ ĐŽĐ»Ń запОтіĐČ. ĐĄŃ…ĐŸĐ¶Đ”, цД ĐżĐŸŃ€ŃƒŃˆŃƒŃ” ĐŸĐŽĐžĐœ Ń–Đ· ĐŸŃĐœĐŸĐČĐœĐžŃ… ĐżŃ€ĐžĐœŃ†ĐžĐżŃ–ĐČ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ — ĐżŃ–ĐŽŃ‚Ń€ĐžĐŒŃƒĐČато тДст аĐČŃ‚ĐŸĐœĐŸĐŒĐœĐžĐŒ бДз Đ·ĐČâ€™ŃĐ·Đșу Ń€Đ”ŃŃƒŃ€ŃŃ–ĐČ. Đ„ĐŸŃ‡Đ° цД ĐŸĐ±Ò‘Ń€ŃƒĐœŃ‚ĐŸĐČĐ°ĐœĐ” Đ·Đ°ĐœĐ”ĐżĐŸĐșĐŸŃ”ĐœĐœŃ, у тДстах E2E ĐżŃ€ĐŸĐŽŃƒĐșтоĐČĐœŃ–ŃŃ‚ŃŒ є ĐșĐ»ŃŽŃ‡ĐŸĐČĐŸŃŽ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐŸŃŽ, і стĐČĐŸŃ€Đ”ĐœĐœŃ 1-3 запОтіĐČ API пДрДЎ ĐżĐŸŃ‡Đ°Ń‚ĐșĐŸĐŒ ĐșĐŸĐ¶ĐœĐŸĐłĐŸ ĐŸĐșŃ€Đ”ĐŒĐŸĐłĐŸ Ń‚Đ”ŃŃ‚Ńƒ ĐŒĐŸĐ¶Đ” прОзĐČДстО ĐŽĐŸ жахлОĐČĐŸĐłĐŸ часу ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ. ĐŸĐŸĐČŃ‚ĐŸŃ€ĐœĐ” ĐČĐžĐșĐŸŃ€ĐžŃŃ‚Đ°ĐœĐœŃ ĐŸĐ±Đ»Ń–ĐșĐŸĐČох ĐŽĐ°ĐœĐžŃ… ĐœĐ” ĐŸĐ·ĐœĐ°Ń‡Đ°Ń”, Ń‰ĐŸ тДстО ĐżĐŸĐČĐžĐœĐœŃ– Юіято Đ· Ń‚ĐžĐŒĐž ŃĐ°ĐŒĐžĐŒĐž Đ·Đ°ĐżĐžŃĐ°ĐŒĐž ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČачіĐČ. ĐŻĐșŃ‰ĐŸ ĐČĐž ĐżĐŸĐșĐ»Đ°ĐŽĐ°Ń”Ń‚Đ”ŃŃ ĐœĐ° запОсО ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČачіĐČ (ĐœĐ°ĐżŃ€ĐžĐșлаЎ, Ń–ŃŃ‚ĐŸŃ€Ń–ŃŽ платДжіĐČ Ń‚Đ”ŃŃ‚ĐŸĐČĐŸĐłĐŸ ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČача), пДрДĐșĐŸĐœĐ°ĐčŃ‚Đ”ŃŃ, Ń‰ĐŸ стĐČĐŸŃ€ĐžĐ»Đž ці запОсО яĐș Ń‡Đ°ŃŃ‚ĐžĐœŃƒ Ń‚Đ”ŃŃ‚Ńƒ та ĐœĐ” ĐżĐŸĐČŃ–ĐŽĐŸĐŒĐ»ŃĐ»Đž ĐżŃ€ĐŸ їх Ń–ŃĐœŃƒĐČĐ°ĐœĐœŃ Ń–ĐœŃˆĐžĐŒ Ń‚Đ”ŃŃ‚Đ°ĐŒ. йаĐșĐŸĐ¶ ĐżĐ°ĐŒâ€™ŃŃ‚Đ°ĐčтД, Ń‰ĐŸ бДĐșĐ”ĐœĐŽ ĐŒĐŸĐ¶Đ” Đ±ŃƒŃ‚Đž ĐżŃ–ĐŽŃ€ĐŸĐ±Đ»Đ”ĐœĐžĐŒ – яĐșŃ‰ĐŸ ĐČаші тДстО Đ·ĐŸŃĐ”Ń€Đ”ĐŽĐ¶Đ”ĐœŃ– ĐœĐ° Ń–ĐœŃ‚Đ”Ń€Ń„Đ”Đčсі, ĐŒĐŸĐ¶Đ»ĐžĐČĐŸ, ĐșращД Ń–Đ·ĐŸĐ»ŃŽĐČато ĐčĐŸĐłĐŸ та Đ·Đ°ĐłĐ»ŃƒŃˆĐžŃ‚Đž бДĐșĐ”ĐœĐŽ API (ĐŽĐžĐČ. [ĐżŃƒĐœĐșт 3.6](https://github.com/goldbergyoni/javascript-testing- best-practices#-%EF%B8%8F-36-stub-flaky-and-slow-resources-like-backend-apis)). + +
    + +❌ **Đ†ĐœĐ°ĐșшД:** ĐĐ°ĐŽĐ°ĐœĐŸ 200 тДстіĐČ Ń– ĐżŃ€ĐžĐżŃƒŃ‰Đ”ĐœĐœŃ, Ń‰ĐŸ login=100ms=20 сДĐșŃƒĐœĐŽ лОшД ĐŽĐ»Ń ĐČŃ…ĐŸĐŽŃƒ Đ·ĐœĐŸĐČу і Đ·ĐœĐŸĐČу + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :clap: ПроĐșлаЎ праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: ВхіЮ пДрДЎ ŃƒŃŃ–ĐŒĐ°, а ĐœĐ” пДрДЎ ĐșĐŸĐ¶ĐœĐžĐŒ + +![](https://img.shields.io/badge/🔹%20Example%20using%20Cypress-blue.svg "ВоĐșĐŸŃ€ĐžŃŃ‚Đ°ĐœĐœŃ Cypress ĐŽĐ»Ń ілюстраціі іЎДї") + +```javascript +let authenticationToken; + +// ĐČŃ–ĐŽĐ±ŃƒĐČається пДрДЎ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃĐŒ УХІЄ тДстіĐČ +before(() => { + cy.request('POST', 'http://localhost:3000/login', { + username: Cypress.env('username'), + password: Cypress.env('password'), + }) + .its('body') + .then((responseFromLogin) => { + authenticationToken = responseFromLogin.token; + }) +}) + +// ĐČŃ–ĐŽĐ±ŃƒĐČається пДрДЎ КОЖНИМ Ń‚Đ”ŃŃ‚ĐŸĐŒ +beforeEach(setUser => { + cy.visit('/home', { + onBeforeLoad (win) { + win.localStorage.setItem('token', JSON.stringify(authenticationToken)) + }, + }) +}) + +``` + +
    + +
    + +## âšȘ  3.9 ĐŸŃ€ĐŸĐČĐ”ĐŽŃ–Ń‚ŃŒ ĐŸĐŽĐžĐœ тДст "smoke" E2E, яĐșĐžĐč ĐżŃ€ĐŸŃŃ‚ĐŸ ĐżŃ€ĐŸĐčĐŽĐ” ĐżĐŸ Đșарті саĐčту + +:white_check_mark: **Đ ĐŸĐ±Đž:** Đ”Đ»Ń ĐŒĐŸĐœŃ–Ń‚ĐŸŃ€ĐžĐœĐłŃƒ ĐČĐžŃ€ĐŸĐ±ĐœĐžŃ†Ń‚ĐČа та пДрДĐČірĐșĐž ĐżŃ€Đ°Ń†Đ”Đ·ĐŽĐ°Ń‚ĐœĐŸŃŃ‚Ń– піЮ час Ń€ĐŸĐ·Ń€ĐŸĐ±ĐșĐž Đ·Đ°ĐżŃƒŃŃ‚Ń–Ń‚ŃŒ Ń”ĐŽĐžĐœĐžĐč тДст E2E, яĐșĐžĐč ĐČіЮĐČіЮує ĐČсі/Đ±Ń–Đ»ŃŒŃˆŃ–ŃŃ‚ŃŒ ŃŃ‚ĐŸŃ€Ń–ĐœĐŸĐș саĐčту та ĐłĐ°Ń€Đ°ĐœŃ‚ŃƒŃ”, Ń‰ĐŸ ĐœŃ–Ń…Ń‚ĐŸ ĐœĐ” Đ·Đ»Đ°ĐŒĐ°ĐČся. ĐŠĐ”Đč топ Ń‚Đ”ŃŃ‚Ńƒ Đ·Đ°Đ±Đ”Đ·ĐżĐ”Ń‡ŃƒŃ” ĐČĐžŃĐŸĐșу ĐŸĐșŃƒĐżĐœŃ–ŃŃ‚ŃŒ Ń–ĐœĐČДстОціĐč, ĐŸŃĐșŃ–Đ»ŃŒĐșĐž ĐčĐŸĐłĐŸ ЎужД лДгĐșĐŸ посато та ĐżŃ–ĐŽŃ‚Ń€ĐžĐŒŃƒĐČато, алД ĐČŃ–Đœ ĐŒĐŸĐ¶Đ” ĐČояĐČото Đ±ŃƒĐŽŃŒ-яĐșі Đ·Đ±ĐŸŃ—, ĐČĐșлючаючО Ń„ŃƒĐœĐșŃ†Ń–ĐŸĐœĐ°Đ»ŃŒĐœŃ– ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐž, ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐž Đ· ĐŒĐ”Ń€Đ”Đ¶Đ”ŃŽ та Ń€ĐŸĐ·ĐłĐŸŃ€Ń‚Đ°ĐœĐœŃ. Đ†ĐœŃˆŃ– стОлі пДрДĐČірĐșĐž ĐŽĐžĐŒŃƒ та ĐżŃ€Đ°Ń†Đ”Đ·ĐŽĐ°Ń‚ĐœĐŸŃŃ‚Ń– ĐœĐ” таĐșі ĐœĐ°ĐŽŃ–ĐčĐœŃ– та ĐČĐžŃ‡Đ”Ń€ĐżĐœŃ– — ĐŽĐ”ŃĐșі ĐŸĐżĐ”Ń€Đ°Ń‚ĐžĐČĐœŃ– ĐșĐŸĐŒĐ°ĐœĐŽĐž ĐżŃ€ĐŸŃŃ‚ĐŸ пДрДĐČіряють ĐŽĐŸĐŒĐ°ŃˆĐœŃŽ ŃŃ‚ĐŸŃ€Ń–ĐœĐșу (ĐČĐžŃ€ĐŸĐ±ĐœĐžŃ†Ń‚ĐČĐŸ) Đ°Đ±ĐŸ Ń€ĐŸĐ·Ń€ĐŸĐ±ĐœĐžĐșĐž, яĐșі запусĐșають Đ±Đ°ĐłĐ°Ń‚ĐŸ Ń–ĐœŃ‚Đ”ĐłŃ€Đ°Ń†Ń–ĐčĐœĐžŃ… тДстіĐČ, яĐșі ĐœĐ” ĐČояĐČĐ»ŃŃŽŃ‚ŃŒ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒ Đ· паĐșуĐČĐ°ĐœĐœŃĐŒ і Đ±Ń€Đ°ŃƒĐ·Đ”Ń€ĐŸĐŒ. ĐĄĐ°ĐŒĐŸ ŃĐŸĐ±ĐŸŃŽ Đ·Ń€ĐŸĐ·ŃƒĐŒŃ–Đ»ĐŸ, Ń‰ĐŸ ĐŽĐžĐŒĐŸĐČĐžĐč тДст ĐœĐ” Đ·Đ°ĐŒŃ–ĐœŃŽŃ” Ń„ŃƒĐœĐșŃ†Ń–ĐŸĐœĐ°Đ»ŃŒĐœŃ– тДстО, а лОшД ŃĐ»ŃƒĐ¶ĐžŃ‚ŃŒ шĐČОЎĐșĐžĐŒ ЎДтДĐșŃ‚ĐŸŃ€ĐŸĐŒ ĐŽĐžĐŒŃƒ + +
    + +❌ **Đ†ĐœĐ°ĐșшД:** ВсД ĐŒĐŸĐ¶Đ” Đ·ĐŽĐ°Ń‚ĐžŃŃ Ń–ĐŽĐ”Đ°Đ»ŃŒĐœĐžĐŒ, усі тДстО ĐżŃ€ĐŸĐčшлО, пДрДĐČірĐșа Ń€ĐŸĐ±ĐŸŃ‡ĐŸĐłĐŸ ŃŃ‚Đ°ĐœŃƒ таĐșĐŸĐ¶ ĐżĐŸĐ·ĐžŃ‚ĐžĐČĐœĐ°, алД ĐżĐ»Đ°Ń‚Ń–Đ¶ĐœĐžĐč ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚ ĐŒĐ°ĐČ ĐŽĐ”ŃĐșі ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐž Đ· упаĐșĐŸĐČĐșĐŸŃŽ, і лОшД ĐŒĐ°Ń€ŃˆŃ€ŃƒŃ‚ /Payment ĐœĐ” ĐČŃ–ĐŽĐŸĐ±Ń€Đ°Đ¶Đ°Ń”Ń‚ŃŒŃŃ + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :clap: ПроĐșлаЎ праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: "Smoke" тДст ĐżĐŸ ĐČсіх ŃŃ‚ĐŸŃ€Ń–ĐœĐșах + +![](https://img.shields.io/badge/🔹%20Example%20using%20Cypress-blue.svg "ВоĐșĐŸŃ€ĐžŃŃ‚Đ°ĐœĐœŃ Cypress") + +```javascript +it("When doing smoke testing over all page, should load them all successfully", () => { + // Smoke тДст ĐœĐ° ĐČсіх ŃŃ‚ĐŸŃ€Ń–ĐœĐșах + // ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČуючо Đ±ŃƒĐŽŃŒ-яĐșĐžĐč паĐșДт E2E + cy.visit("https://mysite.com/home"); + cy.contains("Home"); + cy.visit("https://mysite.com/Login"); + cy.contains("Login"); + cy.visit("https://mysite.com/About"); + cy.contains("About"); +}); +``` + +
    + +
    + +## âšȘ  3.10 ВостаĐČтД тДстО яĐș жОĐČĐžĐč ŃĐżŃ–Đ»ŃŒĐœĐžĐč ĐŽĐŸĐșŃƒĐŒĐ”ĐœŃ‚ + +:white_check_mark: **Đ ĐŸĐ±Đž:** ОĐșŃ€Ń–ĐŒ піЮĐČĐžŃ‰Đ”ĐœĐœŃ ĐœĐ°ĐŽŃ–ĐčĐœĐŸŃŃ‚Ń– ĐŽĐŸĐŽĐ°Ń‚Đșа, тДстО ĐœĐ°ĐŽĐ°ŃŽŃ‚ŃŒ щД ĐŸĐŽĐœŃƒ проĐČаблОĐČу ĐŒĐŸĐ¶Đ»ĐžĐČість – слугуĐČато жОĐČĐŸŃŽ ĐŽĐŸĐșŃƒĐŒĐ”ĐœŃ‚Đ°Ń†Ń–Ń”ŃŽ ĐŽĐŸĐŽĐ°Ń‚Đșа. ОсĐșŃ–Đ»ŃŒĐșĐž тДстО за сĐČĐŸŃ”ŃŽ суттю Ń€ĐŸĐ·ĐŒĐŸĐČĐ»ŃŃŽŃ‚ŃŒ ĐŒĐ”ĐœŃˆ Ń‚Đ”Ń…ĐœŃ–Ń‡ĐœĐŸŃŽ ĐŒĐŸĐČĐŸŃŽ та ĐŒĐŸĐČĐŸŃŽ ĐżŃ€ĐŸĐŽŃƒĐșту/UX, ĐČĐžĐșĐŸŃ€ĐžŃŃ‚Đ°ĐœĐœŃ праĐČĐžĐ»ŃŒĐœĐžŃ… Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Ń–ĐČ ĐŒĐŸĐ¶Đ” ŃĐ»ŃƒĐ¶ĐžŃ‚Đž артДфаĐșŃ‚ĐŸĐŒ спілĐșуĐČĐ°ĐœĐœŃ, яĐșĐžĐč Đ·ĐœĐ°Ń‡ĐœĐŸŃŽ ĐŒŃ–Ń€ĐŸŃŽ Đ·Đ±Đ»ĐžĐ¶ŃƒŃ” ĐČсіх ĐșĐŸĐ»Đ”Đł – Ń€ĐŸĐ·Ń€ĐŸĐ±ĐœĐžĐșіĐČ Ń‚Đ° Ń—Ń…ĐœŃ–Ń… ĐșĐ»Ń–Ń”ĐœŃ‚Ń–ĐČ. НапроĐșлаЎ, ĐŽĐ”ŃĐșі фрДĐčĐŒĐČĐŸŃ€ĐșĐž ĐŽĐŸĐ·ĐČĐŸĐ»ŃŃŽŃ‚ŃŒ ĐČОражатО ĐżĐŸŃ‚Ń–Đș і ĐŸŃ‡Ń–ĐșуĐČĐ°ĐœĐœŃ (Ń‚ĐŸĐ±Ń‚ĐŸ ĐżĐ»Đ°Đœ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ) за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ Đ·Ń€ĐŸĐ·ŃƒĐŒŃ–Đ»ĐŸŃ— Đ»ŃŽĐŽĐžĐœŃ– ĐŒĐŸĐČĐž, Ń‰ĐŸĐ± Đ±ŃƒĐŽŃŒ-яĐșа заціĐșаĐČĐ»Đ”ĐœĐ° â€‹â€‹ŃŃ‚ĐŸŃ€ĐŸĐœĐ°, ĐČĐșлючаючО ĐŒĐ”ĐœĐ”ĐŽĐ¶Đ”Ń€Ń–ĐČ Ń–Đ· ĐżŃ€ĐŸĐŽŃƒĐșтіĐČ, ĐŒĐŸĐłĐ»Đ° чотато, затĐČĐ”Ń€ĐŽĐ¶ŃƒĐČато та спіĐČпрацюĐČато ĐœĐ°ĐŽ Ń‚Đ”ŃŃ‚Đ°ĐŒĐž, яĐșі Ń‰ĐŸĐčĐœĐŸ сталО аĐșŃ‚ŃƒĐ°Đ»ŃŒĐœĐžĐŒ ĐŽĐŸĐșŃƒĐŒĐ”ĐœŃ‚ĐŸĐŒ ĐČĐžĐŒĐŸĐł. Щю Ń‚Đ”Ń…ĐœŃ–Đșу таĐșĐŸĐ¶ ĐœĐ°Đ·ĐžĐČають «прОĐčĐŒĐ°Đ»ŃŒĐœĐžĐŒ Ń‚Đ”ŃŃ‚ĐŸĐŒÂ», ĐŸŃĐșŃ–Đ»ŃŒĐșĐž ĐČĐŸĐœĐ° ĐŽĐŸĐ·ĐČĐŸĐ»ŃŃ” ĐșĐ»Ń–Ń”ĐœŃ‚Ńƒ ĐČĐžĐ·ĐœĐ°Ń‡ĐžŃ‚Đž сĐČĐŸŃ— ĐșрОтДрії проĐčĐœŃŃ‚ĐœĐŸŃŃ‚Ń– ĐżŃ€ĐŸŃŃ‚ĐŸŃŽ ĐŒĐŸĐČĐŸŃŽ. ĐŠĐ” [BDD (Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ, ĐșĐ”Ń€ĐŸĐČĐ°ĐœĐ” ĐżĐŸĐČĐ”ĐŽŃ–ĐœĐșĐŸŃŽ)](https://en.wikipedia.org/wiki/Behavior-driven_development) у Ń‡ĐžŃŃ‚ĐŸĐŒŃƒ ĐČĐžĐłĐ»ŃĐŽŃ–. ĐžĐŽĐœĐžĐŒ Ń–Đ· ĐżĐŸĐżŃƒĐ»ŃŃ€ĐœĐžŃ… фрДĐčĐŒĐČĐŸŃ€ĐșіĐČ, яĐșі цД ĐŽĐŸĐ·ĐČĐŸĐ»ŃŃŽŃ‚ŃŒ, є [Cucumber, яĐșĐžĐč ĐŒĐ°Ń” ŃĐŒĐ°Đș JavaScript](https://github.com/cucumber/cucumber-js), ĐŽĐžĐČ. проĐșлаЎ ĐœĐžĐ¶Ń‡Đ”. Đ†ĐœŃˆĐ° ŃŃ…ĐŸĐ¶Đ°, алД Ń–ĐœŃˆĐ° ĐŒĐŸĐ¶Đ»ĐžĐČість, [StoryBook](https://storybook.js.org/), ĐŽĐŸĐ·ĐČĐŸĐ»ŃŃ” ĐČостаĐČĐ»ŃŃ‚Đž ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đž Ń–ĐœŃ‚Đ”Ń€Ń„Đ”Đčсу ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČача яĐș ĐłŃ€Đ°Ń„Ń–Ń‡ĐœĐžĐč ĐșĐ°Ń‚Đ°Đ»ĐŸĐł, ĐŽĐ” ĐŒĐŸĐ¶ĐœĐ° ĐżĐ”Ń€Đ”ĐłĐ»ŃĐŽĐ°Ń‚Đž Ń€Ń–Đ·ĐœŃ– ŃŃ‚Đ°ĐœĐž ĐșĐŸĐ¶ĐœĐŸĐłĐŸ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đ° (ĐœĐ°ĐżŃ€ĐžĐșлаЎ, ĐČŃ–Đ·ŃƒĐ°Đ»Ń–Đ·ŃƒĐČато сітĐșу бДз Ń„Ń–Đ»ŃŒŃ‚Ń€Ń–ĐČ). , ĐČŃ–Đ·ŃƒĐ°Đ»Ń–Đ·ŃƒĐčтД цю сітĐșу Đ· ĐșŃ–Đ»ŃŒĐșĐŸĐŒĐ° ряЮĐșĐ°ĐŒĐž Đ°Đ±ĐŸ бДз Đ¶ĐŸĐŽĐœĐŸĐłĐŸ Ń‚ĐŸŃ‰ĐŸ), ĐżĐŸĐŽĐžĐČіться, яĐș цД ĐČĐžĐłĐ»ŃĐŽĐ°Ń” та яĐș аĐșтоĐČуĐČато цДĐč ŃŃ‚Đ°Đœ - цД таĐșĐŸĐ¶ ĐŒĐŸĐ¶Đ” ŃĐżĐŸĐŽĐŸĐ±Đ°Ń‚ĐžŃŃ ŃĐżĐ”Ń†Ń–Đ°Đ»Ń–ŃŃ‚Đ°ĐŒ Ń–Đ· ĐżŃ€ĐŸĐŽŃƒĐșтіĐČ, алД Đ·ĐŽĐ”Đ±Ń–Đ»ŃŒŃˆĐŸĐłĐŸ ŃĐ»ŃƒĐ¶ĐžŃ‚ŃŒ жОĐČĐŸŃŽ ĐŽĐŸĐșŃƒĐŒĐ”ĐœŃ‚Đ°Ń†Ń–Ń”ŃŽ ĐŽĐ»Ń Ń€ĐŸĐ·Ń€ĐŸĐ±ĐœĐžĐșіĐČ, яĐșі ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČують ці ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đž. + +❌ **Đ†ĐœĐ°ĐșшД:** ĐŸŃ–ŃĐ»Ń Ń–ĐœĐČĐ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐœĐ°ĐčĐșращох Ń€Đ”ŃŃƒŃ€ŃŃ–ĐČ Ńƒ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐżŃ€ĐŸŃŃ‚ĐŸ шĐșĐŸĐŽĐ° ĐœĐ” ĐČĐžĐșĐŸŃ€ĐžŃŃ‚Đ°Ń‚Đž ці Ń–ĐœĐČДстОції та ĐŸŃ‚Ń€ĐžĐŒĐ°Ń‚Đž ĐČДлОĐșу Ń†Ń–ĐœĐœŃ–ŃŃ‚ŃŒ + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :clap: ПроĐșлаЎ праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: Опос тДстіĐČ Đ»ŃŽĐŽŃŃŒĐșĐŸŃŽ ĐŒĐŸĐČĐŸŃŽ за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ cucumber-js + +![](https://img.shields.io/badge/🔹%20Example%20using%20Cucumber-blue.svg "ПроĐșлаЎ Đ· Cucumber") + +```javascript +// ĐŸŃŃŒ яĐș ĐŒĐŸĐ¶ĐœĐ° ĐŸĐżĐžŃĐ°Ń‚Đž тДстО за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ Cucumber: ĐżŃ€ĐŸŃŃ‚Đ° ĐŒĐŸĐČа, яĐșа ĐŽĐŸĐ·ĐČĐŸĐ»ŃŃ” Đ±ŃƒĐŽŃŒ-ĐșĐŸĐŒŃƒ Ń€ĐŸĐ·ŃƒĐŒŃ–Ń‚Đž та спіĐČпрацюĐČато + +Feature: Twitter new tweet + + I want to tweet something in Twitter + + @focus + Scenario: Tweeting from the home page + Given I open Twitter home + Given I click on "New tweet" button + Given I type "Hello followers!" in the textbox + Given I click on "Submit" button + Then I see message "Tweet saved" + +``` + +### :clap: ПроĐșлаЎ праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: Đ’Ń–Đ·ŃƒĐ°Đ»Ń–Đ·Đ°Ń†Ń–Ń ĐœĐ°ŃˆĐžŃ… ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Ń–ĐČ, їх Ń€Ń–Đ·ĐœĐžŃ… ŃŃ‚Đ°ĐœŃ–ĐČ Ń– ĐČŃ…Ń–ĐŽĐœĐžŃ… ĐŽĐ°ĐœĐžŃ… за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ Storybook + +![](https://img.shields.io/badge/🔹%20Example%20using%20StoryBook-blue.svg "StoryBook") + +![alt text](assets/story-book.jpg "Storybook") + +
    + +

    + +## âšȘ  3.11 ВояĐČĐ»ŃĐčтД ĐČŃ–Đ·ŃƒĐ°Đ»ŃŒĐœŃ– ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐž за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ аĐČŃ‚ĐŸĐŒĐ°Ń‚ĐžĐ·ĐŸĐČĐ°ĐœĐžŃ… Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Ń–ĐČ + +:white_check_mark: **Đ ĐŸĐ±Đž:** ĐĐ°Đ»Đ°ŃˆŃ‚ŃƒĐčтД аĐČŃ‚ĐŸĐŒĐ°Ń‚ĐžŃ‡ĐœŃ– Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Đž ĐŽĐ»Ń стĐČĐŸŃ€Đ”ĐœĐœŃ сĐșŃ€Ń–ĐœŃˆĐŸŃ‚Ń–ĐČ Ń–ĐœŃ‚Đ”Ń€Ń„Đ”Đčсу ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČача, ĐșĐŸĐ»Đž прДЎстаĐČĐ»Đ”ĐœŃ– Đ·ĐŒŃ–ĐœĐž, і ĐČояĐČĐ»Đ”ĐœĐœŃ ĐČŃ–Đ·ŃƒĐ°Đ»ŃŒĐœĐžŃ… ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒ, яĐș-ĐŸŃ‚ ĐœĐ°ĐșĐ»Đ°ĐŽĐ”ĐœĐœŃ ĐČĐŒŃ–ŃŃ‚Ńƒ Đ°Đ±ĐŸ ĐżĐŸŃ€ŃƒŃˆĐ”ĐœĐœŃ. ĐŠĐ” ĐłĐ°Ń€Đ°ĐœŃ‚ŃƒŃ”, Ń‰ĐŸ ĐœĐ” Ń‚Ń–Đ»ŃŒĐșĐž праĐČĐžĐ»ŃŒĐœŃ– ĐŽĐ°ĐœŃ– ĐżŃ–ĐŽĐłĐŸŃ‚ĐŸĐČĐ»Đ”ĐœŃ–, алД Đč ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČач ĐŒĐŸĐ¶Đ” їх Đ·Ń€ŃƒŃ‡ĐœĐŸ ĐżĐ”Ń€Đ”ĐłĐ»ŃĐŽĐ°Ń‚Đž. Щя Ń‚Đ”Ń…ĐœŃ–Đșа ĐœĐ” є ŃˆĐžŃ€ĐŸĐșĐŸ ĐżĐŸŃˆĐžŃ€Đ”ĐœĐŸŃŽ, ĐœĐ°ŃˆĐ” ĐŒĐžŃĐ»Đ”ĐœĐœŃ Ń‰ĐŸĐŽĐŸ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ŃŃ…ĐžĐ»ŃŃ”Ń‚ŃŒŃŃ ĐŽĐŸ Ń„ŃƒĐœĐșŃ†Ń–ĐŸĐœĐ°Đ»ŃŒĐœĐžŃ… тДстіĐČ, алД ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČач ĐČіЮчуĐČає ĐČŃ–Đ·ŃƒĐ°Đ»ŃŒĐœŃ– ДфДĐșто, а Đ· таĐșĐŸŃŽ ĐșŃ–Đ»ŃŒĐșістю топіĐČ ĐżŃ€ĐžŃŃ‚Ń€ĐŸŃ—ĐČ ĐŽŃƒĐ¶Đ” лДгĐșĐŸ ĐœĐ” ĐżĐŸĐŒŃ–Ń‚ĐžŃ‚Đž ĐœĐ”ĐżŃ€ĐžŃ”ĐŒĐœŃƒ ĐżĐŸĐŒĐžĐ»Đșу Ń–ĐœŃ‚Đ”Ń€Ń„Đ”Đčсу ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČача. Đ”Đ”ŃĐșі бДзĐșĐŸŃˆŃ‚ĐŸĐČĐœŃ– Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Đž ĐŒĐŸĐ¶ŃƒŃ‚ŃŒ ĐœĐ°ĐŽĐ°Ń‚Đž ĐŸŃĐœĐŸĐČĐž — стĐČĐŸŃ€ŃŽĐČато та збДрігатО Đ·ĐœŃ–ĐŒĐșĐž Đ”ĐșŃ€Đ°ĐœĐ° ĐŽĐ»Ń ĐŸĐłĐ»ŃĐŽŃƒ ĐŸŃ‡Đ”Đč Đ»ŃŽĐŽĐžĐœĐž. Đ„ĐŸŃ‡Đ° Ń†ŃŒĐŸĐłĐŸ ĐżŃ–ĐŽŃ…ĐŸĐŽŃƒ ĐŒĐŸĐ¶Đ” Đ±ŃƒŃ‚Đž ĐŽĐŸŃŃ‚Đ°Ń‚ĐœŃŒĐŸ ĐŽĐ»Ń ĐœĐ”ĐČДлОĐșох ĐŽĐŸĐŽĐ°Ń‚ĐșіĐČ, ĐČŃ–Đœ ĐŒĐ°Ń” ĐœĐ”ĐŽĐŸĐ»Ń–ĐșĐž, яĐș і Đ±ŃƒĐŽŃŒ-яĐșĐ” Ń–ĐœŃˆĐ” Ń€ŃƒŃ‡ĐœĐ” Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ, яĐșĐ” ĐżĐŸŃ‚Ń€Đ”Đ±ŃƒŃ” Đ»ŃŽĐŽŃŃŒĐșĐŸŃ— праці Ń‰ĐŸŃ€Đ°Đ·Ńƒ, ĐșĐŸĐ»Đž Ń‰ĐŸŃŃŒ Đ·ĐŒŃ–ĐœŃŽŃ”Ń‚ŃŒŃŃ. З Ń–ĐœŃˆĐŸĐłĐŸ Đ±ĐŸĐșу, ĐŽĐŸŃĐžŃ‚ŃŒ сĐșĐ»Đ°ĐŽĐœĐŸ аĐČŃ‚ĐŸĐŒĐ°Ń‚ĐžŃ‡ĐœĐŸ ĐČояĐČĐ»ŃŃ‚Đž ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐž Đ· Ń–ĐœŃ‚Đ”Ń€Ń„Đ”ĐčŃĐŸĐŒ ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČача чДрДз ĐČŃ–ĐŽŃŃƒŃ‚ĐœŃ–ŃŃ‚ŃŒ чітĐșĐŸĐłĐŸ ĐČĐžĐ·ĐœĐ°Ń‡Đ”ĐœĐœŃ – тут ŃĐżŃ€Đ°Ń†ŃŒĐŸĐČує ĐżĐŸĐ»Đ” Â«Đ’Ń–Đ·ŃƒĐ°Đ»ŃŒĐœĐ° Ń€Đ”ĐłŃ€Đ”ŃŃ–ŃÂ» та Ń€ĐŸĐ·ĐČâ€™ŃĐ·ŃƒŃ” цю загаЎĐșу, ĐżĐŸŃ€Ń–ĐČĐœŃŽŃŽŃ‡Đž староĐč Ń–ĐœŃ‚Đ”Ń€Ń„Đ”Đčс ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČача Đ· ĐŸŃŃ‚Đ°ĐœĐœŃ–ĐŒĐž Đ·ĐŒŃ–ĐœĐ°ĐŒĐž та ĐČояĐČĐ»ŃŃŽŃ‡Đž ĐČŃ–ĐŽĐŒŃ–ĐœĐœĐŸŃŃ‚Ń–. Đ”Đ”ŃĐșі OSS/бДзĐșĐŸŃˆŃ‚ĐŸĐČĐœŃ– Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Đž ĐŒĐŸĐ¶ŃƒŃ‚ŃŒ ĐœĐ°ĐŽĐ°ĐČато ĐŽĐ”ŃĐșі Đ· цох Ń„ŃƒĐœĐșціĐč (ĐœĐ°ĐżŃ€ĐžĐșлаЎ, [wraith](https://github.com/BBC-News/wraith), [PhantomCSS](<[https://github.com/HuddleEng/PhantomCSS] (https://github.com/HuddleEng/PhantomCSS)>), алД ĐŒĐŸĐ¶Đ” стягуĐČато Đ·ĐœĐ°Ń‡ĐœĐžĐč час ĐœĐ°Đ»Đ°ŃˆŃ‚ŃƒĐČĐ°ĐœĐœŃ.ĐšĐŸĐŒĐ”Ń€Ń†Ń–ĐčĐœĐ° Đ»Ń–ĐœŃ–Ń Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Ń–ĐČ (ĐœĐ°ĐżŃ€ĐžĐșлаЎ, [Applitools](https://applitools.com/), [Percy.io](https ://percy.io/)) Ń€ĐŸĐ±ĐžŃ‚ŃŒ ĐșŃ€ĐŸĐș Ўалі, Đ·ĐłĐ»Đ°ĐŽĐ¶ŃƒŃŽŃ‡Đž Ń–ĐœŃŃ‚Đ°Đ»ŃŃ†Ń–ŃŽ та Ń€ĐŸĐ·ŃˆĐžŃ€ŃŽŃŽŃ‡Đž таĐșі Ń„ŃƒĐœĐșції, яĐș Ń–ĐœŃ‚Đ”Ń€Ń„Đ”Đčс ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČача ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČача, ŃĐżĐŸĐČŃ–Ń‰Đ”ĐœĐœŃ, Ń–ĐœŃ‚Đ”Đ»Đ”ĐșŃ‚ŃƒĐ°Đ»ŃŒĐœĐ” Đ·Đ°Ń…ĐŸĐżĐ»Đ”ĐœĐœŃ ŃˆĐ»ŃŃ…ĐŸĐŒ ŃƒŃŃƒĐœĐ”ĐœĐœŃ «ĐČŃ–Đ·ŃƒĐ°Đ»ŃŒĐœĐŸĐłĐŸ ŃˆŃƒĐŒŃƒÂ» (ĐœĐ°ĐżŃ€ĐžĐșлаЎ, рДĐșĐ»Đ°ĐŒĐž, Đ°ĐœŃ–ĐŒĐ°Ń†Ń–Ń—) і ĐœĐ°ĐČіть Đ°ĐœĐ°Đ»Ń–Đ· ĐżĐ”Ń€ŃˆĐŸĐżŃ€ĐžŃ‡ĐžĐœĐž DOM /CSS Đ·ĐŒŃ–ĐœĐž, яĐșі прОзĐČДлО ĐŽĐŸ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐž + +
    + +❌ **Đ†ĐœĐ°ĐșшД:** НасĐșŃ–Đ»ŃŒĐșĐž яĐșŃ–ŃĐœĐŸŃŽ є ŃŃ‚ĐŸŃ€Ń–ĐœĐșа Đ· ĐČĐŒŃ–ŃŃ‚ĐŸĐŒ, яĐșа ĐČŃ–ĐŽĐŸĐ±Ń€Đ°Đ¶Đ°Ń” Ń‡ŃƒĐŽĐŸĐČĐžĐč ĐČĐŒŃ–ŃŃ‚ (ĐżŃ€ĐŸĐčĐŽĐ”ĐœĐŸ 100% тДстіĐČ), заĐČĐ°ĐœŃ‚Đ°Đ¶ŃƒŃ”Ń‚ŃŒŃŃ ĐŒĐžŃ‚Ń‚Ń”ĐČĐŸ, алД ĐżĐŸĐ»ĐŸĐČĐžĐœĐ° ĐŸĐ±Đ»Đ°ŃŃ‚Ń– ĐČĐŒŃ–ŃŃ‚Ńƒ ĐżŃ€ĐžŃ…ĐŸĐČĐ°ĐœĐ°? + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :thumbsdown: ПроĐșлаЎ Đ°ĐœŃ‚ĐžŃˆĐ°Đ±Đ»ĐŸĐœŃƒ: ĐąĐžĐżĐŸĐČа ĐČŃ–Đ·ŃƒĐ°Đ»ŃŒĐœĐ° Ń€Đ”ĐłŃ€Đ”ŃŃ–Ń – праĐČĐžĐ»ŃŒĐœĐžĐč ĐșĐŸĐœŃ‚Đ”ĐœŃ‚, яĐșĐžĐč ĐżĐŸĐŽĐ°Ń”Ń‚ŃŒŃŃ ĐżĐŸĐłĐ°ĐœĐŸ + +![alt text](assets/amazon-visual-regression.jpeg "Amazon - Đ·Đ»Đ°ĐŒĐ°ĐœĐ° ŃŃ‚ĐŸŃ€ĐœŃ–Đșа") + +
    + +### :clap: ПроĐșлаЎ праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: ĐĐ°Đ»Đ°ŃˆŃ‚ŃƒĐČĐ°ĐœĐœŃ wraith ĐŽĐ»Ń Đ·Đ°Ń…ĐŸĐżĐ»Đ”ĐœĐœŃ та ĐżĐŸŃ€Ń–ĐČĐœŃĐœĐœŃ Đ·ĐœŃ–ĐŒĐșіĐČ Ń–ĐœŃ‚Đ”Ń€Ń„Đ”Đčсу ĐșĐŸŃ€ĐžŃŃ‚ŃƒĐČача + +![](https://img.shields.io/badge/🔹%20Example%20using%20Wraith-blue.svg "Using Wraith") + +``` +​# Add as many domains as necessary. Key will act as a label​ + +domains: + english: "http://www.mysite.com"​ + +​# Type screen widths below, here are a couple of examples​ + +screen_widths: + + - 600​ + - 768​ + - 1024​ + - 1280​ + +​# Type page URL paths below, here are a couple of examples​ +paths: + about: + path: /about + selector: '.about'​ + subscribe: + selector: '.subscribe'​ + path: /subscribe +``` + +### :clap: ПроĐșлаЎ праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: ВоĐșĐŸŃ€ĐžŃŃ‚Đ°ĐœĐœŃ Applitools ĐŽĐ»Ń ĐżĐŸŃ€Ń–ĐČĐœŃĐœĐœŃ Đ·ĐœŃ–ĐŒĐșіĐČ Ń‚Đ° Ń–ĐœŃˆĐžŃ… Ń€ĐŸĐ·ŃˆĐžŃ€Đ”ĐœĐžŃ… Ń„ŃƒĐœĐșціĐč + +![](https://img.shields.io/badge/🔹%20Example%20using%20AppliTools-blue.svg "ВоĐșĐŸŃ€ĐžŃŃ‚Đ°ĐœĐœŃ Applitools") ![](https://img.shields.io/badge/🔹%20Example%20using%20Cypress-blue.svg "ВоĐșĐŸŃ€ĐžŃŃ‚Đ°ĐœĐœŃ Cypress") + +```javascript +import * as todoPage from "../page-objects/todo-page"; + +describe("visual validation", () => { + before(() => todoPage.navigate()); + beforeEach(() => cy.eyesOpen({ appName: "TAU TodoMVC" })); + afterEach(() => cy.eyesClose()); + + it("should look good", () => { + cy.eyesCheckWindow("empty todo list"); + todoPage.addTodo("Clean room"); + todoPage.addTodo("Learn javascript"); + cy.eyesCheckWindow("two todos"); + todoPage.toggleTodo(0); + cy.eyesCheckWindow("mark as completed"); + }); +}); +``` + +
    + +

    + +# Section 4ïžâƒŁ: Đ’ĐžĐŒŃ–Ń€ŃŽĐČĐ°ĐœĐœŃ ДфДĐșтоĐČĐœĐŸŃŃ‚Ń– Ń‚Đ”ŃŃ‚Ńƒ + +

    + +## âšȘ  4.1 ĐžŃ‚Ń€ĐžĐŒĐ°ĐčтД ĐŽĐŸŃŃ‚Đ°Ń‚ĐœŃ” ĐżĐŸĐșроття, Ń‰ĐŸĐ± Đ±ŃƒŃ‚Đž ĐČпДĐČĐœĐ”ĐœĐžĐŒ, ~80%, Đ·ĐŽĐ°Ń”Ń‚ŃŒŃŃ, цД щаслОĐČĐ” Ń‡ĐžŃĐ»ĐŸ + +:white_check_mark: **Đ ĐŸĐ±Đž:** ĐœĐ”Ń‚Đ° Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐżĐŸĐ»ŃĐłĐ°Ń” ĐČ Ń‚ĐŸĐŒŃƒ, Ń‰ĐŸĐ± ĐŸŃ‚Ń€ĐžĐŒĐ°Ń‚Đž ĐŽĐŸŃŃ‚Đ°Ń‚ĐœŃŽ ĐČпДĐČĐœĐ”ĐœŃ–ŃŃ‚ŃŒ ĐŽĐ»Ń шĐČОЎĐșĐŸĐłĐŸ ĐżŃ€ĐŸŃŃƒĐČĐ°ĐœĐœŃ; ĐŸŃ‡Đ”ĐČĐžĐŽĐœĐŸ, Ń‡ĐžĐŒ Đ±Ń–Đ»ŃŒŃˆĐ” ĐșĐŸĐŽŃƒ Ń‚Đ”ŃŃ‚ŃƒŃ”Ń‚ŃŒŃŃ, Ń‚ĐžĐŒ ĐČпДĐČĐœĐ”ĐœŃ–ŃˆĐŸŃŽ ĐŒĐŸĐ¶Đ” Đ±ŃƒŃ‚Đž ĐșĐŸĐŒĐ°ĐœĐŽĐ°. ĐŸĐŸĐșроття — цД ĐżĐŸĐșĐ°Đ·ĐœĐžĐș Ń‚ĐŸĐłĐŸ, сĐșŃ–Đ»ŃŒĐșĐž ряЮĐșіĐČ ĐșĐŸĐŽŃƒ (і Ń€ĐŸĐ·ĐłĐ°Đ»ŃƒĐ¶Đ”ĐœŃŒ, ĐŸĐżĐ”Ń€Đ°Ń‚ĐŸŃ€Ń–ĐČ Ń‚ĐŸŃ‰ĐŸ) ĐŸŃ…ĐŸĐżĐ»Đ”ĐœĐŸ Ń‚Đ”ŃŃ‚Đ°ĐŒĐž. ĐąĐŸ сĐșŃ–Đ»ŃŒĐșĐž ĐČостачоть? 10–30% — цД, ĐŸŃ‡Đ”ĐČĐžĐŽĐœĐŸ, Đ·Đ°ĐœĐ°ĐŽŃ‚ĐŸ ĐŒĐ°Đ»ĐŸ, Ń‰ĐŸĐ± ĐŸŃ‚Ń€ĐžĐŒĐ°Ń‚Đž Đ±ŃƒĐŽŃŒ-яĐșĐ” уяĐČĐ»Đ”ĐœĐœŃ ĐżŃ€ĐŸ праĐČĐžĐ»ŃŒĐœŃ–ŃŃ‚ŃŒ збірĐșĐž, Đ· Ń–ĐœŃˆĐŸĐłĐŸ Đ±ĐŸĐșу, 100% — цД ЎужД ĐŽĐŸŃ€ĐŸĐłĐŸ Đč ĐŒĐŸĐ¶Đ” ĐżĐ”Ń€Đ”ĐŒŃ–ŃŃ‚ĐžŃ‚Đž ĐČашу уĐČагу Đ· ĐșŃ€ĐžŃ‚ĐžŃ‡ĐœĐžŃ… ŃˆĐ»ŃŃ…Ń–ĐČ ĐœĐ° Đ”ĐșĐ·ĐŸŃ‚ĐžŃ‡ĐœŃ– ĐșŃƒŃ‚ĐŸŃ‡ĐșĐž ĐșĐŸĐŽŃƒ. Đ”ĐŸĐČга ĐČŃ–ĐŽĐżĐŸĐČіЮь ĐżĐŸĐ»ŃĐłĐ°Ń” ĐČ Ń‚ĐŸĐŒŃƒ, Ń‰ĐŸ цД Đ·Đ°Đ»Đ”Đ¶ĐžŃ‚ŃŒ ĐČіЮ Đ±Đ°ĐłĐ°Ń‚ŃŒĐŸŃ… фаĐșŃ‚ĐŸŃ€Ń–ĐČ, таĐșох яĐș топ Đ·Đ°ŃŃ‚ĐŸŃŃƒĐČĐ°ĐœĐœŃâ€Šâ€”Â«ŃĐșŃ‰ĐŸ ĐČĐž стĐČĐŸŃ€ŃŽŃ”Ń‚Đ” ĐœĐ°ŃŃ‚ŃƒĐżĐœĐ” ĐżĐŸĐșĐŸĐ»Ń–ĐœĐœŃ Airbus A380, 100% є ĐŸĐ±ĐŸĐČâ€™ŃĐ·ĐșĐŸĐČĐžĐŒ, ĐŽĐ»Ń ĐČДб-саĐčту Đ· ĐŒŃƒĐ»ŃŒŃ‚Ń„Ń–Đ»ŃŒĐŒĐ°ĐŒĐž 50% ĐŒĐŸĐ¶Đ” Đ±ŃƒŃ‚Đž Đ·Đ°Đ±Đ°ĐłĐ°Ń‚ĐŸ. Đ„ĐŸŃ‡Đ° Đ±Ń–Đ»ŃŒŃˆŃ–ŃŃ‚ŃŒ Đ”ĐœŃ‚ŃƒĐ·Ń–Đ°ŃŃ‚Ń–ĐČ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ стĐČĐ”Ń€ĐŽĐ¶ŃƒŃŽŃ‚ŃŒ, Ń‰ĐŸ праĐČĐžĐ»ŃŒĐœĐžĐč ĐżĐŸŃ€Ń–Đł ĐŸŃ…ĐŸĐżĐ»Đ”ĐœĐœŃ є ĐșĐŸĐœŃ‚Đ”ĐșŃŃ‚ĐœĐžĐŒ, Đ±Ń–Đ»ŃŒŃˆŃ–ŃŃ‚ŃŒ Ń–Đ· ĐœĐžŃ… таĐșĐŸĐ¶ Đ·ĐłĐ°ĐŽŃƒŃ” Ń‡ĐžŃĐ»ĐŸ 80% яĐș праĐČĐžĐ»ĐŸ ([Fowler: "in the upper 80s or 90s"](https://martinfowler. com/bliki/TestCoverage.html)), яĐșĐžĐč, Ń–ĐŒĐŸĐČŃ–Ń€ĐœĐŸ, ĐżĐŸĐČĐžĐœĐ”Đœ Đ·Đ°ĐŽĐŸĐČĐŸĐ»ŃŒĐœĐžŃ‚Đž Đ±Ń–Đ»ŃŒŃˆŃ–ŃŃ‚ŃŒ ĐżŃ€ĐŸĐłŃ€Đ°ĐŒ. + +ĐŸĐŸŃ€Đ°ĐŽĐž Ń‰ĐŸĐŽĐŸ ĐČĐżŃ€ĐŸĐČĐ°ĐŽĐ¶Đ”ĐœĐœŃ: ĐœĐŸĐ¶Đ»ĐžĐČĐŸ, ĐČĐž Đ·Đ°Ń…ĐŸŃ‡Đ”Ń‚Đ” ĐœĐ°Đ»Đ°ŃˆŃ‚ŃƒĐČато бДзпДрДрĐČĐœŃƒ Ń–ĐœŃ‚Đ”ĐłŃ€Đ°Ń†Ń–ŃŽ (CI), Ń‰ĐŸĐ± ĐŒĐ°Ń‚Đž ĐżĐŸŃ€Ń–Đł ĐżĐŸĐșроття ([Jest link](https://jestjs.io/docs/en/configuration.html#collectcoverage-boolean)) і Đ·ŃƒĐżĐžĐœĐžŃ‚Đž збірĐșу Ń‰ĐŸ ĐœĐ” ĐČŃ–ĐŽĐżĐŸĐČіЮає Ń†ŃŒĐŸĐŒŃƒ ŃŃ‚Đ°ĐœĐŽĐ°Ń€Ń‚Ńƒ (таĐșĐŸĐ¶ ĐŒĐŸĐ¶ĐœĐ° ĐœĐ°Đ»Đ°ŃˆŃ‚ŃƒĐČато ĐżĐŸŃ€ĐŸĐłĐŸĐČĐ” Đ·ĐœĐ°Ń‡Đ”ĐœĐœŃ ĐŽĐ»Ń ĐșĐŸĐ¶ĐœĐŸĐłĐŸ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đ°, ĐŽĐžĐČ. проĐșлаЎ ĐșĐŸĐŽŃƒ ĐœĐžĐ¶Ń‡Đ”). На ĐŽĐŸĐŽĐ°Ń‚ĐŸĐș ĐŽĐŸ Ń†ŃŒĐŸĐłĐŸ, ĐżĐŸĐŽŃƒĐŒĐ°ĐčтД ĐżŃ€ĐŸ ĐČояĐČĐ»Đ”ĐœĐœŃ Đ·ĐŒĐ”ĐœŃˆĐ”ĐœĐœŃ ĐżĐŸĐșроття збірĐșĐž (ĐșĐŸĐ»Đž ĐœĐ”Ń‰ĐŸĐŽĐ°ĐČĐœĐŸ зафіĐșŃĐŸĐČĐ°ĐœĐžĐč ĐșĐŸĐŽ ĐŒĐ°Ń” ĐŒĐ”ĐœŃˆĐ” ĐżĐŸĐșроття) — цД ĐżŃ–ĐŽŃˆŃ‚ĐŸĐČŃ…ĐœĐ” Ń€ĐŸĐ·Ń€ĐŸĐ±ĐœĐžĐșіĐČ Đ·Đ±Ń–Đ»ŃŒŃˆĐžŃ‚Đž Đ°Đ±ĐŸ ĐżŃ€ĐžĐœĐ°ĐčĐŒĐœŃ– збДрДгтО ĐșŃ–Đ»ŃŒĐșість пДрДĐČŃ–Ń€Đ”ĐœĐŸĐłĐŸ ĐșĐŸĐŽŃƒ. Đ—Đ°ĐłĐ°Đ»ĐŸĐŒ, ĐŸŃ…ĐŸĐżĐ»Đ”ĐœĐœŃ — цД лОшД ĐŸĐŽĐžĐœ ĐżĐŸĐșĐ°Đ·ĐœĐžĐș, ĐșŃ–Đ»ŃŒĐșŃ–ŃĐœĐžĐč, яĐșĐŸĐłĐŸ ĐœĐ”ĐŽĐŸŃŃ‚Đ°Ń‚ĐœŃŒĐŸ, Ń‰ĐŸĐ± ĐČĐžĐ·ĐœĐ°Ń‡ĐžŃ‚Đž ĐœĐ°ĐŽŃ–ĐčĐœŃ–ŃŃ‚ŃŒ ĐČĐ°ŃˆĐŸĐłĐŸ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ. І ĐčĐŸĐłĐŸ таĐșĐŸĐ¶ ĐŒĐŸĐ¶ĐœĐ° ĐŸĐ±ĐŽŃƒŃ€ĐžŃ‚Đž, яĐș ĐżĐŸĐșĐ°Đ·Đ°ĐœĐŸ ĐČ ĐœĐ°ŃŃ‚ŃƒĐżĐœĐžŃ… ĐżŃƒĐœĐșтах + +
    + +❌ **Đ†ĐœĐ°ĐșшД:** ВпДĐČĐœĐ”ĐœŃ–ŃŃ‚ŃŒ і цофро ĐčЮуть пліч-ĐŸ-пліч, ĐœĐ°ĐČіть ĐœĐ” Đ·ĐœĐ°ŃŽŃ‡Đž, Ń‰ĐŸ ĐČĐž пДрДĐČірОлО Đ±Ń–Đ»ŃŒŃˆŃƒ Ń‡Đ°ŃŃ‚ĐžĐœŃƒ ŃĐžŃŃ‚Đ”ĐŒĐž -таĐșĐŸĐ¶ буЎД пДĐČĐœĐžĐč страх, і страх ŃĐżĐŸĐČŃ–Đ»ŃŒĐœĐžŃ‚ŃŒ ĐČас + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :clap: ПроĐșлаЎ: ĐąĐžĐżĐŸĐČĐžĐč Đ·ĐČіт ĐżŃ€ĐŸ ĐżĐŸĐșроття + +![alt text](assets/bp-18-yoni-goldberg-code-coverage.png "ĐąĐžĐżĐŸĐČĐžĐč Đ·ĐČіт ĐżŃ€ĐŸ ĐżĐŸĐșроття") + +
    + +### :clap: ПроĐșлаЎ праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: ĐĐ°Đ»Đ°ŃˆŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐżĐŸĐșроття ĐŽĐ»Ń ĐșĐŸĐ¶ĐœĐŸĐłĐŸ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đ° (за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ Jest) + +![](https://img.shields.io/badge/🔹%20Example%20using%20Jest-blue.svg "Jest") + +![alt text](assets/bp-18-code-coverage2.jpeg "ĐĐ°Đ»Đ°ŃˆŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐżĐŸĐșроття ĐŽĐ»Ń ĐșĐŸĐ¶ĐœĐŸĐłĐŸ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃ‚Đ° (за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ Jest)") + +
    + +

    + +## âšȘ  4.2 ĐŸĐ”Ń€Đ”ĐČіртД Đ·ĐČіто ĐżŃ€ĐŸ ĐżĐŸĐșроття, Ń‰ĐŸĐ± ĐČояĐČото ĐœĐ”ĐżĐ”Ń€Đ”ĐČŃ–Ń€Đ”ĐœŃ– ĐŸĐ±Đ»Đ°ŃŃ‚Ń– та Ń–ĐœŃˆŃ– ĐŽĐžĐČацтĐČа + +:white_check_mark: **Đ ĐŸĐ±Đž:** Đ”Đ”ŃĐșі ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐž ĐœĐ”ĐżĐŸĐŒŃ–Ń‡Đ”ĐœŃ–, і їх ЎужД ĐČажĐșĐŸ Đ·ĐœĐ°Đčто за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ траЮоціĐčĐœĐžŃ… Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Ń–ĐČ. НаспраĐČЮі цД ĐœĐ” ĐżĐŸĐŒĐžĐ»ĐșĐž, а сĐșĐŸŃ€Ń–ŃˆĐ” ĐŽĐžĐČĐŸĐČĐžĐ¶ĐœĐ° ĐżĐŸĐČĐ”ĐŽŃ–ĐœĐșа ĐżŃ€ĐŸĐłŃ€Đ°ĐŒĐž, яĐșа ĐŒĐŸĐ¶Đ” ĐŒĐ°Ń‚Đž сДрĐčĐŸĐ·ĐœŃ– ĐœĐ°ŃĐ»Ń–ĐŽĐșĐž. НапроĐșлаЎ, Ń‡Đ°ŃŃ‚ĐŸ ĐŽĐ”ŃĐșі ĐŸĐ±Đ»Đ°ŃŃ‚Ń– ĐșĐŸĐŽŃƒ ĐœŃ–ĐșĐŸĐ»Đž Đ°Đ±ĐŸ ріЮĐșĐŸ ĐČĐžĐșлОĐșаються — ĐČĐž ĐŽŃƒĐŒĐ°Đ»Đž, Ń‰ĐŸ Đșлас «PricingCalculator» заĐČжЎО ĐČŃŃ‚Đ°ĐœĐŸĐČлює Ń†Ń–ĐœŃƒ ĐżŃ€ĐŸĐŽŃƒĐșту, алД ĐČояĐČĐžĐ»ĐŸŃŃ, Ń‰ĐŸ ĐČŃ–Đœ ĐœĐ°ŃĐżŃ€Đ°ĐČЮі ĐœŃ–ĐșĐŸĐ»Đž ĐœĐ” ĐČĐžĐșлОĐșається, Ń…ĐŸŃ‡Đ° ĐŒĐž ĐŒĐ°Ń”ĐŒĐŸ 10000 ĐżŃ€ĐŸĐŽŃƒĐșтіĐČ Ńƒ БД і Đ±Đ°ĐłĐ°Ń‚ĐŸ ĐżŃ€ĐŸĐŽĐ°Đ¶Ń–ĐČ
 ĐŸĐŸĐșроття ĐșĐŸĐŽŃƒ Đ·ĐČіто ĐŽĐŸĐżĐŸĐŒĐ°ĐłĐ°ŃŽŃ‚ŃŒ Đ·Ń€ĐŸĐ·ŃƒĐŒŃ–Ń‚Đž, чо працює ĐżŃ€ĐŸĐłŃ€Đ°ĐŒĐ° таĐș, яĐș ĐČĐž ĐŽŃƒĐŒĐ°Ń”Ń‚Đ”. ОĐșŃ€Ń–ĐŒ Ń†ŃŒĐŸĐłĐŸ, ĐČŃ–Đœ таĐșĐŸĐ¶ ĐŒĐŸĐ¶Đ” піЮĐșрДслОтО, яĐșі топо ĐșĐŸĐŽŃƒ ĐœĐ” пДрДĐČŃ–Ń€Đ”ĐœĐŸâ€Šâ€”Â«Ń–ĐœŃ„ĐŸŃ€ĐŒĐ°Ń†Ń–Ń ĐżŃ€ĐŸ тД, Ń‰ĐŸ 80% ĐșĐŸĐŽŃƒ пДрДĐČŃ–Ń€Đ”ĐœĐŸ, ĐœĐ” ĐłĐŸĐČĐŸŃ€ĐžŃ‚ŃŒ ĐżŃ€ĐŸ тД, чо ĐŸŃ…ĐŸĐżĐ»Đ”ĐœĐŸ ĐșŃ€ĐžŃ‚ĐžŃ‡ĐœŃ– Ń‡Đ°ŃŃ‚ĐžĐœĐž. СтĐČĐŸŃ€ŃŽĐČато Đ·ĐČіто лДгĐșĐŸâ€Šâ€”â€ŠĐżŃ€ĐŸŃŃ‚ĐŸ Đ·Đ°ĐżŃƒŃŃ‚Ń–Ń‚ŃŒ сĐČĐŸŃŽ ĐżŃ€ĐŸĐłŃ€Đ°ĐŒŃƒ ĐČ Ń€ĐŸĐ±ĐŸŃ‡ĐŸĐŒŃƒ ŃŃ‚Đ°ĐœŃ– Đ°Đ±ĐŸ піЮ час Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ Đ· ĐČŃ–ĐŽŃŃ‚Đ”Đ¶Đ”ĐœĐœŃĐŒ ĐżĐŸĐșроття, а ĐżĐŸŃ‚Ń–ĐŒ ĐżĐ”Ń€Đ”ĐłĐ»ŃĐœŃŒŃ‚Đ” ĐșĐŸĐ»ŃŒĐŸŃ€ĐŸĐČі Đ·ĐČіто, у яĐșох ĐżĐŸĐșĐ°Đ·Đ°ĐœĐŸ, яĐș Ń‡Đ°ŃŃ‚ĐŸ ĐČĐžĐșлОĐșається ĐșĐŸĐ¶ĐœĐ° ĐŸĐ±Đ»Đ°ŃŃ‚ŃŒ ĐșĐŸĐŽŃƒ. ĐŻĐșŃ‰ĐŸ ĐČĐž ĐœĐ” ĐżĐŸŃĐżŃ–ŃˆĐ°Ń”Ń‚Đ” Đ·Đ°Đ·ĐžŃ€ĐœŃƒŃ‚Đž ĐČ Ń†Ń– ĐŽĐ°ĐœŃ–â€Šâ€”â€ŠĐČĐž ĐŒĐŸĐ¶Đ”Ń‚Đ” Đ·ĐœĐ°Đčто ĐŽĐ”ŃĐșі ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐž +
    + +❌ **Đ†ĐœĐ°ĐșшД:** ĐŻĐșŃ‰ĐŸ ĐČĐž ĐœĐ” Đ·ĐœĐ°Ń”Ń‚Đ”, яĐșі Ń‡Đ°ŃŃ‚ĐžĐœĐž ĐČĐ°ŃˆĐŸĐłĐŸ ĐșĐŸĐŽŃƒ Đ·Đ°Đ»ĐžŃˆĐžĐ»ĐžŃŃ ĐœĐ”ĐŸĐ±Ń€ĐŸĐ±Đ»Đ”ĐœĐžĐŒĐž, ĐČĐž ĐœĐ” Đ·ĐœĐ°Ń”Ń‚Đ”, Đ·ĐČіЮĐșĐž ĐŒĐŸĐ¶ŃƒŃ‚ŃŒ ĐČĐžĐœĐžĐșĐœŃƒŃ‚Đž ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐž + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :thumbsdown: ПроĐșлаЎ Đ°ĐœŃ‚ĐžŃˆĐ°Đ±Đ»ĐŸĐœŃƒ: Đ©ĐŸ ĐœĐ” таĐș Ń–Đ· Ń†ĐžĐŒ Đ·ĐČŃ–Ń‚ĐŸĐŒ ĐżŃ€ĐŸ ĐżĐŸĐșроття? + +На ĐŸŃĐœĐŸĐČі Ń€Đ”Đ°Đ»ŃŒĐœĐŸĐłĐŸ ŃŃ†Đ”ĐœĐ°Ń€Ń–ŃŽ, ĐșĐŸĐ»Đž ĐŒĐž ĐČŃ–ĐŽŃŃ‚Đ”Đ¶ŃƒĐČалО ĐČĐžĐșĐŸŃ€ĐžŃŃ‚Đ°ĐœĐœŃ ĐœĐ°ŃˆĐŸŃ— ĐżŃ€ĐŸĐłŃ€Đ°ĐŒĐž ĐČ QA та Đ·ĐœĐ°Ń…ĐŸĐŽĐžĐ»Đž ціĐșаĐČі ŃˆĐ°Đ±Đ»ĐŸĐœĐž ĐČŃ…ĐŸĐŽŃƒ (ПіЮĐșазĐșа: ĐșŃ–Đ»ŃŒĐșість ĐżĐŸĐŒĐžĐ»ĐŸĐș ĐČŃ…ĐŸĐŽŃƒ ĐœĐ”ĐżŃ€ĐŸĐżĐŸŃ€Ń†Ń–ĐčĐœĐ°, Ń‰ĐŸŃŃŒ яĐČĐœĐŸ ĐœĐ” таĐș. ĐĐ°Ń€Đ”ŃˆŃ‚Ń– ĐČояĐČĐžĐ»ĐŸŃŃ, Ń‰ĐŸ яĐșась ĐżĐŸĐŒĐžĐ»Đșа Ń–ĐœŃ‚Đ”Ń€Ń„Đ”Đčсу ĐżĐŸŃŃ‚Ń–ĐčĐœĐŸ ĐČражає сДрĐČĐ”Ń€ĐœĐžĐč API ĐČŃ…ĐŸĐŽŃƒ) + +![alt text](assets/bp-19-coverage-yoni-goldberg-nodejs-consultant.png "Đ©ĐŸ ĐœĐ” таĐș Ń–Đ· Ń†ĐžĐŒ Đ·ĐČŃ–Ń‚ĐŸĐŒ ĐżŃ€ĐŸ ĐżĐŸĐșроття?") + +
    + +

    + +## âšȘ  4.3 Đ’ĐžĐŒŃ–Ń€ŃĐčтД Đ»ĐŸĐłŃ–Ń‡ĐœĐ” ĐżĐŸĐșроття за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐœĐ° ĐŒŃƒŃ‚Đ°Ń†Ń–Ń— + +:white_check_mark: **Đ ĐŸĐ±Đž:** браЮоціĐčĐœĐžĐč ĐżĐŸĐșĐ°Đ·ĐœĐžĐș ĐżĐŸĐșроття Ń‡Đ°ŃŃ‚ĐŸ Đ±Ń€Đ”Ń…ĐœŃ: ĐČŃ–Đœ ĐŒĐŸĐ¶Đ” ĐżĐŸĐșазатО ĐČĐ°ĐŒ 100% ĐżĐŸĐșроття ĐșĐŸĐŽŃƒ, алД Đ¶ĐŸĐŽĐœĐ° Đ· ĐČашох Ń„ŃƒĐœĐșціĐč, ĐœĐ°ĐČіть Đ¶ĐŸĐŽĐœĐ°, ĐœĐ” ĐżĐŸĐČДртає праĐČĐžĐ»ŃŒĐœŃƒ ĐČŃ–ĐŽĐżĐŸĐČіЮь. ĐŻĐș таĐș? ĐČŃ–Đœ ĐżŃ€ĐŸŃŃ‚ĐŸ ĐČĐžĐŒŃ–Ń€ŃŽŃ”, яĐșі ряЮĐșĐž ĐșĐŸĐŽŃƒ ĐČіЮĐČіЮаĐČ Ń‚Đ”ŃŃ‚, алД ĐœĐ” пДрДĐČіряє, чо тДстО спраĐČЮі Ń‰ĐŸŃŃŒ пДрДĐČŃ–Ń€ŃĐ»Đžâ€Šâ€”â€ŠĐ·Đ°ŃĐČĐ»Đ”ĐœĐŸ ĐŽĐ»Ń праĐČĐžĐ»ŃŒĐœĐŸŃ— ĐČŃ–ĐŽĐżĐŸĐČіЮі. ĐŻĐș Ń…Ń‚ĐŸŃŃŒ, Ń…Ń‚ĐŸ ĐżĐŸĐŽĐŸŃ€ĐŸĐ¶ŃƒŃ” у спраĐČах і ĐżĐŸĐșĐ°Đ·ŃƒŃ” ŃˆŃ‚Đ°ĐŒĐżĐž ĐČ ĐżĐ°ŃĐżĐŸŃ€Ń‚Ń–â€Šâ€”Â«Ń†Đ” ĐœĐ” ĐŽĐŸĐČĐŸĐŽĐžŃ‚ŃŒ Đ¶ĐŸĐŽĐœĐŸŃ— Ń€ĐŸĐ±ĐŸŃ‚Đž, лОшД тД, Ń‰ĐŸ ĐČŃ–Đœ ĐČіЮĐČіЮаĐČ ĐșŃ–Đ»ŃŒĐșа Đ°Đ”Ń€ĐŸĐżĐŸŃ€Ń‚Ń–ĐČ Ń– ĐłĐŸŃ‚Đ”Đ»Ń–ĐČ. + +ĐąĐ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐœĐ° ĐŸŃĐœĐŸĐČі ĐŒŃƒŃ‚Đ°Ń†Ń–Đč тут, Ń‰ĐŸĐ± ĐŽĐŸĐżĐŸĐŒĐŸĐłŃ‚Đž, ĐČĐžĐŒŃ–Ń€ŃŽŃŽŃ‡Đž ĐșŃ–Đ»ŃŒĐșість ĐșĐŸĐŽŃƒ, яĐșĐžĐč буĐČ Ń„Đ°ĐșŃ‚ĐžŃ‡ĐœĐŸ ПРОбЕСбОВАНИЙ, а ĐœĐ” ĐżŃ€ĐŸŃŃ‚ĐŸ ВІДВІДАНИЙ. [Stryker](https://stryker-mutator.io/) — цД Đ±Ń–Đ±Đ»Ń–ĐŸŃ‚Đ”Đșа JavaScript ĐŽĐ»Ń Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐœĐ° ĐŒŃƒŃ‚Đ°Ń†Ń–Ń—, Ń€Đ”Đ°Đ»Ń–Đ·Đ°Ń†Ń–Ń яĐșĐŸŃ— ЎужД ĐłĐ°Ń€ĐœĐ°: + +(1) ĐČŃ–Đœ ĐœĐ°ĐČĐŒĐžŃĐœĐŸ Đ·ĐŒŃ–ĐœŃŽŃ” ĐșĐŸĐŽ і Â«ĐżŃ–ĐŽŃĐ°ĐŽĐ¶ŃƒŃ” ĐżĐŸĐŒĐžĐ»ĐșО». НапроĐșлаЎ, ĐșĐŸĐŽ newOrder.price===0 стає newOrder.price!=0. Щі Â«ĐżĐŸĐŒĐžĐ»ĐșО» ĐœĐ°Đ·ĐžĐČаються ĐŒŃƒŃ‚Đ°Ń†Ń–ŃĐŒĐž + +(2) ĐČŃ–Đœ запусĐșає тДстО, яĐșŃ‰ĐŸ ĐČсі ĐČЮаються, Ń‚ĐŸĐŽŃ– у ĐœĐ°Ń є ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐ°â€Šâ€”Â«Ń‚Đ”ŃŃ‚Đž ĐœĐ” ĐČĐžĐșĐŸĐœĐ°Đ»Đž сĐČĐŸŃ”Ń— ĐŒĐ”Ń‚Đž ĐČояĐČĐ»Đ”ĐœĐœŃ ĐżĐŸĐŒĐžĐ»ĐŸĐș, ĐŒŃƒŃ‚Đ°Ń†Ń–Ń— таĐș Đ·ĐČĐ°ĐœŃ– ĐČОжОлО. ĐŻĐșŃ‰ĐŸ тДстО ĐżŃ€ĐŸĐČĐ°Đ»ĐžĐ»ĐžŃŃ, Ń‚ĐŸ Ń‡ŃƒĐŽĐŸĐČĐŸ, ĐŒŃƒŃ‚Đ°Ń†Ń–Ń— булО ĐČбОті. + +Đ—ĐœĐ°ĐœĐœŃ Ń‚ĐŸĐłĐŸ, Ń‰ĐŸ ĐČсі Đ°Đ±ĐŸ Đ±Ń–Đ»ŃŒŃˆŃ–ŃŃ‚ŃŒ ĐŒŃƒŃ‚Đ°Ń†Ń–Đč булО Đ·ĐœĐžŃ‰Đ”ĐœŃ–, Юає ĐœĐ°Đ±Đ°ĐłĐ°Ń‚ĐŸ Đ±Ń–Đ»ŃŒŃˆŃƒ ĐČпДĐČĐœĐ”ĐœŃ–ŃŃ‚ŃŒ, ĐœŃ–Đ¶ траЮоціĐčĐœĐ” ĐżĐŸĐșроття, а час ĐœĐ°Đ»Đ°ŃˆŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐżĐŸĐŽŃ–Đ±ĐœĐžĐč +
    + +❌ **Đ†ĐœĐ°ĐșшД:** Во Đ±ŃƒĐŽĐ”Ń‚Đ” ĐŸĐ±ĐŽŃƒŃ€Đ”ĐœŃ–, яĐșŃ‰ĐŸ ĐżĐŸĐČірОтД, Ń‰ĐŸ 85% ĐżĐŸĐșроття ĐŸĐ·ĐœĐ°Ń‡Đ°Ń”, Ń‰ĐŸ ĐČаш тДст ĐČояĐČоть ĐżĐŸĐŒĐžĐ»ĐșĐž ĐČ 85% ĐČĐ°ŃˆĐŸĐłĐŸ ĐșĐŸĐŽŃƒ + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :thumbsdown: ПроĐșлаЎ Đ°ĐœŃ‚ĐžŃˆĐ°Đ±Đ»ĐŸĐœŃƒ: 100% ĐżĐŸĐșроття, 0% Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ + +![](https://img.shields.io/badge/🔹%20Example%20using%20Stryker-blue.svg "ВоĐșĐŸŃ€ĐžŃŃ‚Đ°ĐœĐœŃ Stryker") + +```javascript +function addNewOrder(newOrder) { + logger.log(`Adding new order ${newOrder}`); + DB.save(newOrder); + Mailer.sendMail(newOrder.assignee, `A new order was places ${newOrder}`); + + return { approved: true }; +} + +it("Test addNewOrder, don't use such test names", () => { + addNewOrder({ assignee: "John@mailer.com", price: 120 }); +}); // ЗапусĐșає 100% ĐżĐŸĐșроття ĐșĐŸĐŽŃƒ, алД ĐœŃ–Ń‡ĐŸĐłĐŸ ĐœĐ” пДрДĐČіряє +``` + +
    + +### :clap: ПроĐșлаЎ праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: ЗĐČіто Stryker, Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚ ĐŽĐ»Ń пДрДĐČірĐșĐž ĐŒŃƒŃ‚Đ°Ń†Ń–Đč, ĐČояĐČĐ»ŃŃŽŃ‚ŃŒ і ĐżŃ–ĐŽŃ€Đ°Ń…ĐŸĐČують ĐșŃ–Đ»ŃŒĐșість ĐșĐŸĐŽŃƒ, яĐșĐžĐč ĐœĐ” пДрДĐČіряється (Мутації) + +![alt text](assets/bp-20-yoni-goldberg-mutation-testing.jpeg "ЗĐČіто Stryker, Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚ ĐŽĐ»Ń пДрДĐČірĐșĐž ĐŒŃƒŃ‚Đ°Ń†Ń–Đč, ĐČояĐČĐ»ŃŃŽŃ‚ŃŒ і ĐżŃ–ĐŽŃ€Đ°Ń…ĐŸĐČують ĐșŃ–Đ»ŃŒĐșість ĐșĐŸĐŽŃƒ, яĐșĐžĐč ĐœĐ” пДрДĐČіряється (Мутації)") + +
    + +

    + +## âšȘ 4.4 Đ—Đ°ĐżĐŸĐ±Ń–ĐłĐ°ĐœĐœŃ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐ°ĐŒ Ń‚Đ”ŃŃ‚ĐŸĐČĐŸĐłĐŸ ĐșĐŸĐŽŃƒ за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ Ń‚Đ”ŃŃ‚ĐŸĐČох Đ»Ń–ĐœŃ‚Đ”Ń€Ń–ĐČ + +:white_check_mark: **Đ ĐŸĐ±Đž:** ĐĐ°Đ±Ń–Ń€ ĐżĐ»Đ°ĐłŃ–ĐœŃ–ĐČ ESLint стĐČĐŸŃ€Đ”ĐœĐŸ ŃĐżĐ”Ń†Ń–Đ°Đ»ŃŒĐœĐŸ ĐŽĐ»Ń пДрДĐČірĐșĐž ŃˆĐ°Đ±Đ»ĐŸĐœŃ–ĐČ ĐșĐŸĐŽŃƒ тДстіĐČ Ń– ĐČояĐČĐ»Đ”ĐœĐœŃ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒ. НапроĐșлаЎ, [eslint-plugin-mocha](https://www.npmjs.com/package/eslint-plugin-mocha) ĐżĐŸĐżĐ”Ń€Đ”ĐŽĐ¶Đ°Ń‚ĐžĐŒĐ”, ĐșĐŸĐ»Đž тДст ĐœĐ°ĐżĐžŃĐ°ĐœĐžĐč ĐœĐ° ĐłĐ»ĐŸĐ±Đ°Đ»ŃŒĐœĐŸĐŒŃƒ ріĐČĐœŃ– (а ĐœĐ” ŃĐžĐœ ĐŸĐżĐ”Ń€Đ°Ń‚ĐŸŃ€Đ° describe()) Đ°Đ±ĐŸ ĐșĐŸĐ»Đž тДстО [skipped](https://mochajs.org/#inclusive-tests), Ń‰ĐŸ ĐŒĐŸĐ¶Đ” прОзĐČДстО ĐŽĐŸ Ń…ĐžĐ±ĐœĐŸĐłĐŸ пДрДĐșĐŸĐœĐ°ĐœĐœŃ, Ń‰ĐŸ ĐČсі тДстО ŃƒŃĐżŃ–ŃˆĐœĐŸ ĐżŃ€ĐŸĐčĐŽĐ”ĐœŃ–. ĐŸĐŸĐŽŃ–Đ±ĐœĐžĐŒ Ń‡ĐžĐœĐŸĐŒ [eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest) ĐŒĐŸĐ¶Đ”, ĐœĐ°ĐżŃ€ĐžĐșлаЎ, ĐżĐŸĐżĐ”Ń€Đ”ĐŽĐžŃ‚Đž, ĐșĐŸĐ»Đž тДст ĐČзагалі ĐœĐ” ĐŒĐ°Ń” тĐČĐ”Ń€ĐŽĐ¶Đ”ĐœŃŒ (ĐœŃ–Ń‡ĐŸĐłĐŸ ĐœĐ” пДрДĐČіряє) + +
    + +❌ **Đ†ĐœĐ°ĐșшД:** ĐŸĐŸĐ±Đ°Ń‡ĐžĐČшО 90% ĐŸŃ…ĐŸĐżĐ»Đ”ĐœĐœŃ ĐșĐŸĐŽŃƒ та 100% Đ·Đ”Đ»Đ”ĐœĐžŃ… тДстіĐČ, ĐČашД ĐŸĐ±Đ»ĐžŃ‡Ń‡Ń буЎД ŃˆĐžŃ€ĐŸĐșĐŸ ĐżĐŸŃĐŒŃ–Ń…Đ°Ń‚ĐžŃŃ, ĐŽĐŸĐșĐž ĐČĐž ĐœĐ” Đ·Ń€ĐŸĐ·ŃƒĐŒŃ–Ń”Ń‚Đ”, Ń‰ĐŸ Đ±Đ°ĐłĐ°Ń‚ĐŸ тДстіĐČ ĐœŃ–Ń‡ĐŸĐłĐŸ ĐœĐ” стĐČĐ”Ń€ĐŽĐ¶ŃƒŃŽŃ‚ŃŒ, а Đ±Đ°ĐłĐ°Ń‚ĐŸ ĐœĐ°Đ±ĐŸŃ€Ń–ĐČ Ń‚Đ”ŃŃ‚Ń–ĐČ ĐżŃ€ĐŸŃŃ‚ĐŸ ĐżŃ€ĐŸĐżŃƒŃ‰Đ”ĐœĐŸ. ĐĄĐżĐŸĐŽŃ–ĐČаюся, ĐČĐž ĐœŃ–Ń‡ĐŸĐłĐŸ ĐœĐ” Ń€ĐŸĐ·ĐłĐŸŃ€ĐœŃƒĐ»Đž ĐœĐ° ĐŸŃĐœĐŸĐČі Ń†ŃŒĐŸĐłĐŸ ĐżĐŸĐŒĐžĐ»ĐșĐŸĐČĐŸĐłĐŸ ŃĐżĐŸŃŃ‚Đ”Ń€Đ”Đ¶Đ”ĐœĐœŃ + +
    +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :thumbsdown: ПроĐșлаЎ Đ°ĐœŃ‚ĐžŃˆĐ°Đ±Đ»ĐŸĐœŃƒ: ĐąĐ”ŃŃ‚ĐŸĐČĐžĐč проĐșлаЎ, ĐżĐŸĐČĐœĐžĐč ĐżĐŸĐŒĐžĐ»ĐŸĐș, ĐœĐ° щастя, усі ĐČĐŸĐœĐž ĐČояĐČĐ»Đ”ĐœŃ– за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ Đ»Ń–ĐœŃ‚Đ”Ń€Ń–ĐČ (Linters) + +```javascript +describe("Too short description", () => { + const userToken = userService.getDefaultToken() // *error:no-setup-in-describe, use hooks (sparingly) instead + it("Some description", () => {});//* error: valid-test-description. Must include the word "Should" + at least 5 words +}); + +it.skip("Test name", () => {// *error:no-skipped-tests, error:error:no-global-tests. Put tests only under describe or suite + expect("somevalue"); // error:no-assert +}); + +it("Test name", () => {*//error:no-identical-title. Assign unique titles to tests +}); +``` + +
    + +

    + +# Section 5ïžâƒŁ: CI та Ń–ĐœŃˆŃ– ĐżĐŸĐșĐ°Đ·ĐœĐžĐșĐž яĐșĐŸŃŃ‚Ń– + +

    + +## âšȘ  5.1 Đ—Đ±Đ°ĐłĐ°Ń‡ŃƒĐčтД ĐČаші Đ»Ń–ĐœŃ‚Đ”Ń€Đž та ĐżŃ€ĐžĐżĐžĐœŃĐčтД збірĐșĐž, яĐșі ĐŒĐ°ŃŽŃ‚ŃŒ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐž Đ· Đ»Ń–ĐœŃ–ĐœĐłĐ°ĐŒĐž + +:white_check_mark: **Đ ĐŸĐ±Đž:** Linters — цД бДзĐșĐŸŃˆŃ‚ĐŸĐČĐœĐžĐč ĐŸĐ±Ń–ĐŽ, ĐżŃ–ŃĐ»Ń 5-хĐČĐžĐ»ĐžĐœĐœĐŸĐłĐŸ ĐœĐ°Đ»Đ°ŃˆŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐČĐž бДзĐșĐŸŃˆŃ‚ĐŸĐČĐœĐŸ ĐŸŃ‚Ń€ĐžĐŒŃƒŃ”Ń‚Đ” аĐČŃ‚ĐŸĐżŃ–Đ»ĐŸŃ‚, яĐșĐžĐč ĐŸŃ…ĐŸŃ€ĐŸĐœŃŃ” ĐČаш ĐșĐŸĐŽ і ĐČояĐČĐ»ŃŃ” Đ·ĐœĐ°Ń‡ĐœŃ– ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐž піЮ час ĐČĐČĐ”ĐŽĐ”ĐœĐœŃ. ĐŸŃ€ĐŸĐčшлО ті часо, ĐșĐŸĐ»Đž Ń€ĐŸĐ·ĐŒĐŸĐČĐž ŃŃ‚ĐŸŃŃƒĐČĐ°Đ»ĐžŃŃ ĐșĐŸŃĐŒĐ”Ń‚ĐžĐșĐž (бДз ĐșŃ€Đ°ĐżĐŸĐș Đ· ĐșĐŸĐŒĐŸŃŽ!). ĐĄŃŒĐŸĐłĐŸĐŽĐœŃ– Linters ĐŒĐŸĐ¶Đ” ĐČояĐČĐ»ŃŃ‚Đž сДрĐčĐŸĐ·ĐœŃ– ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐž, яĐș-ĐŸŃ‚ ĐżĐŸĐŒĐžĐ»ĐșĐž, яĐșі ĐœĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœĐŸ ĐČоЮаються та ĐČтрачають Ń–ĐœŃ„ĐŸŃ€ĐŒĐ°Ń†Ń–ŃŽ. На ĐŽĐŸĐŽĐ°Ń‚ĐŸĐș ĐŽĐŸ ĐČĐ°ŃˆĐŸĐłĐŸ Đ±Đ°Đ·ĐŸĐČĐŸĐłĐŸ ĐœĐ°Đ±ĐŸŃ€Ńƒ праĐČОл (ĐœĐ°ĐżŃ€ĐžĐșлаЎ, [ŃŃ‚Đ°ĐœĐŽĐ°Ń€Ń‚ ESLint](https://www.npmjs.com/package/eslint-plugin-standard) Đ°Đ±ĐŸ [ŃŃ‚ĐžĐ»ŃŒ Airbnb](https://www.npmjs.com/package /eslint-config-airbnb)), ĐżĐŸĐŽŃƒĐŒĐ°ĐčтД ĐżŃ€ĐŸ ĐČĐșĐ»ŃŽŃ‡Đ”ĐœĐœŃ ĐŽĐ”ŃĐșох ŃĐżĐ”Ń†Ń–Đ°Đ»Ń–Đ·ĐŸĐČĐ°ĐœĐžŃ… Đ»Ń–ĐœŃ‚Đ”Ń€Ń–ĐČ, таĐșох яĐș [eslint-plugin-chai-expect](https://www.npmjs.com/package/eslint-plugin-chai-expect), яĐșі ĐŒĐŸĐ¶ŃƒŃ‚ŃŒ ĐČояĐČĐ»ŃŃ‚Đž тДстО бДз тĐČĐ”Ń€ĐŽĐ¶Đ”ĐœĐœŃ, [eslint-plugin-promise](https://www.npmjs.com/package/eslint-plugin-promise?activeTab=readme) ĐŒĐŸĐ¶Đ” ĐČояĐČĐ»ŃŃ‚Đž ĐŸĐ±Ń–Ń†ŃĐœĐșĐž бДз ĐČĐžŃ€Ń–ŃˆĐ”ĐœĐœŃ (ĐČаш ĐșĐŸĐŽ ĐœŃ–ĐșĐŸĐ»Đž ĐœĐ” ĐżŃ€ĐŸĐŽĐŸĐČĐ¶ĐžŃ‚ŃŒŃŃ), [eslint-plugin-security](https://www.npmjs.com/package/eslint-plugin-security?activeTab=readme), яĐșĐžĐč ĐŒĐŸĐ¶Đ” ĐČояĐČото аĐșтоĐČĐœŃ– Ń€Đ”ĐłŃƒĐ»ŃŃ€ĐœŃ– ĐČОразО, яĐșі ĐŒĐŸĐ¶ŃƒŃ‚ŃŒ Đ±ŃƒŃ‚Đž ĐČĐžĐșĐŸŃ€ĐžŃŃ‚Đ°ĐœŃ– ĐŽĐ»Ń атаĐș DOS, і [eslint-plugin-you-dont-need-lodash-underscore](https://www.npmjs.com/package/eslint-plugin-you-dont-need-lodash-underscore) Đ·ĐŽĐ°Ń‚ĐœĐžĐč ĐČĐžĐșлОĐșато троĐČĐŸĐłŃƒ, ĐșĐŸĐ»Đž ĐșĐŸĐŽ ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČує ĐŒĐ”Ń‚ĐŸĐŽĐž ŃĐ»ŃƒĐ¶Đ±ĐŸĐČĐŸŃ— Đ±Ń–Đ±Đ»Ń–ĐŸŃ‚Đ”ĐșĐž, яĐșі є Ń‡Đ°ŃŃ‚ĐžĐœĐŸŃŽ V8 ĐŸŃĐœĐŸĐČĐœŃ– ĐŒĐ”Ń‚ĐŸĐŽĐž, таĐșі яĐș Lodash.\_map(
) +
    + +❌ **Đ†ĐœĐ°ĐșшД:** УяĐČіть ŃĐŸĐ±Ń– ĐŽĐŸŃ‰ĐŸĐČĐžĐč ĐŽĐ”ĐœŃŒ, ĐșĐŸĐ»Đž ĐČаша ĐżŃ€ĐŸĐŽŃƒĐșтоĐČĐœŃ–ŃŃ‚ŃŒ ĐżŃ€ĐŸĐŽĐŸĐČĐ¶ŃƒŃ” ЎаĐČато Đ·Đ±ĐŸŃ—, алД Đ¶ŃƒŃ€ĐœĐ°Đ»Đž ĐœĐ” ĐČŃ–ĐŽĐŸĐ±Ń€Đ°Đ¶Đ°ŃŽŃ‚ŃŒ трасуĐČĐ°ĐœĐœŃ стДĐșа ĐżĐŸĐŒĐžĐ»ĐŸĐș. Đ©ĐŸ Ń‚Ń€Đ°ĐżĐžĐ»ĐŸŃŃŒ? Ваш ĐșĐŸĐŽ ĐżĐŸĐŒĐžĐ»ĐșĐŸĐČĐŸ ĐČĐžĐșĐžĐœŃƒĐČ ĐŸĐ±â€™Ń”Đșт бДз ĐżĐŸĐŒĐžĐ»ĐșĐž, і трасуĐČĐ°ĐœĐœŃ стДĐșа Đ±ŃƒĐ»ĐŸ ĐČŃ‚Ń€Đ°Ń‡Đ”ĐœĐŸ, Ń‰ĐŸ є ĐČĐ°ĐłĐŸĐŒĐŸŃŽ ĐżŃ€ĐžŃ‡ĐžĐœĐŸŃŽ ĐŽĐ»Ń Ń‚ĐŸĐłĐŸ, Ń‰ĐŸĐ± Đ±ĐžŃ‚ĐžŃŃ ĐłĐŸĐ»ĐŸĐČĐŸŃŽ ĐŸĐ± Ń†Đ”ĐłĐ»ŃĐœŃƒ ŃŃ‚Ń–ĐœŃƒ. 5-хĐČĐžĐ»ĐžĐœĐœĐ” ĐœĐ°Đ»Đ°ŃˆŃ‚ŃƒĐČĐ°ĐœĐœŃ Linter ĐŒĐŸĐ¶Đ” ĐČояĐČото цю TYPO і ĐČрятуĐČато ĐČаш ĐŽĐ”ĐœŃŒ + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :thumbsdown: ПроĐșлаЎ Đ°ĐœŃ‚ĐžŃˆĐ°Đ±Đ»ĐŸĐœŃƒ: ĐŸĐŸĐŒĐžĐ»ĐșĐŸĐČĐŸ стĐČĐŸŃ€Đ”ĐœĐŸ ĐœĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœĐžĐč ĐŸĐ±â€™Ń”Đșт Error, трасуĐČĐ°ĐœĐœŃ стДĐșа ĐŽĐ»Ń цієї ĐżĐŸĐŒĐžĐ»ĐșĐž ĐœĐ” Đ·â€™ŃĐČоться. На щастя, ESLint ĐČояĐČĐ»ŃŃ” ĐœĐ°ŃŃ‚ŃƒĐżĐœŃƒ ĐČĐžŃ€ĐŸĐ±ĐœĐžŃ‡Ńƒ ĐżĐŸĐŒĐžĐ»Đșу + +![alt text](assets/bp-21-yoni-goldberg-eslint.jpeg "ĐŸĐŸĐŒĐžĐ»ĐșĐŸĐČĐŸ стĐČĐŸŃ€Đ”ĐœĐŸ ĐœĐ”ĐżŃ€Đ°ĐČĐžĐ»ŃŒĐœĐžĐč ĐŸĐ±â€™Ń”Đșт Error, трасуĐČĐ°ĐœĐœŃ стДĐșа ĐŽĐ»Ń цієї ĐżĐŸĐŒĐžĐ»ĐșĐž ĐœĐ” Đ·â€™ŃĐČоться. На щастя, ESLint ĐČояĐČĐ»ŃŃ” ĐœĐ°ŃŃ‚ŃƒĐżĐœŃƒ ĐČĐžŃ€ĐŸĐ±ĐœĐžŃ‡Ńƒ ĐżĐŸĐŒĐžĐ»Đșу") + +
    + +

    + +## âšȘ  5.2 ĐĄĐșĐŸŃ€ĐŸŃ‚Ń–Ń‚ŃŒ цоĐșĐ» Đ·ĐČĐŸŃ€ĐŸŃ‚ĐœĐŸĐłĐŸ Đ·ĐČâ€™ŃĐ·Đșу за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ Đ»ĐŸĐșĐ°Đ»ŃŒĐœĐŸĐłĐŸ Developer-CI + +:white_check_mark: **Đ ĐŸĐ±Đž:** ВоĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČŃƒŃ”Ń‚Đ” CI Đ· блОсĐșŃƒŃ‡ĐŸŃŽ пДрДĐČірĐșĐŸŃŽ яĐșĐŸŃŃ‚Ń–, яĐș-ĐŸŃ‚ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ, Đ»Ń–ĐœŃ‚ŃƒĐČĐ°ĐœĐœŃ, пДрДĐČірĐșа ĐČразлОĐČĐŸŃŃ‚Đ”Đč Ń‚ĐŸŃ‰ĐŸ? Đ”ĐŸĐżĐŸĐŒĐŸĐ¶Ń–Ń‚ŃŒ Ń€ĐŸĐ·Ń€ĐŸĐ±ĐœĐžĐșĐ°ĐŒ Đ·Đ°ĐżŃƒŃŃ‚ĐžŃ‚Đž цДĐč ĐșĐŸĐœĐČДєр таĐșĐŸĐ¶ Đ»ĐŸĐșĐ°Đ»ŃŒĐœĐŸ, Ń‰ĐŸĐ± ĐŸŃ‚Ń€ĐžĐŒĐ°Ń‚Đž ĐŒĐžŃ‚Ń‚Ń”ĐČĐžĐč ĐČіЮгуĐș і сĐșĐŸŃ€ĐŸŃ‚ĐžŃ‚Đž [цоĐșĐ» Đ·ĐČĐŸŃ€ĐŸŃ‚ĐœĐŸĐłĐŸ Đ·ĐČâ€™ŃĐ·Đșу](https://www.gocd.org/2016/03/15/are-you-ready-for-continuous-delivery-part-2 -пДтлі Đ·ĐČĐŸŃ€ĐŸŃ‚ĐœĐŸĐłĐŸ Đ·ĐČ'ŃĐ·Đșу/). Ń‡ĐŸĐŒŃƒ ДфДĐșтоĐČĐœĐžĐč ĐżŃ€ĐŸŃ†Đ”Ń Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ сĐșĐ»Đ°ĐŽĐ°Ń”Ń‚ŃŒŃŃ Đ· Đ±Đ°ĐłĐ°Ń‚ŃŒĐŸŃ… ітДраціĐčĐœĐžŃ… цоĐșліĐČ: (1) Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ -> (2) Đ·ĐČĐŸŃ€ĐŸŃ‚ĐœŃ–Đč Đ·ĐČ'ŃĐ·ĐŸĐș -> (3) рДфаĐșŃ‚ĐŸŃ€ĐžĐœĐł. Đ§ĐžĐŒ шĐČОЎшОĐč Đ·ĐČĐŸŃ€ĐŸŃ‚ĐœŃ–Đč Đ·ĐČâ€™ŃĐ·ĐŸĐș, Ń‚ĐžĐŒ Đ±Ń–Đ»ŃŒŃˆĐ” ітДраціĐč ĐżĐŸĐșŃ€Đ°Ń‰Đ”ĐœĐœŃ ĐŒĐŸĐ¶Đ” ĐČĐžĐșĐŸĐœĐ°Ń‚Đž Ń€ĐŸĐ·Ń€ĐŸĐ±ĐœĐžĐș ĐŽĐ»Ń ĐșĐŸĐ¶ĐœĐŸĐłĐŸ ĐŒĐŸĐŽŃƒĐ»Ń та ĐżĐŸĐșращото Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚Đž. З Ń–ĐœŃˆĐŸĐłĐŸ Đ±ĐŸĐșу, ĐșĐŸĐ»Đž Đ·ĐČĐŸŃ€ĐŸŃ‚ĐœŃ–Đč Đ·ĐČâ€™ŃĐ·ĐŸĐș ĐœĐ°ĐŽŃ…ĐŸĐŽĐžŃ‚ŃŒ Ń–Đ· Đ·Đ°ĐżŃ–Đ·ĐœĐ”ĐœĐœŃĐŒ, ĐŒĐ”ĐœŃˆĐ” ітДраціĐč ĐżĐŸĐșŃ€Đ°Ń‰Đ”ĐœĐœŃ ĐŒĐŸĐ¶Đ” Đ±ŃƒŃ‚Đž упаĐșĐŸĐČĐ°ĐœĐŸ ĐČ ĐŸĐŽĐžĐœ ĐŽĐ”ĐœŃŒ, ĐșĐŸĐŒĐ°ĐœĐŽĐ° ĐŒĐŸĐ¶Đ” ĐČжД пДрДĐčто ĐŽĐŸ Ń–ĐœŃˆĐŸŃ— Ń‚Đ”ĐŒĐž/заĐČĐŽĐ°ĐœĐœŃ/ĐŒĐŸĐŽŃƒĐ»Ń та ĐœĐ” ĐłĐŸŃ‚ĐŸĐČа ĐČĐŽĐŸŃĐșĐŸĐœĐ°Đ»ŃŽĐČато цДĐč ĐŒĐŸĐŽŃƒĐ»ŃŒ. + +На праĐșтоці ĐŽĐ”ŃĐșі ĐżĐŸŃŃ‚Đ°Ń‡Đ°Đ»ŃŒĐœĐžĐșĐž CI (ПроĐșлаЎ: [CircleCI local CLI](https://circleci.com/docs/2.0/local-cli/)) ĐŽĐŸĐ·ĐČĐŸĐ»ŃŃŽŃ‚ŃŒ запусĐșато ĐșĐŸĐœĐČДєр Đ»ĐŸĐșĐ°Đ»ŃŒĐœĐŸ. Đ”Đ”ŃĐșі ĐșĐŸĐŒĐ”Ń€Ń†Ń–ĐčĐœŃ– Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Đž, яĐș-ĐŸŃ‚ [wallaby, ĐœĐ°ĐŽĐ°ŃŽŃ‚ŃŒ ЎужД Ń†Ń–ĐœĐœŃƒ Ń–ĐœŃ„ĐŸŃ€ĐŒĐ°Ń†Ń–ŃŽ ĐŽĐ»Ń Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ](https://wallabyjs.com/) яĐș ĐżŃ€ĐŸŃ‚ĐŸŃ‚ĐžĐż Ń€ĐŸĐ·Ń€ĐŸĐ±ĐœĐžĐșа (бДз Đ·ĐČâ€™ŃĐ·Đșу). ĐšŃ€Ń–ĐŒ Ń‚ĐŸĐłĐŸ, ĐČĐž ĐŒĐŸĐ¶Đ”Ń‚Đ” ĐżŃ€ĐŸŃŃ‚ĐŸ ĐŽĐŸĐŽĐ°Ń‚Đž ŃŃ†Đ”ĐœĐ°Ń€Ń–Đč npm ĐŽĐŸ package.json, яĐșĐžĐč запусĐșає ĐČсі ĐșĐŸĐŒĐ°ĐœĐŽĐž яĐșĐŸŃŃ‚Ń– (ĐœĐ°ĐżŃ€ĐžĐșлаЎ, test, lint, vulnerabilities) — ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČуĐčтД таĐșі Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Đž, яĐș [concurrently](https://www.npmjs.com/package/concurrently) ĐŽĐ»Ń Ń€ĐŸĐ·ĐżĐ°Ń€Đ°Đ»Đ”Đ»ŃŽĐČĐ°ĐœĐœŃ і ĐœĐ”ĐœŃƒĐ»ŃŒĐŸĐČĐžĐč ĐșĐŸĐŽ ĐČĐžŃ…ĐŸĐŽŃƒ, яĐșŃ‰ĐŸ ĐŸĐŽĐžĐœ Ń–Đ· Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Ń–ĐČ ĐČĐžĐčŃˆĐŸĐČ Đ· лаЎу. йДпДр Ń€ĐŸĐ·Ń€ĐŸĐ±ĐœĐžĐș ĐŒĐ°Ń” ĐżŃ€ĐŸŃŃ‚ĐŸ ĐČĐžĐșлОĐșато ĐŸĐŽĐœŃƒ ĐșĐŸĐŒĐ°ĐœĐŽŃƒâ€Šâ€”â€ŠĐœĐ°ĐżŃ€ĐžĐșлаЎ. «ЯĐșість ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ npmÂ»â€Šâ€”â€ŠŃ‰ĐŸĐ± ĐŸŃ‚Ń€ĐžĐŒĐ°Ń‚Đž ĐŒĐžŃ‚Ń‚Ń”ĐČĐžĐč ĐČіЮгуĐș. йаĐșĐŸĐ¶ ĐżĐŸĐŽŃƒĐŒĐ°ĐčтД ĐżŃ€ĐŸ пДрДрОĐČĐ°ĐœĐœŃ ĐșĐŸĐŒŃ–Ń‚Ńƒ, яĐșŃ‰ĐŸ пДрДĐČірĐșа яĐșĐŸŃŃ‚Ń– ĐœĐ” ĐČĐŽĐ°Đ»Đ°ŃŃ за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ githook ([husky ĐŒĐŸĐ¶Đ” ĐŽĐŸĐżĐŸĐŒĐŸĐłŃ‚Đž](https://github.com/typicode/husky)) +
    + +❌ **Đ†ĐœĐ°ĐșшД:** ĐšĐŸĐ»Đž яĐșŃ–ŃĐœŃ– Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚Đž ĐœĐ°ĐŽŃ…ĐŸĐŽŃŃ‚ŃŒ ĐœĐ°ŃŃ‚ŃƒĐżĐœĐŸĐłĐŸ ĐŽĐœŃ ĐżŃ–ŃĐ»Ń ĐșĐŸĐŽŃƒ, Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐœĐ” стає плаĐČĐœĐŸŃŽ Ń‡Đ°ŃŃ‚ĐžĐœĐŸŃŽ Ń€ĐŸĐ·Ń€ĐŸĐ±ĐșĐž, а Ń„ĐŸŃ€ĐŒĐ°Đ»ŃŒĐœĐžĐŒ артДфаĐșŃ‚ĐŸĐŒ. + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :clap: ПроĐșлаЎ праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: ĐĄŃ†Đ”ĐœĐ°Ń€Ń–Ń— npm, яĐșі ĐČĐžĐșĐŸĐœŃƒŃŽŃ‚ŃŒ пДрДĐČірĐșу яĐșĐŸŃŃ‚Ń– ĐșĐŸĐŽŃƒ, усі ĐČĐžĐșĐŸĐœŃƒŃŽŃ‚ŃŒŃŃ ĐżĐ°Ń€Đ°Đ»Đ”Đ»ŃŒĐœĐŸ ĐœĐ° ĐČĐžĐŒĐŸĐłŃƒ Đ°Đ±ĐŸ ĐșĐŸĐ»Đž Ń€ĐŸĐ·Ń€ĐŸĐ±ĐœĐžĐș ĐœĐ°ĐŒĐ°ĐłĐ°Ń”Ń‚ŃŒŃŃ ĐœĐ°ĐŽŃ–ŃĐ»Đ°Ń‚Đž ĐœĐŸĐČĐžĐč ĐșĐŸĐŽ + +```javascript +"scripts": { + "inspect:sanity-testing": "mocha **/**--test.js --grep \"sanity\"", + "inspect:lint": "eslint .", + "inspect:vulnerabilities": "npm audit", + "inspect:license": "license-checker --failOn GPLv2", + "inspect:complexity": "plato .", + + "inspect:all": "concurrently -c \"bgBlue.bold,bgMagenta.bold,yellow\" \"npm:inspect:quick-testing\" \"npm:inspect:lint\" \"npm:inspect:vulnerabilities\" \"npm:inspect:license\"" + }, + "husky": { + "hooks": { + "precommit": "npm run inspect:all", + "prepush": "npm run inspect:all" + } +} + +``` + +
    + +

    + +## âšȘ 5.3 ВоĐșĐŸĐœĐ°ĐčтД Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ e2e ĐœĐ° спраĐČĐ¶ĐœŃŒĐŸĐŒŃƒ Ń€ĐŸĐ±ĐŸŃ‡ĐŸĐŒŃƒ ЎзДрĐșалі + +:white_check_mark: **Đ ĐŸĐ±Đž:** НасĐșŃ€Ń–Đ·ĐœĐ” Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ (e2e) є ĐłĐŸĐ»ĐŸĐČĐœĐŸŃŽ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐŸŃŽ ĐșĐŸĐ¶ĐœĐŸĐłĐŸ ĐșĐŸĐœĐČДєра CI —«стĐČĐŸŃ€Đ”ĐœĐœŃ Ń–ĐŽĐ”ĐœŃ‚ĐžŃ‡ĐœĐŸĐłĐŸ Đ”Ń„Đ”ĐŒĐ”Ń€ĐœĐŸĐłĐŸ Ń€ĐŸĐ±ĐŸŃ‡ĐŸĐłĐŸ ЎзДрĐșала ĐœĐ° Đ»ŃŒĐŸŃ‚Ńƒ Đ· ŃƒŃŃ–ĐŒĐ° ĐżĐŸĐČâ€™ŃĐ·Đ°ĐœĐžĐŒĐž Ń…ĐŒĐ°Ń€ĐœĐžĐŒĐž ŃĐ»ŃƒĐ¶Đ±Đ°ĐŒĐž ĐŒĐŸĐ¶Đ” Đ±ŃƒŃ‚Đž ĐČĐžŃĐœĐ°Đ¶Đ»ĐžĐČĐžĐŒ і ĐŽĐŸŃ€ĐŸĐłĐžĐŒ. ĐŸĐŸŃˆŃƒĐș ĐœĐ°ĐčĐșŃ€Đ°Ń‰ĐŸĐłĐŸ ĐșĐŸĐŒĐżŃ€ĐŸĐŒŃ–ŃŃƒ — ĐČаша гра: [Docker-compose](https://serverless.com/) ĐŽĐŸĐ·ĐČĐŸĐ»ŃŃ” стĐČĐŸŃ€ŃŽĐČато Ń–Đ·ĐŸĐ»ŃŒĐŸĐČĐ°ĐœĐ” ĐŽĐŸĐșĐ”Ń€ĐžĐ·ĐŸĐČĐ°ĐœĐ” ŃĐ”Ń€Đ”ĐŽĐŸĐČОщД Đ· Ń–ĐŽĐ”ĐœŃ‚ĐžŃ‡ĐœĐžĐŒĐž ĐșĐŸĐœŃ‚Đ”ĐčĐœĐ”Ń€Đ°ĐŒĐž за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ ĐŸĐŽĐœĐŸĐłĐŸ ĐżŃ€ĐŸŃŃ‚ĐŸĐłĐŸ тДĐșŃŃ‚ĐŸĐČĐŸĐłĐŸ фаĐčлу, алД Ń‚Đ”Ń…ĐœĐŸĐ»ĐŸĐłŃ–Ń ĐżŃ–ĐŽŃ‚Ń€ĐžĐŒĐșĐž (ĐœĐ°ĐżŃ€ĐžĐșлаЎ, ĐŒĐ”Ń€Đ”Đ¶Đ°, ĐŒĐŸĐŽĐ”Đ»ŃŒ Ń€ĐŸĐ·ĐłĐŸŃ€Ń‚Đ°ĐœĐœŃ) ĐČŃ–ĐŽŃ€Ń–Đ·ĐœŃŃ”Ń‚ŃŒŃŃ Đ· Ń€Đ”Đ°Đ»ŃŒĐœĐžŃ… ĐČĐžŃ€ĐŸĐ±ĐœĐžŃ†Ń‚ĐČ. Во ĐŒĐŸĐ¶Đ”Ń‚Đ” ĐżĐŸŃ”ĐŽĐœĐ°Ń‚Đž ĐčĐŸĐłĐŸ Đ· [‘AWS Local’](https://github.com/localstack/localstack), Ń‰ĐŸĐ± працюĐČато Ń–Đ· заглушĐșĐŸŃŽ спраĐČĐ¶ĐœŃ–Ń… служб AWS. ĐŻĐșŃ‰ĐŸ ĐČĐž ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČŃƒŃ”Ń‚Đ” [бДзсДрĐČĐ”Ń€ĐœŃ–](https://serverless.com/) ĐșŃ–Đ»ŃŒĐșа фрДĐčĐŒĐČĐŸŃ€ĐșіĐČ, яĐș-ĐŸŃ‚ бДзсДрĐČĐ”Ń€ĐœŃ–, і [AWS SAM](https://docs.aws.amazon.com/lambda/latest/dg/serverless_app.html) ĐŽĐŸĐ·ĐČĐŸĐ»ŃŃ” Đ»ĐŸĐșĐ°Đ»ŃŒĐœĐžĐč ĐČĐžĐșлОĐș ĐșĐŸĐŽŃƒ FaaS. + +Đ’Đ”Đ»ĐžŃ‡Đ”Đ·ĐœĐ° Đ”ĐșĐŸŃĐžŃŃ‚Đ”ĐŒĐ° Kubernetes щД ĐŒĐ°Ń” Ń„ĐŸŃ€ĐŒĐ°Đ»Ń–Đ·ŃƒĐČато ŃŃ‚Đ°ĐœĐŽĐ°Ń€Ń‚ĐœĐžĐč Đ·Ń€ŃƒŃ‡ĐœĐžĐč Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚ ĐŽĐ»Ń Đ»ĐŸĐșĐ°Đ»ŃŒĐœĐŸĐłĐŸ та CI-ЎзДрĐșалюĐČĐ°ĐœĐœŃ, Ń…ĐŸŃ‡Đ° Ń‡Đ°ŃŃ‚ĐŸ запусĐșається Đ±Đ°ĐłĐ°Ń‚ĐŸ ĐœĐŸĐČох Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Ń–ĐČ. ĐžĐŽĐœĐžĐŒ Ń–Đ· ĐżŃ–ĐŽŃ…ĐŸĐŽŃ–ĐČ Ń” запусĐș Â«ĐŒŃ–ĐœŃ–ĐŒŃ–Đ·ĐŸĐČĐ°ĐœĐŸĐłĐŸ Kubernetes» за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ таĐșох Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Ń–ĐČ, яĐș [Minikube](https://kubernetes.io/docs/setup/minikube/) і [MicroK8s](https://microk8s.io/), яĐșі ĐœĐ°ĐłĐ°ĐŽŃƒŃŽŃ‚ŃŒ спраĐČĐ¶ĐœŃ–Đč річ лОшД Đ· ĐŒĐ”ĐœŃˆĐžĐŒĐž ĐœĐ°ĐșĐ»Đ°ĐŽĐœĐžĐŒĐž ĐČĐžŃ‚Ń€Đ°Ń‚Đ°ĐŒĐž. Đ†ĐœŃˆĐžĐč піЮхіЮ ĐżĐŸĐ»ŃĐłĐ°Ń” ĐČ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ– ĐœĐ° ĐČŃ–ĐŽĐŽĐ°Đ»Đ”ĐœĐŸĐŒŃƒ Â«Ń€Đ”Đ°Đ»ŃŒĐœĐŸĐŒŃƒ Kubernetes». Đ”Đ”ŃĐșі ĐżĐŸŃŃ‚Đ°Ń‡Đ°Đ»ŃŒĐœĐžĐșĐž CI (ĐœĐ°ĐżŃ€ĐžĐșлаЎ, [Codefresh](https://codefresh.io/)) ĐŒĐ°ŃŽŃ‚ŃŒ ĐČĐ»Đ°ŃĐœŃƒ Ń–ĐœŃ‚Đ”ĐłŃ€Đ°Ń†Ń–ŃŽ Đ· ŃĐ”Ń€Đ”ĐŽĐŸĐČĐžŃ‰Đ”ĐŒ Kubernetes і ĐŽĐŸĐ·ĐČĐŸĐ»ŃŃŽŃ‚ŃŒ лДгĐșĐŸ запусĐșато ĐșĐŸĐœĐČДєр CI чДрДз Ń€Đ”Đ°Đ»ŃŒĐœĐžĐč Ń–ĐœŃˆŃ– ĐŽĐŸĐ·ĐČĐŸĐ»ŃŃŽŃ‚ŃŒ стĐČĐŸŃ€ŃŽĐČато ŃĐżĐ”Ń†Ń–Đ°Đ»ŃŒĐœŃ– ŃŃ†Đ”ĐœĐ°Ń€Ń–Ń— ĐŽĐ»Ń ĐČŃ–ĐŽĐŽĐ°Đ»Đ”ĐœĐŸĐłĐŸ Kubernetes. +
    + +❌ **Đ†ĐœĐ°ĐșшД:** ВоĐșĐŸŃ€ĐžŃŃ‚Đ°ĐœĐœŃ Ń€Ń–Đ·ĐœĐžŃ… Ń‚Đ”Ń…ĐœĐŸĐ»ĐŸĐłŃ–Đč ĐŽĐ»Ń ĐČĐžŃ€ĐŸĐ±ĐœĐžŃ†Ń‚ĐČа та Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐČĐžĐŒĐ°ĐłĐ°Ń” ĐżŃ–ĐŽŃ‚Ń€ĐžĐŒĐșĐž ĐŽĐČĐŸŃ… ĐŒĐŸĐŽĐ”Đ»Đ”Đč Ń€ĐŸĐ·ĐłĐŸŃ€Ń‚Đ°ĐœĐœŃ та Ń€ĐŸĐ·â€™Ń”ĐŽĐœŃƒŃ” Ń€ĐŸĐ·Ń€ĐŸĐ±ĐœĐžĐșіĐČ Ń– ĐșĐŸĐŒĐ°ĐœĐŽŃƒ ĐŸĐżĐ”Ń€Đ°Ń†Ń–Đč + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :clap: ПроĐșлаЎ: ĐșĐŸĐœĐČДєр CI, яĐșĐžĐč ĐłĐ”ĐœĐ”Ń€ŃƒŃ” ĐșластДр Kubernetes ĐœĐ° Đ»ŃŒĐŸŃ‚Ńƒ ([АĐČŃ‚ĐŸŃ€ŃŃ‚ĐČĐŸ: Dynamic-environments Kubernetes](https://container-solutions.com/dynamic-environments-kubernetes/)) + +
    deploy:
    stage: deploy
    image: registry.gitlab.com/gitlab-examples/kubernetes-deploy
    script:
    - ./configureCluster.sh $KUBE_CA_PEM_FILE $KUBE_URL $KUBE_TOKEN
    - kubectl create ns $NAMESPACE
    - kubectl create secret -n $NAMESPACE docker-registry gitlab-registry --docker-server="$CI_REGISTRY" --docker-username="$CI_REGISTRY_USER" --docker-password="$CI_REGISTRY_PASSWORD" --docker-email="$GITLAB_USER_EMAIL"
    - mkdir .generated
    - echo "$CI_BUILD_REF_NAME-$CI_BUILD_REF"
    - sed -e "s/TAG/$CI_BUILD_REF_NAME-$CI_BUILD_REF/g" templates/deals.yaml | tee ".generated/deals.yaml"
    - kubectl apply --namespace $NAMESPACE -f .generated/deals.yaml
    - kubectl apply --namespace $NAMESPACE -f templates/my-sock-shop.yaml
    environment:
    name: test-for-ci
    + +
    + +

    + +## âšȘ 5.4 ĐŸĐ°Ń€Đ°Đ»Đ”Đ»Ń–Đ·ŃƒĐčтД ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ Ń‚Đ”ŃŃ‚Ńƒ + +:white_check_mark: **Đ ĐŸĐ±Đž:** ĐŻĐșŃ‰ĐŸ ĐČсД Đ·Ń€ĐŸĐ±Đ»Đ”ĐœĐŸ праĐČĐžĐ»ŃŒĐœĐŸ, Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ŃŃ‚Đ°ĐœĐ” ĐČĐ°ŃˆĐžĐŒ ĐŽŃ€ŃƒĐłĐŸĐŒ Ń†Ń–Đ»ĐŸĐŽĐŸĐ±ĐŸĐČĐŸ та бДз ĐČĐžŃ…Ń–ĐŽĐœĐžŃ…, Ń‰ĐŸ ĐœĐ°ĐŽĐ°Ń” ĐŒĐ°ĐčжД ĐŒĐžŃ‚Ń‚Ń”ĐČĐžĐč ĐČіЮгуĐș. На праĐșтоці ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ 500 ĐŸĐ±ĐŒĐ”Đ¶Đ”ĐœĐžŃ… ЩП ĐŒĐŸĐŽŃƒĐ»ŃŒĐœĐžŃ… тДстіĐČ ĐČ ĐŸĐŽĐœĐŸĐŒŃƒ ĐżĐŸŃ‚ĐŸŃ†Ń– ĐŒĐŸĐ¶Đ” троĐČато Đ·Đ°ĐœĐ°ĐŽŃ‚ĐŸ ĐŽĐŸĐČĐłĐŸ. На щастя, ŃŃƒŃ‡Đ°ŃĐœŃ– Ń‚Đ”ŃŃ‚ŃƒĐČĐ°Đ»ŃŒĐœĐžĐșĐž та ĐżĐ»Đ°Ń‚Ń„ĐŸŃ€ĐŒĐž CI (яĐș-ĐŸŃ‚ [Jest](https://github.com/facebook/jest), [AVA](https://github.com/avajs/ava) і [Ń€ĐŸĐ·ŃˆĐžŃ€Đ”ĐœĐœŃ Mocha](https ://github.com/yandex/mocha-parallel-tests)) ĐŒĐŸĐ¶ĐœĐ° Ń€ĐŸĐ·ĐżĐ°Ń€Đ°Đ»Đ”Đ»ĐžŃ‚Đž тДст ĐœĐ° ĐșŃ–Đ»ŃŒĐșа ĐżŃ€ĐŸŃ†Đ”ŃŃ–ĐČ Ń– Đ·ĐœĐ°Ń‡ĐœĐŸ ĐżĐŸĐșращото час Đ·ĐČĐŸŃ€ĐŸŃ‚ĐœĐŸĐłĐŸ Đ·ĐČâ€™ŃĐ·Đșу. Đ”Đ”ŃĐșі ĐżĐŸŃŃ‚Đ°Ń‡Đ°Đ»ŃŒĐœĐžĐșĐž CI таĐșĐŸĐ¶ Ń€ĐŸĐ·ĐżĐ°Ń€Đ°Đ»Đ”Đ»ŃŽŃŽŃ‚ŃŒ тДстО ĐŒŃ–Đ¶ ĐșĐŸĐœŃ‚Đ”ĐčĐœĐ”Ń€Đ°ĐŒĐž (!), Ń‰ĐŸ щД Đ±Ń–Đ»ŃŒŃˆĐ” сĐșĐŸŃ€ĐŸŃ‡ŃƒŃ” цоĐșĐ» Đ·ĐČĐŸŃ€ĐŸŃ‚ĐœĐŸĐłĐŸ Đ·ĐČâ€™ŃĐ·Đșу. ĐĐ”Đ·Đ°Đ»Đ”Đ¶ĐœĐŸ ĐČіЮ Ń‚ĐŸĐłĐŸ, Đ»ĐŸĐșĐ°Đ»ŃŒĐœĐŸ ĐœĐ°ĐŽ ĐșŃ–Đ»ŃŒĐșĐŸĐŒĐ° ĐżŃ€ĐŸŃ†Đ”ŃĐ°ĐŒĐž чо ĐœĐ°ĐŽ ĐŽĐ”ŃĐșĐžĐŒ Ń…ĐŒĐ°Ń€ĐœĐžĐŒ CLI Đ· ĐČĐžĐșĐŸŃ€ĐžŃŃ‚Đ°ĐœĐœŃĐŒ ĐșŃ–Đ»ŃŒĐșĐŸŃ… ĐŒĐ°ŃˆĐžĐœâ€Šâ€”â€ŠŃ€ĐŸĐ·ĐżĐ°Ń€Đ°Đ»Đ”Đ»ŃŽĐČĐ°ĐœĐœŃ ĐżĐŸĐżĐžŃ‚Ńƒ, збДрігаючО аĐČŃ‚ĐŸĐœĐŸĐŒĐœŃ–ŃŃ‚ŃŒ тДстіĐČ, ĐŸŃĐșŃ–Đ»ŃŒĐșĐž ĐșĐŸĐ¶Đ”Đœ ĐŒĐŸĐ¶Đ” ĐČĐžĐșĐŸĐœŃƒĐČатося ĐœĐ° Ń€Ń–Đ·ĐœĐžŃ… ĐżŃ€ĐŸŃ†Đ”ŃĐ°Ń… + +❌ **Đ†ĐœĐ°ĐșшД:** ĐžŃ‚Ń€ĐžĐŒĐ°ĐœĐœŃ Ń€Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚Ń–ĐČ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ чДрДз 1 ĐłĐŸĐŽĐžĐœŃƒ ĐżŃ–ŃĐ»Ń ĐČĐČĐ”ĐŽĐ”ĐœĐœŃ ĐœĐŸĐČĐŸĐłĐŸ ĐșĐŸĐŽŃƒ, ĐŸŃĐșŃ–Đ»ŃŒĐșĐž ĐČĐž ĐČжД ĐșĐŸĐŽŃƒŃ”Ń‚Đ” ĐœĐ°ŃŃ‚ŃƒĐżĐœŃ– Ń„ŃƒĐœĐșції, є Ń‡ŃƒĐŽĐŸĐČĐžĐŒ Ń€Đ”Ń†Đ”ĐżŃ‚ĐŸĐŒ ĐŽĐ»Ń Ń‚ĐŸĐłĐŸ, Ń‰ĐŸĐ± Đ·Ń€ĐŸĐ±ĐžŃ‚Đž Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐŒĐ”ĐœŃˆ аĐșŃ‚ŃƒĐ°Đ»ŃŒĐœĐžĐŒ + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :clap: ПроĐșлаЎ праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: Mocha parallel & Jest лДгĐșĐŸ ĐČĐžĐżĐ”Ń€Đ”ĐŽĐ¶Đ°ŃŽŃ‚ŃŒ траЮоціĐčĐœŃƒ Mocha заĐČЮяĐșĐž Ń€ĐŸĐ·ĐżĐ°Ń€Đ°Đ»Đ”Đ»ŃŽĐČĐ°ĐœĐœŃŽ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ([АĐČŃ‚ĐŸŃ€ŃŃ‚ĐČĐŸ: JavaScript Test-Runners Benchmark](https://medium.com/dailyjs/javascript-test-runners-benchmark-3a78d4117b4)) + +![alt text](assets/bp-24-yonigoldberg-jest-parallel.png "Mocha parallel & Jest лДгĐșĐŸ ĐČĐžĐżĐ”Ń€Đ”ĐŽĐ¶Đ°ŃŽŃ‚ŃŒ траЮоціĐčĐœŃƒ Mocha заĐČЮяĐșĐž Ń€ĐŸĐ·ĐżĐ°Ń€Đ°Đ»Đ”Đ»ŃŽĐČĐ°ĐœĐœŃŽ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ") + +
    + +

    + +## âšȘ 5.5 ĐąŃ€ĐžĐŒĐ°ĐčŃ‚Đ”ŃŃ ĐżĐŸĐŽĐ°Đ»Ń– ĐČіЮ ŃŽŃ€ĐžĐŽĐžŃ‡ĐœĐžŃ… ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒ, ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČуючо Đ»Ń–Ń†Đ”ĐœĐ·Ń–ŃŽ та пДрДĐČірĐșу ĐœĐ° плагіат + +:white_check_mark: **Đ ĐŸĐ±Đž:** ĐŸŃ€ĐŸĐ±Đ»Đ”ĐŒĐž Đ»Ń–Ń†Đ”ĐœĐ·ŃƒĐČĐ°ĐœĐœŃ та ĐżĐ»Đ°ĐłŃ–Đ°Ń‚Ńƒ, ĐčĐŒĐŸĐČŃ–Ń€ĐœĐŸ, ĐœĐ” є ĐČĐ°ŃˆĐŸŃŽ ĐłĐŸĐ»ĐŸĐČĐœĐŸŃŽ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐŸŃŽ зараз, алД Ń‡ĐŸĐŒŃƒ б таĐșĐŸĐ¶ ĐœĐ” ĐżĐŸŃŃ‚Đ°ĐČото ĐłĐ°Đ»ĐŸŃ‡Đșу ĐČ Ń†ŃŒĐŸĐŒŃƒ ĐżĐŸĐ»Ń– чДрДз 10 хĐČĐžĐ»ĐžĐœ? Купа паĐșДтіĐČ npm, таĐșох яĐș [пДрДĐČірĐșа Đ»Ń–Ń†Đ”ĐœĐ·Ń–Ń—](https://www.npmjs.com/package/license-checker) і [пДрДĐČірĐșа ĐœĐ° плагіат](https://www.npmjs.com/package/plagiarism-checker) ( рДĐșĐ»Đ°ĐŒĐœĐžĐč Ń€ĐŸĐ»ĐžĐș Ń–Đ· бДзĐșĐŸŃˆŃ‚ĐŸĐČĐœĐžĐŒ ĐżĐ»Đ°ĐœĐŸĐŒ) ĐŒĐŸĐ¶ĐœĐ° лДгĐșĐŸ ĐČстаĐČото у ĐČаш ĐșĐŸĐœĐČДєр CI та пДрДĐČірото ĐœĐ° ĐœĐ°ŃĐČĐœŃ–ŃŃ‚ŃŒ таĐșох ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒ, яĐș Đ·Đ°Đ»Đ”Đ¶ĐœĐŸŃŃ‚Ń– Đ· ĐŸĐ±ĐŒĐ”Đ¶ŃƒĐČĐ°Đ»ŃŒĐœĐžĐŒĐž Đ»Ń–Ń†Đ”ĐœĐ·Ń–ŃĐŒĐž Đ°Đ±ĐŸ ĐșĐŸĐŽ, яĐșĐžĐč Đ±ŃƒĐ»ĐŸ сĐșĐŸĐżŃ–ĐčĐŸĐČĐ°ĐœĐŸ Đ· Stack Overflow і, ĐŸŃ‡Đ”ĐČĐžĐŽĐœĐŸ, ĐżĐŸŃ€ŃƒŃˆŃƒŃ” ĐŽĐ”ŃĐșі аĐČŃ‚ĐŸŃ€ŃŃŒĐșі праĐČа + +❌ **Đ†ĐœĐ°ĐșшД:** ĐĐ”ĐœĐ°ĐČĐŒĐžŃĐœĐŸ Ń€ĐŸĐ·Ń€ĐŸĐ±ĐœĐžĐșĐž ĐŒĐŸĐ¶ŃƒŃ‚ŃŒ ĐČĐžĐșĐŸŃ€ĐžŃŃ‚Đ°Ń‚Đž паĐșДтО Đ· ĐœĐ”ĐČŃ–ĐŽĐżĐŸĐČŃ–ĐŽĐœĐžĐŒĐž Đ»Ń–Ń†Đ”ĐœĐ·Ń–ŃĐŒĐž Đ°Đ±ĐŸ сĐșĐŸĐżŃ–ŃŽĐČато ĐșĐŸĐŒĐ”Ń€Ń†Ń–ĐčĐœĐžĐč ĐșĐŸĐŽ і Đ·Ń–Ń‚ĐșĐœŃƒŃ‚ĐžŃŃ Đ· ŃŽŃ€ĐžĐŽĐžŃ‡ĐœĐžĐŒĐž ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐ°ĐŒĐž + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :clap: ПроĐșлаЎ праĐČĐžĐ»ŃŒĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: + +```javascript +//install license-checker in your CI environment or also locally +npm install -g license-checker + +//ask it to scan all licenses and fail with exit code other than 0 if it found unauthorized license. The CI system should catch this failure and stop the build +license-checker --summary --failOn BSD + +``` + +
    + +![alt text](assets/bp-25-nodejs-licsense.png) + +
    + +

    + +## âšȘ 5.6 ĐŸĐŸŃŃ‚Ń–ĐčĐœĐŸ пДрДĐČіряĐčтД ĐœĐ°ŃĐČĐœŃ–ŃŃ‚ŃŒ ĐČразлОĐČох Đ·Đ°Đ»Đ”Đ¶ĐœĐŸŃŃ‚Đ”Đč + +:white_check_mark: **Đ ĐŸĐ±Đž:** НаĐČіть ĐœĐ°ĐčаĐČŃ‚ĐŸŃ€ĐžŃ‚Đ”Ń‚ĐœŃ–ŃˆŃ– Đ·Đ°Đ»Đ”Đ¶ĐœĐŸŃŃ‚Ń–, таĐșі яĐș Express, ĐŒĐ°ŃŽŃ‚ŃŒ ĐČŃ–ĐŽĐŸĐŒŃ– ĐČразлОĐČĐŸŃŃ‚Ń–. ĐŠĐ” ĐŒĐŸĐ¶ĐœĐ° лДгĐșĐŸ ĐżŃ€ĐžĐ±ĐŸŃ€Đșато за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Ń–ĐČ ŃĐżŃ–Đ»ŃŒĐœĐŸŃ‚Đž, таĐșох яĐș [npm audit](https://docs.npmjs.com/getting-started/running-a-security-audit), Đ°Đ±ĐŸ ĐșĐŸĐŒĐ”Ń€Ń†Ń–ĐčĐœĐžŃ… Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Ń–ĐČ, таĐșох яĐș [snyk](https:// snyk.io/) (ĐżŃ€ĐŸĐżĐŸĐœŃƒŃ”ĐŒĐŸ таĐșĐŸĐ¶ бДзĐșĐŸŃˆŃ‚ĐŸĐČĐœŃƒ ĐČДрсію ŃĐżŃ–Đ»ŃŒĐœĐŸŃ‚Đž). ОбОЎĐČа ĐŒĐŸĐ¶ĐœĐ° ĐČĐžĐșлОĐșато Đ· ĐČĐ°ŃˆĐŸĐłĐŸ КІ піЮ час ĐșĐŸĐ¶ĐœĐŸŃ— збірĐșĐž + +❌ **Đ†ĐœĐ°ĐșшД:** Đ©ĐŸĐ± захОстОтО ĐČаш ĐșĐŸĐŽ ĐČіЮ ŃƒŃ€Đ°Đ·Đ»ĐžĐČĐŸŃŃ‚Đ”Đč бДз ŃĐżĐ”Ń†Ń–Đ°Đ»ŃŒĐœĐžŃ… Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Ń–ĐČ, ĐżĐŸŃ‚Ń€Ń–Đ±ĐœĐŸ ĐżĐŸŃŃ‚Ń–ĐčĐœĐŸ стДжОтО за ĐżŃƒĐ±Đ»Ń–ĐșĐ°Ń†Ń–ŃĐŒĐž ĐČ Đ†ĐœŃ‚Đ”Ń€ĐœĐ”Ń‚Ń– ĐżŃ€ĐŸ ĐœĐŸĐČі Đ·Đ°ĐłŃ€ĐŸĐ·Đž. Đ”ĐŸŃĐžŃ‚ŃŒ ĐœŃƒĐŽĐœĐŸ + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :clap: ПроĐșлаЎ: Đ Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚ ауЮоту NPM + +![alt text](assets/bp-26-npm-audit-snyk.png "Đ Đ”Đ·ŃƒĐ»ŃŒŃ‚Đ°Ń‚ ауЮоту NPM") + +
    + +

    + +## âšȘ 5.7 АĐČŃ‚ĐŸĐŒĐ°Ń‚ĐžĐ·ŃƒĐčтД ĐŸĐœĐŸĐČĐ»Đ”ĐœĐœŃ Đ·Đ°Đ»Đ”Đ¶ĐœĐŸŃŃ‚Đ”Đč + +:white_check_mark: **Đ ĐŸĐ±Đž:** Yarn і npm ĐŸŃŃ‚Đ°ĐœĐœŃ” прДЎстаĐČĐ»Đ”ĐœĐœŃ package-lock.json ĐżĐŸŃŃ‚Đ°ĐČĐžĐ»ĐŸ сДрĐčĐŸĐ·ĐœŃƒ ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒŃƒ (ĐŽĐŸŃ€ĐŸĐłĐ° ĐČ ĐżĐ”ĐșĐ»ĐŸ ĐČĐžĐŒĐŸŃ‰Đ”ĐœĐ° Đ±Đ»Đ°ĐłĐžĐŒĐž ĐœĐ°ĐŒŃ–Ń€Đ°ĐŒĐž) — тДпДр за Đ·Đ°ĐŒĐŸĐČчуĐČĐ°ĐœĐœŃĐŒ паĐșŃƒĐœĐșĐž Đ±Ń–Đ»ŃŒŃˆĐ” ĐœĐ” ĐŸŃ‚Ń€ĐžĐŒŃƒŃŽŃ‚ŃŒ ĐŸĐœĐŸĐČĐ»Đ”ĐœĐœŃ. НаĐČіть ĐșĐŸĐŒĐ°ĐœĐŽĐ°, яĐșа ĐČĐžĐșĐŸĐœŃƒŃ” Đ±Đ°ĐłĐ°Ń‚ĐŸ сĐČіжОх Ń€ĐŸĐ·ĐłĐŸŃ€Ń‚Đ°ĐœŃŒ за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ «ĐČŃŃ‚Đ°ĐœĐŸĐČĐ»Đ”ĐœĐœŃ npm» і Â«ĐŸĐœĐŸĐČĐ»Đ”ĐœĐœŃ npm», ĐœĐ” ĐŸŃ‚Ń€ĐžĐŒĐ°Ń” ĐœĐŸĐČох ĐŸĐœĐŸĐČĐ»Đ”ĐœŃŒ. ĐŁ ĐșŃ€Đ°Ń‰ĐŸĐŒŃƒ ĐČОпаЎĐșу цД прОзĐČĐŸĐŽĐžŃ‚ŃŒ ĐŽĐŸ ĐœĐ”ĐżĐŸĐČĐœĐŸŃ†Ń–ĐœĐœĐžŃ… ĐČДрсіĐč паĐșŃƒĐœĐșіĐČ Đ°Đ±ĐŸ ĐŽĐŸ ĐČразлОĐČĐŸĐłĐŸ ĐșĐŸĐŽŃƒ ĐČ ĐłŃ–Ń€ŃˆĐŸĐŒŃƒ. ĐšĐŸĐŒĐ°ĐœĐŽĐž тДпДр ĐżĐŸĐșĐ»Đ°ĐŽĐ°ŃŽŃ‚ŃŒŃŃ ĐœĐ° ĐŽĐŸĐ±Ń€Ńƒ ĐČĐŸĐ»ŃŽ та ĐżĐ°ĐŒâ€™ŃŃ‚ŃŒ Ń€ĐŸĐ·Ń€ĐŸĐ±ĐœĐžĐșіĐČ, Ń‰ĐŸĐ± ĐČŃ€ŃƒŃ‡ĐœŃƒ ĐŸĐœĐŸĐČото package.json Đ°Đ±ĐŸ ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČуĐČато Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Đž [ĐœĐ°ĐżŃ€ĐžĐșлаЎ, ncu](https://www.npmjs.com/package/npm-check-updates) ĐČŃ€ŃƒŃ‡ĐœŃƒ. НаЮіĐčĐœŃ–ŃˆĐžĐŒ ŃĐżĐŸŃĐŸĐ±ĐŸĐŒ ĐŒĐŸĐ¶Đ” Đ±ŃƒŃ‚Đž аĐČŃ‚ĐŸĐŒĐ°Ń‚ĐžĐ·Đ°Ń†Ń–Ń ĐżŃ€ĐŸŃ†Đ”ŃŃƒ ĐŸŃ‚Ń€ĐžĐŒĐ°ĐœĐœŃ ĐœĐ°ĐčĐœĐ°ĐŽŃ–ĐčĐœŃ–ŃˆĐžŃ… ĐČДрсіĐč Đ·Đ°Đ»Đ”Đ¶ĐœĐŸŃŃ‚Đ”Đč, Ń…ĐŸŃ‡Đ° ĐœĐ”ĐŒĐ°Ń” ŃƒĐœŃ–ĐČĐ”Ń€ŃĐ°Đ»ŃŒĐœĐžŃ… Ń€Ń–ŃˆĐ”ĐœŃŒ, алД є ĐŽĐČа ĐŒĐŸĐ¶Đ»ĐžĐČі ŃˆĐ»ŃŃ…Đž аĐČŃ‚ĐŸĐŒĐ°Ń‚ĐžĐ·Đ°Ń†Ń–Ń—: + +(1) CI ĐŒĐŸĐ¶Đ” ЎаĐČато збіĐč збірĐșĐ°ĐŒ Ń–Đ· Đ·Đ°ŃŃ‚Đ°Ń€Ń–Đ»ĐžĐŒĐž Đ·Đ°Đ»Đ”Đ¶ĐœĐŸŃŃ‚ŃĐŒĐžâ€Šâ€”â€ŠĐČĐžĐșĐŸŃ€ĐžŃŃ‚Đ°ĐœĐœŃĐŒ таĐșох Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Ń–ĐČ, яĐș [‘npm outdated’](https://docs.npmjs.com/cli/outdated) Đ°Đ±ĐŸ ‘npm-check-updates (ncu)’ . ĐŠĐ” Đ·ĐŒŃƒŃĐžŃ‚ŃŒ Ń€ĐŸĐ·Ń€ĐŸĐ±ĐœĐžĐșіĐČ ĐŸĐœĐŸĐČото Đ·Đ°Đ»Đ”Đ¶ĐœĐŸŃŃ‚Ń–. + +(2) ВоĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČуĐčтД ĐșĐŸĐŒĐ”Ń€Ń†Ń–ĐčĐœŃ– Ń–ĐœŃŃ‚Ń€ŃƒĐŒĐ”ĐœŃ‚Đž, яĐșі сĐșĐ°ĐœŃƒŃŽŃ‚ŃŒ ĐșĐŸĐŽ і аĐČŃ‚ĐŸĐŒĐ°Ń‚ĐžŃ‡ĐœĐŸ ĐœĐ°ĐŽŃĐžĐ»Đ°ŃŽŃ‚ŃŒ запОтО ĐœĐ° ĐŸŃ‚Ń€ĐžĐŒĐ°ĐœĐœŃ Đ· ĐŸĐœĐŸĐČĐ»Đ”ĐœĐžĐŒĐž Đ·Đ°Đ»Đ”Đ¶ĐœĐŸŃŃ‚ŃĐŒĐž. Đ—Đ°Đ»ĐžŃˆĐ°Ń”Ń‚ŃŒŃŃ ĐŸĐŽĐœĐ” ціĐșаĐČĐ” ĐżĐžŃ‚Đ°ĐœĐœŃ: яĐșĐŸŃŽ ĐŒĐ°Ń” Đ±ŃƒŃ‚Đž ĐżĐŸĐ»Ń–Ń‚ĐžĐșа ĐŸĐœĐŸĐČĐ»Đ”ĐœĐœŃ Đ·Đ°Đ»Đ”Đ¶ĐœĐŸŃŃ‚Đ”Đčâ€Šâ€”Â«ĐŸĐœĐŸĐČĐ»Đ”ĐœĐœŃ ĐșĐŸĐ¶ĐœĐŸĐłĐŸ патча стĐČĐŸŃ€ŃŽŃ” ĐœĐ°ĐŽŃ‚ĐŸ Đ±Đ°ĐłĐ°Ń‚ĐŸ ĐœĐ°ĐșĐ»Đ°ĐŽĐœĐžŃ… ĐČотрат, ĐŸĐœĐŸĐČĐ»Đ”ĐœĐœŃ Đ±Đ”Đ·ĐżĐŸŃĐ”Ń€Đ”ĐŽĐœŃŒĐŸ піЮ час ĐČОпусĐșу ĐŸŃĐœĐŸĐČĐœĐŸĐłĐŸ ĐŒĐŸĐ¶Đ” ĐČĐșазуĐČато ĐœĐ° ĐœĐ”ŃŃ‚Đ°Đ±Ń–Đ»ŃŒĐœŃƒ ĐČДрсію (Đ±Đ°ĐłĐ°Ń‚ĐŸ паĐșДтіĐČ ĐČояĐČĐ»ŃŃŽŃ‚ŃŒŃŃ ĐČразлОĐČĐžĐŒĐž ĐČ ĐżĐ”Ń€ŃˆŃ– ĐŽĐœŃ– ĐżŃ–ŃĐ»Ń ĐČОпусĐșу, [ĐżĐ”Ń€Đ”ĐłĐ»ŃĐœŃŒŃ‚Đ”](https://nodesource.com/blog/a-high-level-post-mortem-of-the-eslint-scope-security-incident/) Ń–ĐœŃ†ĐžĐŽĐ”ĐœŃ‚ eslint-scope). + +ЕфДĐșтоĐČĐœĐ° ĐżĐŸĐ»Ń–Ń‚ĐžĐșа ĐŸĐœĐŸĐČĐ»Đ”ĐœĐœŃ ĐŒĐŸĐ¶Đ” ĐŽĐŸĐ·ĐČĐŸĐ»ĐžŃ‚Đž пДĐČĐœĐžĐč Â«ĐżĐ”Ń€Ń–ĐŸĐŽ ĐœĐ°Đ±ŃƒŃ‚Ń‚Ń праĐČÂ»â€Šâ€”â€ŠĐœĐ”Ń…Đ°Đč ĐșĐŸĐŽ ĐČіЮстає ĐČіЮ @latest ĐœĐ° ĐŽĐ”ŃĐșĐžĐč час і ĐČДрсії, ĐżĐ”Ń€Ńˆ ĐœŃ–Đ¶ ĐČĐČажатО Đ»ĐŸĐșĐ°Đ»ŃŒĐœŃƒ ĐșĐŸĐżŃ–ŃŽ Đ·Đ°ŃŃ‚Đ°Ń€Ń–Đ»ĐŸŃŽ (ĐœĐ°ĐżŃ€ĐžĐșлаЎ, Đ»ĐŸĐșĐ°Đ»ŃŒĐœĐ° ĐČĐ”Ń€ŃŃ–Ń — 1.3.1, а ĐČĐ”Ń€ŃŃ–Ń ŃŃ…ĐŸĐČоща — 1.3.8)
    + +❌ **Đ†ĐœĐ°ĐșшД:** ĐŁ ĐČĐ°ŃˆĐŸĐŒŃƒ ĐČĐžŃ€ĐŸĐ±ĐœĐžŃ†Ń‚ĐČі працюĐČĐ°Ń‚ĐžĐŒŃƒŃ‚ŃŒ паĐșŃƒĐœĐșĐž, яĐșі булО яĐČĐœĐŸ ĐżĐŸĐ·ĐœĐ°Ń‡Đ”ĐœŃ– їх аĐČŃ‚ĐŸŃ€ĐŸĐŒ яĐș рОзОĐșĐŸĐČĐ°ĐœŃ– + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :clap: ПроĐșлаЎ: [ncu](https://www.npmjs.com/package/npm-check-updates) ĐŒĐŸĐ¶ĐœĐ° ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČуĐČато ĐČŃ€ŃƒŃ‡ĐœŃƒ Đ°Đ±ĐŸ ĐČ ĐșĐŸĐœĐČДєрі CI, Ń‰ĐŸĐ± ĐČĐžĐ·ĐœĐ°Ń‡ĐžŃ‚Đž, ĐœĐ°ŃĐșŃ–Đ»ŃŒĐșĐž ĐșĐŸĐŽ ĐČіЮстає ĐČіЮ ĐŸŃŃ‚Đ°ĐœĐœŃ–Ń… ĐČДрсіĐč + +![alt text](assets/bp-27-yoni-goldberg-npm.png "ĐŒĐŸĐ¶ĐœĐ° ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČуĐČато ĐČŃ€ŃƒŃ‡ĐœŃƒ Đ°Đ±ĐŸ ĐČ ĐșĐŸĐœĐČДєрі CI, Ń‰ĐŸĐ± ĐČĐžĐ·ĐœĐ°Ń‡ĐžŃ‚Đž, ĐœĐ°ŃĐșŃ–Đ»ŃŒĐșĐž ĐșĐŸĐŽ ĐČіЮстає ĐČіЮ ĐŸŃŃ‚Đ°ĐœĐœŃ–Ń… ĐČДрсіĐč") + +
    + +

    + +## âšȘ  5.8 Đ†ĐœŃˆŃ– ĐżĐŸŃ€Đ°ĐŽĐž CI, ĐœĐ” ĐżĐŸĐČâ€™ŃĐ·Đ°ĐœŃ– Đ· Node + +:white_check_mark: **Đ ĐŸĐ±Đž:** Щя ĐżŃƒĐ±Đ»Ń–Đșація Đ·ĐŸŃĐ”Ń€Đ”ĐŽĐ¶Đ”ĐœĐ° ĐœĐ° ĐżĐŸŃ€Đ°ĐŽĐ°Ń… Ń‰ĐŸĐŽĐŸ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ, яĐșі ĐżĐŸĐČâ€™ŃĐ·Đ°ĐœŃ– Đ· Node JS Đ°Đ±ĐŸ, ĐżŃ€ĐžĐœĐ°ĐčĐŒĐœŃ–, ĐŒĐŸĐ¶ŃƒŃ‚ŃŒ Đ±ŃƒŃ‚Đž прДЎстаĐČĐ»Đ”ĐœŃ– ĐœĐ° проĐșлаЎі Node JS. ĐžĐŽĐœĐ°Đș у Ń†ŃŒĐŸĐŒŃƒ Ń€ĐŸĐ·ĐŽŃ–Đ»Ń– Đ·ĐłŃ€ŃƒĐżĐŸĐČĐ°ĐœĐŸ ĐșŃ–Đ»ŃŒĐșа ĐŽĐŸĐ±Ń€Đ” ĐČŃ–ĐŽĐŸĐŒĐžŃ… ĐżĐŸŃ€Đ°ĐŽ, ĐœĐ” ĐżĐŸĐČâ€™ŃĐ·Đ°ĐœĐžŃ… Đ· Node + +
    1. Use a declarative syntax. This is the only option for most vendors but older versions of Jenkins allows using code or UI
    2. Opt for a vendor that has native Docker support
    3. Fail early, run your fastest tests first. Create a ‘Smoke testing’ step/milestone that groups multiple fast inspections (e.g. linting, unit tests) and provide snappy feedback to the code committer
    4. Make it easy to skim-through all build artifacts including test reports, coverage reports, mutation reports, logs, etc
    5. Create multiple pipelines/jobs for each event, reuse steps between them. For example, configure a job for feature branch commits and a different one for master PR. Let each reuse logic using shared steps (most vendors provide some mechanism for code reuse)
    6. Never embed secrets in a job declaration, grab them from a secret store or from the job’s configuration
    7. Explicitly bump version in a release build or at least ensure the developer did so
    8. Build only once and perform all the inspections over the single build artifact (e.g. Docker image)
    9. Test in an ephemeral environment that doesn’t drift state between builds. Caching node_modules might be the only exception
    +
    + +❌ **Đ†ĐœĐ°ĐșшД:** Во ĐżŃ€ĐŸĐżŃƒŃŃ‚ĐžŃ‚Đ” Ń€ĐŸĐșĐž ĐŒŃƒĐŽŃ€ĐŸŃŃ‚Ń– + +

    + +## âšȘ  5.9 Матроця ĐżĐŸĐ±ŃƒĐŽĐŸĐČĐž: ĐČĐžĐșĐŸĐœŃƒĐčтД ті ŃĐ°ĐŒŃ– ĐșŃ€ĐŸĐșĐž CI, ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČуючо ĐșŃ–Đ»ŃŒĐșа ĐČДрсіĐč Node + +:white_check_mark: **Đ ĐŸĐ±Đž:** ĐŸĐ”Ń€Đ”ĐČірĐșа яĐșĐŸŃŃ‚Ń– ĐżĐŸĐČâ€™ŃĐ·Đ°ĐœĐ° Đ· Ń–ĐœŃ‚ŃƒŃ—Ń†Ń–Ń”ŃŽ, Ń‡ĐžĐŒ Đ±Ń–Đ»ŃŒŃˆĐ” ĐČĐž ĐŸŃ…ĐŸĐżĐžŃ‚Đ”, Ń‚ĐžĐŒ Đ±Ń–Đ»ŃŒŃˆĐ” ĐČĐ°ĐŒ ĐżĐŸŃ‰Đ°ŃŃ‚ĐžŃ‚ŃŒ у ĐČояĐČĐ»Đ”ĐœĐœŃ– ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒ ĐœĐ° Ń€Đ°ĐœĐœŃ–Đč стаЮії. ПіЮ час Ń€ĐŸĐ·Ń€ĐŸĐ±ĐșĐž паĐșДтіĐČ ĐŽĐ»Ń Đ±Đ°ĐłĐ°Ń‚ĐŸŃ€Đ°Đ·ĐŸĐČĐŸĐłĐŸ ĐČĐžĐșĐŸŃ€ĐžŃŃ‚Đ°ĐœĐœŃ Đ°Đ±ĐŸ запусĐșу ĐČĐžŃ€ĐŸĐ±ĐœĐžŃ†Ń‚ĐČа ĐŽĐ»Ń ĐșŃ–Đ»ŃŒĐșĐŸŃ… ĐșĐ»Ń–Ń”ĐœŃ‚Ń–ĐČ Ń–Đ· Ń€Ń–Đ·ĐœĐžĐŒĐž ĐșĐŸĐœŃ„Ń–ĐłŃƒŃ€Đ°Ń†Ń–ŃĐŒĐž та ĐČĐ”Ń€ŃŃ–ŃĐŒĐž Node, CI ĐŒĐ°Ń” Đ·Đ°ĐżŃƒŃŃ‚ĐžŃ‚Đž ĐșĐŸĐœĐČДєр тДстіĐČ ĐŽĐ»Ń ĐČсіх ĐżĐ”Ń€Đ”ŃŃ‚Đ°ĐœĐŸĐČĐŸĐș ĐșĐŸĐœŃ„Ń–ĐłŃƒŃ€Đ°Ń†Ń–Đč. НапроĐșлаЎ, яĐșŃ‰ĐŸ пропустото, Ń‰ĐŸ ĐŒĐž ĐČĐžĐșĐŸŃ€ĐžŃŃ‚ĐŸĐČŃƒŃ”ĐŒĐŸ MySQL ĐŽĐ»Ń ĐŸĐŽĐœĐžŃ… ĐșĐ»Ń–Ń”ĐœŃ‚Ń–ĐČ, а Postgres ĐŽĐ»Ń Ń–ĐœŃˆĐžŃ…â€Šâ€”Â«ĐŽĐ”ŃĐșі ĐżĐŸŃŃ‚Đ°Ń‡Đ°Đ»ŃŒĐœĐžĐșĐž CI ĐżŃ–ĐŽŃ‚Ń€ĐžĐŒŃƒŃŽŃ‚ŃŒ Ń„ŃƒĐœĐșцію піЮ ĐœĐ°Đ·ĐČĐŸŃŽ Â«ĐœĐ°Ń‚Ń€ĐžŃ†ŃÂ», яĐșа ĐŽĐŸĐ·ĐČĐŸĐ»ŃŃ” ĐČĐžĐșĐŸĐœŃƒĐČато Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ ĐČсіх ĐżĐ”Ń€Đ”ŃŃ‚Đ°ĐœĐŸĐČĐŸĐș MySQL, Postgres і ĐșŃ–Đ»ŃŒĐșĐŸŃ… ĐČДрсіĐč Node, таĐșох яĐș 8, 9 і 10. ĐŠĐ” Ń€ĐŸĐ±ĐžŃ‚ŃŒŃŃ лОшД за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃŽ ĐșĐŸĐœŃ„Ń–ĐłŃƒŃ€Đ°Ń†Ń–Ń— бДз Đ±ŃƒĐŽŃŒ-яĐșох ĐŽĐŸĐŽĐ°Ń‚ĐșĐŸĐČох Đ·ŃƒŃĐžĐ»ŃŒ (за ŃƒĐŒĐŸĐČĐž, Ń‰ĐŸ у ĐČас є Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ чо Đ±ŃƒĐŽŃŒ-яĐșі Ń–ĐœŃˆŃ– пДрДĐČірĐșĐž яĐșĐŸŃŃ‚Ń–). Đ†ĐœŃˆŃ– КІ, яĐșі ĐœĐ” ĐżŃ–ĐŽŃ‚Ń€ĐžĐŒŃƒŃŽŃ‚ŃŒ Matrix, ĐŒĐŸĐ¶ŃƒŃ‚ŃŒ ĐŒĐ°Ń‚Đž Ń€ĐŸĐ·ŃˆĐžŃ€Đ”ĐœĐœŃ Đ°Đ±ĐŸ ĐœĐ°Đ»Đ°ŃˆŃ‚ŃƒĐČĐ°ĐœĐœŃ, Ń‰ĐŸĐ± ĐŽĐŸĐ·ĐČĐŸĐ»ĐžŃ‚Đž цД +
    + +❌ **Đ†ĐœĐ°ĐșшД:** ОтжД, ĐżŃ–ŃĐ»Ń ĐČсієї цієї ĐČажĐșĐŸŃ— Ń€ĐŸĐ±ĐŸŃ‚Đž Đ· ĐœĐ°ĐżĐžŃĐ°ĐœĐœŃ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ, ĐŒĐž ĐŽĐŸĐ·ĐČĐŸĐ»ĐžĐŒĐŸ ĐżĐŸĐŒĐžĐ»ĐșĐ°ĐŒ ĐżŃ€ĐŸĐșраЮатося лОшД чДрДз ĐżŃ€ĐŸĐ±Đ»Đ”ĐŒĐž Đ· ĐșĐŸĐœŃ„Ń–ĐłŃƒŃ€Đ°Ń†Ń–Ń”ŃŽ? + +
    + +
    ✏ ПроĐșлаЎО ĐșĐŸĐŽŃƒ + +
    + +### :clap: ПроĐșлаЎ: ВоĐșĐŸŃ€ĐžŃŃ‚Đ°ĐœĐœŃ ĐČĐžĐ·ĐœĐ°Ń‡Đ”ĐœĐœŃ збірĐșĐž Travis (ĐżĐŸŃŃ‚Đ°Ń‡Đ°Đ»ŃŒĐœĐžĐș CI) ĐŽĐ»Ń запусĐșу ĐŸĐŽĐœĐŸĐłĐŸ Ń‚Đ”ŃŃ‚Ńƒ ĐœĐ° ĐșŃ–Đ»ŃŒĐșĐŸŃ… ĐČĐ”Ń€ŃŃ–ŃŃ… Node + +
    language: node_js
    node_js:
    - "7"
    - "6"
    - "5"
    - "4"
    install:
    - npm install
    script:
    - npm run test
    +
    + +

    + +# Team + +## Yoni Goldberg + +
    + +
    + +**Đ ĐŸĐ»ŃŒ:** ĐŸĐžŃŃŒĐŒĐ”ĐœĐœĐžĐș + +**Опос:** ĐŻ ĐœĐ”Đ·Đ°Đ»Đ”Đ¶ĐœĐžĐč ĐșĐŸĐœŃŃƒĐ»ŃŒŃ‚Đ°ĐœŃ‚, яĐșĐžĐč працює Đ· ĐșĐŸĐŒĐżĐ°ĐœŃ–ŃĐŒĐž Đ·Ń– спОсĐșу Fortune 500 і ĐłĐ°Ń€Đ°Đ¶ĐœĐžĐŒĐž ŃŃ‚Đ°Ń€Ń‚Đ°ĐżĐ°ĐŒĐž ĐœĐ°ĐŽ ĐČĐŽĐŸŃĐșĐŸĐœĐ°Đ»Đ”ĐœĐœŃĐŒ Ń—Ń…ĐœŃ–Ń… ĐŽĐŸĐŽĐ°Ń‚ĐșіĐČ JS і Node.js. Đ‘Ń–Đ»ŃŒŃˆĐ” ĐœŃ–Đ¶ Đ±ŃƒĐŽŃŒ-яĐșа Ń–ĐœŃˆĐ° Ń‚Đ”ĐŒĐ° ĐŒĐ”ĐœĐ” Đ·Đ°Ń…ĐŸĐżĐ»ŃŽŃ”, і я ĐżŃ€Đ°ĐłĐœŃƒ ĐŸĐČĐŸĐ»ĐŸĐŽŃ–Ń‚Đž ĐŒĐžŃŃ‚Đ”Ń†Ń‚ĐČĐŸĐŒ Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ. ĐŻ таĐșĐŸĐ¶ аĐČŃ‚ĐŸŃ€ [ĐœĐ°ĐčĐșращох праĐșтоĐș Node.js](https://github.com/goldbergyoni/nodebestpractices) + +**📗 ĐžĐœĐ»Đ°ĐčĐœ-Đșурс:** ĐĄĐżĐŸĐŽĐŸĐ±Đ°ĐČся цДĐč ĐżĐŸŃŃ–Đ±ĐœĐžĐș і ĐČĐž бажаєтД ĐČĐŽĐŸŃĐșĐŸĐœĐ°Đ»ĐžŃ‚Đž сĐČĐŸŃ— ĐœĐ°ĐČочĐșĐž Ń‚Đ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ? ВіЮĐČіЮаĐčтД ĐŒŃ–Đč ĐșĐŸĐŒĐżĐ»Đ”ĐșŃĐœĐžĐč Đșурс [ĐąĐ”ŃŃ‚ŃƒĐČĐ°ĐœĐœŃ Node.js і JavaScript ĐČіЮ А ĐŽĐŸ ĐŻ](https://www.testjavascript.com) + +
    + +**Follow:** + +- [🐩 Twitter](https://twitter.com/goldbergyoni/) +- [📞 Contact](https://testjavascript.com/contact-2/) +- [✉ Newsletter](https://testjavascript.com/newsletter//) + +
    +
    +
    + +## [Bruno Scheufler](https://github.com/BrunoScheufler) + +**Đ ĐŸĐ»ŃŒ:** ĐąĐ”Ń…ĐœŃ–Ń‡ĐœĐžĐč ĐŸĐłĐ»ŃĐŽĐ°Ń‡ і Ń€Đ°ĐŽĐœĐžĐș + +ĐŸĐŸĐŽĐ±Đ°ĐČ ĐżŃ€ĐŸ тД, Ń‰ĐŸĐ± ĐżĐ”Ń€Đ”ĐłĐ»ŃĐœŃƒŃ‚Đž, ĐČĐŽĐŸŃĐșĐŸĐœĐ°Đ»ĐžŃ‚Đž, ĐČŃ–ĐŽŃˆĐ»Ń–Ń„ŃƒĐČато та ĐČŃ–ĐŽŃˆĐ»Ń–Ń„ŃƒĐČато ĐČсі тДĐșсто + +**Опос:** full-stack ĐČДб-Ń–ĐœĐ¶Đ”ĐœĐ”Ń€, Đ”ĐœŃ‚ŃƒĐ·Ń–Đ°ŃŃ‚ Node.js та GraphQL +
    +
    + +## [Ido Richter](https://github.com/idori) + +**Đ ĐŸĐ»ŃŒ:** Concept, design and great advice + +**Опос:** ĐšĐŒŃ–Ń‚Đ»ĐžĐČĐžĐč Ń€ĐŸĐ·Ń€ĐŸĐ±ĐœĐžĐș Ń–ĐœŃ‚Đ”Ń€Ń„Đ”Đčсу, Đ”ĐșспДрт Ń–Đ· CSS і Ń„Đ°ĐœĐ°Ń‚ Đ”ĐŒĐŸĐŽĐ·Ń– + +## [Kyle Martin](https://github.com/js-kyle) + +**Đ ĐŸĐ»ŃŒ:** Đ”ĐŸĐżĐŸĐŒĐ°ĐłĐ°Ń” ĐżŃ–ĐŽŃ‚Ń€ĐžĐŒŃƒĐČато Ń€ĐŸĐ±ĐŸŃ‚Ńƒ Ń†ŃŒĐŸĐłĐŸ ĐżŃ€ĐŸĐ”Đșту та ĐżĐ”Ń€Đ”ĐłĐ»ŃĐŽĐ°Ń” ĐŒĐ”Ń‚ĐŸĐŽĐž бДзпДĐșĐž + +**Опос:** Đ›ŃŽĐ±ĐžŃ‚ŃŒ працюĐČато ĐœĐ°ĐŽ ĐżŃ€ĐŸĐ”ĐșŃ‚Đ°ĐŒĐž Node.js і бДзпДĐșĐŸŃŽ ĐČДб-ĐŽĐŸĐŽĐ°Ń‚ĐșіĐČ. + +## АĐČŃ‚ĐŸŃ€Đž ✹ + +ДяĐșŃƒŃ”ĐŒĐŸ Ń†ĐžĐŒ Ń‡ŃƒĐŽĐŸĐČĐžĐŒ Đ»ŃŽĐŽŃĐŒ, яĐșі Đ·Ń€ĐŸĐ±ĐžĐ»Đž ĐČĐœĐ”ŃĐŸĐș у цД ŃŃ…ĐŸĐČОщД! + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    Scott Davis

    🖋

    Adrien REDON

    🖋

    Stefano Magni

    🖋

    Yeoh Joer

    🖋

    Jhonny Moreira

    🖋

    Ian Germann

    🖋

    Hafez

    🖋

    Ruxandra Fediuc

    🖋

    Jack

    🖋

    Peter Carrero

    🖋

    Huhgawz

    🖋

    Haakon Borch

    🖋

    Jaime Mendoza

    🖋

    Cameron Dunford

    🖋

    John Gee

    🖋

    Aurelijus RoĆŸÄ—nas

    🖋

    Aaron

    🖋

    Tom Nagle

    🖋

    Yves yao

    🖋

    Userbit

    🖋

    Glaucia Lemos

    🚧

    koooge

    🖋

    Michal

    🖋

    roywalker

    🖋

    dangen

    🖋

    biesiadamich

    🖋

    Yanlin Jiang

    🖋

    sanguino

    🖋

    Morgan

    🖋

    Lukas Bischof

    ⚠ 🖋

    JuanMa Ruiz

    🖋

    Luís Ângelo Rodrigues Jr.

    🖋

    José Fernåndez

    🖋

    Alejandro Gutierrez Barcenilla

    🖋

    Jason

    🖋

    Otavio Araujo

    ⚠ 🖋

    Alex Ivanov

    🖋

    Yiqiao Xu

    🖋

    YuBin, Hsu

    🌍 đŸ’»
    + + + + + diff --git a/readme-zh-CN.md b/readme-zh-CN.md index 0566c382..18b3ee5f 100644 --- a/readme-zh-CN.md +++ b/readme-zh-CN.md @@ -68,11 +68,11 @@ JS 鱆柟的 CI æŒ‡ć—ïŒˆ9 æĄïŒ‰ æˆ‘ä»Źçš„æ€ç»Žç©șé—Žèą«äž»äœ“ç”Ÿäș§ä»Łç ć……æ»ĄïŒŒć› æ­€æ— æł•è…Ÿć‡șéąć€–çš„â€œć€§è„‘ç©șé—Žâ€ć­˜æ”Ÿć€æ‚çš„äžœè„żă€‚ćŠ‚æžœć‘ćŻæ€œçš„ć€§è„‘äž­ćĄžèż›ć…¶ä»–ć€æ‚ä»Łç ïŒŒć°†äŒšäœżćŸ—æ•ŽäžȘéƒšćˆ†ć˜æ…ąïŒŒè€Œèż™äžȘéƒšćˆ†æ­Łæ˜Żç”šæ„è§Łć†łæˆ‘ä»Źéœ€èŠæ”‹èŻ•çš„é—źéą˜çš„ă€‚èż™ä蟿˜Żć€§éƒšćˆ†ć›ąé˜Ÿæ”ŸćŒƒæ”‹èŻ•çš„ćŽŸć› ă€‚ -揩侀æ–čéąïŒŒæ”‹èŻ•æ˜Żäž€äžȘć‹ć„œçš„ćŠ©æ‰‹ïŒŒäž€äžȘ䜠äčäșŽäžŽäč‹ćˆäœœă€æŠ•è”„ć°æ±‡æŠ„ć€§çš„ćŠ©æ‰‹ă€‚ç§‘ć­ŠèŻæ˜Žæˆ‘ä»Źæœ‰äž€ć„—ć€§è„‘çł»ç»ŸïŒšçł»ç»Ÿ 1 甹äșŽæ— éœ€ćŠȘćŠ›çš„æŽ»ćŠšćŠ‚ćœšäž€äžȘç©șæ—·çš„è·ŻäžŠćŒ€èœŠïŒ›çł»ç»Ÿ 2 甹äșŽć€æ‚ć’Œçčççš„ć·„äœœćŠ‚çź—äž€é“æ•°ć­ŠèĄšèŸŸćŒă€‚ć°†äœ çš„æ”‹èŻ•äžșçł»ç»Ÿ 1 èźŸèźĄïŒŒćœ“äœ çœ‹äž€æź”æ”‹èŻ•ä»Łç æ—¶ïŒŒéœ€èŠćƒæ”č HTML æ–‡æĄŁäž€æ ·çź€ć•è€Œäžæ˜ŻćƒèźĄçź— 2 × (17 × 24)。 +揩侀æ–čéąïŒŒæ”‹èŻ•æ˜Żäž€äžȘć‹ć„œçš„ćŠ©æ‰‹ïŒŒäž€äžȘ䜠äčäșŽäžŽäč‹ćˆäœœă€æŠ•è”„ć°ć›žæ±‡æŠ„ć€§çš„ćŠ©æ‰‹ă€‚ç§‘ć­ŠèŻæ˜Žæˆ‘ä»Źæœ‰äž€ć„—ć€§è„‘çł»ç»ŸïŒšçł»ç»Ÿ 1 甹äșŽæ— éœ€ćŠȘćŠ›çš„æŽ»ćŠšćŠ‚ćœšäž€äžȘç©șæ—·çš„è·ŻäžŠćŒ€èœŠïŒ›çł»ç»Ÿ 2 甹äșŽć€æ‚ć’Œçčççš„ć·„äœœćŠ‚çź—äž€é“æ•°ć­ŠèĄšèŸŸćŒă€‚ć°†äœ çš„æ”‹èŻ•äžșçł»ç»Ÿ 1 èźŸèźĄïŒŒćœ“äœ çœ‹äž€æź”æ”‹èŻ•ä»Łç æ—¶ïŒŒéœ€èŠćƒæ”č HTML æ–‡æĄŁäž€æ ·çź€ć•è€Œäžæ˜ŻćƒèźĄçź— 2 × (17 × 24)。 äžșäș†èŸŸćˆ°èż™äžȘç›źçš„ïŒŒæˆ‘ä»ŹćŻä»„é€šèż‡é€‰æ‹©æ€§ä»·æŻ”é«˜ă€æŠ•ć…„äș§ć‡șæŻ”ïŒˆROIïŒ‰é«˜çš„æŠ€æœŻă€ć·„ć…·ä»„ćŠæ”‹èŻ•ćŻčè±Ąă€‚ä»…æ”‹èŻ•éœ€èŠçš„ć†…ćźčćŠȘćŠ›äżæŒć…¶ç”æŽ»æ€§ïŒŒæŸäș›æ—¶ć€™ç”šè‡łć€ŒćŸ—ćŽ»èˆćŒƒäž€äș›æ”‹èŻ•æ„æąć–ç”æŽ»æ€§ć’Œçź€æŽæ€§ă€‚ -![alt text](/assets/headspace.png "We have no head room for additional complexity") +![alt text](/assets/zh-CN/headspace.png "We have no head room for additional complexity") äž‹éąçš„ć€§éƒšćˆ†ć»șèźźèĄç”Ÿè‡Șèż™äž€ćŽŸćˆ™ă€‚ @@ -129,7 +129,7 @@ describe('Products Service', function() {
    ### :clap: æ­ŁäŸ‹: 侀äžȘćŒ…ć«äž‰éƒšćˆ†çš„ç”šäŸ‹ć -![alt text](/assets/bp-1-3-parts.jpeg "A test name that constitutes 3 parts") +![alt text](/assets/zh-CN/bp-1-3-parts.jpg "A test name that constitutes 3 parts") @@ -732,7 +732,7 @@ describe('Order service', function() {
    ### :clap: æ­ŁäŸ‹: Cindy Sridharan 朹ć„čçš„æ–‡ç« â€œæ”‹èŻ•ćŸźæœćŠĄâ€”â€”ç†æ™ș的æ–čćŒâ€äž­æć‡șäș†äž€äžȘäž°ćŻŒçš„æ”‹èŻ•ç»„ćˆ -![alt text](assets/bp-12-rich-testing.jpeg "Cindy Sridharan suggests a rich testing portfolio in her amazing post ‘Testing Microservices — the sane way’") +![alt text](assets/zh-CN/bp-12-rich-testing.jpg "Cindy Sridharan suggests a rich testing portfolio in her amazing post ‘Testing Microservices — the sane way’") â˜șExample: [YouTube: “Beyond Unit Tests: 5 Shiny Node.JS Test Types (2018)” (Yoni Goldberg)](https://www.youtube.com/watch?v=-2zP494wdUY&feature=youtu.be) @@ -770,7 +770,7 @@ describe('Order service', function() { ![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "Examples with Jest") -![alt text](assets/bp-13-component-test-yoni-goldberg.png " [Supertest](https://www.npmjs.com/package/supertest) allows approaching Express API in-process (fast and cover many layers)") +![alt text](assets/zh-CN/bp-13-component-test-yoni-goldberg.png " [Supertest](https://www.npmjs.com/package/supertest) allows approaching Express API in-process (fast and cover many layers)") @@ -797,7 +797,7 @@ describe('Order service', function() { ![](https://img.shields.io/badge/🔧%20Example%20using%20PACT-blue.svg "Examples with PACT") -![alt text](assets/bp-14-testing-best-practices-contract-flow.png ) +![alt text](assets/zh-CN/bp-14-testing-best-practices-contract-flow.png ) @@ -1625,7 +1625,7 @@ describe('visual validation', () => { ### :thumbsdown: ćäŸ‹: èż™ä»œèŠ†ç›–çŽ‡æŠ„ć‘Šæœ‰ä»€äčˆé—źéą˜ïŒŸćŸșäșŽäž€äžȘ真漞的ćœșæ™ŻïŒŒæˆ‘ä»Źè·ŸèžȘäș† QA 侭的ćș”甚皋ćșäœżç”šæƒ…ć†”ćč¶ć‘现äș†äž€äș›æœ‰è¶Łçš„ç™»ćœ•æšĄćŒ(提ç€ș:ç™»ćœ•ć€±èŽ„çš„æ•°é‡æ˜ŻäžæˆæŻ”äŸ‹çš„ïŒŒæœ‰äș›ćœ°æ–čæ˜Ÿç„¶æœ‰é—źéą˜ă€‚æœ€ç»ˆèĄšçŽ°äžș侀äș›ć‰ç«Żçš„ bug äžæ–­è§Šć‘ćŽç«Żç™»ćœ•API) -![alt text](assets/bp-19-coverage-yoni-goldberg-nodejs-consultant.png "What’s wrong with this coverage report? based on a real-world scenario where we tracked our application usage in QA and find out interesting login patterns (Hint: the amount of login failures is non-proportional, something is clearly wrong. Finally it turned out that some frontend bug keeps hitting the backend login API) +![alt text](assets/bp-19-coverage-yoni-goldberg-nodejs-consultant.png "What’s wrong with this coverage report? based on a real-world scenario where we tracked our application usage in QA and find out interesting login patterns (Hint: the amount of login failures is non-proportional, something is clearly wrong. Finally it turned out that some frontend bug keeps hitting the backend login API") @@ -1674,7 +1674,7 @@ it("Test addNewOrder, don't use such test names", () => { ### :clap: æ­ŁäŸ‹: Stryker æŠ„ć‘ŠïŒŒäž€äžȘçŒ–èŻ‘æ”‹èŻ•ć·„ć…·ïŒŒć‘çŽ°ćč¶ç»ŸèźĄæČĄæœ‰èą«æ”‹èŻ•ćˆ°çš„ä»Łç ïŒˆć˜ćŒ‚ïŒ‰ -![alt text](assets/bp-20-yoni-goldberg-mutation-testing.jpeg "Stryker reports, a tool for mutation testing, detects and counts the amount of code that is not tested (Mutations)") +![alt text](assets/zh-CN/bp-20-yoni-goldberg-mutation-testing.jpg "Stryker reports, a tool for mutation testing, detects and counts the amount of code that is not tested (Mutations)") @@ -2052,3 +2052,6 @@ script: **Role:** ćžźćŠ©äżæŒæœŹéĄčç›źçš„èżèĄŒïŒŒćč¶ćźĄæŸ„äžŽćź‰ć…šæ€§æœ‰ć…łçš„ćźžè·” **About:** ć–œæŹąä»Žäș‹ Node.js éĄč盼撌 Web ćș”ç”šćź‰ć…šæ€§çš„ć·„äœœă€‚ + \ No newline at end of file diff --git a/readme-zh-TW.md b/readme-zh-TW.md new file mode 100644 index 00000000..d45796a7 --- /dev/null +++ b/readme-zh-TW.md @@ -0,0 +1,1905 @@ + + +
    + +# 👇 ç‚ș什éșŒæœŹæŒ‡ć—ćŻä»„ćč«ćŠ©äœ ć°‡æžŹè©ŠèƒœćŠ›æć‡ćˆ°äž‹äž€ć€‹ç­‰çޚ + +
    + +## 📗 46+ ć€‹æœ€äœłćŻŠèžïŒšéžćžžć…šéąäž”ćŸčćș• + +é€™æ˜ŻćŸž A 戰 Z 的 JavaScript 揊 Node.js ćŻé çš„æŒ‡ć—ă€‚ćźƒç‚șäœ çžœç”ćŠèŠćŠƒäș†ćž‚ć ŽäžŠć€§é‡çš„éƒšèœæ Œæ–‡ç« ă€æ›žç±ćŠć·„慷。 + +## 🚱 é€ČéšŽïŒšćŸžćŸșç€Žć‘ć‰é‚é€Č 10,000 英里 + +ćŸžćŸșç€ŽćŸ€ć‰é‚é€Čçš„æ—…çš‹ïŒŒćŒ…æ‹ŹïŒšćœšç”Ÿç”ą(production)ç’°ćąƒäž­æžŹè©Šă€èźŠç•°æžŹè©Š(mutation testing)ă€ä»„ć±Źæ€§ç‚șćŸș瀎(property-based)çš„æžŹè©Šä»„ćŠèš±ć€šç­–ç•„ć’Œć°ˆæ„­ć·„ć…·ă€‚ćŠ‚æžœäœ èȘçœŸé–±èꀿœŹæŒ‡ć—æ›žïŒŒäœ çš„æžŹè©ŠæŠ€èƒœćŻèƒœæœƒé«˜æ–Œćčłć‡æ°Žæș–。 + +## 🌐 ć…šç«ŻïŒšć‰ç«Żă€ćŸŒç«Żă€CIă€æ‰€æœ‰éƒšćˆ† + +驖慈äș†è§Łä»»äœ•æ‡‰ç”šçš‹ćŒéƒœé€šç”šçš„æžŹè©ŠćŻŠèžă€‚ç„¶ćŸŒć†æ·±ć…„ç ”ç©¶äœ éžæ“‡çš„é ˜ćŸŸïŒšć‰ç«Ż/UIă€ćŸŒç«Żă€CI æˆ–è€…ć…šéƒšă€‚ + + +
    + +### 䜜者 Yoni Goldberg + +- A JavaScript & Node.js consultant +- 📗 [Testing Node.js & JavaScript From A To Z](https://www.testjavascript.com) - My comprehensive online course with more than [10 hours of video](https://www.testjavascript.com), 14 test types and more than 40 best practices +- [Follow me on Twitter](https://twitter.com/goldbergyoni/) + +
    + +### çż»è­Ż - 仄䜠的èȘžèš€äŸ†é–±èꀿœŹæ–‡ + +- đŸ‡čđŸ‡Œ[Traditional Chinese](readme-zh-TW.md) - Courtesy of [Yubin Hsu](https://github.com/yubinTW) +- 🇹🇳[Chinese](readme-zh-CN.md) - Courtesy of [Yves yao](https://github.com/yvesyao) +- đŸ‡°đŸ‡·[Korean](readme.kr.md) - Courtesy of [Rain Byun](https://github.com/ragubyun) +- đŸ‡”đŸ‡±[Polish](readme-pl.md) - Courtesy of [Michal Biesiada](https://github.com/mbiesiad) +- đŸ‡Ș🇾[Spanish](readme-es.md) - Courtesy of [Miguel G. Sanguino](https://github.com/sanguino) +- đŸ‡§đŸ‡·[Portuguese-BR](readme-pt-br.md) - Courtesy of [Iago Angelim Costa Cavalcante](https://github.com/iagocavalcante) , [Douglas Mariano Valero](https://github.com/DouglasMV) and [koooge](https://github.com/koooge) +- đŸ‡«đŸ‡·[French](readme-fr.md) - Courtesy of [Mathilde El Mouktafi](https://github.com/mel-mouk) +- đŸ‡ș🇩[Ukrainian](readme-ua.md) - Courtesy of [Serhii Shramko](https://github.com/Shramkoweb) +- Want to translate to your own language? please open an issue 💜 + +

    + +## `盼錄` + +#### [`珏 0 ç« ïŒšé»ƒé‡‘ćŽŸć‰‡`](#珏-0-ç« é»ƒé‡‘ćŽŸć‰‡-1) + +äž€ć€‹æż€ç™Œæ‰€æœ‰äșș的ć»șè­° (1怋ç‰čæźŠé …ç›ź) + +#### [`珏 1 ç« ïŒšæžŹè©Šć‰–æž`](#珏-1-ç« æžŹè©Šć‰–æž-1) + +ćŸș瀎 - ć»ș立äčŸæ·šçš„æžŹè©Š (12項) + +#### [`珏 2 ç« ïŒšćŸŒç«Ż`](#珏-2-ç« ćŸŒç«ŻæžŹè©Š) + +æœ‰æ•ˆçŽ‡ćœ°æ’°ćŻ«ćŸŒç«ŻćŠćŸźæœć‹™çš„æžŹè©Š (8項) + +#### [`珏 3 ç« ïŒšć‰ç«Ż`](#珏-3-ç« ć‰ç«ŻæžŹè©Š) + +ç‚șç¶Č頁 UI (ćŒ…æ‹Źç”„ä»¶ćŠE2E) æ’°ćŻ«æžŹè©Š (11項) + +#### [`珏 4 ç« ïŒšæžŹé‡æžŹè©Šçš„æœ‰æ•ˆçš‹ćșŠ`](#珏-4-ç« æžŹé‡æžŹè©Šæ•ˆæžœ) + +æžŹé‡æžŹè©Šçš„ć“èłȘ (4項) + +#### [`珏 5 章持çșŒæ•Žćˆ (Continuous Integration)`](#珏-5-章持çșŒæ•Žćˆ-ci-æˆ–ć…¶ä»–æé«˜ć“èłȘçš„æ‰‹æź”) + +JavaScript 侖界的 CI æŒ‡ć— (9項) + +

    + +# 珏 0 ç« ïŒšé»ƒé‡‘ćŽŸć‰‡ + +
    + +## âšȘ 0 é»ƒé‡‘ćŽŸć‰‡ïŒšDesign for lean testing + +:white_check_mark: **ć»ș議** +æžŹè©Šçš‹ćŒèˆ‡äž»èŠç”Ÿç”ąç’°ćąƒçš„çš‹ćŒäžćŒïŒŒèŠæŠŠä»–èš­èšˆçš„æ„”ć…¶ç°Ąć–źă€ç°ĄçŸ­ă€ć…·é«”ă€æ‰ćčłă€äœżäșșæ„‰æ‚…çš„ćŽ»äœżç”šćŠć­žçż’ă€‚äž€æź”æžŹè©Šçš‹ćŒæ‡‰è©ČèŠćŻä»„èź“äșșäž€çœŒć°±çœ‹æ‡‚ć…¶ç›źçš„ă€‚ + +æˆ‘ć€‘çš„æ€è€ƒç©șé–“èą«äž»èŠçš„çš‹ćŒé‚èŒŻæ‰€ć æ»żïŒŒäžŠæČ’æœ‰éĄć€–çš„è…Šćźčé‡ćŽ»è™•ç†è€‡é›œçš„æ±è„żă€‚ćŠ‚æžœæŠŠć…¶ä»–è€‡é›œçš„çš‹ćŒćĄžé€Čæˆ‘ć€‘ćŻæ†çš„ć€§è…ŠïŒŒć°‡æœƒäœżćŸ—æ•Žć€‹ćœ˜éšŠçš„é‹äœœèźŠæ…ąïŒŒè€Œé€™äș›è€‡é›œçš„çš‹ćŒæ­Łæ˜Żç”šäŸ†è§Łæ±șæˆ‘ć€‘éœ€èŠæžŹè©Šçš„ć•éĄŒă€‚é€™ä蟿˜Żèš±ć€šćœ˜éšŠæ”ŸæŁ„æžŹè©Šçš„ćŽŸć› ă€‚ + +揩侀æ–čéąïŒŒæžŹè©Šæ˜Żäž€ć€‹ć‹ć„œçš„ćŠ©æ‰‹ïŒŒäž€ć€‹èź“äœ æš‚æ„èˆ‡ä»–ćˆäœœă€æŠ•èł‡ć°äž”ć›žć ±ć€§çš„ćŠ©æ‰‹ă€‚ç§‘ć­žè­‰æ˜Žæˆ‘ć€‘æœ‰ć…©ć„—ć€§è…Šçł»ç”±ïŒšçł»ç”± 1 ç”šæ–Œç„Ąé ˆćŠȘćŠ›çš„æŽ»ć‹•ïŒŒćŠ‚ćœšç©șæ› çš„è·ŻäžŠé–‹è»ŠïŒ›çł»ç”± 2 ç”šæ–Œè€‡é›œć’Œçčç‘Łçš„ć·„äœœïŒŒćŠ‚èšˆçź—äž€é“æ•žć­žćŒă€‚æŠŠäœ çš„æžŹè©Šçš‹ćŒèš­èšˆæˆćŠ‚çł»ç”± 1 äž€èˆŹïŒŒç•¶äœ çœ‹è‘—äœ çš„æžŹè©ŠïŒŒèŠćƒäżźæ”č HTML æ–‡ä»¶äž€æšŁçš„ç°Ąć–źïŒŒè€Œäžæ˜Żćƒèšˆçź— 2 x (17 x 24)。 + +ç‚șäș†é”ćˆ°é€™ć€‹ç›źæš™ïŒŒæˆ‘ć€‘ćŻä»„éžæ“‡ć…·æœ‰æˆæœŹæ•ˆç›Šć’Œé«˜æŠ•èł‡ć ±é…ŹçŽ‡çš„çš„æŠ€èĄ“ă€ć·„ć…·ć’ŒæžŹè©Šç›źæš™ă€‚ćȘæžŹè©Šéœ€èŠçš„ć…§ćźčćŠȘćŠ›äżæŒä»–çš„éˆæŽ»æ€§ïŒŒæŸäș›æ™‚ć€™ç”šè‡łćŸ—æšæŁ„äž€äș›æžŹè©ŠäŸ†æ›ć–éˆæŽ»æ€§ć’Œç°Ąæœ”æ€§ă€‚ + +![alt text](/assets/headspace.png "We have no head room for additional complexity") + +ä»„äž‹ć€§éƒšćˆ†çš„ć»șè­°èĄç”Ÿè‡Ș這侀掟扇。 + +### æș–ć‚™ć„œäș†ć—ŽïŒŸ + +

    + +# 珏 1 ç« ïŒšæžŹè©Šć‰–æž + +
    + +## âšȘ  1.1 æŻć€‹æžŹè©Šçš„ćçš±èŠćŒ…ć«çš„äž‰ć€‹éƒšćˆ† + +:white_check_mark: **ć»ș議** äž€ä»œæžŹè©Šć ±ć‘Šæ‡‰è©Čć‘ŠèšŽé‚Łäș›äžäž€ćźšç†Ÿæ‚‰çš‹ćŒçš„äșșïŒŒç›źć‰æ‡‰ç”šçš‹ćŒçš„äżźèš‚ç‰ˆæœŹæ˜ŻćŠçŹŠćˆä»–ć€‘çš„èŠæ±‚ïŒŒćŒ…æ‹ŹïŒšæžŹè©Šäșș擡、DevOps ć·„çš‹ćž«ć’Œć…©ćčŽćŸŒçš„äœ ă€‚ćŠ‚æžœæžŹè©ŠèƒœćŒ…ć«é€™äž‰ć€‹éœ€æ±‚éąçš„æèż°ïŒŒć°±èƒœćŸˆć„œçš„ćŻŠçŸé€™äž€é»žïŒš + +(1) æžŹè©Šçš„ć°è±Ąæ˜Żä»€éșŒïŒŸ äŸ‹ćŠ‚ïŒŒProductsService.addNewProduct 這怋æ–čæł•。 + +(2) ćœšä»€éșŒæƒ…æłæˆ–ć Žæ™Żäž‹ïŒŸ äŸ‹ćŠ‚ïŒŒćƒčæ ŒæČ’æœ‰ć‚łç”Šè©Čæ–čæł•。 + +(3) é æœŸçš„ç”æžœæ˜Żä»€éșŒïŒŸ äŸ‹ćŠ‚ïŒŒæ–°çš„ç”ąć“æČ’有è૿‰č懆。 + +
    + +❌ **ćŠć‰‡ïŒš** äž€ć€‹ćć«"æ–°ćąžç”ąć“"çš„æžŹè©Šć€±æ•—äș†ă€‚這有çąșćˆ‡ćœ°ć‘ŠèšŽäœ ćˆ°ćș•æ˜Żä»€éșŒćœ°æ–čć‡șć•éĄŒć—ŽïŒŸ + +
    + +**👇 Note:** æŻć€‹é …ç›źéƒœæœƒæœ‰äž€ć€‹çš‹ćŒçŻ„äŸ‹ïŒŒæœ‰æ™‚ć€™é‚„æœƒæ­é…ćœ–ç‰‡ă€‚ +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :clap: æ­ŁäŸ‹ïŒšäž€ć€‹ćŒ…ć«é€™äž‰éƒšćˆ†çš„æžŹè©Šćçš± + +![](https://img.shields.io/badge/🔹%20Example%20using%20Mocha-blue.svg "Using Mocha to illustrate the idea") + +```javascript +// 1. unit under test +describe('Products Service', function() { + describe('Add new product', function() { + // 2. scenario and 3. expectation + it('When no price is specified, then the product status is pending approval', ()=> { + const newProduct = new ProductService().add(...); + expect(newProduct.status).to.equal('pendingApproval'); + }); + }); +}); +``` + +
    + +### :clap: æ­ŁäŸ‹ïŒšäž€ć€‹ćŒ…ć«é€™äž‰éƒšćˆ†çš„æžŹè©Šćçš± + +![alt text](/assets/bp-1-3-parts.jpeg "A test name that constitutes 3 parts") + +
    + +
    +
    © Credits & read-more + 1. Roy Osherove - Naming standards for unit tests +
    + +

    + +## âšȘ  1.2 仄 AAA æšĄćŒäŸ†ć»șæ§‹æžŹè©Š + +:white_check_mark: **ć»ș議** ç”šäž‰ć€‹éƒšćˆ†äŸ†ç”„çč”äœ çš„æžŹè©ŠïŒšArrange ćź‰æŽ’ă€Act ćŸ·èĄŒă€Assert 斷蚀 (AAA)ă€‚äŸç…§é€™ć€‹ç”æ§‹ïŒŒćŻä»„çąșäżèź€è€…äžç”šèŠ±èČ»è…ŠćŠ›ćŽ»ç†è§Łäœ çš„æžŹè©Šă€‚ + +第侀怋 A - Arrange ćź‰æŽ’ïŒšæ‰€æœ‰äœżçł»ç”±é”ćˆ°æžŹè©Šæ‰€èŠæšĄæ“Źçš„æƒ…ćąƒçš„çš‹ćŒă€‚é€™ćŻèƒœćŒ…ć«ćŻŠé«”ćŒ–æŸć€‹ćŸ…æžŹć–źć…ƒçš„ć»șæ§‹ć­ă€æ–°ćąž DB çš„èł‡æ–™ă€mocking/stubbing ç‰©ä»¶ć’Œć…¶ä»–æș–ć‚™çš‹ćŒă€‚ + +珏äșŒć€‹ A - Act ćŸ·èĄŒïŒšćŸ·èĄŒæžŹè©Šć–źć…ƒă€‚é€šćžžç‚șäž€èĄŒçš‹ćŒă€‚ + +第侉怋 A - Assert 斷蚀çąșäżćŸ—ćˆ°çš„ć€ŒçŹŠćˆæœŸćŸ…ă€‚é€šćžžç‚șäž€èĄŒçš‹ćŒă€‚ + +
    + +❌ **ćŠć‰‡ïŒš** äœ äžćƒ…éœ€èŠèŠ±ćŸˆć€šæ™‚é–“ćŽ»ç†è§Łäž»èŠçš‹ćŒïŒŒè€Œäž”æœŹæ‡‰æ˜Żæœ€ç°Ąć–źçš„éƒšćˆ† - æžŹè©ŠïŒŒä蟿œƒèź“äœ è…ŠćŠ›è€—ç›Ąă€‚ + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :clap: æ­ŁäŸ‹ïŒšä»„ AAA æšĄćŒäŸ†ć»șæ§‹æžŹè©Š + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Jest") ![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "Examples with Mocha") + +```javascript +describe("Customer classifier", () => { + test("When customer spent more than 500$, should be classified as premium", () => { + // Arrange + const customerToClassify = { spent: 505, joined: new Date(), id: 1 }; + const DBStub = sinon.stub(dataAccess, "getCustomer").reply({ id: 1, classification: "regular" }); + + // Act + const receivedClassification = customerClassifier.classifyCustomer(customerToClassify); + + // Assert + expect(receivedClassification).toMatch("premium"); + }); +}); +``` + +
    + +### :thumbsdown: ćäŸ‹ïŒšæČ’æœ‰ćˆ†éš”ă€äž€ć€§ćšă€é›Łä»„ç†è§Ł + +```javascript +test("Should be classified as premium", () => { + const customerToClassify = { spent: 505, joined: new Date(), id: 1 }; + const DBStub = sinon.stub(dataAccess, "getCustomer").reply({ id: 1, classification: "regular" }); + const receivedClassification = customerClassifier.classifyCustomer(customerToClassify); + expect(receivedClassification).toMatch("premium"); +}); +``` + +
    + +

    + +## âšȘ 1.3 甹由擁èȘžèš€äŸ†æèż°é æœŸïŒšäœżç”š BDD éąšæ Œçš„æ–·èš€ + +:white_check_mark: **ć»ș議** äœżç”šèČ明的æ–čćŒæ’°ćŻ«æžŹè©ŠïŒŒćŻä»„äœżèź€è€…ç„Ąè…Šçš„ get ćˆ°é‡é»žă€‚ćŠ‚æžœäœ çš„çš‹ćŒäœżç”šć„çšźæąä»¶é‚èŒŻćŒ…è”·äŸ†ïŒŒæœƒćąžćŠ èź€è€…çš„ç†è§Łé›ŁćșŠă€‚ć› æ­€ïŒŒæˆ‘ć€‘æ‡‰è©Čç›Ąé‡äœżç”šéĄžäŒŒäșș類èȘžèš€çš„æèż°èˆ‡èš€ćŠ‚ ```expect``` 或 ```should``` è€Œäžæ˜Żè‡Șć·±ćŻ«çš‹ćŒă€‚ćŠ‚æžœ Chai 或 Jest æČ’æœ‰äœ æƒłèŠç”šçš„æ–·èš€ïŒŒäž”é€™ć€‹æ–·èš€ćŻä»„èą«é »çčçš„é‡è€‡ćˆ©ç”šçš„è©±ïŒŒćŻä»„è€ƒæ…ź [æ“Žć…… Jest 的ćŒč配晹 (Jest)](https://jestjs.io/docs/en/expect#expectextendmatchers) æˆ–æ˜ŻćŻ«äž€ć€‹ [ćźąèŁœćŒ–çš„ Chai 插件](https://www.chaijs.com/guide/plugins/)。 + +
    + +❌ **ćŠć‰‡ïŒš** ćœ˜éšŠçš„æžŹè©Šæœƒè¶ŠćŻ«è¶Šć°‘ïŒŒäž”æœƒç”š .skip() æŠŠèšŽćŽ­çš„æžŹè©Šç•„éŽă€‚ + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹
    + +![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "Examples with Mocha & Chai") ![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Jest") + +### :thumbsdown: ćäŸ‹ïŒšèź€è€…ćż…é ˆćż«é€Ÿçš„çœ‹ćźŒć†—é•·äž”è€‡é›œçš„çš‹ćŒçąŒïŒŒæ‰èƒœç†è§Łè©ČæžŹè©Šçš„ç›źçš„ + +```javascript +test("When asking for an admin, ensure only ordered admins in results", () => { + // assuming we've added here two admins "admin1", "admin2" and "user1" + const allAdmins = getUsers({ adminOnly: true }); + + let admin1Found, + adming2Found = false; + + allAdmins.forEach(aSingleUser => { + if (aSingleUser === "user1") { + assert.notEqual(aSingleUser, "user1", "A user was found and not admin"); + } + if (aSingleUser === "admin1") { + admin1Found = true; + } + if (aSingleUser === "admin2") { + admin2Found = true; + } + }); + + if (!admin1Found || !admin2Found) { + throw new Error("Not all admins were returned"); + } +}); +``` + +
    + +### :clap: æ­ŁäŸ‹ïŒšćż«é€Ÿç€èŠœä»„äž‹çš„èČæ˜ŽćŒæžŹè©ŠéžćžžèŒ•éŹ† + +```javascript +it("When asking for an admin, ensure only ordered admins in results", () => { + // assuming we've added here two admins + const allAdmins = getUsers({ adminOnly: true }); + + expect(allAdmins) + .to.include.ordered.members(["admin1", "admin2"]) + .but.not.include.ordered.members(["user1"]); +}); +``` + +
    + +

    + +## âšȘ  1.4 ć …æŒé»‘çź±æžŹè©ŠïŒšćȘæžŹè©Šć…Źé–‹æ–čæł• + +:white_check_mark: **ć»ș議** æžŹè©Šć…§éƒšé‚èŒŻæ˜Żç„Ąæ„çŸ©äž”æ”ȘèČ»æ™‚é–“çš„ă€‚ćŠ‚æžœäœ çš„çš‹ćŒ/API 曞悳äș†æ­Łçąșçš„ç”æžœïŒŒäœ çœŸçš„éœ€èŠèŠ±äž‰ć€‹ć°æ™‚çš„æ™‚é–“ćŽ»æžŹè©Šćźƒć…§éƒšç©¶ç«ŸćŠ‚äœ•ćŻŠçŸçš„ïŒŒäžŠäž”ćœšäč‹ćŸŒç¶­è­·é€™äž€ć †è„†ćŒ±çš„æžŹè©Šć—ŽïŒŸæŻç•¶æžŹè©Šäž€ć€‹ć…Źé–‹æ–čæł•æ™‚ïŒŒć…¶ç§æœ‰æ–čæł•çš„ćŻŠäœœä蟿œƒèą«é𱿀§ćœ°æžŹè©ŠïŒŒćȘæœ‰ç•¶ć­˜ćœšæŸć€‹ć•éĄŒ(äŸ‹ćŠ‚éŒŻèȘ€çš„茞ć‡ș)æ™‚æžŹè©Šæ‰æœƒäž­æ–·ă€‚é€™çšźæ–čæł•äčŸçš±ç‚ș ```èĄŒç‚șæžŹè©Š```。揩侀æ–čéąïŒŒćŠ‚æžœäœ æžŹè©Šć…§éƒšæ–čæł• (ç™œçź±æ–čæł•) — äœ çš„é—œæłšé»žć°‡ćŸžç”„ä»¶çš„èŒžć‡șç”æžœèœ‰ç§»ćˆ°ć…·é«”çš„ćŻŠäœœçŽ°çŻ€äžŠïŒŒćŠ‚æžœæŸć€©ć…§éƒšé‚èŒŻæ”čèźŠäș†ïŒŒćłäœżç”æžœäŸç„¶æ­Łçąș䜠äčŸèŠèŠ±çČŸćŠ›ćŽ»ç¶­è­·äč‹ć‰çš„æžŹè©Šé‚èŒŻïŒŒé€™ç„Ąćœąäž­ćąžćŠ äș†ç¶­è­·æˆæœŹă€‚ +
    + +❌ **ćŠć‰‡ïŒš** äœ çš„æžŹè©Šæœƒćƒ[狌䟆äș†](https://en.wikipedia.org/wiki/The_Boy_Who_Cried_Wolf)äž€æšŁïŒŒçžœæ˜Żć«ć–šè‘—ć‡ș敏題äș† (äŸ‹ćŠ‚äž€ć€‹ć› ç‚șć…§éƒšèźŠæ•žćçš±æ”čèźŠè€Œć°Žè‡Žçš„æžŹè©Šć€±æ•—)。侍ć‡ș所料äșșć€‘ćŸˆćż«ć°±æœƒé–‹ć§‹ćżœèŠ– CI çš„é€šçŸ„ïŒŒç›Žćˆ°æŸć€©ïŒŒäž€ć€‹çœŸæ­Łçš„ bug èą«ćżœèŠ–... + +
    +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :thumbsdown: ćäŸ‹ïŒšäž€ć€‹ç„Ąè…ŠæžŹè©Šć…§éƒšæ–čæł•çš„æžŹè©Š + +![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "Examples with Mocha & Chai") + +```javascript +class ProductService { + // this method is only used internally + // Change this name will make the tests fail + calculateVATAdd(priceWithoutVAT) { + return { finalPrice: priceWithoutVAT * 1.2 }; + // Change the result format or key name above will make the tests fail + } + // public method + getPrice(productId) { + const desiredProduct = DB.getProduct(productId); + finalPrice = this.calculateVATAdd(desiredProduct.price).finalPrice; + return finalPrice; + } +} + +it("White-box test: When the internal methods get 0 vat, it return 0 response", async () => { + // There's no requirement to allow users to calculate the VAT, only show the final price. Nevertheless we falsely insist here to test the class internals + expect(new ProductService().calculateVATAdd(0).finalPrice).to.equal(0); +}); +``` + +
    + +

    + +## âšȘ  1.5 äœżç”šæ­Łçąșçš„æžŹè©Šæ›żèș« (Test Double)ïŒšéżć…çžœæ˜Żäœżç”š stub 撌 spy + +:white_check_mark: **ć»ș議** æžŹè©Šæ›żèș«æ˜ŻæŠŠé›™ćˆƒćŠïŒŒä»–ć€‘ćœšæäŸ›ć·šć€§ćƒčć€Œçš„ćŒæ™‚ïŒŒè€Šćˆäș†æ‡‰ç”šçš„ć…§éƒšé‚èŒŻ ([äž€çŻ‡é—œæ–ŒæžŹè©Šæ›żèș«çš„æ–‡ç« : mocks vs stubs vs spies](https://martinfowler.com/articles/mocksArentStubs.html)) ćœšäœżç”šæžŹè©Šæ›żèș«ć‰ïŒŒć•è‡Șć·±äž€ć€‹ćŸˆç°Ąć–źçš„ć•éĄŒïŒšæˆ‘æ˜Żç”šćźƒäŸ†æžŹè©Šéœ€æ±‚æ–‡ä»¶äž­ćźšçŸ©çš„ćŻèŠ‹çš„ćŠŸèƒœæˆ–è€…ćŻèƒœćŻèŠ‹çš„ćŠŸèƒœć—ŽïŒŸćŠ‚æžœäžæ˜ŻïŒŒé‚Łć°±ćŻèƒœæ˜Żç™œç›’æžŹè©Šäș†ă€‚ + +舉䟋䟆èȘȘïŒŒćŠ‚æžœäœ æƒłæžŹè©Šäœ çš„æ‡‰ç”šçš‹ćŒćœšæ”Żä»˜æœć‹™ç•¶æ©Ÿæ™‚çš„é æœŸèĄŒç‚șïŒŒäœ ćŻä»„ stub æ”Żä»˜æœć‹™äžŠè§žç™Œäž€äș›"æČ’æœ‰ć›žæ‡‰"çš„ć›žć‚łèĄŒç‚ș仄çąșäżèą«æžŹè©Šçš„ć–źć…ƒć›žć‚łæ­Łçąșçš„ć€Œă€‚é€™ćŻä»„æžŹè©Šç‰čćźšć Žæ™Żäž‹æ‡‰ç”šçš‹ćŒçš„èĄŒç‚șă€ć›žæ‡‰ćŠèŒžć‡șç”æžœă€‚äœ äčŸćŻä»„äœżç”šäž€ć€‹ spy äŸ†æ–·èš€ç•¶æœć‹™ç•¶æ©Ÿæ™‚æ˜ŻćŠæœ‰ç™Œé€é›»ć­éƒ”ä»¶ - é€™ćˆæ˜Żäž€ć€‹é‡ć°ćŻèƒœć‡șçŸćœšéœ€æ±‚æ–‡ä»¶äž­èĄŒç‚ș的æȘ࿟„ ("ćŠ‚æžœç„Ąæł•ć„Čć­˜ä»˜æŹŸèł‡èšŠïŒŒç™Œé€é›»ć­éƒ”ä»¶")ă€‚ćéŽäŸ†èȘȘïŒŒćŠ‚æžœäœ  mock çš„æ”Żä»˜æœć‹™ïŒŒèƒœçąșäżćźƒèą«æ­Łçąșć‘Œć«äžŠć‚łć…„æ­Łçąș的 JavaScript ćž‹ćˆ„ïŒŒé‚ŁéșŒäœ çš„æžŹè©Šé‡é»žæ˜Żć…§éƒšçš„é‚èŒŻïŒŒćźƒèˆ‡æ‡‰ç”šçš‹ćŒçš„ćŠŸèƒœé—œäż‚äžć€§ïŒŒè€Œäž”ćŻèƒœæœƒç¶“ćžžèźŠćŒ–ă€‚ + +
    + +❌ **ćŠć‰‡ïŒš** ä»»äœ•çš‹ćŒçš„é‡æ§‹éƒœæœƒéœ€èŠçš‹ćŒäž­æ‰€æœ‰çš„ mock é€ČèĄŒç›žć°æ‡‰çš„æ›Žæ–°ă€‚æžŹè©ŠèźŠæˆäș†äž€çšźèČ æ“”ïŒŒè€Œäžæ˜Żäž€ć€‹ćŠ©ćŠ›ă€‚ + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :thumbsdown: ćäŸ‹ïŒšé—œæłšć…§éƒšćŻŠäœœçš„ mock + +![](https://img.shields.io/badge/🔧%20Example%20using%20Sinon-blue.svg "Examples with Sinon") + +```javascript +it("When a valid product is about to be deleted, ensure data access DAL was called once, with the right product and right config", async () => { + // Assume we already added a product + const dataAccessMock = sinon.mock(DAL); + // hmmm BAD: testing the internals is actually our main goal here, not just a side-effect + dataAccessMock + .expects("deleteProduct") + .once() + .withArgs(DBConfig, theProductWeJustAdded, true, false); + new ProductService().deletePrice(theProductWeJustAdded); + dataAccessMock.verify(); +}); +``` + +
    + +### :clap: æ­ŁäŸ‹ïŒšSpy ć°ˆæłšæ–ŒæžŹè©Šéœ€æ±‚ïŒŒäœ†èș«ç‚ș侀怋 side effectïŒŒç„ĄćŻéżć…ćœ°æœƒæŽ„è§žćˆ°ć…§éƒšçš‹ćŒç”æ§‹ + +```javascript +it("When a valid product is about to be deleted, ensure an email is sent", async () => { + // Assume we already added here a product + const spy = sinon.spy(Emailer.prototype, "sendEmail"); + new ProductService().deletePrice(theProductWeJustAdded); + // hmmm OK: we deal with internals? Yes, but as a side effect of testing the requirements (sending an email) + expect(spy.calledOnce).to.be.true; +}); +``` + +
    + +

    + +## 📗 æƒłèŠé€éŽćœ±ç‰‡äŸ†ć­žçż’é€™äș›ćšæł•ć—ŽïŒŸ + +### æ­ĄèżŽäŸ†æˆ‘çš„ç·šäžŠèȘČ繋ç¶Č站 [Testing Node.js & JavaScript From A To Z](https://www.testjavascript.com) + +

    + +## âšȘ 1.6 䞍芁 "foo", äœżç”šçœŸćŻŠçš„èł‡æ–™ + +:white_check_mark: **ć»ș議** 生由環汃侭的 bug é€šćžžæ˜Żćœšäž€äș›ç‰čæźŠæˆ–è€…æ„ć€–çš„èŒžć…„äž‹ć‡ș珟的 — æ‰€ä»„æžŹè©Šçš„èŒžć…„èł‡æ–™è¶ŠçœŸćŻŠïŒŒè¶Šćźčæ˜“ćœšæ—©æœŸæŠ“äœć•éĄŒă€‚äœżç”šçŸæœ‰çš„äž€äș›ć‡œćŒćș«ïŒˆæŻ”抂 [Faker](https://www.npmjs.com/package/faker)ïŒ‰ćŽ»é€ "恇"çœŸćŻŠæ•žæ“šäŸ†æšĄæ“Źç”Ÿç”ąç’°ćąƒæ•žæ“šçš„ć€šæšŁæ€§ć’ŒćœąćŒă€‚æŻ”ćŠ‚ïŒŒé€™äș›ć‡œç€șćș«ćŻä»„ç”ąç”ŸçœŸćŻŠçš„é›»è©±è™ŸçąŒă€ç”šæˆ¶ćçš±ă€äżĄç”šćĄă€ć…Źćžćçš±ç­‰ç­‰ă€‚äœ é‚„ćŻä»„ć‰”ć»ș侀äș›æžŹè©Š(ćœšć–źć…ƒæžŹè©Šäč‹äžŠïŒŒè€Œäžæ˜Żæ›żä»Ł)ç”Ÿç”ąéššæ©Ÿ fakers æ•žæ“šäŸ†æ“Žć……äœ çš„æžŹè©Šć–źć…ƒïŒŒç”šè‡łćŸžç”Ÿç”ąç’°ćąƒäž­ć°Žć…„çœŸćŻŠçš„èł‡æ–™ă€‚ćŠ‚æžœæƒłèŠæ›Žé€ČéšŽçš„è©±ïŒŒè«‹çœ‹äž‹äž€ć€‹é …ç›źïŒšćŸșæ–Œć±Źæ€§çš„æžŹè©Š (property-based testing)。 +
    + +❌ **ćŠć‰‡ïŒš** äœ èŠéƒšć±Źçš„çš‹ćŒéƒœćœš "foo" äč‹éĄžçš„èŒžć…„ć€Œäž­æ­Łçąșçš„é€šéŽæžŹè©ŠïŒŒç”æžœäžŠç·šäč‹ćŸŒæ”¶ćˆ°ćƒæ˜Ż ```@3e2ddsf . ##’ 1 fdsfds . fds432 AAAA``` äč‹éĄžçš„èŒžć…„ć€ŒćŸŒæŽ›æŽ‰äș†ă€‚ + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :thumbsdown: ćäŸ‹: äž€ć€‹æžŹè©ŠæĄˆäŸ‹äœżç”šéžçœŸćŻŠèł‡æ–™ćŽ»é€šéŽæžŹè©Š + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Jest") + +```javascript +const addProduct = (name, price) => { + const productNameRegexNoSpace = /^\S*$/; // no white-space allowed + + if (!productNameRegexNoSpace.test(name)) return false; // this path never reached due to dull input + + // some logic here + return true; +}; + +test("Wrong: When adding new product with valid properties, get successful confirmation", async () => { + // The string "Foo" which is used in all tests never triggers a false result + const addProductResult = addProduct("Foo", 5); + expect(addProductResult).toBe(true); + // Positive-false: the operation succeeded because we never tried with long + // product name including spaces +}); +``` + +
    + +### :clap:æ­ŁäŸ‹ïŒšäœżç”šéššæ©Ÿç”ąç”Ÿçš„çœŸćŻŠèł‡æ–™äŸ†èŒžć…„ + +```javascript +it("Better: When adding new valid product, get successful confirmation", async () => { + const addProductResult = addProduct(faker.commerce.productName(), faker.random.number()); + // Generated random input: {'Sleek Cotton Computer', 85481} + expect(addProductResult).to.be.true; + // Test failed, the random input triggered some path we never planned for. + // We discovered a bug early! +}); +``` + +
    + +

    + +## âšȘ  1.7 Property-based testing ćŸșæ–Œć±Źæ€§çš„æžŹè©ŠïŒšæžŹè©ŠèŒžć…„çš„ć€šçšźç”„ćˆ + +:white_check_mark: **ć»ș議** é€šćžžæˆ‘ć€‘ćȘæœƒéžæ“‡ć°‘éƒšćˆ†çš„èŒžć…„æšŁæœŹćŽ»ćšæžŹè©Šă€‚ ćłäœżæ˜Żäœżç”šäș†äžŠäž€é …æćˆ°çš„ć·„ć…·ćŽ»æšĄæ“ŹçœŸćŻŠæ•žæ“šïŒŒæˆ‘ć€‘äčŸćȘèŠ†è“‹ćˆ°äș†äž€éƒšćˆ†èŒžć…„çš„ç”„ćˆ (```method('', true, 1)```, ```method('string', false , 0)```)ă€‚ç„¶è€Œćœšç”Ÿç”ąç’°ćąƒäž­ïŒŒäž€ć€‹æ“æœ‰ 5 ć€‹ćƒæ•žçš„ APIïŒŒćŻèƒœæœƒé‡ćˆ°äžŠćƒçšźæŽ’ćˆ—ç”„ćˆçš„èŒžć…„ïŒŒè€Œć…¶äž­çš„æŸäž€çšźćŻèƒœæœƒæŠŠäœ çš„çš‹ćŒæžæŽ›ïŒˆćŻćƒè€ƒ [Fuzz Testing](https://en.wikipedia.org/wiki/Fuzzing)ïŒ‰ă€‚ + +ćŠ‚äœ•æ’°ćŻ«äž€ć€‹æžŹè©ŠïŒŒćŻä»„è‡Șć‹•ç™Œé€ 1000 çšźäžćŒèŒžć…„çš„æŽ’ćˆ—ç”„ćˆïŒŒäžŠæ•æ‰ćˆ°äœżæˆ‘ć€‘çš„çš‹ćŒäžèƒœæ­Łçąșć›žć‚łçš„èŒžć…„ïŒŸćŸșæ–Œć±Źæ€§çš„æžŹè©Š (Property-based testing) ć°±æ˜Żé€™æšŁäž€çšźæŠ€èĄ“ïŒšé€éŽç™Œé€æ‰€æœ‰ćŻèƒœçš„èŒžć…„ç”„ćˆćˆ°äœ çš„æžŹè©Šć–źć…ƒäž­ïŒŒćźƒćąžćŠ äș†ç™ŒçŸ bug çš„ćŻèƒœæ€§ă€‚ + +äŸ‹ćŠ‚ïŒŒç”Šćźšäž€ć€‹æ–čæł• — ```addNewProduct(id, name, isDiscount)``` — ć‡œç€șćș«ć°‡äœżç”šèš±ć€š ```(number, string, boolean)``` çš„ç”„ćˆäŸ†ć‘Œć«é€™ć€‹æ–čæł•ïŒŒæŻ”ćŠ‚ ```(1, 'iPhone', false)``````(2, 'Galaxy', true)```ă€‚æ‚šćŻä»„äœżç”šæ‚šć–œæ­Ąçš„æžŹè©Šé‹èĄŒć™š(Mocha、Jest等)ïŒŒäœżç”š [js-verify](https://github.com/jsverify/jsverify) 或者 [testcheck](https://github.com/leebyron/testcheck-js) (æ–‡ä»¶ćŻ«ćŸ—æŻ”èŒƒć„œ) äŸ†ćŸ·èĄŒćŸșæ–Œć±Źæ€§çš„æžŹè©Šă€‚ + +曎新Nicolas Dubien ćœšäž‹éąçš„ć›žćŸ©äž­ć»șè­°äœżç”š [fast-check](https://github.com/dubzzz/fast-check#readme)ïŒŒćźƒäŒŒäčŽæäŸ›äș†æ›Žć€šçš„ćŠŸèƒœïŒŒäž”æœ‰èą«ç©æ„”ç¶­è­·ă€‚ + +
    + +❌ **ćŠć‰‡ïŒš** äœ ç„Ąæ„äž­éžæ“‡çš„æžŹè©ŠèŒžć…„ćȘæ¶”è“‹ćˆ°é‹äœœæ­Łćžžçš„çš‹ćŒç‰‡æź”ă€‚äžćčžçš„æ˜ŻïŒŒä»–æČ’æœ‰ç™ŒçŸçœŸæ­Łçš„éŒŻèȘ€ïŒŒé€™äčŸé™äœŽäș†æŠŠæžŹè©Šç•¶äœœç™ŒçŸéŒŻèȘ€çš„ć·„ć…·çš„æˆæ•ˆă€‚ + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :clap: æ­ŁäŸ‹ïŒš äœżç”š fast-check äŸ†æžŹè©Šèš±ć€šçš„èŒžć…„ç”„ćˆ + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Jest") + +```javascript +import fc from "fast-check"; + +describe("Product service", () => { + describe("Adding new", () => { + // this will run 100 times with different random properties + it("Add new product with random yet valid properties, always successful", () => + fc.assert( + fc.property(fc.integer(), fc.string(), (id, name) => { + expect(addNewProduct(id, name).status).toEqual("approved"); + }) + )); + }); +}); +``` + +
    + +

    + +## âšȘ  1.8 ćŠ‚æžœéœ€èŠïŒŒćȘäœżç”šç°ĄçŸ­çš„èĄŒć…§ćż«ç…§ (inline snapshots) + +:white_check_mark: **ć»ș議** ćŠ‚æžœäœ éœ€èŠé€ČèĄŒ ćż«ç…§æžŹè©Š ([snapshot testing](https://jestjs.io/docs/en/snapshot-testing))ćȘäœżç”šçŸ­è€Œé›†äž­çš„ćż«ç…§ (橂3~7èĄŒ)è©Čćż«ç…§æ˜ŻæžŹè©Šçš‹ćŒçš„äž€éƒšä»œïŒŒè€Œäžæ˜Żćœšć€–éƒšæ–‡ä»¶äž­ă€‚äżæŒć„œé€™äž€ćŽŸć‰‡ïŒŒć°‡æœƒçąșäżäœ çš„æžŹè©Šçš„è‡Șæˆ‘è§Łé‡‹æ€§äž”äžæœƒé‚ŁéșŒè„†ćŒ±ă€‚ + +揩侀æ–čéąïŒŒ"classic snapshots"çš„æ•™ć­žć’Œć·„ć…·éŒ“ć‹”ć°‡ć€§æ–‡ä»¶ (抂甄件的æžČæŸ“ç”æžœă€API 的 JSON 甐果) 歘ć„Č朹侀äș›ć€–郚ćȘ’介䞊䞊çąșäżæŻæŹĄæžŹè©Šé‹èĄŒæ™‚ïŒŒć°‡æ”¶ćˆ°çš„ç”æžœèˆ‡äżć­˜çš„ç‰ˆæœŹé€ČèĄŒæŻ”èŒƒă€‚èˆ‰ć€‹äŸ‹ć­ïŒŒé€™ć°‡æœƒéš±æ€§ćœ°ć°‡æˆ‘ć€‘çš„æžŹè©Šèˆ‡ćŒ…ć«3000ć€‹æ•žć€Œçš„1000èĄŒć…§ćźčè€Šćˆćœšäž€è”·ïŒŒè€ŒæžŹè©Šè€…ćŸžæœȘé–±èź€ć’ŒæŽšç†éŽé€™äș›æ•žæ“šă€‚ç‚ș什éșŒé€™æšŁæ˜Żäžć°çš„ é€™æšŁćšïŒŒć°‡æœƒæœ‰1000ć€‹ćŽŸć› èź“äœ çš„æžŹè©Šć€±æ•— - ćȘèŠæœ‰äž€èĄŒæ”čèźŠïŒŒćż«ç…§æŻ”ć°ć°±æœƒ failïŒŒè€Œé€™ćŻèƒœæœƒç¶“ćžžç™Œç”Ÿă€‚ć€šé »çčïŒŸç•¶æœ‰æŻäž€ć€‹ç©șæ Œă€èš»è§Łæˆ–äž€é»ž CSS/HTML çš„èźŠćŒ–ă€‚äžćƒ…ćŠ‚æ­€ïŒŒæžŹè©Šćçš±äčŸäžæœƒæäŸ›é—œæ–Œć€±æ•—çš„ç·šçŽąïŒŒć› ç‚ș柃ćȘæ˜ŻæȘ࿟„這1000èĄŒæ˜ŻćŠæœ‰èźŠćŒ–ïŒŒè€Œäž”ćźƒé‚„éŒ“ć‹”æžŹè©Šè€…ćŽ»æŽ„ć—äž€ć€‹ä»–ç„Ąæł•æȘ࿟„ć’Œé©—è­‰çš„ć€§æ–‡ä»¶äœœç‚șæœŸæœ›çš„ç”æžœă€‚æ‰€æœ‰é€™äș›éƒœæ˜ŻæžŹè©Šç›źæš™äžæ˜Žçąșă€æžŹè©Šç›źæš™éŽć€šçš„ç—‡ç‹€ă€‚ + +ć€ŒćŸ—æłšæ„çš„æ˜ŻïŒŒćœšć°‘æ•žæƒ…æłäž‹ïŒŒć€§ćž‹çš„ć€–éƒšćż«ç…§æ˜ŻćŻä»„æŽ„ć—çš„ - ç•¶æ–·èš€çš„ć°è±Ąæ˜Ż schema è€Œäžæ˜Żæ‰€æœ‰ć…§ćźč時 (æć–ć‡șèŠçš„ć€ŒäžŠć°ˆæłšćœšæŸć€‹æŹ„äœäžŠ)ïŒŒæˆ–è€…ç•¶æ”¶ćˆ°çš„æ–‡ä»¶ć…§ćźčćčŸäčŽäžæœƒæ”čèźŠæ™‚ă€‚ + +
    + +❌ **ćŠć‰‡ïŒš** 侀怋 UI çš„æžŹè©Šć€±æ•—äș†ă€‚çš‹ćŒçœ‹è”·äŸ†æ˜Żć°çš„ïŒŒç•«éąäžŠäčŸćźŒçŸŽæžČ染äș†æŻć€‹ćƒçŽ ïŒŒäœ†æ€ŽéșŒäș†ïŒŸ äœ çš„æžŹè©Šçš‹ćŒç™ŒçŸæ”¶ćˆ°çš„ć…§ćźčèˆ‡æœŸæœ›çš„äžćŒïŒŒæˆ–èš±ćȘæ˜Żć€šäș†äž€ć€‹ç©șæ Œ... + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :thumbsdown: ćäŸ‹ïŒš 氇看侍戰的 2000 èĄŒçš‹ćŒè€Šćˆé€Čæˆ‘ć€‘çš„æžŹè©ŠæĄˆäŸ‹äž­ + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Jest") + +```javascript +it("TestJavaScript.com is renderd correctly", () => { + // Arrange + + // Act + const receivedPage = renderer + .create( Test JavaScript ) + .toJSON(); + + // Assert + expect(receivedPage).toMatchSnapshot(); + // We now implicitly maintain a 2000 lines long document + // every additional line break or comment - will break this test +}); +``` + +
    + +### :clap: æ­ŁäŸ‹ïŒšæœŸæœ›æ˜ŻćŻèŠ‹äž”é›†äž­çš„ + +```javascript +it("When visiting TestJavaScript.com home page, a menu is displayed", () => { + // Arrange + + // Act + const receivedPage = renderer + .create( Test JavaScript ) + .toJSON(); + + // Assert + + const menu = receivedPage.content.menu; + expect(menu).toMatchInlineSnapshot(` + +`); +}); +``` + +
    + +

    + +## âšȘ 1.9 éżć…äœżç”šć…šćŸŸçš„ test fixtures 或 seedsïŒŒè€Œæ˜Żæ”Ÿé€ČæŻć€‹æžŹè©Šäž­ + +:white_check_mark: **ć»ș議** ćƒç…§é»ƒé‡‘ćŽŸć‰‡ïŒŒæŻć€‹æžŹè©Šéœ€èŠćœšćźƒè‡Ș深的 DB äž­é€ČèĄŒæ“äœœéżć…äș’ç›žæ±ĄæŸ“ă€‚äœ†çŸćŻŠäž­ïŒŒé€™æąèŠć‰‡ç¶“ćžžèą«æ‰“ç ŽïŒšç‚șäș†æ€§èƒœçš„æć‡è€ŒćœšćŸ·èĄŒæžŹè©Šć‰ćˆć§‹ćŒ–ć…šćŸŸèł‡æ–™ćș« (äčŸèą«çš±ç‚ș"[test fixture](https://en.wikipedia.org/wiki/Test_fixture)")ă€‚ć„˜çźĄæ€§èƒœćŸˆé‡èŠïŒŒäœ†æ˜ŻćźƒćŻä»„é€šéŽćŸŒéąèŹ›çš„ă€Œç”„ä»¶æžŹè©Šă€äŸ†ćšć–æšă€‚ç‚șäș†æž›èŒ•耇雜ćșŠïŒŒæˆ‘ć€‘ćŻä»„ćœšæŻć€‹æžŹè©Šäž­ćȘ戝構挖è‡Șć·±éœ€èŠçš„æ•žæ“šă€‚é™€éžæ€§èƒœć•éĄŒçœŸçš„éžćžžćšŽé‡ïŒŒé‚Łé‚„æ˜ŻćŻä»„ćšäž€ćźšçš‹ćșŠçš„ćŠ„ć” - ćƒ…ćœšć…šćŸŸæ”Ÿäžæœƒæ”čèźŠçš„æ•žæ“š (æŻ”ćŠ‚ query)。 + +
    + +❌ **ćŠć‰‡ïŒš** 有䞀äș›æžŹè©Š fail äș†ïŒŒćœ˜éšŠèбäș†èš±ć€šæ™‚é–“ćŸŒç™ŒçŸïŒŒćȘæ˜Żć› ç‚șć…©ć€‹æžŹè©ŠćŒæ™‚æ”čèźŠäș†ćŒäž€ć€‹ seed。 + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :thumbsdown: ćäŸ‹ïŒšæžŹè©ŠæĄˆäŸ‹äč‹é–“äžæ˜Żçšç«‹çš„ă€‚è€Œæ˜Żç›žäŸæ–Œć…šćŸŸçš„ DB èł‡æ–™ + +![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "Examples with Mocha") + +```javascript +before(async () => { + // adding sites and admins data to our DB. Where is the data? outside. At some external json or migration framework + await DB.AddSeedDataFromJson('seed.json'); +}); +it("When updating site name, get successful confirmation", async () => { + // I know that site name "portal" exists - I saw it in the seed files + const siteToUpdate = await SiteService.getSiteByName("Portal"); + const updateNameResult = await SiteService.changeName(siteToUpdate, "newName"); + expect(updateNameResult).to.be(true); +}); +it("When querying by site name, get the right site", async () => { + // I know that site name "portal" exists - I saw it in the seed files + const siteToCheck = await SiteService.getSiteByName("Portal"); + expect(siteToCheck.name).to.be.equal("Portal"); // Failure! The previous test change the name :[ +}); + +``` + +
    + +### :clap: æ­ŁäŸ‹ïŒšæŻć€‹æžŹè©ŠæĄˆäŸ‹ćȘ操䜜他è‡Șć·±çš„èł‡æ–™ + +```javascript +it("When updating site name, get successful confirmation", async () => { + // test is adding a fresh new records and acting on the records only + const siteUnderTest = await SiteService.addSite({ + name: "siteForUpdateTest" + }); + + const updateNameResult = await SiteService.changeName(siteUnderTest, "newName"); + + expect(updateNameResult).to.be(true); +}); +``` + +
    + +
    + +## âšȘ  1.10 䞍芁 catch 錯èȘ€ïŒŒexpect 他怑 + +:white_check_mark: **ć»ș議** ç•¶äœ èŠæžŹè©Šäž€äș›èŒžć…„æ˜ŻćŠæœ‰è§žç™ŒéŒŻèȘ€æ™‚ïŒŒäœżç”š ```try-catch-finally``` 䟆æȘ࿟„ä»–æ˜ŻćŠæœƒé€Čć…„ćˆ° catch ć€ćĄŠïŒŒçœ‹è”·äŸ†æČ’什éșŒć•éĄŒă€‚䜆會èꊿˆäž€ć€‹çŹšæ‹™äž”ć†—é•·çš„æžŹè©ŠæĄˆäŸ‹ (ćŠ‚äž‹éąçš‹ćŒçŻ„äŸ‹)ïŒŒä»–æœƒéš±è—ç°Ąć–źçš„æžŹè©Šæ„ćœ–ć’Œé æœŸçš„ç”æžœă€‚ + +äž€ć€‹æ›Žç‚șć„Șé›…çš„äœœæł•æ˜Żäœżç”šć°ˆç”šçš„ć–źèĄŒæ–·èš€ïŒšćŠ‚ Chai 侭的 ```expect(method).to.throw``` æˆ–æ˜Ż Jest 侭的 ```expect(method).toThrow()```ă€‚ćż…é ˆèŠçąș保這怋 expection ćŒ…ć«æŸć€‹é æœŸçš„ error typeïŒŒćŠ‚æžœćȘćŸ—ćˆ°äž€ć€‹é€šç”šçš„éŒŻèȘ€ćž‹æ…‹ïŒŒé‚Łæ‡‰ç”šçš‹ćŒć°‡ç„Ąæł•èĄšæ˜Žæ›Žć€šèšŠæŻç”Šäœżç”šè€…ă€‚ + +
    + +❌ **ćŠć‰‡ïŒš** ćŸžæžŹè©Šć ±ć‘Š (橂 CI 栱摊) 䞭芁看ć‡șć“ȘèŁĄæœ‰éŒŻæœƒéžćžžć›°é›Łă€‚ + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :thumbsdown: ćäŸ‹ïŒšäž€ć€‹ćŸˆé•·çš„æžŹè©ŠæĄˆäŸ‹ïŒŒć˜—è©Šäœżç”š ```try-catch``` äŸ†æ–·èš€éŒŻèȘ€ + +![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "Examples with Mocha") + +```javascript +it("When no product name, it throws error 400", async () => { + let errorWeExceptFor = null; + try { + const result = await addNewProduct({}); + } catch (error) { + expect(error.code).to.equal("InvalidInput"); + errorWeExceptFor = error; + } + expect(errorWeExceptFor).not.to.be.null; + // if this assertion fails, the tests results/reports will only show + // that some value is null, there won't be a word about a missing Exception +}); +``` + +
    + +### :clap: æ­ŁäŸ‹ïŒšäž€ć€‹ćźčæ˜“é–±èź€ćŠèą«äș†è§Łçš„ expectionïŒŒç”šè‡łèƒœèą« QA 或 PM ç†è§Ł + +```javascript +it("When no product name, it throws error 400", async () => { + await expect(addNewProduct({})) + .to.eventually.throw(AppError) + .with.property("code", "InvalidInput"); +}); +``` + +
    + +

    + +## âšȘ  1.11 ç‚șæžŹè©ŠæĄˆäŸ‹æ‰“äžŠæš™ç±€ + +:white_check_mark: **ć»ș議** äžćŒçš„æžŹè©Šéœ€èŠćœšäžćŒçš„æƒ…ćąƒäž‹ćŸ·èĄŒïŒšćż«é€Ÿć†’ç…™æžŹè©Šă€ç„Ą IO çš„æžŹè©Šă€é–‹ç™Œè€…ć„Čć­˜æˆ–æäș€æȘ”æĄˆçš„æžŹè©Šă€é€ć‡ș侀怋 PR ćŸŒçš„ end-to-end æžŹè©Šç­‰ç­‰ă€‚ ćŻä»„ç”šäž€äș› ```#cold``` ```#api``` ```#sanity``` äč‹éĄžçš„æš™ç±€äŸ†æš™èš»é€™äș›æžŹè©ŠïŒŒé€™æšŁć°±ćŻä»„ćœšæžŹè©Šæ™‚ćȘćŸ·èĄŒç‰čćźšçš„ć­é›†ćˆă€‚äŸ‹ćŠ‚ćœš Mocha äž­ćŻä»„é€™æšŁäŸ†ćŸ·èĄŒïŒš```mocha -- grep 'sanity'```。 +
    + +❌ **ćŠć‰‡ïŒš** ćŸ·èĄŒæ‰€æœ‰æžŹè©ŠæĄˆäŸ‹ïŒŒćŒ…æ‹ŹćŸ·èĄŒć€§é‡æŸ„è©ą DB çš„æžŹè©ŠïŒŒé–‹ç™Œè€…ćšçš„ä»»äœ•ćŸźć°çš„èźŠæ›Žéƒœéœ€èŠèŠ±ćŸˆé•·çš„æ™‚é–“ćŽ»è·‘ćźŒæ‰€æœ‰çš„æžŹè©ŠïŒŒć°‡æœƒć°Žè‡Žé–‹ç™Œè€…äžæƒłć†ćŸ·èĄŒæžŹè©Šă€‚ + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ïŒš + +
    + +### :clap: æ­ŁäŸ‹ïŒšć°‡æžŹè©ŠæĄˆäŸ‹æš™èš˜ç‚ș '#cold-test' èź“ćŸ·èĄŒæžŹè©Šçš„äșșćŻä»„ćȘćŸ·èĄŒé€ŸćșŠćż«çš„æžŹè©ŠæĄˆäŸ‹ (cold æŒ‡çš„æ˜ŻæČ’有 IO çš„ćż«é€ŸæžŹè©ŠïŒŒç”šè‡łćŻä»„ćœšé–‹ç™Œäșșć“Ąæ‰“ć­—æ™‚é »çčćœ°ćŸ·èĄŒ) + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Jest") + +```javascript +// this test is fast (no DB) and we're tagging it correspondigly +// now the user/CI can run it frequently +describe("Order service", function() { + describe("Add new order #cold-test #sanity", function() { + test("Scenario - no currency was supplied. Expectation - Use the default currency #sanity", function() { + // code logic here + }); + }); +}); +``` + +
    + +

    + +## âšȘ  1.12 æŠŠæžŹè©ŠæĄˆäŸ‹é€ČèĄŒè‡łć°‘ć…©ć€‹ć±€æŹĄçš„ćˆ†éĄž + +:white_check_mark: **ć»ș議** ć°æžŹè©ŠæĄˆäŸ‹ć„—ç”šäž€äș›ç”æ§‹ïŒŒèź“æŻć€‹çœ‹ćˆ°é€™ć€‹æžŹè©ŠæĄˆäŸ‹çš„äșșéƒœćŻä»„ćŸˆćźčæ˜“ćŸ—ç†è§Łéœ€æ±‚ (æžŹè©Šæ˜Żæœ€ć„œçš„æ–‡ä»¶) ć’Œæ­ŁćœšæžŹè©Šçš„ć„çšźæƒ…ćąƒă€‚äž€ć€‹ćžžèŠ‹çš„æ–čæł•æ˜ŻćœšæžŹè©ŠäžŠæ–čćŻ«è‡łć°‘ć…©ć€‹ç”šäŸ†"æèż°"çš„ć€ćĄŠïŒšçŹŹäž€ć€‹æ˜ŻæžŹè©Šć–źć…ƒçš„ćçš±ïŒŒçŹŹäșŒć€‹æ˜ŻéĄć€–çš„ćˆ†éĄžćçš±ïŒŒćŠ‚æƒ…ćąƒæˆ–è‡ȘćźšçŸ©çš„éĄžćˆ„ (ćƒè€ƒäž‹éąçš„çš‹ćŒçŻ„äŸ‹ć’Œç•«éąèŒžć‡ș)ă€‚é€™æšŁçš„ćšæł•ä蟿œƒć€§ćč…çš„æ”čć–„æžŹè©Šć ±ć‘Šçš„ć‘ˆçŸă€‚èź€è€…ć°‡æœƒćŸˆćźč易的掚斷ć‡șæžŹè©Šçš„éĄžćˆ„ïŒŒèź€æ‡‚è©ČæžŹè©Šçš„ć…§ćźčäžŠèˆ‡ć€±æ•—çš„æžŹè©Šé—œèŻè”·äŸ†ă€‚æ­€ć€–ïŒŒć°é–‹ç™Œè€…äŸ†èȘȘïŒŒç€èŠœé€™äž€é€ŁäžČçš„æžŹè©ŠäčŸèźŠćŸ—æ›ŽćŠ ćźčæ˜“ă€‚æœ‰èš±ć€šéĄć€–çš„ç”æ§‹ä蟿˜ŻćŻä»„è€ƒæ…źäœżç”šçš„ïŒŒćƒæ˜Ż [given-when-then](https://github.com/searls/jasmine-given) 或 [RITE](https://github.com/ericelliott/riteway)。 + +
    + +❌ **ćŠć‰‡ïŒš** ç•¶çœ‹ćˆ°äž€ä»œæŻ«ç„Ąç”æ§‹äž”æ•žé‡çœŸć€šçš„æžŹè©Šć ±ć‘Šæ™‚ïŒŒèź€è€…ćȘ胜透過çČ—ç•„ćœ°é–±èź€æ•Žä»œć ±ć‘ŠäŸ†çžœç”ïŒŒäžŠć°‡ć€±æ•—çš„éŒŻèȘ€æĄˆäŸ‹é—œèŻè”·äŸ†ă€‚æ€è€ƒäž€ć€‹æƒ…æłïŒŒç•¶100ć€‹æžŹè©ŠæĄˆäŸ‹äž­æœ‰7ć€‹ć€±æ•—æ™‚ïŒŒçœ‹äž€ć€‹ćˆ†ć±€ç”æ§‹è‰Żć„œçš„æžŹè©Šć ±ć‘Šèˆ‡çœ‹äž€ć€‹æ‰ćčłçš„æžŹè©Šç”æžœæž…ć–źç›žæŻ”ïŒŒé‚Łäș›éŒŻèȘ€çš„æžŹè©ŠæĄˆäŸ‹ćŸˆæœ‰ćŻèƒœéƒœćœšćŒäž€ć€‹æ”çš‹æˆ–ćˆ†éĄžćș•äž‹ïŒŒèź€è€…ć°‡ćŻä»„ćŸˆćż«çš„æŽšæ–·ć‡ș錯èȘ€çš„ćœ°æ–č或看ć‡șć“Șéƒšćˆ†æ˜Żä»–ć€‘ć€±æ•—çš„ćŽŸć› ă€‚ + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :clap: æ­ŁäŸ‹ïŒšćˆ©ç”šæžŹè©ŠæĄˆäŸ‹çš„ćçš±ć’Œæƒ…ćąƒäŸ†ç”„çč”ïŒŒćŻä»„ç”ąç”Ÿè‰Żć„œçš„æžŹè©Šć ±ć‘ŠïŒŒćŠ‚äž‹æ‰€ç€ș + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Jest") + +```javascript +// Unit under test +describe("Transfer service", () => { + // Scenario + describe("When no credit", () => { + // Expectation + test("Then the response status should decline", () => {}); + + // Expectation + test("Then it should send email to admin", () => {}); + }); +}); +``` + +![alt text](assets/hierarchical-report.png) + +
    + +### :thumbsdown: ćäŸ‹ïŒšæ‰ćčłçš„æžŹè©Šćˆ—èĄšæœƒäœżèź€è€…ćŸˆé›ŁćŽ»çœ‹æ‡‚ user story ć’Œć€±æ•—çš„æžŹè©Šäč‹é–“çš„é—œäż‚ + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Mocha") + +```javascript +test("Then the response status should decline", () => {}); + +test("Then it should send email", () => {}); + +test("Then there should not be a new transfer record", () => {}); +``` + +![alt text](assets/flat-report.png) + +
    + +
    + +

    + +## âšȘ 1.13 ć…¶ä»–é€šç”šäž”è‰Żć„œçš„æžŹè©Šçż’æ…Ł + +:white_check_mark: **ć»ș議** æœŹçŻ‡æ–‡ç« çš„é‡é»žæ˜Żèˆ‡ NodeJS ç›žé—œçš„æžŹè©Šć»șè­°æˆ–è‡łć°‘ćŻä»„ç”š NodeJS 䟆舉䟋èȘȘæ˜Žçš„ć…§ćźčă€‚ç„¶è€ŒïŒŒé€™èŁĄæœ‰ćčŸć€‹èˆ‡ NodeJS 無關的ć»șè­°ïŒŒäž”æ˜ŻçœŸæ‰€çš†çŸ„çš„ă€‚ + +ć­žçż’äžŠćŻŠçŸ [TDD掟扇](https://www.sm-cloud.com/book-review-test-driven-development-by-example-a-tldr/) - ä»–ć°èš±ć€šäșș䟆èȘȘéžćžžæœ‰ćƒčć€ŒïŒŒäœ†ćŠ‚æžœä»–äžé©ćˆäœ çš„éąšæ ŒïŒŒäžèŠèą«ćš‡ćˆ°ïŒŒäžæ˜ŻćȘæœ‰äœ é€™æšŁă€‚è©Šè‘—ćœšćŻ«çš‹ćŒäč‹ć‰äœżç”š [red-green-refactor](https://blog.cleancoder.com/uncle-bob/2014/12/17/TheCyclesOfTDD.html) çš„éąšæ ŒäŸ†æ’°ćŻ«æžŹè©ŠïŒŒäžŠçąșäżæŻć€‹æžŹè©ŠæĄˆäŸ‹ćȘæȘ࿟„äž€ć€‹æžŹè©Šç›źæš™ă€‚ç•¶äœ ç™ŒçŸäž€ć€‹ bug æ™‚ïŒŒćœšäżźćŸ©ćźƒäč‹ć‰ć…ˆæ–°ćąžäž€ć€‹ćŻä»„æȘąæžŹćˆ°ćźƒçš„æžŹè©ŠæĄˆäŸ‹ïŒŒèź“æŻć€‹æžŹè©ŠæĄˆäŸ‹ćœšèźŠç¶ äč‹ć‰è‡łć°‘ć€±æ•—äž€æŹĄïŒŒæŽ„è‘—ćż«é€Ÿæ’°ćŻ«ç°Ąć–źçš„çš‹ćŒèź“é€™ć€‹æžŹè©Šé€šéŽ - ç„¶ćŸŒé€æ­„é‡æ§‹é€™äș›çš‹ćŒćˆ°ćŻä»„侊 production 的氎æș–ïŒŒéżć…ć°ç’°ćąƒ (ćŠ‚è·ŻćŸ‘æˆ–äœœæ„­çł»ç”±ç­‰) æœ‰ä»»äœ•ç›žäŸæ€§ă€‚ + +
    + +❌ **ćŠć‰‡ïŒš** äœ æœƒéŒŻéŽé€™æ•žććčŽäŸ†çš„æ™ș慧甐晶 + +

    + +# 珏 2 ç« ïŒšćŸŒç«ŻæžŹè©Š + +## âšȘ 2.1 è±ćŻŒæ‚šçš„æžŹè©Šç”„ćˆïŒšäžć±€é™æ–Œć–źć…ƒæžŹè©Šć’ŒæžŹè©Šé‡‘ć­—ćĄ” + +:white_check_mark: **ć»ș議** 雖然 [æžŹè©Šé‡‘ć­—ćĄ”](https://martinfowler.com/bliki/TestPyramid.html) ć·Čç¶“æœ‰è¶…éŽććčŽçš„æ­·ćČäș†ïŒŒäœ†ä»–ä»ç„¶æ˜Żć€‹ćŸˆć„œçš„æšĄćž‹ïŒŒä»–æć‡șäș†äž‰çšźæžŹè©ŠéĄžćž‹ïŒŒäžŠćœ±éŸżäș†ć€§ć€šæ•žé–‹ç™Œè€…çš„æžŹè©Šç­–ç•„ă€‚èˆ‡æ­€ćŒæ™‚ïŒŒć€§é‡é–ƒäșźçš„æ–°æžŹè©ŠæŠ€èĄ“ć‡ș珟äș†ïŒŒäžŠéš±è—ćœšæžŹè©Šé‡‘ć­—ćĄ”çš„é™°ćœ±äž‹ă€‚è€ƒæ…źćˆ°èż‘ććčŽäŸ†æˆ‘ć€‘æ‰€çœ‹ćˆ°çš„æ‰€æœ‰ć·šèźŠ (Microservices, cloud, serverless)ïŒŒé€™ć€‹éžćžžè€çš„æšĄćž‹æ˜ŻćŠä»èƒœé©ç”šæ–Œæ‰€æœ‰éĄžćž‹çš„æ‡‰ç”šïŒŸæžŹè©Šç•Œäžæ‡‰è©Čè€ƒæ…źæ–°çš„æžŹè©ŠæŠ€èĄ“ć—ŽïŒŸ + +䞍芁èȘ€æœƒïŒŒćœš 2019 ćčŽïŒŒæžŹè©Šé‡‘歗楔、TDDă€ć–źć…ƒæžŹè©Šä»ç„¶æ˜ŻćŒ·ć€§çš„æŠ€èĄ“ïŒŒäž”ć°æ–Œć€§ć€šæ•žæ‡‰ç”šä»æ˜Żæœ€äœłéžæ“‡ă€‚äœ†æ˜Żćƒć…¶ä»–æšĄćž‹äž€æšŁïŒŒć„˜çźĄćźƒæœ‰ç”šïŒŒäœ†æ˜Żäž€ćźš[æœƒćœšæŸäș›æ™‚怙ć‡ș敏題](https://en.wikipedia.org/wiki/All_models_are_wrong)ă€‚äŸ‹ćŠ‚ïŒŒæˆ‘ć€‘æœ‰äž€ć€‹ IoT çš„æ‡‰ç”šçš‹ćŒïŒŒć°‡èš±ć€šäș‹ä»¶ć‚łć…„侀怋 Kafka/RabbitMQ é€™æšŁçš„ message-bus äž­ïŒŒç„¶ćŸŒé€™äș›äș‹ä»¶æ”ć…„èł‡æ–™ćș«äžŠèą«ç¶“ç”± UI äŸ†ćšæŸ„è©ąă€‚æˆ‘ć€‘çœŸçš„éœ€èŠèŠ±èČ» 50% çš„æžŹè©Šé çź—ćŽ»ç‚ș這怋ćčŸäčŽæČ’æœ‰é‚èŒŻçš„äž­ćżƒćŒ–çš„æ•Žćˆæ‡‰ç”šçš‹ćŒćŻ«ć–źć…ƒæžŹè©Šć—ŽïŒŸéššè‘—æ‡‰ç”šéĄžćž‹ (bots, crypto, Alexa-skills) çš„ć€šæšŁćąžé•·ïŒŒæžŹè©Šé‡‘ć­—ćĄ”ćŻèƒœć°‡äžć†æ˜ŻæŸäș›ć Žæ™Żçš„æœ€äœłéžæ“‡äș†ă€‚ + +æ˜Żæ™‚ć€™è±ćŻŒäœ çš„æžŹè©Šç”„ćˆäžŠäș†è§Łæ›Žć€šçš„æžŹè©ŠéĄžćž‹äș†ïŒˆäž‹äž€çŻ€æœƒç”Šäœ äž€äș›ć°ć»ș議這äș›éĄžäŒŒæ–ŒæžŹè©Šé‡‘ć­—ćĄ”çš„æ€ç¶­æšĄćž‹èˆ‡äœ æ‰€éąè‡šçš„çŸćŻŠć•éĄŒæœƒæ›ŽćŠ ćŒč配"ć˜żïŒŒæˆ‘ć€‘çš„ API 掛äș†ïŒŒæˆ‘ć€‘äŸ†ćŻ« consumer-driven contract testing ć§ïŒ"ïŒ‰ă€‚èź“æ‚šçš„æžŹè©Šć€šæšŁćŒ–ïŒŒæŻ”ćŠ‚ć»ș立ćŸșæ–ŒéąšéšȘćˆ†æžçš„æȘ࿟„æšĄćž‹ — è©•äŒ°ćŻèƒœć‡șçŸć•éĄŒçš„ćœ°æ–č䞊提䟛䞀äș›é é˜ČæŽȘ斜仄枛茕這äș›æœ›ćœšéąšéšȘ。 + +éœ€èŠæłšæ„çš„æ˜ŻïŒšè»Ÿé«”äž–ç•Œäž­çš„ TDD æšĄćž‹éąè‡šć…©ć€‹æ„”ç«Żçš„æ…‹ćșŠïŒŒäž€äș›äșș錓ćčćˆ°è™•äœżç”šćźƒïŒŒćŠäž€äș›äșș扇èȘç‚șćźƒæ˜Żé­”éŹŒă€‚æŻć€‹èȘȘç”•ć°çš„äșșéƒœæ˜ŻéŒŻçš„ :] + +
    + +❌ **ćŠć‰‡ïŒš** äœ ć°‡éŒŻéŽäž€äș›è¶…高 CP ć€Œçš„ć·„ć…·ïŒŒæŻ”ćŠ‚ Fuzz、lint、mutation這äș›ć·„ć…·ćȘ需 10 ćˆ†é˜èš­ćźšć°±èƒœç‚șäœ æäŸ›èš±ć€šć„œè™•ă€‚ + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :clap: æ­ŁäŸ‹ïŒšCindy Sridharan 朹ć„č的文章 "Testing Microservices — the sane way" 䞭提ć‡șäș†äž€ć€‹è±ćŻŒçš„æžŹè©Šç”„ćˆ + +![alt text](assets/bp-12-rich-testing.jpeg "Cindy Sridharan suggests a rich testing portfolio in her amazing post ‘Testing Microservices — the sane way’") + +â˜șExample: [YouTube: “Beyond Unit Tests: 5 Shiny Node.JS Test Types (2018)” (Yoni Goldberg)](https://www.youtube.com/watch?v=-2zP494wdUY&feature=youtu.be) + +
    + +![alt text](assets/bp-12-Yoni-Goldberg-Testing.jpeg "A test name that constitutes 3 parts") + +
    + +

    + +## âšȘ 2.2 ç”„ä»¶ćŒ–æžŹè©ŠćŻèƒœæ˜Żæœ€æœ‰æ•ˆçš„ćˆ©ć™š + +:white_check_mark: **ć»ș議** æ‡‰ç”šçš‹ćŒäž­çš„æŻć€‹ć–źć…ƒæžŹè©Šćƒ…èƒœèŠ†è“‹æ•Žć€‹çš‹ćŒçš„äž€ć°éƒšćˆ†ïŒŒèŠèŠ†è“‹ć…šéƒšæœƒéžćžžéș»ç…©ïŒŒè€Œç«Żćˆ°ç«ŻæžŹè©ŠćŻä»„ćŸˆèŒ•éŹ†ćœ°èŠ†è“‹ć€§é‡ć€ćŸŸïŒŒäœ†æ˜ŻæŻ”èŒƒè„†ćŒ±è€Œäž”ćŸˆæ…ąă€‚äœ•äžæ‰Ÿäž€ć€‹ćčłèĄĄé»žïŒšćŻ«äž€äș›æŻ”ć–źć…ƒæžŹè©Šć€§ïŒŒäœ†æ˜ŻæŻ”ç«Żćˆ°ç«ŻæžŹè©Šć°çš„æžŹè©Šă€‚ç”„ä»¶æžŹè©Šæ˜ŻæžŹè©Šäž–ç•Œçš„äž€éĄ†éș珠 — ćźƒæ‰Ÿćˆ°äș†ć…©ć€‹æšĄćŒçš„æœ€äœłćčłèĄĄé»žïŒšäžéŒŻçš„æ€§èƒœć’Œäœżç”š TDD æšĄćŒçš„ćŻèƒœæ€§èˆ‡çœŸćŻŠäž”ćŒ·ć€§çš„èŠ†è“‹çŽ‡ă€‚ + +ç”„ä»¶æžŹè©Šé—œæłšæ–ŒćŸźæœć‹™"於慃"ïŒŒä»–ć€‘é‡ć° API äŸ†ćšäș‹ïŒŒäž mock ä»»äœ•ć±Źæ–ŒćŸźæœć‹™æœŹèș«çš„æ±è„żïŒˆćƒæ˜ŻçœŸćŻŠçš„ DBïŒŒç”šè‡łæ˜Żè©Č DB 的 in-memory ç‰ˆæœŹïŒ‰äœ†æ˜Ż stub æ‰€æœ‰ć€–éƒšçš„æ±è„żïŒŒćƒæ˜Żć‘Œć«ć…¶ä»–çš„ćŸźæœć‹™ă€‚è—‰ç”±é€™çšźæ–čćŒïŒŒæˆ‘ć€‘ćŻä»„æžŹè©Šæˆ‘ć€‘éƒšçœČçš„éƒšćˆ†ïŒŒç”±ć€–è€Œć…§ćœ°èŠ†è“‹æ‡‰ç”šçš‹ćŒïŒŒćŻä»„çŻ€çœć€§é‡æ™‚é–“äžŠçČćŸ—äżĄćżƒă€‚ + +
    + +❌ **ćŠć‰‡ïŒš** äœ ćŻèƒœèŠ±äș†ć„œćčŸć€©äŸ†ćŻ«ć–źć…ƒæžŹè©ŠïŒŒć»ç™ŒçŸćȘćŸ—ćˆ°äș† 20% çš„èŠ†è“‹çŽ‡ă€‚ + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :clap: æ­ŁäŸ‹ïŒšäœżç”š Supertest äŸ†æžŹè©Š Express API (ćż«é€Ÿäž”èŠ†è“‹ć€šć€‹ć±€æŹĄ) + +![](https://img.shields.io/badge/🔧%20Example%20using%20Mocha-blue.svg "Examples with Mocha") + +![alt text](assets/bp-13-component-test-yoni-goldberg.png " [Supertest](https://www.npmjs.com/package/supertest) allows approaching Express API in-process (fast and cover many layers)") + +
    + +

    + +## âšȘ 2.3 ćˆ©ç”š contract tests 䟆çąșäżæ–°çš„ release äžæœƒç ŽćŁž API çš„äœżç”š + +:white_check_mark: **ć»ș議** äœ çš„ćŸźæœć‹™æœ‰èš±ć€šćźąæˆ¶ïŒŒè€Œäœ ç‚șäș†ć…Œćźčæ€§è€Œé‹èĄŒè‘—ćŸˆć€šçšźç‰ˆæœŹ (keeping everyone happy)ă€‚ç•¶äœ æ”čäș†æŸäș›çš‹ćŒćŸŒ "砰"某äș›äœżç”šè©Čæœć‹™çš„é‡èŠćźąæˆ¶ç”Ÿæ°Łäș†ă€‚äŒșæœç«ŻèŠæ»żè¶łæ‰€æœ‰ćźąæˆ¶çš„æœŸæœ›æ˜Żéžćžžć›°é›Łçš„ - 揩侀æ–čéąïŒŒćźąæˆ¶ç«Żç„Ąæł•ćŸ·èĄŒä»»äœ•æžŹè©ŠïŒŒć› ç‚ș release çš„æ—„æœŸæ˜ŻäŒșæœç«Żæ±ș漚的。 + +[Consumer-driven contracts and the framework PACT](https://docs.pact.io/) èȘ•生äș†ïŒŒćźƒä»„äž€çšźç ŽćŁžæ€§çš„æ–čćŒèŠçŻ„äș†é€™äž€æ”çš‹ — äžć†ç”±äŒșæœç«ŻćźšçŸ©æžŹè©ŠèšˆćŠƒïŒŒè€Œæ˜Żćźąæˆ¶ç«Żæ±ș漚äŒșæœç«Żçš„æžŹè©ŠïŒ PACT ćŻä»„èš˜éŒ„ćźąæˆ¶ç«Żçš„æœŸæœ›äžŠć­˜æ”Ÿćœšäž€ć€‹ć…±äș«çš„äœçœź — 侭間äșșBrokeräŒșæœç«ŻćŻä»„ pull 例這äș›æœŸæœ›äžŠćˆ©ç”š PACT çš„ć‡œç€șćș«ćœšæ‰€æœ‰ç‰ˆæœŹäž­æȘąæžŹæ˜ŻćŠæœ‰èą«ç ŽćŁžçš„ć„‘çŽ„ïŒŒäčŸć°±æ˜Żćźąæˆ¶ç«Żçš„æœŸæœ›æČ’有è૿»żè¶łă€‚通過這皟æ–čćŒïŒŒæ‰€æœ‰ äŒșæœç«Ż-ç”šæˆ¶ç«Ż æČ’ć°ć„œçš„ API ć°‡æœƒćœš build/CI éšŽæź”èą«ç™ŒçŸïŒŒćŸžè€Œæž›ć°‘äœ çš„ç…©æƒ±ă€‚ +
    + +❌ **ćŠć‰‡ïŒš** æ‰€æœ‰çš„èźŠæ›Žéƒœć°‡æœƒé€ æˆçčç‘Łçš„äșșć·„æžŹè©ŠïŒŒć°Žè‡Žé–‹ç™Œè€…ćźłæ€•éƒšć±Ź + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :clap: æ­ŁäŸ‹ïŒš + +![](https://img.shields.io/badge/🔧%20Example%20using%20PACT-blue.svg "Examples with PACT") + +![alt text](assets/bp-14-testing-best-practices-contract-flow.png) + +
    + +

    + +## âšȘ 2.4 ć–źçšæžŹè©Šäœ çš„ middlewares + +:white_check_mark: **ć»ș議** èš±ć€šäșșæ‹’ç”•æžŹè©Š middlewareïŒŒć› ç‚ș漃怑ćȘäœ”çł»ç”±çš„äž€ć°éƒšćˆ†è€Œäž”ç›žäŸæ–ŒçœŸćŻŠçš„ Express serveră€‚é€™ć…©ć€‹ćŽŸć› éƒœäžæ­Łçąș — middleware é›–ç„¶ć°ïŒŒäœ†æ˜Żćœ±éŸżè‘—æ‰€æœ‰æˆ–è‡łć°‘ć€§éƒšćˆ†è«‹æ±‚ïŒŒè€Œäž”ćŻä»„èą«ç°Ąć–źćœ°äœœç‚șçŽ”ć‡œæ•žæžŹè©Š (ćƒæ•žç‚ș ```{req,res}``` 的 JavaScript 物件)ă€‚èŠæžŹè©Š middleware ć‡œæ•žïŒŒćȘéœ€èŠć‘Œć«ćźƒïŒŒäžŠäž”ç›Łçœ‹ ([ćŠ‚äœżç”š Sinon](https://www.npmjs.com/package/sinon)) 與 ```{req,res}``` 的äș’ć‹•äŸ†çąșäżć‡œæ•žæœ‰ćŸ·èĄŒæ­Łçąșçš„èĄŒç‚ș。 [node-mock-http](https://www.npmjs.com/package/node-mocks-http) ć‡œç€șćș«ć‰‡æ›Žé€Čäž€æ­„ïŒšćźƒé‚„ç›Łèœäș† ```{req,res}``` ç‰©ä»¶çš„èĄŒç‚șă€‚äŸ‹ćŠ‚ïŒŒćźƒćŻä»„æ–·èš€ res 物件䞊的 http ç‹€æ…‹æ˜ŻćŠçŹŠćˆé æœŸă€‚(çœ‹äž‹éąçš„çš‹ćŒçŻ„äŸ‹) +
    + +❌ **ćŠć‰‡ïŒš** Express middlewares 的 bug === æ‰€æœ‰æˆ–ć€§éƒšćˆ† request 的 bug + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :clap: æ­ŁäŸ‹ïŒšć–źçšæžŹè©Š middleware䞍癌ć‡șç¶Čè·Żè«‹æ±‚æˆ–ć•Ÿć‹•æ•Žć€‹ Express äŒșæœć™š + +![](https://img.shields.io/badge/🔧%20Example%20using%20Jest-blue.svg "Examples with Jest") + +```javascript +// the middleware we want to test +const unitUnderTest = require("./middleware"); +const httpMocks = require("node-mocks-http"); +// Jest syntax, equivelant to describe() & it() in Mocha +test("A request without authentication header, should return http status 403", () => { + const request = httpMocks.createRequest({ + method: "GET", + url: "/user/42", + headers: { + authentication: "" + } + }); + const response = httpMocks.createResponse(); + unitUnderTest(request, response); + expect(response.statusCode).toBe(403); +}); +``` + +
    + +

    + +## âšȘ 2.5 äœżç”šéœæ…‹ćˆ†æžć·„ć…·äŸ†æžŹé‡èˆ‡é‡æ§‹ + +:white_check_mark: **ć»ș議** äœżç”šéœæ…‹ćˆ†æžć·„ć…·ćŻä»„ćč«ćŠ©äœ ćźąè§€ćœ°æć‡çš‹ćŒć“èłȘäžŠäżæŒćŻç¶­è­·æ€§ă€‚äœ ćŻä»„ć°‡éœæ…‹ćˆ†æžć·„ć…·æ”Ÿćœšäœ çš„ CI 䞭。陀äș†æ™źé€šçš„ linting ć€–ïŒŒćźƒçš„äž»èŠèłŁé»žæ˜ŻæŸ„çœ‹ć€šć€‹æȘ”æĄˆçš„äžŠäž‹æ–‡äŸ†æȘ࿟„çš‹ćŒçąŒć“èłȘ (äŸ‹ćŠ‚ïŒšç™ŒçŸçš‹ćŒæœ‰æČ’æœ‰é‡è€‡ćźšçŸ©çš„ćœ°æ–č)ă€ćŸ·èĄŒé€ČéšŽçš„ćˆ†æž (äŸ‹ćŠ‚ïŒšçš‹ćŒè€‡é›œćșŠ) ä»„ćŠèżœèč€ code issue 的歷ćČ撌é€ČćșŠă€‚æœ‰ć…©ć€‹ć·„ć…·äŸ›äœ äœżç”šïŒš[SonarQube](https://www.sonarqube.org/) (6,300+ [stars](https://github.com/SonarSource/sonarqube)) 撌 [Code Climate](https://codeclimate.com/) (2,300+ [stars](https://github.com/codeclimate/codeclimate))。 + +Credit: [Keith Holliday](https://github.com/TheHollidayInn) + +
    + +❌ **ćŠć‰‡ïŒ›** çš‹ćŒçąŒçš„ć“èłȘéŽć·źïŒŒć†æ–°çš„ć‡œćŒćș«æˆ–ćŠŸèƒœéƒœç„Ąæł•æ‹Żæ•‘äœ çš„ bug ć’Œæ€§èƒœ + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :clap: æ­ŁäŸ‹ïŒšCodeClimateïŒŒäž€ć€‹ćŻä»„ç™ŒçŸè€‡é›œæ–čæł•çš„ć•†æ„­ć·„ć…· + +![](https://img.shields.io/badge/🔧%20Example%20using%20Code%20Climate-blue.svg "Examples with CodeClimate") + +![alt text](assets/bp-16-yoni-goldberg-quality.png "CodeClimate, a commercial tool that can identify complex methods:") + +
    + +

    + +## âšȘ  2.6 æȘ࿟„äœ ć° Node 盞關枟æČŒçš„æș–悙淄䜜 + +:white_check_mark: **ć»ș議** 愇æ€Șçš„æ˜ŻïŒŒć€§éƒšćˆ†çš„è»Ÿé«”æžŹè©ŠéƒœćȘć°ˆæłšćœšé‚èŒŻć’Œèł‡æ–™ć±€éąïŒŒäœ†æœ€é‡èŠäž”ćŸˆé›Łèą«ç·©è§Łçš„ïŒŒæ˜Żé‚Łäș›ćŸșç€Žèš­æ–œçš„ć•éĄŒă€‚äŸ‹ćŠ‚ïŒŒäœ æœ‰æžŹè©ŠéŽç•¶äœ çš„çš‹ćșèš˜æ†¶é«”éŽèŒ‰ă€äŒșæœć™šæˆ–çš‹ćșæ­»æŽ‰æ™‚çš„èĄšçŸć—ŽïŒŸæˆ–è€…äœ çš„ç›ŁæŽ§çł»ç”±ćŻä»„æȘąæžŹćˆ° API çš„ć›žæ‡‰æ™‚é–“æ…ąäș† 50% ć—ŽïŒŸç‚șäș†æžŹè©Šèˆ‡æž›èŒ•éĄžäŒŒçš„ć•éĄŒïŒŒNetflix èš­ç«‹äș†æ··æČŒć·„繋 [Chaos engineering](https://principlesofchaos.org/)ă€‚ćźƒçš„ç›źçš„æ˜ŻæäŸ›æ„è­˜ă€æĄ†æž¶ćŠć·„ć…·äŸ†æžŹè©Šæˆ‘ć€‘çš„æ‡‰ç”šçš‹ćŒć°æ–Œæ··æČŒć•éĄŒçš„ćœˆæ€§ă€‚æŻ”ćŠ‚ïŒŒæœ€è‘—ćçš„ć·„ć…·äč‹äž€ïŒŒæžŸæČŒçŒŽć­ [the chaos monkey](https://github.com/Netflix/chaosmonkey)ïŒŒä»–æœƒéššæ©Ÿćœ°æźșæŽ‰æœć‹™ä»„çąșäżæˆ‘ć€‘çš„æœć‹™ä»ç„¶ćŻä»„æäŸ›æœć‹™ç”Šćźąæˆ¶ïŒŒè€Œäžæ˜Żćƒ…äŸèłŽäž€ć€‹ć–źçšçš„äŒșæœć™š (Kubernetes ä蟿œ‰äž€ć€‹ [kube-monkey](https://github.com/asobti/kube-monkey) 甚䟆æźș掉 pods)。這äș›ć·„ć…·éƒœæ˜Żäœœç”šćœšäŒșæœć™š/ćčłć°éąïŒŒäœ†ćŠ‚æžœäœ æƒłæžŹè©ŠćŠç”ąç”Ÿć–źçŽ”çš„ Node 枟æČŒïŒŒæŻ”抂æȘ࿟„䜠的 Node 繋ćșćŠ‚äœ•è™•ç†æœȘçŸ„éŒŻèȘ€ă€æœȘ矄的 promise rejection、v8 äœżç”šçš„èš˜æ†¶é«”è¶…éŽ 1.7GB çš„é™ćˆ¶ä»„ćŠç•¶ event loop ćĄäœćŸŒäœ çš„ UX æ˜ŻćŠä»ç„¶ćŻä»„æ­Łćžžé‹èĄŒïŒŸç‚șäș†è§Łæ±șäžŠéąæćˆ°çš„ć•éĄŒïŒŒ [node-chaos](https://github.com/i0natan/node-chaos-monkey) 提䟛äș†ć„çšź Node 盞關的枟æČŒă€‚ + +
    + +❌ **ćŠć‰‡ïŒš** èŽ«éžćźšćŸ‹äž€ćźšæœƒæ“Šäž­äœ çš„ç”ąć“ïŒŒç„ĄćŻéżć…çš„ + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :clap: æ­ŁäŸ‹ïŒšNode-chaos ćŻä»„ç”ąç”Ÿæ‰€æœ‰éĄžćž‹çš„ Node.js ć•éĄŒïŒŒć› æ­€æ‚šćŻä»„æžŹè©Šæ‚šçš„æ‡‰ç”šçš‹ćșć°æ–ŒæžŸæČŒçš„é©æ‡‰èƒœćŠ› + +![alt text](assets/bp-17-yoni-goldberg-chaos-monkey-nodejs.png "Node-chaos can generate all sort of Node.js pranks so you can test how resilience is your app to chaos") + +
    + +

    + +# 珏 3 ç« ïŒšć‰ç«ŻæžŹè©Š + +## âšȘ  3.1 氇 UI èˆ‡ćŠŸèƒœćˆ†é›ą + +:white_check_mark: **ć»ș議** ç•¶ć°ˆæłšæ–ŒæžŹè©Šç”„ä»¶é‚èŒŻæ™‚ïŒŒUI çš„çŽ°çŻ€ć°±èźŠæˆäș†æ‡‰è©Čèą«ć‰”é™€çš„é›œéŸłïŒŒé€™æšŁæ‚šçš„æžŹè©Šç›źæš™ć°±ćŻä»„é›†äž­ćœšèł‡æ–™éąäžŠă€‚ćŻŠéš›äžŠïŒŒæć–ć‡șçš‹ćŒäž­æ‰€éœ€çš„èł‡æ–™ïŒŒć°‡é™äœŽèˆ‡ç•«éąçš„è€ŠćˆïŒŒćƒ…ć°ć–źçŽ”çš„èł‡æ–™ (與 HTML/CSS ç­‰ćœ–ćœąçŽ°çŻ€ç›žæŻ”) é€ČèĄŒæ–·èš€ïŒŒäžŠćœç”šæœƒæ‹–æ…ąé€ŸćșŠçš„ć‹•ç•«ă€‚æ‚šæ‡‰è©ČèŠè©Šè‘—éżć…ç•«éąçš„æžČæŸ“ïŒŒćƒ…æžŹè©Š UI ćŸŒéąçš„éƒšćˆ† (äŸ‹ćŠ‚ïŒŒæœć‹™ă€ć‹•äœœă€ć­˜ć„Č)ïŒŒäœ†é€™ć°‡ć°Žè‡ŽæžŹè©Šèˆ‡ćŻŠéš›æƒ…æłäžç›žçŹŠïŒŒă€Œæ­Łçąșçš„èł‡æ–™æ čæœŹç„Ąæł•ć‘ˆçŸćœš UI äžŠă€é€™çšźć•éĄŒć°±ç„Ąæł•ç™ŒçŸă€‚ + +
    + +❌ **ćŠć‰‡ïŒš** äœ çš„æžŹè©ŠćŻèƒœèŠ±äș† 10ms ć°±æș–ć‚™ć„œèł‡æ–™ïŒŒäœ†ć› ç‚ș侀äș›ç„Ąé—œç·ŠèŠçš„èŠ±äżć‹•ç•«ïŒŒèź“æ•Žć€‹æžŹè©ŠæĄˆäŸ‹æŒçșŒäș† 500ms。(100ć€‹æžŹè©Š = 1戆鐘) + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ïŒš + +
    + +### :clap: æ­ŁäŸ‹ïŒ›ćˆ†é›ą UI 的箰節 + +![](https://img.shields.io/badge/🔧%20Example%20using%20React-blue.svg "Examples with React") ![](https://img.shields.io/badge/🔧%20Example%20using%20React%20Testing%20Library-blue.svg "Examples with react-testing-library") + +```javascript +test("When users-list is flagged to show only VIP, should display only VIP members", () => { + // Arrange + const allUsers = [{ id: 1, name: "Yoni Goldberg", vip: false }, { id: 2, name: "John Doe", vip: true }]; + + // Act + const { getAllByTestId } = render(); + + // Assert - Extract the data from the UI first + const allRenderedUsers = getAllByTestId("user").map(uiElement => uiElement.textContent); + const allRealVIPUsers = allUsers.filter(user => user.vip).map(user => user.name); + expect(allRenderedUsers).toEqual(allRealVIPUsers); // compare data with data, no UI here +}); +``` + +
    + +### :thumbsdown: ćäŸ‹ïŒšæ··é›œäș† UI èˆ‡èł‡æ–™çš„æ–·èš€ + +```javascript +test("When flagging to show only VIP, should display only VIP members", () => { + // Arrange + const allUsers = [{ id: 1, name: "Yoni Goldberg", vip: false }, { id: 2, name: "John Doe", vip: true }]; + + // Act + const { getAllByTestId } = render(); + + // Assert - Mix UI & data in assertion + expect(getAllByTestId("user")).toEqual('[
  • John Doe
  • ]'); +}); +``` + +
    + +

    + +## âšȘ  3.2 äœżç”šäžæ˜“æ”čèźŠçš„ć±Źæ€§äŸ†æŸ„èŻą HTML 慃箠 + +:white_check_mark: **ć»ș議** äœżç”šäžć€Șćźčæ˜“ć—ç•«éąèźŠæ›Žè€Œćœ±éŸżçš„ć±Źæ€§äŸ†æŸ„è©ą HTML 慃箠 (äŸ‹ćŠ‚ form labelïŒŒè€Œäžæ˜Ż CSS selector)ă€‚ćŠ‚æžœæŒ‡ćźšçš„ć…ƒçŽ æČ’æœ‰é€™æšŁçš„ć±Źæ€§ïŒŒć‰‡ć‰”ć»șäž€ć€‹ć°ˆç”šçš„æžŹè©Šć±Źæ€§ïŒŒćŠ‚ `test-id-submit-button`ă€‚é€™æšŁćšäžćƒ…ćŻä»„çąșäżæ‚šçš„ćŠŸèƒœ/é‚èŒŻæžŹè©Šäžæœƒć› ç‚șć€–è§€èźŠćŒ–è€Œäž­æ–·ïŒŒè€Œäž”æ•Žć€‹ćœ˜éšŠćŻä»„æž…æ„šćœ°çœ‹ćˆ°ïŒŒæžŹè©ŠæĄˆäŸ‹äœżç”šäș†é€™ć€‹ć…ƒçŽ ć’Œć±Źæ€§ïŒŒäžæ‡‰è©ČćˆȘé™€ćźƒă€‚ + +
    + +❌ **ćŠć‰‡ïŒš** ć‡èš­äœ æƒłèŠæžŹè©Šäž€ć€‹è·šè¶Šèš±ć€šç”„ä»¶ă€é‚èŒŻć’Œæœć‹™çš„ç™»ć…„ćŠŸèƒœïŒŒäž€ćˆ‡éƒœèš­çœźćŸ—ćŸˆćźŒçŸŽ - stub、spy、Ajax çš„ć‘Œć«éƒœæ˜Żéš”é›ąçš„ă€‚çœ‹äŒŒäž€ćˆ‡éƒœćŸˆćźŒçŸŽïŒŒäœ†ć»ç™ŒçŸæžŹè©Šć€±æ•—äș†ïŒŒć› ç‚șé–‹ç™Œè€…ć°‡ div 的 class ćŸž `thick-border` æ”čç‚ș `thin-border`。 + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :clap: æ­ŁäŸ‹ïŒš äœżç”šć°ˆç”šçš„ attribute äŸ†æŸ„è©ąć…ƒçŽ äŸ†é€ČèĄŒæžŹè©Š + +![](https://img.shields.io/badge/🔧%20Example%20using%20React-blue.svg "Examples with React") + +```html +// the markup code (part of React component) +

    + + {value} + + +

    +``` + +```javascript +// this example is using react-testing-library +test("Whenever no data is passed to metric, show 0 as default", () => { + // Arrange + const metricValue = undefined; + + // Act + const { getByTestId } = render(); + + expect(getByTestId("errorsLabel").text()).toBe("0"); +}); +``` + +
    + +### :thumbsdown: ćäŸ‹ïŒš 䟝靠斌 CSS attributes + +```html + +{value} + +``` + +```javascript +// this exammple is using enzyme +test("Whenever no data is passed, error metric shows zero", () => { + // ... + + expect(wrapper.find("[className='d-flex-column']").text()).toBe("0"); +}); +``` + +
    + +
    + +## âšȘ  3.3 ćŠ‚æžœćŻä»„ïŒŒäœżç”šçœŸćŻŠäž”ćźŒć…šæžČ染的甄件䟆é€ČèĄŒæžŹè©Š + +:white_check_mark: **ć»ș議** ćȘ芁ć°șćŻžćˆé©ïŒŒćƒäœżç”šè€…é‚ŁæšŁćŸžć€–éƒšæžŹè©Šäœ çš„ç”„ä»¶ïŒŒćźŒć…šæžČ染 UIïŒŒć°ć…¶é€ČèĄŒæ“äœœïŒŒäžŠæ–·èš€ć°é‚Łäș› UI çš„èĄŒç‚șæ˜ŻćŠçŹŠćˆé æœŸă€‚éżć…ć„çšź mock、partial 撌 shallow rendering - é€™æšŁćšćŻèƒœæœƒć› ç‚șçŒșäčçŽ°çŻ€è€Œć°Žè‡Žæœ‰æœȘæ•æ‰ćˆ°çš„ bugïŒŒè€Œäž”ç”±æ–ŒæžŹè©Šæœƒæ“Ÿäș‚ć…§éƒšçš„ç”æ§‹è€ŒäœżćŸ—ç¶­è­·èźŠćŸ—æ›ŽćŠ ć›°é›Ł (ćƒè€ƒ ć …æŒé»‘çź±æžŹè©Š)ă€‚ćŠ‚æžœć…¶äž­äž€ć€‹ć­ç”„ä»¶æ˜ŽéĄŻæ‹–æ…ąé€ŸćșŠ (橂 ć‹•ç•«) æˆ–ćŸˆé›ŁćŽ»èš­ćźšïŒŒćŻä»„è€ƒæ…źäœżç”šć‡çš„ç”„ä»¶ćŽ»æ›żæ›ćźƒă€‚ + +綜䞊所èȘȘïŒŒéœ€èŠæłšæ„çš„æ˜ŻïŒšé€™çšźæŠ€èĄ“é©ç”šæ–ŒćŒ…ć«ćˆç†ć€§ć°ć­ç”„ä»¶çš„äž­ć°ćž‹ç”„ä»¶ă€‚ćźŒć…šæžČæŸ“äž€ć€‹æœ‰ć€Șć€šć­ç”„ä»¶çš„ç”„ä»¶æœƒèź“ä»–ćŸˆé›Łèą«çœ‹ć‡șć€±æ•—çš„ćŽŸć›  (root cause analysis)ïŒŒè€Œäž”ćŻèƒœæœƒéžćžžæ…ąă€‚ćœšé€™çšźæƒ…æłäž‹ïŒŒćŻä»„ć°é‚Łäș›ćŸˆè‚„çš„çˆ¶ç”„ä»¶æ’°ćŻ«ć°‘é‡çš„æžŹè©ŠïŒŒäžŠć°ć…¶ć­ç”„ä»¶ć€šćŻ«ćčŸć€‹æžŹè©Šă€‚ + +
    + +❌ **ćŠć‰‡ïŒš** ć‘Œć«ç”„ä»¶çš„ç§æœ‰æ–čæł•äŸ†æžŹè©Šç”„ä»¶çš„ć…§éƒšç‹€æ…‹ă€‚ćŸŒçșŒé‡æ§‹ç”„ä»¶æ™‚äœ ćż…é ˆé‡æ§‹æ‰€æœ‰æžŹè©Šă€‚äœ çœŸçš„æœ‰èƒœćŠ›é€ČèĄŒé€™çšźçš‹ćșŠçš„ç¶­è­·ć—ŽïŒŸ + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :clap: æ­ŁäŸ‹ïŒš æ“äœœäž€ć€‹ć……ćˆ†æžČæŸ“çš„çœŸćŻŠç”„ä»¶ + +![](https://img.shields.io/badge/🔧%20Example%20using%20React-blue.svg "Examples with React") ![](https://img.shields.io/badge/🔧%20Example%20using%20Enzyme-blue.svg "Examples with Enzyme") + +```javascript +class Calendar extends React.Component { + static defaultProps = { showFilters: false }; + + render() { + return ( +
    + A filters panel with a button to hide/show filters + +
    + ); + } +} + +// Examples use React & Enzyme +test("Realistic approach: When clicked to show filters, filters are displayed", () => { + // Arrange + const wrapper = mount(); + + // Act + wrapper.find("button").simulate("click"); + + // Assert + expect(wrapper.text().includes("Choose Filter")); + // This is how the user will approach this element: by text +}); +``` + +### :thumbsdown: ćäŸ‹ïŒš äœżç”š shallow rendering äŸ†æžŹè©Š + +```javascript +test("Shallow/mocked approach: When clicked to show filters, filters are displayed", () => { + // Arrange + const wrapper = shallow(); + + // Act + wrapper + .find("filtersPanel") + .instance() + .showFilters(); + // Tap into the internals, bypass the UI and invoke a method. White-box approach + + // Assert + expect(wrapper.find("Filter").props()).toEqual({ title: "Choose Filter" }); + // what if we change the prop name or don't pass anything relevant? +}); +``` + +
    + +
    + +## âšȘ  3.4 䞍芁 sleepïŒŒć–„ç”šæĄ†æž¶ć…§ć»șć°éžćŒæ­„äș‹ä»¶çš„æ”ŻæŽïŒŒäžŠè©Šè‘—ćŠ é€Ÿä»– + +:white_check_mark: **ć»ș議** ćœšèš±ć€šæƒ…æłäž‹ïŒŒèą«æžŹè©Šć–źć…ƒçš„ćźŒæˆæ™‚é–“æ˜ŻæœȘ矄的 (äŸ‹ćŠ‚ïŒŒć› ç‚șć‹•ç•«è€Œć»¶éČäș†ć…ƒä»¶çš„ć‡ș珟) — ćœšé€™çšźæƒ…æłäž‹ïŒŒäžèŠ sleep (äŸ‹ćŠ‚äœżç”š setTimeout)ïŒŒè€Œæ˜Żäœżç”šć€§ć€šæ•žæĄ†æž¶æäŸ›çš„æ›Žé è­œçš„æ–čæł•。䞀äș›ć‡œç€șćș«ć…èš±ç­‰ćŸ…操䜜 (äŸ‹ćŠ‚ [Cypress .request('url')](https://docs.cypress.io/guides/references/best-practices.html#Unnecessary-Waiting))ïŒŒćŠäž€äș›ć‡œç€șćș«æäŸ›ç”šæ–Œç­‰ćŸ…çš„ APIïŒŒćŠ‚ [@testing-library/dom 的æ–čæł• wait(expect(element))](https://testing-library.com/docs/guide-disappearance)ă€‚æœ‰æ™‚ćŸŒïŒŒæ›Žć„Ș雅的æ–čæł•æ˜Ż stub 那äș›æŻ”èŒƒæ…ąçš„èł‡æșïŒŒćƒæ˜Ż APIïŒŒç„¶ćŸŒäž€æ—ŠéŸżæ‡‰æ™‚é–“èźŠćŸ—çąșćźšïŒŒç”„ä»¶ć°±ćŻä»„éĄŻćŒćœ°é‡æ–°æžČæŸ“ă€‚ç•¶äŸèłŽäž€äș› sleep çš„ć€–éƒšç”„ä»¶æ™‚ïŒŒ[ćŠ ćż«æ™‚é˜çš„é€ŸćșŠ](https://jestjs.io/docs/en/timer-mocks)或蚱胜提䟛ćč«ćŠ©ă€‚ sleep æ˜Żäž€çšźéœ€èŠéżć…çš„æšĄćŒïŒŒć› ç‚șćźƒæœƒć°Žè‡Žäœ çš„æžŹè©ŠèźŠćŸ—ç·©æ…ąæˆ–æœ‰éąšéšȘ(ćŠ‚æžœç­‰ćŸ…çš„æ™‚é–“ć€Ș短)。當 sleep 撌èŒȘè©ąäžćŻéżć…äž”æžŹè©ŠæĄ†æž¶ćŽŸç”Ÿäžæ”ŻæŒæ™‚ïŒŒäž€äș› npm çš„ć‡œç€șćș« (橂 [wait-for-expect](https://www.npmjs.com/package/wait-for-expect)) ćŻä»„ćč«ćŠ©è§Łæ±ș捊çąșćźšæ€§ć•éĄŒă€‚ + +
    + +❌ **ćŠć‰‡ïŒš** 當 sleep 的時間ć€Șé•·æ™‚ïŒŒæžŹè©Šé€ŸćșŠæœƒæ…ąäžŠäž€ć€‹æ•žé‡çŽšă€‚ç•¶ć˜—è©ŠçžźçŸ­ sleep æ™‚é–“æ™‚ïŒŒćŠ‚æžœèą«æžŹè©Šçš„ć–źć…ƒæČ’æœ‰ćŠæ™‚éŸżæ‡‰ïŒŒæžŹè©Šć°‡æœƒć€±æ•—ă€‚é€™æ™‚äœ äžćŸ—äžćœšè„†ćŒ±çš„æžŹè©Šć’ŒçłŸçł•çš„æ€§èƒœäč‹é–“é€ČèĄŒæŹŠèĄĄă€‚ + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :clap: æ­ŁäŸ‹ïŒš E2E API ćœšéžćŒæ­„çš„è™•ç†ćźŒćŸŒ resolves (Cypress) + +![](https://img.shields.io/badge/🔹%20Example%20using%20Cypress-blue.svg "Using Cypress to illustrate the idea") +![](https://img.shields.io/badge/🔧%20Example%20using%20React%20Testing%20Library-blue.svg "Examples with react-testing-library") + +```javascript +// using Cypress +cy.get("#show-products").click(); // navigate +cy.wait("@products"); // wait for route to appear +// this line will get executed only when the route is ready +``` + +### :clap: æ­ŁäŸ‹ïŒšæžŹè©Šçš„ć‡œç€șćș«ç­‰ćŸ… DOM 慃箠 + +```javascript +// @testing-library/dom +test("movie title appears", async () => { + // element is initially not present... + + // wait for appearance + await wait(() => { + expect(getByText("the lion king")).toBeInTheDocument(); + }); + + // wait for appearance and return the element + const movie = await waitForElement(() => getByText("the lion king")); +}); +``` + +### :thumbsdown: ćäŸ‹ïŒš è‡ȘèŁœçš„ sleep çš‹ćŒ + +```javascript +test("movie title appears", async () => { + // element is initially not present... + + // custom wait logic (caution: simplistic, no timeout) + const interval = setInterval(() => { + const found = getByText("the lion king"); + if (found) { + clearInterval(interval); + expect(getByText("the lion king")).toBeInTheDocument(); + } + }, 100); + + // wait for appearance and return the element + const movie = await waitForElement(() => getByText("the lion king")); +}); +``` + +
    + +
    + +## âšȘ  3.5 è§€ćŻŸèł‡æșç¶“ç”±ç¶Čè·Żèą«æäŸ›çš„æƒ…æł + +![](https://img.shields.io/badge/🔧%20Example%20using%20Google%20LightHouse-blue.svg "Examples with Lighthouse") + +✅ **ć»ș議** äœżç”šäž€äș›æŽ»ć‹•ç›ŁèŠ–ć™šïŒŒä»„çąș保朹真毩ç¶Čè·Żäž‹çš„é éąèŒ‰ć…„æƒ…æłæ˜Żæœ€äœłçš„ — é€™ćŒ…ć«äž€äș›äœżç”šè€…é«”é©—çš„ć•éĄŒïŒšćƒæ˜Żç·©æ…ąçš„é éąèŒ‰ć…„時間或æœȘç¶“ćŁ“çžźçš„èł‡æșă€‚ćž‚éąäžŠæœ‰ćŸˆè±ćŻŒçš„æȘ࿟„ć·„ć…·ïŒšćƒ [pingdom](https://www.pingdom.com/)、AWS CloudWatch、[GCP StackDriver](https://cloud.google.com/monitoring/uptime-checks/) 這äș›ć·„ć…·ćŻä»„ćŸˆćźčæ˜“ćœ°ç›ŁèŠ–äŒșæœć™šæ˜ŻćŠæ­Łćžžé‹äœœè‘—ïŒŒæ˜ŻćŠæœ‰ćœšćˆç†çš„ SLA äž‹ć›žæ‡‰ă€‚äžéŽé€™ćȘè§Łæ±șäș†èĄšéąäžŠçš„ć•éĄŒïŒŒæœ€ć„œéžæ“‡ć‰ç«Żć°ˆç”šçš„ć·„ć…· (橂 [lighthouse](https://developers.google.com/web/tools/lighthouse/)、[pagespeed](https://developers.google.com/speed/pagespeed/insights/)) 䟆é€ČèĄŒæ›Žć…šéąçš„ćˆ†æžă€‚äžŠèšç„Šćœšé‚Łäș›ç›ŽæŽ„ćœ±éŸżäœżç”šè€…é«”é©—çš„æŒ‡æš™äžŠïŒŒćƒæ˜Żé éąèŒ‰ć…„æ™‚é–“ă€[有意矩的çčȘèŁœ](https://scotch.io/courses/10-web-performance-audit-tips-for-your-next-billion-users-in-2018/fmp-first-meaningful-paint)、[頁靱揯äș’ć‹•æ™‚é–“(TTI)](https://calibreapp.com/blog/time-to-interactive/)ă€‚æ›Žé‡èŠçš„æ˜ŻïŒŒé‚„ćŻä»„é—œæłšć…¶ä»–ćŽŸć› ïŒŒćƒæ˜Żçąș保慧ćźčæœ‰èą«ćŁ“çžźă€[第侀怋 byte 的時間](https://developer.mozilla.org/en-US/docs/Glossary/time_to_first_byte)ă€ćœ–ç‰‡çš„æœ€äœłćŒ–ă€äžŠçąș保搈理的 DOM ć°ș毾、SSL æˆ–ć…¶ä»–ă€‚ć»șè­°ćœšé–‹ç™ŒæœŸé–“ć°‡é€™äș›ç›ŁèŠ–ć™šçŽć…„ CI çš„äž€éƒšćˆ†ïŒŒä»„ćŠæ›Žé‡èŠçš„ïŒŒćœš 24x7 的 production äŒșæœć™š/ CDN äžŠäœżç”šćźƒć€‘ă€‚ + +
    + +❌ **ćŠć‰‡ïŒš** 蚭蚈äș†äž€ć€‹çČŸçŸŽçš„ UI、侔通過äș† 100% çš„ćŠŸèƒœæžŹè©Šèˆ‡çČŸćżƒçš„ćŒ…èŁïŒŒäœżç”šè€…é«”é©—ć»ć› ç‚ș CDN 的錯èȘ€èš­ćźšè€ŒèźŠćŸ—çłŸçł•ćŠç·©æ…ąă€‚ + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +### :clap: æ­ŁäŸ‹ïŒšLighthouse çš„é éąèŒ‰ć…„æȘąæžŹć ±ć‘Š + +![](/assets/lighthouse2.png "Lighthouse page load inspection report") + +
    + +
    + +## âšȘ  3.6 stub 那äș›äžç©©ćꚿˆ–ç·©æ…ąçš„èł‡æșćŠ‚ćŸŒç«Ż API + +:white_check_mark: **ć»ș議** ç•¶æ’°ćŻ«äœ äž»èŠçš„æžŹè©Š (äžæ˜Ż E2E æžŹè©Š) æ™‚ïŒŒéżć…æŽ„è§žä»»äœ•è¶…ć‡ș䜠職èČŹć’ŒæŽ§ćˆ¶çŻ„ćœçš„èł‡æșïŒŒćŠ‚ćŸŒç«Ż APIïŒŒè€Œæ˜Żäœżç”š stub (æžŹè©Šæ›żèș«)ă€‚äœżç”šäž€äș›æžŹè©Šæ›żèș«çš„ć‡œćŒćș« (橂 [Sinon](https://sinonjs.org/)、[Test doubles](https://www.npmjs.com/package/testdouble) 等) 䟆 stub API çš„ć›žæ‡‰ïŒŒè€Œäžæ˜ŻçœŸæ­Łçš„ć° API é€ČèĄŒć‘Œć«ă€‚æœ€ć€§çš„ć„œè™•æ˜Żé˜Čæ­ąć‡ș珟故障 — æžŹè©Šæˆ– API çš„ćźšçŸ©ćžžćžžćœšèźŠć‹•çš„æ™‚ć€™ïŒŒć„˜çźĄç”„ä»¶çš„èĄšçŸæ­Łçąș (ç”Ÿç”ąç’°ćąƒäžé©ćˆé€ČèĄŒæžŹè©ŠïŒŒćźƒé€šćžžć° API çš„ć‘Œć«é€ČèĄŒé™ćˆ¶)ïŒŒäœ†æœ‰æ™‚æœƒć‘Œć«ć€±æ•—ă€‚é€šéŽ stub äŸ†æšĄæ“Źć„çšź API èĄŒç‚șïŒŒæŻ”ćŠ‚ç•¶æČ’æœ‰æ‰Ÿćˆ°èł‡æ–™æˆ– API 拋ć‡ș錯èȘ€æ™‚æžŹè©Šç”„ä»¶èĄŒç‚șă€‚æœ€ćŸŒäœ†äžŠéžæœ€äžé‡èŠçš„ćŽŸć› æ˜ŻïŒŒç¶“éŽç¶Čç”Ąçš„ć‘Œć«ć°‡æœƒć€§ć€§é™äœŽćŸ·èĄŒæžŹè©Šçš„é€ŸćșŠă€‚ + +
    + +❌ **ćŠć‰‡ïŒš** ćčłć‡ćŸ·èĄŒæžŹè©Šçš„æ™‚é–“äžć†ćȘæ˜ŻćčŸæŻ«ç§’è€Œć·ČïŒŒäž€ć€‹æ™źé€šçš„ API ć‘Œć«è‡łć°‘éœ€èŠ 100 æŻ«ç§’ïŒŒé€™æœƒèź“äœ çš„æžŹè©Šæ…ą 20 ć€ä»„äžŠă€‚ + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :clap: æ­ŁäŸ‹ïŒš Stub 或攔æˆȘ API çš„ć‘Œć« + +![](https://img.shields.io/badge/🔧%20Example%20using%20React-blue.svg "Examples with React") ![](https://img.shields.io/badge/🔧%20Example%20using%20React%20Testing%20Library-blue.svg "Examples with react-testing-library") + +```javascript +// unit under test +export default function ProductsList() { + const [products, setProducts] = useState(false); + + const fetchProducts = async () => { + const products = await axios.get("api/products"); + setProducts(products); + }; + + useEffect(() => { + fetchProducts(); + }, []); + + return products ?
    {products}
    :
    No products
    ; +} + +// test +test("When no products exist, show the appropriate message", () => { + // Arrange + nock("api") + .get(`/products`) + .reply(404); + + // Act + const { getByTestId } = render(); + + // Assert + expect(getByTestId("no-products-message")).toBeTruthy(); +}); +``` + +
    + +
    + +## âšȘ  3.7 毫ćčŸć€‹è·šè¶Šæ•Žć€‹çł»ç”±çš„ E2E æžŹè©Š + +:white_check_mark: **ć»ș議** 雖然 E2E (end to endïŒŒç«Żćˆ°ç«Ż) é€šćžžä»ŁèĄšćœšçœŸćŻŠç€èŠœć™šäž­é€ČèĄŒ UI æžŹè©Š (ćƒè€ƒ 3.6 節)某äș›æƒ…æłäž‹ïŒŒćźƒć€‘èĄšç€șèŠ†è“‹æ•Žć€‹çł»ç”±çš„æžŹè©ŠïŒŒćŒ…æ‹Źé€ŁæŽ„çœŸæ­Łçš„ćŸŒç«Żă€‚ćŸŒè€…çš„æžŹè©Šéžćžžæœ‰ćƒčć€ŒïŒŒć› ç‚șćźƒć€‘æ¶”è“‹é‚Łäș›ć‰ç«Żć’ŒćŸŒç«Żäč‹é–“æ•Žćˆçš„ć•éĄŒïŒŒé€™äș›ć•éĄŒćŻèƒœæ˜Żç”±æ–Œæșé€šäžŠïŒŒschema 由生èȘ€æœƒæ‰€ć°Žè‡Žă€‚ćźƒć€‘ä蟿˜Żäž€çšźæœ‰æ•ˆçš„æ–čæł•䟆癌珟 backend-to-backend çš„æ•Žćˆć•éĄŒ (äŸ‹ćŠ‚ćŸźæœć‹™ A 氇錯èȘ€çš„èšŠæŻç™Œé€ç”ŠćŸźæœć‹™ B) ç”šè‡łćŻä»„æȘąæžŹć‡ș郚çœČ侊的錯èȘ€ïŒŒç›źć‰ćŸŒç«ŻæČ’æœ‰ćƒć‰ç«Ż UI æžŹè©Šć·„ć…·ćŠ‚ [Cypress](https://www.cypress.io/) 或 [Puppeteer](https://github.com/GoogleChrome/puppeteer) äž€æšŁć‹ć–„äž”æˆç†Ÿçš„ E2E æĄ†æž¶ă€‚é€™çšźæžŹè©Šçš„çŒșé»žæ˜ŻïŒŒèš­ćźšæ¶”è“‹é€™éșŒć€šç”„ä»¶çš„ç’°ćąƒçš„æˆæœŹćŸˆé«˜ïŒŒè€Œäž”ć€§ć€šæ•žç”„ä»¶éƒœćŸˆè„†ćŒ± — ć‡èš­æœ‰ 50 ć€‹ćŸźæœć‹™ïŒŒćȘèŠć…¶äž­äž€ć€‹æ­»æŽ‰ïŒŒæ•Žć€‹ E2E ć°±æœƒć€±æ•—ă€‚ćŸșæ–Œé€™ć€‹ćŽŸć› ïŒŒæˆ‘ć€‘æ‡‰è©Čć°‘ç”šé€™çšźæŠ€èĄ“ïŒŒć€§æŠ‚ 1-10 怋氱怠äș†ă€‚äčŸć°±æ˜ŻèȘȘïŒŒćłäœżæ˜Żć°‘é‡çš„ E2E æžŹè©Šä蟿œ‰æ©Ÿæœƒæ•çČ漃怑 — 郚çœČæˆ–æ•Žćˆçš„ć•éĄŒă€‚ć»șè­°ćœšèˆ‡ç”Ÿç”ąç’°ćąƒç›žäŒŒçš„ stage é‹èĄŒćźƒć€‘ă€‚ + +
    + +❌ **ćŠć‰‡ïŒš** UI ćŻèƒœćœšćŠŸèƒœæžŹè©ŠäžŠèŠ±èČ»äș†ć€§é‡çš„çČŸćŠ›ïŒŒäœ†æœ€ćŸŒæ‰ç™ŒçŸćŸŒç«Żć›žć‚łçš„ć…§ćźč (UI èŠäœżç”šçš„èł‡æ–™æ ŒćŒ) èˆ‡é æœŸäž­çš„äžäž€æšŁă€‚ + +
    + +## âšȘ  3.8 è—‰ç”±é‡è€‡äœżç”šç™»ć…„æ†‘è­‰äŸ†ćŠ é€Ÿ E2E æžŹè©Š + +white_check_mark: **ć»ș議** ćœšæ¶‰ćŠçœŸćŻŠçš„ćŸŒç«ŻäžŠćż…é ˆäœżç”šæœ‰æ•ˆçš„äœżç”šè€… token é€ČèĄŒ API ć‘Œć«çš„ E2E æžŹè©Šäž­ïŒŒæˆ‘ć€‘æČ’æœ‰ćż…èŠć°‡æŻć€‹æžŹè©ŠéƒœćŸžă€Œæ–°ćąžäœżç”šè€…äžŠç™»éŒ„ă€é–‹ć§‹ă€‚ç›žćçš„ïŒŒćœšæžŹè©ŠćŸ·èĄŒé–‹ć§‹äč‹ć‰ćȘç™»éŒ„äž€æŹĄ (äœżç”š before-all hook)ïŒŒć°‡ token ć„Čć­˜ćœšæœŹćœ°ç«Żäž­ïŒŒäžŠćœšæŻć€‹ request äč‹é–“é‡è€‡äœżç”šćźƒă€‚é›–ç„¶é€™äŒŒäčŽé•揍äș†æžŹè©Šçš„æ žćżƒćŽŸć‰‡äč‹äž€ — äżæŒæžŹè©Šçš„çšç«‹æ€§ïŒŒäžèŠè€Šćˆèł‡æșă€‚é€™æ˜Żäž€ć€‹ćˆç†çš„æ“”æ†‚ïŒŒäœ†ćœš E2E æžŹè©Šäž­ïŒŒćŸ·èĄŒæžŹè©Šçš„æ€§èƒœæ˜Żäž€ć€‹é—œé”ć•éĄŒïŒŒćœšćŸ·èĄŒæŻć€‹æžŹè©ŠæĄˆäŸ‹äč‹ć‰ć‘Œć« 1-3 怋 API ćŻèƒœæœƒć€§ć€§ćąžćŠ ćŸ·èĄŒæ™‚é–“ă€‚é‡è€‡äœżç”šæ†‘è­‰äžŠäžæ„ć‘łè‘—æžŹè©Šćż…é ˆćŸșæ–Œç›žćŒçš„äœżç”šè€…èł‡æ–™ — ćŠ‚æžœç›žäŸæ–Œäœżç”šè€…èł‡æ–™ (äŸ‹ćŠ‚æžŹè©Šäœżç”šè€…ä»˜æŹŸçš„æ­·ćČ蚘錄)ïŒŒé‚ŁéșŒèЁçąș保由生這äș›èł‡æ–™äŸ†äœœç‚șæžŹè©Šçš„äž€éƒšćˆ†ïŒŒäžŠéżć…èˆ‡ć…¶ä»–æžŹè©Šć…±äș«ćźƒć€‘ă€‚é‚„èŠèš˜äœïŒŒćŸŒç«Żæ˜ŻćŻä»„ fake 的 — ćŠ‚æžœäœ çš„é‡é»žæ˜ŻæžŹè©Šć‰ç«ŻïŒŒé‚ŁéșŒæœ€ć„œéš”é›ąćźƒïŒŒç„¶ćŸŒ stub ćŸŒç«Ż API (ćƒè€ƒ 3.6 節)。 + +
    + +❌ **ćŠć‰‡ïŒš** 甊柚 200 ć€‹æžŹè©ŠæĄˆäŸ‹ïŒŒć‡èš­ç™»éŒ„éœ€èŠèŠ±èČ»çš„æ™‚é–“ç‚ș 100msïŒŒć‰‡è‡łć°‘éœ€èŠèŠ±èČ» 20sïŒŒćœšé€™äž€ééçš„ç™»éŒ„äžŠă€‚ + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :clap: æ­ŁäŸ‹ïŒš 朹 before-all äž­ç™»éŒ„ïŒŒè€Œäžæ˜Ż before-each + +![](https://img.shields.io/badge/🔹%20Example%20using%20Cypress-blue.svg "Using Cypress to illustrate the idea") + +```javascript +let authenticationToken; + +// happens before ALL tests run +before(() => { + cy.request('POST', 'http://localhost:3000/login', { + username: Cypress.env('username'), + password: Cypress.env('password'), + }) + .its('body') + .then((responseFromLogin) => { + authenticationToken = responseFromLogin.token; + }) +}) + +// happens before EACH test +beforeEach(setUser => () { + cy.visit('/home', { + onBeforeLoad (win) { + win.localStorage.setItem('token', JSON.stringify(authenticationToken)) + }, + }) +}) + +``` + +
    + +
    + +## âšȘ  3.9 ćŻ«äž€ć€‹è”°éŽæ•Žć€‹ç¶Č站的 E2E ć†’ç…™æžŹè©Š + +:white_check_mark: **ć»ș議** ç‚șäș† production ç’°ćąƒçš„ç›ŁæŽ§ćŠé–‹ç™Œæ™‚æœŸçš„ćźŒæ•Žæ€§æȘ࿟„ïŒŒćŸ·èĄŒäž€ć€‹ E2E æžŹè©ŠïŒŒèź“é€™ć€‹æžŹè©Šè”°èšȘéŽæ‰€æœ‰æˆ–ć€§ć€šæ•žçš„ç¶Čç«™é éąïŒŒäžŠçąș保那äș›é éąæČ’æœ‰ææŻ€ă€‚é€™éĄžćž‹çš„æžŹè©ŠæŠ•èł‡ć ±é…ŹçŽ‡ćŸˆé«˜ïŒŒć› ç‚șä»–ćŸˆćźčæ˜“ćŽ»æ’°ćŻ«ćŠç¶­è­·ïŒŒć»ćŻä»„æȘąæžŹć‡șć„çšźéĄžćž‹çš„æ•…éšœïŒŒćŒ…æ‹ŹćŠŸèƒœæ€§ă€ç¶Čè·Żæˆ–äœˆć±Źçš„ć•éĄŒă€‚ć…¶ä»–éĄžćž‹çš„ć†’ç…™æžŹè©Šæˆ–ćźŒæ•Žæ€§æȘ࿟„䞊æČ’æœ‰é‚ŁéșŒćŻé ćŠè©łç›Ą - 有äș› ops 㜘隊ćȘæ˜Ż ping ç¶Č站銖頁 (朹production環汃)或開癌äșșć“ĄćŸ·èĄŒäș†äž€äș›æ•ŽćˆæžŹè©ŠïŒŒć»æČ’ç™ŒçŸćˆ°æ‰“ćŒ…æˆ–ç€èŠœć™šçš„ć•éĄŒă€‚æŻ«ç„Ąç–‘ć•çš„ïŒŒć†’ç…™æžŹè©ŠäžŠäžæœƒć–ä»ŁćŠŸèƒœæžŹè©ŠïŒŒè€ŒćȘæ˜Żäœœç‚șäž€ć€‹ćż«é€Ÿçš„ç…™éœ§ć”æžŹć™šă€‚ + +
    + +❌ **ćŠć‰‡ïŒš** äž€ćˆ‡çœ‹äŒŒćŸˆćźŒçŸŽïŒŒæ‰€æœ‰çš„æžŹè©Šéƒœé€šéŽäș†ïŒŒćœš production ç’°ćąƒçš„ć„ćș·ç‹€æ…‹æȘ࿟„ä蟿˜Ż ok 的䜆 Payment é€™ć€‹ç”„ä»¶æœ‰äž€äș›æ‰“ćŒ…çš„ć•éĄŒïŒŒć°Žè‡Ž /Paymout é€™ć€‹è·ŻćŸ‘æČ’有è૿žČæŸ“ă€‚ + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :clap: æ­ŁäŸ‹ïŒšäž€ć€‹è·‘éŽæ‰€æœ‰é éąçš„ć†’ç…™æžŹè©Š + +![](https://img.shields.io/badge/🔹%20Example%20using%20Cypress-blue.svg "Using Cypress to illustrate the idea") + +```javascript +it("When doing smoke testing over all page, should load them all successfully", () => { + // exemplified using Cypress but can be implemented easily + // using any E2E suite + cy.visit("https://mysite.com/home"); + cy.contains("Home"); + cy.visit("https://mysite.com/Login"); + cy.contains("Login"); + cy.visit("https://mysite.com/About"); + cy.contains("About"); +}); +``` + +
    + +
    + +## âšȘ  3.10 ć°‡æžŹè©Šäœœç‚șäž€ć€‹æŽ»çš„ć”äœœæ–‡ä»¶äŸ†çœ‹ćŸ… + +:white_check_mark: **ć»ș議** 陀äș†æć‡æ‡‰ç”šçš‹ćŒçš„ćŻé æ€§ïŒŒæžŹè©Šé‚„æœ‰äž€ć€‹éžćžžèȘ˜äșș的應甚 - 䜜ç‚șæŽ»çš„çš‹ćŒæ–‡ä»¶ă€‚ç”±æ–ŒæžŹè©Šçš‹ćŒæœŹèłȘäžŠäœżç”šçš„æ˜Żäž€çšźæŠ€èĄ“ć«é‡èŒƒäœŽçš„ç”ąć“/ UX èȘžèš€ïŒŒć› æ­€äœżç”šæ­Łçąșçš„ć·„ć…·ćŻä»„ć°‡ä»–ć€‘èœ‰æ›æˆäž€çšźæ˜“æ–Œæșé€šçš„ćȘ’介æ–čäŸżé–‹ç™Œäșșć“Ąèˆ‡ä»–ć€‘çš„äœżç”šè€…é€ČèĄŒć”èȘżă€‚舉䟋䟆èȘȘ有䞀äș›æĄ†æž¶ćŻä»„äœżç”šäșșéĄžćŻé–±èź€çš„èȘžèš€äŸ†èĄšé”攁皋與期望 (ćŠ‚ïŒŒæžŹè©Šèšˆç•«)ïŒŒé€™æšŁäž€äŸ†ïŒŒæ‰€æœ‰ç›žé—œäșșć“ĄćŒ…æ‹Źç”ąć“ç¶“ç†ïŒŒéƒœćŻä»„ć°æžŹè©Šé€ČèĄŒé–±èź€ă€æ‰čć‡†ä»„ćŠć”äœœïŒŒćŠ‚æ­€äž€äŸ†ïŒŽé€™ć€‹æžŹè©Šć°±æˆäș†æŽ»çš„éœ€æ±‚æ–‡ä»¶ă€‚é€™æšŁçš„æŠ€èĄ“äčŸèą«çš±äœœ "é©—æ”¶æžŹè©Š"ïŒŒć› ç‚șćźƒćŻä»„èź“äœżç”šè€…ç”šç°Ąć–źçš„èȘžèš€ćźšçŸ©é©—æ”¶æš™æș–ă€‚é€™æ˜Żæœ€çŽ”çČč的 [BDD (èĄŒç‚șé©…ć‹•æžŹè©Š)](https://en.wikipedia.org/wiki/Behavior-driven_development)ïŒŒć…¶äž­äž€ć€‹æ”ŻæŽé€™ć€‹ćŠŸèƒœçš„æĄ†æž¶æ˜Ż [Cucumber](https://github.com/cucumber/cucumber-js)ïŒŒćŻä»„ćƒè€ƒäž‹éąçš„çš‹ćŒçŻ„äŸ‹ă€‚ćŠäž€ć€‹ç›žäŒŒäœ†äžćŒæ‡‰ç”šæƒ…ćąƒçš„æ˜Ż [StoryBook](https://storybook.js.org/)ïŒŒćźƒćŻä»„æŠŠ UI çš„ç”„ä»¶ćŒ„æˆćœ–ćœąćŒ–çš„ç›źéŒ„ïŒŒèź“äœżç”šè€…ćŻä»„ç€èŠœæŻć€‹ç”„ä»¶çš„ć„çšźç‹€æ…‹ (äŸ‹ćŠ‚äž€ć€‹ grid w/o filterïŒŒèź“ä»–ç•«ć‡ș怚怋 row 或æČ’有 row 等。)ïŒŒçœ‹ä»–é•·ćŸ—æ€ŽæšŁä»„ćŠćŠ‚äœ•ćŽ»è§žç™Œä»–çš„äžćŒç‹€æ…‹ - 這äčŸćŻä»„æäŸ›ç”Šç”ąć“ç›žé—œäșșć“ĄïŒŒäœ†äž»èŠæ˜Żäœœç‚șæŽ»çš„æ–‡ä»¶ç”Šäœżç”šé€™äș›ç”„ć»șçš„é–‹ç™Œè€…ć€‘ă€‚ + +❌ **ćŠć‰‡ïŒš** ćœšæžŹè©ŠäžŠć·Č經耗èČ»äș†ć€§é‡çš„èł‡æșïŒŒćŠ‚æžœäžć„œć„œćˆ©ç”šé€™é …æŠ•èł‡äŸ†çČć–æ›Žć€§çš„ćƒčć€ŒïŒŒéžćžžćŻæƒœă€‚ + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :clap: æ­ŁäŸ‹ïŒšćˆ©ç”š cucumber-js 仄äșșéĄžćŻé–±èź€çš„èȘžèš€äŸ†æèż°æžŹè©Š + +![](https://img.shields.io/badge/🔹%20Example%20using%20Cucumber-blue.svg "Examples using Cucumber") + +```javascript +// this is how one can describe tests using cucumber: plain language that allows anyone to understand and collaborate + +Feature: Twitter new tweet + + I want to tweet something in Twitter + + @focus + Scenario: Tweeting from the home page + Given I open Twitter home + Given I click on "New tweet" button + Given I type "Hello followers!" in the textbox + Given I click on "Submit" button + Then I see message "Tweet saved" + +``` + +### :clap: æ­ŁäŸ‹ïŒšćˆ©ç”š Storybook äŸ†ć±•ç€șç”„ä»¶çš„çš„äžćŒç‹€æ…‹ćŠèŒžć…„ + +![](https://img.shields.io/badge/🔹%20Example%20using%20StoryBook-blue.svg "Using StoryBook") + +![alt text](assets/story-book.jpg "Storybook") + +
    + +

    + +## âšȘ  3.11 äœżç”šè‡Șć‹•ćŒ–ć·„äœœäŸ†ć”æžŹèŠ–èŠș敏題 + +:white_check_mark: **ć»ș議** èš­ćźšè‡Șć‹•ćŒ–ć·„ć…·ïŒŒćœšć‡șçŸèźŠćŒ–çš„æ™‚ć€™æ“·ć– UI ç•«éąïŒŒäžŠæȘąæžŹæ˜ŻćŠæœ‰ć…§ćźčé‡ç–Šæˆ–ç Žćœ–ç­‰ć•éĄŒă€‚é€™æšŁćšäžćƒ…ćŻä»„çąșäżèł‡æ–™çš„æ­Łçąșæ€§ïŒŒäœżç”šè€…äčŸćŻä»„ćŸˆæ–čäŸżçš„çœ‹ćˆ°ä»–ć€‘ă€‚é€™æšŁçš„æŠ€èĄ“æČ’æœ‰èą«ć»Łæł›çš„äœżç”šïŒŒæˆ‘ć€‘çš„æžŹè©Šæ€ç¶­æŻ”èŒƒć‚Ÿć‘æ–ŒćŠŸèƒœæžŹè©ŠïŒŒäœ†é€™ä»ŁèĄšäș†çœŸćŻŠçš„äœżç”šè€…é«”é©—ïŒŒè€Œäž”ćŻä»„èŒ•æ˜“ćœ°ç™ŒçŸćƒæ˜Żæœƒćœšć€šć€‹èš­ć‚™äžŠć±•ç€ș的 UI ć•éĄŒă€‚æœ‰äș›ć…èČ»çš„ć·„ć…·ćŻä»„æäŸ›äž€äș›ćŸșæœŹçš„ćŠŸèƒœ - ç”ąç”Ÿæˆ–ć„Čć­˜èžąć蕿ˆȘćœ–ïŒŒèź“è‚‰çœŒćŻä»„æȘ࿟„。雖然這皟æ–čæł•ć°æ–ŒèŠæšĄèŒƒć°çš„æ‡‰ç”šçš‹ćŒć·Čç¶“è¶łć€ ïŒŒäœ†ä»–çš„çŒșé»žć°±è·Ÿä»»äœ•æ‰‹ć‹•æžŹè©Šäž€æšŁ - ćœšä»»äœ•èźŠæ›ŽćŸŒéƒœéœ€èŠäșșćŠ›äŸ†è™•ç†ă€‚ćŠäž€æ–čéąïŒŒç”±æ–ŒçŒșäčæž…æ„šçš„ćźšçŸ©ïŒŒè‡Ș拕æȘąæžŹ UI ć•éĄŒéžćžžæœ‰æŒ‘æˆ°æ€§ïŒŒèŠ–èŠșć›žæ­ž (Visual Regression) è§Łæ±ș這難題的æ–čæł•æ˜ŻïŒŒæŻ”èŒƒèˆŠçš„ UI èˆ‡æœ€æ–°ç‰ˆçš„çš„ć·źç•°ïŒŒäžŠéĄŻç€șæȘąæžŹç”æžœă€‚䞀äș›é–‹æș/慍èČ»çš„ć·„ć…·ćŻä»„æäŸ›é€™æšŁçš„èƒœćŠ› (äŸ‹ćŠ‚ïŒŒ[wraith](https://github.com/BBC-News/wraith), [PhantomCSS](https://github.com/HuddleEng/PhantomCSS))ïŒŒäœ†ä»–ć€‘çš„ćź‰èŁæŻ”èŒƒè€—æ™‚ă€‚äž€äș›ć•†æ„­ć·„ć…· (äŸ‹ćŠ‚ïŒŒ[Applitools](https://applitools.com/), [Percy.io](https://percy.io/)) ć‰‡æ›Žé€Čäž€æ­„ïŒŒä»–ć€‘ç°ĄćŒ–äș†ćź‰èŁæ”çš‹ïŒŒäžŠć°èŁäș†èš±ć€šé€ČéšŽçš„ćŠŸèƒœïŒŒćƒæ˜ŻçźĄç† UIă€è­Šć‘Šă€è—‰ç”±éŽæżŸ "芖èŠșć™Ș音"(ćŠ‚ïŒŒć»Łć‘Šă€ć‹•ç•«)䟆é€ČèĄŒæ™șæ…§æŠ“ć–ïŒŒç”šè‡łćŻä»„ćˆ†æžć‡ș造成 DOM/CSS ç™Œç”Ÿć•éĄŒçš„æ čæœŹćŽŸć› ă€‚ + +
    + +❌ **ćŠć‰‡ïŒš** 侀怋顯ç€ș慧ćźč侔通過100%çš„ćŠŸèƒœæžŹè©Šçš„é éąïŒŒèŒ‰ć…„é€ŸćșŠéžćžžćż«ïŒŒäœ†æœ‰äž€ćŠçš„ć…§ćźčéƒœèą«éš±è—äș†ïŒŒé€™æšŁçš„é éąæ˜Żć„œçš„ć—ŽïŒŸ + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :thumbsdown: ćäŸ‹ïŒšäž€ć€‹ć…žćž‹çš„ visual regressionïŒŒćłćŽć…§ćźč饯ç€ș異澾 + +![alt text](assets/amazon-visual-regression.jpeg "Amazon page breaks") + +
    + +### :clap: æ­ŁäŸ‹ïŒšèš­ćźš wraith äŸ†æŠ“ć–äžŠæŻ”ć° UI æˆȘ朖 + +![](https://img.shields.io/badge/🔹%20Example%20using%20Wraith-blue.svg "Using Wraith") + +``` +​# Add as many domains as necessary. Key will act as a label​ + +domains: + english: "http://www.mysite.com"​ + +​# Type screen widths below, here are a couple of examples​ + +screen_widths: + + - 600​ + - 768​ + - 1024​ + - 1280​ + +​# Type page URL paths below, here are a couple of examples​ +paths: + about: + path: /about + selector: '.about'​ + subscribe: + selector: '.subscribe'​ + path: /subscribe +``` + +### :clap: æ­ŁäŸ‹ïŒšäœżç”š Applitools 䟆çČćŸ—æˆȘćœ–çš„æŻ”ć°ç”æžœä»„ćŠć…¶ä»–é€ČéšŽćŠŸèƒœ + +![](https://img.shields.io/badge/🔹%20Example%20using%20AppliTools-blue.svg "Using Applitools") ![](https://img.shields.io/badge/🔹%20Example%20using%20Cypress-blue.svg "Using Cypress to illustrate the idea") + +```javascript +import * as todoPage from "../page-objects/todo-page"; + +describe("visual validation", () => { + before(() => todoPage.navigate()); + beforeEach(() => cy.eyesOpen({ appName: "TAU TodoMVC" })); + afterEach(() => cy.eyesClose()); + + it("should look good", () => { + cy.eyesCheckWindow("empty todo list"); + todoPage.addTodo("Clean room"); + todoPage.addTodo("Learn javascript"); + cy.eyesCheckWindow("two todos"); + todoPage.toggleTodo(0); + cy.eyesCheckWindow("mark as completed"); + }); +}); +``` + +
    + +

    + +# 珏 4 ç« ïŒšæžŹé‡æžŹè©Šæ•ˆæžœ + +

    + +## âšȘ  4.1 è—‰ç”±è¶łć€ çš„èŠ†è“‹çŽ‡äŸ†çČćŸ—äżĄćżƒïŒŒ~80% çœ‹è”·äŸ†æ˜Żć€‹ćčžé‹æ•ž + +:white_check_mark: **ć»ș議** æžŹè©Šçš„ç›źçš„æ˜Żç‚șäș†ćŸ—ćˆ°è¶łć€ çš„äżĄćżƒćŽ»é€ČèĄŒæ›Žćż«é€Ÿçš„èż­ä»ŁïŒŒćŸˆéĄŻç„¶ćœ°ïŒŒè¶Šć€šçš„çš‹ćŒèą«æžŹè©Šćˆ°ïŒŒćœ˜éšŠæœƒæ›Žç‚șè‡ȘäżĄă€‚æžŹè©ŠèŠ†è“‹çŽ‡æ˜Żç”šäŸ†æžŹé‡æžŹè©Šçš‹ćŒè”°éŽć€šć°‘èĄŒ (或 branch, statement, ...)ă€‚é‚ŁèŠć€šć°‘æ‰ć€ ïŒŸ10% ~ 30% æ˜ŽéĄŻç„Ąæł•è­‰æ˜Žć°ˆæĄˆçš„æ­Łçąș性䜆 100% ć‰‡ćŻèƒœæœƒéŽæ–Œæ”ȘèČ»æ™‚é–“ïŒŒè€Œäž”ćŻèƒœæœƒèż«äœżäœ é—œæłšć€Șć€šæžćŸźæœ«çŻ€çš„çš‹ćŒă€‚ç­”æĄˆæ˜ŻïŒŒéœ€èŠćƒè€ƒćŸˆć€šć› çŽ äžŠć–æ±șæ–Œæ‡‰ç”šçš‹ćŒçš„éĄžćž‹ïŒŒćŠ‚æžœäœ æ­Łćœšć»șç«‹æŹĄäž–ä»Łçš„ç©ș䞭淎棫 A380ïŒŒé‚Ł 100% çš„èŠ†è“‹çŽ‡æ˜Żćż…é ˆçš„ïŒ›ç„¶è€Œć°æ–Œäž€ć€‹ćĄé€šćœ–ç‰‡çš„ç¶Č站䟆èȘȘ50% çš„èŠ†è“‹çŽ‡ćŻèƒœć€Șé«˜ă€‚é›–ç„¶ć€§éƒšćˆ†çš„æžŹè©Šæ„›ć„œè€…éƒœèȘȘ芆蓋率的最䜎門æȘ»èŠäŸćźąè§€ć› çŽ äŸ†æ±șćźšïŒŒäœ†ä»–ć€‘éƒœæćˆ°ïŒŒæ č據經驗 80% æ˜Żć€‹äžéŒŻçš„æ•žć­—ă€‚([Fowler: “in the upper 80s or 90s”](https://martinfowler.com/bliki/TestCoverage.html))ïŒŒè¶łć€ æ»żè¶łć€§ć€šæ•žçš„æ‡‰ç”šçš‹ćŒă€‚ + +ćŻŠäœœć»șè­°ïŒšäœ ćŻèƒœæœƒæƒłćœš CI ć·„ć…·äž­èš­ćźšæžŹè©ŠèŠ†è“‹çŽ‡çš„é–€æȘ» ([Jest link](https://jestjs.io/docs/configuration#collectcoverage-boolean))ïŒŒäžŠäž­æ–·é‚Łäș›æœȘ達芆蓋率門æȘ»çš„ć»șçœź (äčŸćŻä»„ç‚șæŻć€‹ç”„ä»¶èš­ćźšèŠ†è“‹çŽ‡é–€æȘ»ïŒŒćƒè€ƒäž‹éąçš„çš‹ćŒçŻ„äŸ‹)ă€‚ćŠć€–ïŒŒäčŸćŻä»„ç›ŁæžŹć»șçœźçš„èŠ†è“‹çŽ‡æ˜ŻćŠäž‹é™ (ç•¶æœ‰äž€ć€‹æ–°çš„äž”èŠ†è“‹çŽ‡èŒƒäœŽçš„çš‹ćŒèą« commit) — é€™ć°‡äżƒäœżé–‹ç™Œè€…ćŽ»æć‡æˆ–è‡łć°‘ç¶­æŒäž€ćźšçš„æžŹè©Šæ•žé‡ă€‚èȘȘäș†ćŸˆć€šïŒŒäœ†æžŹè©ŠèŠ†è“‹çŽ‡ćȘæ˜Żäž€ć€‹é‡ćŒ–ć‡șäŸ†çš„æ•žć€ŒïŒŒćźƒäžŠäžèƒœè­‰æ˜Žäœ çš„æžŹè©Šæ˜ŻćŒ·ćŁŻçš„ă€‚æˆ–èš±äœ ä蟿œƒèą«ä»–éš™ćˆ° (ćƒè€ƒäž‹äž€ć°çŻ€çš„ć…§ćźč)。 + +
    + +❌ **ćŠć‰‡ïŒš** è‡ȘäżĄèˆ‡æ•žć­—æ˜Żç›žèŒ”ç›žæˆçš„ïŒŒćŠ‚æžœç„Ąæł•çąșäżæžŹè©Šć·Č經芆蓋äș†ć€§éƒšćˆ†çš„çł»ç”±ïŒŒäœ ć°‡æ„Ÿćˆ°ćźłæ€•ïŒŒäž”ææ‡Œæœƒäœżäœ èꊿ…ąă€‚ + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :clap: æ­ŁäŸ‹ïŒšäž€ć€‹ć…žćž‹çš„èŠ†è“‹çŽ‡ć ±ć‘Š + +![alt text](assets/bp-18-yoni-goldberg-code-coverage.png "A typical coverage report") + +
    + +### :clap: æ­ŁäŸ‹ïŒšç‚șæŻć€‹ç”„ä»¶èš­ćźšèŠ†è“‹çŽ‡ (äœżç”š Jest) + +![](https://img.shields.io/badge/🔹%20Example%20using%20Jest-blue.svg "Using Jest") + +![alt text](assets/bp-18-code-coverage2.jpeg "Setting up coverage per component (using Jest)") + +
    + +

    + +## âšȘ  4.2 æȘ࿟„æžŹè©ŠèŠ†è“‹çŽ‡çš„ć ±ć‘ŠäŸ†ç™ŒçŸæČ’有è૿žŹè©Šçš„ć€ćŸŸæˆ–ć„‡æ€Ș的朰æ–č + +:white_check_mark: **ć»ș議** 有äș›ć•éĄŒéš±è—ćœšé›·é”äč‹äž‹ïŒŒäœżç”šć‚łç”±çš„ć·„ć…·ćŸˆé›Łç™ŒçŸćˆ°ćźƒć€‘ă€‚ćźƒć€‘é€šćžžäžæ˜ŻçœŸæ­Łçš„ bugïŒŒć€§ć€šæ•žæƒ…æłäž‹æ˜Żæ‡‰ç”šçš‹ćŒçš„ć„‡æ€ȘèĄŒç‚ș而這äș›èĄŒç‚șćŻèƒœæœƒé€ æˆćšŽé‡ćœ±éŸżă€‚äŸ‹ćŠ‚ïŒŒäž€äș›çš‹ćŒć€ćŸŸćčŸäčŽäžæœƒæˆ–ćŸˆć°‘èą«ć‘Œć« — 䜠仄ç‚ș "PricingCalculator" 這怋 class ćȘæœƒèš­ćźšç”ąć“ćƒč栌甐果他ćčŸäčŽäžæœƒèą«ć‘Œć«ïŒŒćłäœżæˆ‘ć€‘çš„èł‡æ–™ćș«äž­æœ‰ 10000 ä»¶ć•†ć“ä»„ćŠćŸˆć€šç­†äș€æ˜“   æžŹè©ŠèŠ†è“‹çŽ‡ć ±ć‘ŠćŻä»„ćč«ćŠ©äœ ç™ŒçŸæ‡‰ç”šçš‹ćŒæ˜ŻćŠæŒ‰ç…§äœ çš„æœŸæœ›ćœšćŸ·èĄŒă€‚é™€æ­€äč‹ć€–ćꃿœƒ highlight ć‡șć“Șäș›éĄžćž‹çš„çš‹ćŒæČ’有è૿žŹè©Šćˆ°ïŒŒ80% çš„çš‹ćŒèą«æžŹè©ŠäžŠäžèƒœä»ŁèĄšçš‹ćŒäž­é—œé”çš„éƒšćˆ†æœ‰èą«èŠ†è“‹ćˆ°ă€‚ç”ąç”Ÿć ±ć‘ŠćŸˆç°Ąć–źïŒŒćȘéœ€èŠćœšćŸ·èĄŒæžŹè©Šçš„æ™‚ć€™é–‹ć•ŸèŠ†è“‹çŽ‡èżœèč€çš„ćŠŸèƒœïŒŒç„¶ćŸŒèź“é‚Łäș›èŠ±èŠ±ç¶ ç¶ çš„ć ±ć‘ŠäŸ†ć‘ŠèšŽäœ æŻć€‹çš‹ćŒć€ćĄŠèą«ć‘Œć«çš„é »çŽ‡ă€‚ćŠ‚æžœäœ èŠ±æ™‚é–“ćŽ»çœ‹é€™äș›æ•žæ“šïŒŒäœ ćŻèƒœæœƒç™ŒçŸäž€äș›ć•éĄŒă€‚ + +
    + +❌ **ćŠć‰‡ïŒš** ćŠ‚æžœäœ äžçŸ„é“äœ çš„çš‹ćŒèŁĄéąæœ‰ć“Șäș›ćœ°æ–čæČ’有è૿žŹè©Šćˆ°ïŒŒäœ ć°‡ç„Ąæł•çŸ„é“ć•éĄŒçš„äŸ†æșă€‚ + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :thumbsdown: ćäŸ‹ïŒšé€™ć€‹æžŹè©ŠèŠ†è“‹çŽ‡çš„ć ±ć‘Šć‡șäș†ä»€éșŒć•éĄŒïŒŸ + +ćŸșæ–Œäž€ć€‹çœŸćŻŠäž–ç•Œçš„æƒ…ćąƒïŒŒæˆ‘ć€‘ćœš QA äž­èżœèč€äș†æˆ‘ć€‘æ‡‰ç”šçš‹ćŒçš„äœżç”šæƒ…æłïŒŒäžŠç™ŒçŸäș†é€™ć€‹æœ‰è¶Łçš„ç™»éŒ„æšĄćŒ (提ç€șïŒšç™»ć…„ć€±æ•—çš„æ•žé‡äžæˆæ­ŁæŻ”çš„ïŒŒéĄŻç„¶æ˜Żæœ‰ć•éĄŒçš„ă€‚æœ€ćŸŒç™ŒçŸïŒŒæœ‰äž€äș›ć‰ç«Żçš„ bug äž€ç›Žćœšæ‰“ćŸŒç«Żçš„ç™»ć…„ API) + +![alt text](assets/bp-19-coverage-yoni-goldberg-nodejs-consultant.png "What’s wrong with this coverage report?") + +
    + +

    + +## âšȘ  4.3 äœżç”šă€ŒèźŠç•°æžŹè©Šă€æžŹé‡é‚èŒŻèŠ†è“‹çŽ‡ + +:white_check_mark: **ć»ș議** ć‚łç”±çš„æžŹè©ŠèŠ†è“‹çŽ‡é€šćžžæ˜Żéš™äșșçš„ïŒŒä»–ćŻèƒœæœƒć‘ŠèšŽäœ æœ‰ 100% çš„æžŹè©ŠèŠ†è“‹çŽ‡ïŒŒäœ†ćŻèƒœäœ çš„ function 郜æČ’æœ‰ć›žć‚łæ­Łçąșçš„ć€Œă€‚ç‚ș什éșŒæœƒé€™æšŁïŒŸć› ç‚ș他ćȘæ˜ŻćŸˆć–źçŽ”çš„æžŹé‡äœ çš„æžŹè©Šçš‹ćŒè”°éŽć“ȘćčŸèĄŒïŒŒè€ŒäžæœƒæȘ࿟„æžŹè©ŠæĄˆäŸ‹ćˆ°ćș•æžŹè©Šäș†ä»€éșŒïŒŒä»–ćˆ°ćș•有æČ’有çąșćŻŠćŽ»æ–·èš€æ­Łçąșçš„ć›žæ‡‰ă€‚ć°±ćƒæœ‰ć€‹äșș曠慬ć‡șć·źïŒŒä»–ć‡șç€șäș†ä»–çš„è­·ç…§ïŒŒä»–ç„Ąæł•è­‰æ˜Žä»–ćšäș†ä»€éșŒć·„䜜ćȘèƒœè­‰æ˜Žæœ‰ćŽ»éŽć“ȘćčŸć€‹æ©Ÿć Žă€‚ + +ćŸșæ–ŒèźŠç•°çš„æžŹè©ŠïŒŒæ˜Żé€éŽæžŹé‡"ćŻŠéš›æžŹè©Š"çš„çš‹ćŒæ•žé‡è€Œäžćƒ…ćƒ…æ˜Ż"èšȘ敏"éŽçš„æ•žé‡äŸ†æäŸ›ć”ćŠ©ă€‚[Stryker](https://stryker-mutator.io/) æ˜Żäž€ć€‹ç”šæ–Œé€ČèĄŒèźŠç•°æžŹè©Šçš„ JavaScript ć‡œç€șćș«ïŒŒä»–çš„ćŻŠäœœéžćžžć·§ćŠ™ïŒš + +(1) ä»–æœƒćˆ»æ„ćœšäœ çš„çš‹ćŒäž­ă€Œæ€ć…„ bugă€ă€‚äŸ‹ćŠ‚çš‹ćŒ `newOrder.price === 0` æœƒèą«æ”č成 `newOrder.price != 0`ïŒŒé€™ć€‹ "bug" 氱皱ç‚șèźŠç•°ă€‚ + +(2) ä»–æœƒè·‘éŽäž€æŹĄæžŹè©ŠïŒŒćŠ‚æžœæžŹè©Šé€šéŽäș†ä»ŁèĄšæœ‰äș›ć•éĄŒïŒŒé€™äș›æžŹè©ŠæĄˆäŸ‹æČ’æœ‰é”ćˆ°ç™ŒçŸ bug çš„ç›źçš„ïŒŒć°Žè‡Žé€™äș›èźŠç•°æŽ»äș†äž‹äŸ†ă€‚ćŠ‚æžœæžŹè©Šć€±æ•—äș†ïŒŒéžćžžć„œïŒŒé‚Łäș›èźŠç•°ć°±æœƒè૿źșæŽ‰ă€‚ + +ç›žèŒƒæ–Œć‚łç”±çš„æžŹè©ŠèŠ†è“‹çŽ‡ïŒŒćŠ‚æžœçŸ„é“æ‰€æœ‰çš„èźŠç•°éƒœèą«æźșæ­»ïŒŒæœƒèź“äœ æ›Žæœ‰è‡ȘäżĄïŒŒè€Œäž”é€™ć…©è€…èŠ±èČ»çš„æ™‚é–“ć·źäžć€šă€‚ + +
    + +❌ **ćŠć‰‡ïŒš** äœ ćŻèƒœæœƒèȘ€ä»„ç‚ș 85% çš„æžŹè©ŠèŠ†è“‹çŽ‡èƒœç™ŒçŸçš‹ćŒäž­ 85% 的 bug。 + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :thumbsdown: ćäŸ‹ïŒš 100% coverage, 0% testing + +![](https://img.shields.io/badge/🔹%20Example%20using%20Stryker-blue.svg "Using Stryker") + +```javascript +function addNewOrder(newOrder) { + logger.log(`Adding new order ${newOrder}`); + DB.save(newOrder); + Mailer.sendMail(newOrder.assignee, `A new order was places ${newOrder}`); + + return { approved: true }; +} + +it("Test addNewOrder, don't use such test names", () => { + addNewOrder({ assignee: "John@mailer.com", price: 120 }); +}); // Triggers 100% code coverage, but it doesn't check anything +``` + +
    + +### :clap: æ­ŁäŸ‹ïŒšStryker çš„ć ±ć‘ŠïŒŒäž€ć€‹èźŠç•°æžŹè©Šçš„ć·„ć…·ïŒŒć”æžŹäžŠç”±èšˆæČ’有è૿žŹè©Šćˆ°çš„çš‹ćŒ (èźŠç•°) + +![alt text](assets/bp-20-yoni-goldberg-mutation-testing.jpeg "Stryker reports, a tool for mutation testing, detects and counts the amount of code that is not tested (Mutations)") + +
    + +

    + +## âšȘ 4.4 äœżç”š Test linter äŸ†éżć…æžŹè©Šçš‹ćŒçš„ć•éĄŒ + +:white_check_mark: **ć»ș議** æœ‰äž€çł»ćˆ—çš„ ESLint ć€–æŽ›ćŻä»„æȘ࿟„æžŹè©Šçš‹ćŒçš„éąšæ ŒäžŠç™ŒçŸć•éĄŒă€‚æŻ”ćŠ‚ [eslint-plugin-mocha](https://www.npmjs.com/package/eslint-plugin-mocha) æœƒè­Šć‘Šäž€ć€‹ćŻ«ćœš global ć±€çš„æžŹè©ŠæĄˆäŸ‹ (äžæ˜ŻćŻ«ćœš describe() ćș•äž‹)ïŒŒæˆ–è€…ç•¶æžŹè©ŠæĄˆäŸ‹èą« [skip](https://mochajs.org/#inclusive-tests) 時會癌ć‡șè­Šć‘ŠïŒŒć› ç‚șé€™ćŻèƒœæœƒć°Žè‡Žäœ èȘ€æœƒæ‰€æœ‰æžŹè©Šéƒœé€šéŽäș†ă€‚éĄžäŒŒçš„ćƒïŒŒ[eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest) ćŻä»„ćœšäž€ć€‹æžŹè©ŠæĄˆäŸ‹æČ’有任䜕斷蚀 (æČ’有æȘ࿟„ä»»äœ•ć…§ćźč) 時甊ć‡șè­Šć‘Šă€‚ + +
    + +❌ **ćŠć‰‡ïŒš** ç•¶äœ æ»żè¶łæ–Œ 90% çš„æžŹè©ŠèŠ†è“‹çŽ‡æˆ– 100% 的綠è‰Čć ±ć‘Šæ™‚ïŒŒć»ç™ŒçŸćŸˆć€šæžŹè©ŠéƒœæČ’什éșŒæ–·èš€ïŒŒæˆ–æ˜ŻćŸˆć€šæžŹè©Šç›ŽæŽ„èą« skip 掉äș†ă€‚äœ†éĄ˜äœ æČ’æœ‰æŠŠé€™ä»œçš‹ćŒäœˆçœČć‡ș掻。 + +
    +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :thumbsdown: ćäŸ‹ïŒšäž€ć€‹ć……æ»żéŒŻèȘ€çš„æžŹè©ŠæĄˆäŸ‹ïŒŒé‚„ć„œéƒœèą« Linter æŠ“ćˆ°äș† + +```javascript +describe("Too short description", () => { + const userToken = userService.getDefaultToken() // *error:no-setup-in-describe, use hooks (sparingly) instead + it("Some description", () => {}); // *error: valid-test-description. Must include the word "Should" + at least 5 words +}); + +it.skip("Test name", () => { // *error:no-skipped-tests, error:error:no-global-tests. Put tests only under describe or suite + expect("somevalue"); // *error:no-assert +}); + +it("Test name", () => { // *error:no-identical-title. Assign unique titles to tests +}); +``` + +
    + +

    + +# 珏 5 章持çșŒæ•Žćˆ (CI) æˆ–ć…¶ä»–æé«˜ć“èłȘçš„æ‰‹æź” + +

    + +## âšȘ  5.1 è±ćŻŒäœ çš„ linter äžŠæšæŁ„æœ‰ linting 敏題的ć»șçœź + +:white_check_mark: **ć»ș議** ćȘ需芁 5 ćˆ†é˜çš„èš­ćźšïŒŒć°±ćŻä»„ć…èČ»ćŸ—ćˆ°è‡Șć‹•äżè­·çš‹ćŒçąŒçš„ć·„ć…·äŸ†ć”æžŹć‡șçš‹ćŒäž­çš„ć•éĄŒă€‚Linter 侍憍ćȘæ˜ŻæšŁćŒć·„ć…·ïŒŒçŸćœšçš„ linter ćŻä»„æŠ“ćˆ°èš±ć€šćšŽé‡çš„ć•éĄŒïŒŒćƒæ˜Ż error æČ’有è૿­Łçąș的拋ć‡șæˆ–èšŠæŻçš„éșć€±ă€‚ćœšćŸșæœŹçš„èŠć‰‡ (橂 [ESLint standard](https://www.npmjs.com/package/eslint-plugin-standard) 或 [Airbnb style](https://www.npmjs.com/package/eslint-config-airbnb)) äč‹äžŠïŒŒćŻä»„è€ƒæ…źćŠ äžŠäž€äș›ç‰čæźŠçš„ linterïŒŒćƒæ˜Ż [eslint-plugin-chai-expect](https://www.npmjs.com/package/eslint-plugin-chai-expect) ćŻä»„ç”šäŸ†ć”æžŹæžŹè©ŠæĄˆäŸ‹æœ‰æČ’æœ‰ćŻ«æ–·èš€ïŒŒ[eslint-plugin-promise](https://www.npmjs.com/package/eslint-plugin-promise?activeTab=readme) ćŻä»„ç™ŒçŸ Promise 有æČ’有 resolve (ćŠć‰‡æœƒć°Žè‡Žäœ çš„çš‹ćŒäžèƒœçčŒçșŒ)[eslint-plugin-security](https://www.npmjs.com/package/eslint-plugin-security?activeTab=readme) ćŻä»„ç™ŒçŸćŻèƒœæœƒć°Žè‡Ž DOS æ”»æ“Šçš„æ­ŁèŠèĄšç€șćŒïŒŒé‚„æœ‰ [eslint-plugin-you-dont-need-lodash-underscore](https://www.npmjs.com/package/eslint-plugin-you-dont-need-lodash-underscore) æœƒćœšçš‹ćŒçąŒäœżç”šćˆ° V8 çš„æ žćżƒæ–čæł•çš„æ™‚ć€™ç”Šäșˆè­Šć‘ŠïŒŒäŸ‹ćŠ‚ Lodash.\_map(
)。 +
    + +❌ **ćŠć‰‡ïŒš** æƒłćƒćœšæŸć€‹é›šć€©äž­ïŒŒäœ çš„çš‹ćŒäž€ç›Ž crash而䞔 log æČ’æœ‰éĄŻç€ș stack trace çš„èšŠæŻă€‚ćˆ°ćș•癌生什éșŒäș‹äș†ïŒŸäœ çš„çš‹ćŒéŒŻèȘ€ćœ°æ‹‹ć‡șäș†äž€ć€‹éž error 的物件而䞔 stack trace 郜䞍芋äș†ïŒŒé€™æœƒèź“äœ æƒłćŽ»æ’žç‰†ă€‚ćȘ芁甚 5 ćˆ†é˜äŸ†èš­ćźš linter ć°±ćŻä»„ćč«äœ ć”æžŹć‡ș這繼 typo 錯èȘ€ïŒŒäžŠæ‹Żæ•‘äœ äž€æ•Žć€©ă€‚ + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :thumbsdown: ćäŸ‹ïŒšError ç‰©ä»¶èą«æ‹‹ć‡șïŒŒé€™æšŁçš„éŒŻèȘ€äžæœƒć‡ș珟 stack trace。ćčžé‹çš„æ˜ŻïŒŒESLint æŠ“ćˆ°äș†é€™ć€‹ bug。 + +![alt text](assets/bp-21-yoni-goldberg-eslint.jpeg "The wrong Error object is thrown mistakenly, no stack-trace will appear for this error. Luckily, ESLint catches the next production bug") + +
    + +

    + +## âšȘ  5.2 é€éŽæœŹćœ°ç«Żçš„ CI äŸ†çžźçŸ­ć›žé„‹ćŸȘ環 + +:white_check_mark: **ć»ș議** ćœšæœŹćœ°ç«Żäœżç”šäž€ć€‹ćŒ…ć«æžŹè©Šă€Lintă€ç©©ćźšæ€§æȘ࿟„ç­‰ćŠŸèƒœçš„ CIïŒŒćŻä»„ćč«ćŠ©é–‹ç™Œè€…ćż«é€ŸćŸ—ćˆ°ć›žé„‹äžŠçžźçŸ­ [曞鄋ćŸȘ環](https://www.gocd.org/2016/03/15/are-you-ready-for-continuous-delivery-part-2-feedback-loops/)。曠ç‚șäž€ć€‹æœ‰æ•ˆçš„æžŹè©Šæ”çš‹ćŒ…ć«ćŸˆć€šèż­ä»ŁćŸȘ環 (1) ć˜—è©Š -> (2) 曞鄋 -> (3) é‡æ§‹ă€‚æ‰€ä»„ć›žé„‹è¶Šćż«ïŒŒé–‹ç™Œè€…ćŻä»„ćœšæŻć€‹æ”çš‹äž­ćŻä»„ćŸ·èĄŒçš„èż­ä»Łć°±è¶Šć€šïŒŒäž”ćŻä»„ćŸ—ćˆ°æ›Žć„œçš„ç”æžœă€‚ćéŽäŸ†ïŒŒćŠ‚æžœć›žé„‹äŸ†ćŸ—ćŸˆæ…ąïŒŒäž€ć€©ćȘèƒœćŸ·èĄŒćŸˆć°‘ć€‹èż­ä»ŁïŒŒé‚Łćœ˜éšŠćŻèƒœæœƒć› ç‚șæ€„éœ€ćŸ·èĄŒäž‹äž€ć€‹äž»éĄŒ/任拙/ćŸȘç’°ïŒŒè€Œäžć†ć„ȘćŒ–ç•¶ć‰çš„ćŸȘ環。 + +ç›źć‰æœ‰äș› CI çš„æœć‹™äŸ›æ‡‰ć•† (ćŠ‚ïŒš[CircleCI local CLI](https://circleci.com/docs/2.0/local-cli/)) ć…èš±ćœšæœŹćœ°ç«ŻćŸ·èĄŒ CI pipelineă€‚æœ‰äș›ć•†æ„­ć·„ć…·ćƒæ˜Ż [wallaby](https://wallabyjs.com/) ç‚ș開癌提䟛äș†éžćžžæœ‰ç”šçš„æžŹè©ŠćŠŸèƒœă€‚æˆ–è€…äœ ćŻä»„朹 package.json 侭汞抠 npm script 䟆跑䞀äș›æć‡çš‹ćŒć“èłȘ的指什 — äœżç”šć·„ć…·ćŠ‚ [concurrently](https://www.npmjs.com/package/concurrently) äŸ†äžŠèĄŒćŸ·èĄŒïŒŒäžŠćœšä»»äœ•ć·„ć…·ćŸ·èĄŒć€±æ•—ćŸŒæ‹‹ć‡ș非 0 çš„ç”æŸçąŒă€‚é–‹ç™Œè€…ćȘéœ€ćŸ·èĄŒäž€ć€‹æŒ‡ä»€ïŒˆćŠ‚ `npm run quality` ïŒ‰äŸ†ćż«é€ŸçČć–ć›žé„‹ă€‚äčŸćŻä»„甹 githook äŸ†ć–æ¶ˆæČ’æœ‰é€šéŽçš‹ćŒć“èłȘæȘ࿟„的提äș€( [husky](https://github.com/typicode/husky) ćŻä»„ćč«ćŠ©äœ )。 +
    + +❌ **搩扇** ćŠ‚æžœć“èłȘæȘ࿟„çš„ç”æžœćœšçš‹ćŒæäș€ćŸŒçŹŹäșŒć€©æ‰æ”¶ćˆ°ïŒŒé‚ŁæžŹè©Šć°±äžçź—é–‹ç™Œçš„äž€éƒšćˆ†äș†ă€‚ + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :clap: æ­ŁäŸ‹ïŒšç”šäŸ†ćŸ·èĄŒçš‹ćŒć“èłȘæȘ࿟„的 npm scriptïŒŒćœšé–‹ç™Œè€…äž»ć‹•è§žç™Œæˆ–ć˜—è©Šæäș€æ–°çš‹ćŒæ™‚ćŸ·èĄŒă€‚ + +```javascript +"scripts": { + "inspect:sanity-testing": "mocha **/**--test.js --grep \"sanity\"", + "inspect:lint": "eslint .", + "inspect:vulnerabilities": "npm audit", + "inspect:license": "license-checker --failOn GPLv2", + "inspect:complexity": "plato .", + + "inspect:all": "concurrently -c \"bgBlue.bold,bgMagenta.bold,yellow\" \"npm:inspect:quick-testing\" \"npm:inspect:lint\" \"npm:inspect:vulnerabilities\" \"npm:inspect:license\"" + }, + "husky": { + "hooks": { + "precommit": "npm run inspect:all", + "prepush": "npm run inspect:all" + } +} + +``` + +
    + +

    + +## âšȘ 5.3 ćœšçœŸæ­Ł production çš„éĄćƒç’°ćąƒäž­ćŸ·èĄŒ e2e æžŹè©Š + +:white_check_mark: **ć»ș議** End to end (e2e) æžŹè©Šæ˜ŻæŻć€‹ CI pipeline æœƒéąè‡šçš„ć€§æŒ‘æˆ° - ćłæ™‚ć‰”ć»șäž€ć€‹èˆ‡çœŸæ­Ł production ç’°ćąƒç›žćŒçš„éĄćƒç’°ćąƒäžŠæ“æœ‰æ‰€æœ‰ç›žé—œçš„æœć‹™ïŒŒæ˜ŻćŸˆèČ»æ™‚èČ»ćŠ›çš„ă€‚äœ éœ€èŠæ‰Ÿćˆ°é©ćˆçš„æŠ˜èĄ·é»žïŒš[Docker-compose](https://serverless.com/) è—‰ç”±äž€ć€‹çŽ”æ–‡ć­—æȘ”ć°‡ docker ćŒ–çš„ç’°ćąƒæ”Ÿćœšçšç«‹çš„ container äž­ïŒŒäœ†ä»–èƒŒćŸŒäœżç”šçš„æŠ€èĄ“ (äŸ‹ćŠ‚ç¶Čè·Żèˆ‡äœˆçœČæšĄćž‹) ä»ç„¶èˆ‡çœŸćŻŠäž–ç•Œæœ‰æ‰€ć·źç•°ă€‚ćŻä»„ć°‡ć…¶èˆ‡ [AWS Local](https://github.com/localstack/localstack) æ•ŽćˆïŒŒćœšçœŸæ­Łçš„ AWSæœć‹™äž­ćšäœżç”šă€‚ćŠ‚æžœäœ äœżç”š [serverless](https://serverless.com/) æĄ†æž¶ïŒŒ[AWS SAM](https://docs.aws.amazon.com/lambda/latest/dg/serverless_app.html) ćŻä»„èź“äœ ćœšæœŹćœ°ç«ŻèȘżç”š FaaS çš‹ćŒçąŒă€‚ + +éŸć€§çš„ Kubernetes ç”Ÿæ…‹çł»é‚„æČ’æœ‰äž€ć€‹æš™æș–、æ–čäŸżçš„æœŹćœ°ç«Żă€CIéĄćƒçš„ć·„ć…·ïŒŒć„˜çźĄçŸćœšć·Čæœ‰èš±ć€šć·„ć…·çš„ć‡șçŸă€‚æœ‰äž€çšźæ–čæł•æ˜Żäœżç”šćƒæ˜Ż [Minikube](https://kubernetes.io/docs/setup/minikube/) 撌 [MicroK8s](https://microk8s.io/) é€™æšŁçš„ć·„ć…·äŸ†é‹èĄŒäž€ć€‹ "æœ€ć°ćŒ–çš„ Kubernetes"這äș›ć·„ć…·æ›ŽèČŒèż‘çŸćŻŠïŒŒäž”æˆæœŹèŠ±èČ»ćŸˆć°ă€‚ćŠäž€çšźæ–čæł•æ˜Żćœšé ç«Żçš„ "真毩 Kubernetes" ç’°ćąƒäžŠé‹èĄŒæžŹè©ŠïŒŒäž€äș› CI çš„æœć‹™äŸ›æ‡‰ć•† (橂 [Codefresh](https://codefresh.io/)) 與 Kubernetes ç’°ćąƒæ“æœ‰ćŽŸç”Ÿçš„æ•ŽćˆïŒŒèź“ćœš CI pipeline äžŠćŸ·èĄŒçœŸćŻŠçš„ç’°ćąƒèźŠćŸ—æ›Žç‚șćźčæ˜“ă€‚æœ‰çš„äŸ›æ‡‰ć•†ć‰‡ćŻä»„èź“äœ é‡ć°é ç«Żçš„ Kubernetes è‡Șèš‚è…łæœŹă€‚ +
    + +❌ **ćŠć‰‡ïŒš** ćœšç”Ÿç”ąç’°ćąƒć’ŒæžŹè©Šç’°ćąƒäž­äœżç”šäžćŒçš„æŠ€èĄ“ïŒŒć°±æœƒéœ€èŠç¶­è­·ć…©çšźäœˆçœČæšĄćž‹ïŒŒæœƒäœżé–‹ç™Œäșșć“Ąèˆ‡ç¶­é‹äșș擡戆開。 + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :clap: æ­ŁäŸ‹ïŒšć‹•æ…‹ç”ąç”Ÿ Kubernetes cluster 的 CI pipeline ([Credit: Dynamic-environments Kubernetes](https://container-solutions.com/dynamic-environments-kubernetes/)) + +```yaml +deploy: +stage: deploy +image: registry.gitlab.com/gitlab-examples/kubernetes-deploy +script: +- ./configureCluster.sh $KUBE_CA_PEM_FILE $KUBE_URL $KUBE_TOKEN +- kubectl create ns $NAMESPACE +- kubectl create secret -n $NAMESPACE docker-registry gitlab-registry --docker-server="$CI_REGISTRY" --docker-username="$CI_REGISTRY_USER" --docker-password="$CI_REGISTRY_PASSWORD" --docker-email="$GITLAB_USER_EMAIL" +- mkdir .generated +- echo "$CI_BUILD_REF_NAME-$CI_BUILD_REF" +- sed -e "s/TAG/$CI_BUILD_REF_NAME-$CI_BUILD_REF/g" templates/deals.yaml | tee ".generated/deals.yaml" +- kubectl apply --namespace $NAMESPACE -f .generated/deals.yaml +- kubectl apply --namespace $NAMESPACE -f templates/my-sock-shop.yaml +environment: +name: test-for-ci +``` + +
    + +

    + +## âšȘ 5.4 äžŠèĄŒæžŹè©Šć·„äœœ + +:white_check_mark: **ć»ș議** ćœšćˆç†çš„æƒ…æłäž‹ïŒŒæžŹè©Šæ˜Żäœ  24/7 çš„ć„œæœ‹ć‹ïŒŒä»–ç‚șäœ ćž¶äŸ†ćłæ™‚çš„ć›žé„‹ă€‚ćŻŠéš›äžŠïŒŒćœšć–źç·šçš‹çš„ CPU äžŠćŸ·èĄŒ 500 ć€‹ć–źć…ƒæžŹè©ŠćŻèƒœæœƒéžćžžè€—æ™‚ă€‚ćčžé‹çš„æ˜ŻïŒŒèż‘ä»Łçš„æžŹè©ŠćŸ·èĄŒć™šæˆ– CI ćčłć° (橂 [Jest](https://github.com/facebook/jest), [AVA](https://github.com/avajs/ava) 或 [Mocha extensions](https://github.com/yandex/mocha-parallel-tests)) ćŻä»„ć°‡æžŹè©ŠäžŠèĄŒç‚ș怚怋皋ćșäŸ†ćŸ·èĄŒïŒŒè—‰æ­€äŸ†ć€§ćč…çžźçŸ­ć›žé„‹çš„æ™‚é–“ă€‚äž€äș› CI 的滠敆ä蟿”ŻæŽè·šćźčć™šäžŠèĄŒæžŹè©ŠïŒŒæ›Žé€Čäž€æ­„ćœ°çžźçŸ­ć›žé„‹çš„æ™‚é–“ă€‚ç„Ąè«–æ˜ŻćœšæœŹćœ°ç«Żäœżç”šć€šć€‹çš‹ćșïŒŒæˆ–æ˜Żćœšäž€äș› cloud CLI äžŠäœżç”šć€šć°æ©Ÿć™šäŸ†ćŸ·èĄŒæžŹè©ŠïŒŒäžŠèĄŒćŒ–çš„é‡é»žæ˜ŻèŠäżæŒæžŹè©Šçš„è‡Șäž»æ€§ïŒŒć› ç‚șæŻć€‹æžŹè©ŠéƒœćŻèƒœćœšäžćŒçš„çš‹ćșäžŠćšćŸ·èĄŒă€‚ + +❌ **ćŠć‰‡ïŒš** ćŠ‚æžœé€ć‡șçš‹ćŒçąŒäž€ć€‹ć°æ™‚ćŸŒæ‰æ”¶ćˆ°æžŹè©Šç”æžœïŒŒäœ†äœ ć·Čç¶“ćœšé–‹ç™Œäž‹äž€ć€‹ćŠŸèƒœäș†ïŒŒé€™æœƒć°Žè‡ŽæžŹè©Šć°äœ äŸ†èȘȘèźŠçš„äžæ˜Żé‚ŁéșŒé‡èŠă€‚ + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :clap: æ­ŁäŸ‹ïŒšè—‰ç”±æžŹè©ŠäžŠèĄŒćŒ–ïŒŒMocha parallel 與 Jest ćŻä»„èŒ•æ˜“çš„è¶…è¶Šć‚łç”± Mocha ([Credit: JavaScript Test-Runners Benchmark](https://medium.com/dailyjs/javascript-test-runners-benchmark-3a78d4117b4)) + +![alt text](assets/bp-24-yonigoldberg-jest-parallel.png "Mocha parallel & Jest easily outrun the traditional Mocha thanks to testing parallelization (Credit: JavaScript Test-Runners Benchmark)") + +
    + +

    + +## âšȘ 5.5 äœżç”š License ć’ŒæŠ„è„ČæȘ࿟„äŸ†éżć…æł•ć‹™äžŠçš„ć•éĄŒ + +:white_check_mark: **ć»ș議** License ć’ŒæŠ„è„Čçš„ć•éĄŒæˆ–èš±äžæ˜Żäœ çŸćœšé—œæłšçš„é»žïŒŒäœ†ç‚ș什éșŒäžćœš 10 ćˆ†é˜ć…§æŠŠé€™ä»¶ć·„äœœèš­ćźšć„œć‘ąïŒŸèš±ć€š npm çš„ć„—ä»¶ïŒŒćƒæ˜Ż [license check](https://www.npmjs.com/package/license-checker) 撌 [plagiarism check](https://www.npmjs.com/package/plagiarism-checker) (ć•†æ„­è»Ÿé«”ïŒŒäœ†æœ‰ć…èČ»äœżç”šç‰ˆæœŹ) ćŻä»„ćŸˆćźčæ˜“çš„æ•Žćˆé€Č䜠的 CI pipeline 䞭䞊æȘ࿟„那äș›ćƒæ˜Żäœżç”šé™ćˆ¶æ€§ license æˆ–ćŸž Stack Overflow è€‡èŁœèČŒäžŠæ˜ŽéĄŻäŸ”çŠŻç‰ˆæŹŠçš„çš‹ćŒă€‚ + +❌ **ćŠć‰‡ïŒš** ćœšäžç¶“æ„çš„æƒ…æłäž‹ïŒŒé–‹ç™Œäșșć“ĄćŻèƒœæœƒäœżç”šć…·æœ‰äžé©ç•¶ License çš„ć„—ä»¶ïŒŒæˆ–ć°‡ć•†æ„­çš‹ćŒè€‡èŁœèČŒäžŠïŒŒćŸžè€Œé‡ćˆ°æł•ćŸ‹äžŠçš„ć•éĄŒă€‚ + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :clap: æ­ŁäŸ‹ïŒš + +```javascript +//install license-checker in your CI environment or also locally +npm install -g license-checker + +//ask it to scan all licenses and fail with exit code other than 0 if it found unauthorized license. The CI system should catch this failure and stop the build +license-checker --summary --failOn BSD + +``` + +
    + +![alt text](assets/bp-25-nodejs-licsense.png) + +
    + +

    + +## âšȘ 5.6 持çșŒæȘ࿟„æœ‰æŒæŽžçš„ç›žäŸć„—ä»¶ + +:white_check_mark: **ć»ș議** ćłäœżæ˜Żæœ€æœ‰äżĄè­œçš„ç›žäŸć„—ä»¶ïŒŒćŠ‚ Expressä蟿œ‰ć·ČçŸ„çš„æŒæŽžă€‚ćŻä»„è—‰ç”±äœżç”šç€ŸçŸ€ć·„ć…· (橂 [npm audit](https://docs.npmjs.com/getting-started/running-a-security-audit)) æˆ–ć•†æ„­ć·„ć…· (橂 [snyk](https://snyk.io/) (ä蟿œ‰ć…èČ»ç‰ˆæœŹ)) äŸ†èŒ•éŹ†è§Łæ±șć•éĄŒă€‚ćŻä»„ćœšæŻæŹĄçš„ć»șçœźäž­ïŒŒé€éŽ CI pipeline èȘżç”šä»–怑。 + +❌ **ćŠć‰‡ïŒš** 朹æČ’æœ‰ć°ˆç”šć·„ć…·çš„æƒ…æłäž‹ïŒŒèŠäżæŒäœ çš„çš‹ćŒæČ’æœ‰æŒæŽžïŒŒć°±éœ€èŠäžæ–·èżœèč€ç¶Čè·ŻäžŠæ–°ç™Œäœˆçš„æŒæŽžćšè„…èł‡èšŠïŒŒé€™æœƒç›žç•¶ä»€äșșäčć‘łă€‚ + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :clap: æ­ŁäŸ‹ïŒšNPM Audit 的甐果 + +![alt text](assets/bp-26-npm-audit-snyk.png "NPM Audit result") + +
    + +

    + +## âšȘ 5.7 è‡Șć‹•ć‡çŽšç›žäŸć„—ä»¶ + +:white_check_mark: **ć»ș議** Yarn 撌 npm 的 package-lock.json é–“æŽ„ć°Žć…„äș†äž€ć€‹ćšŽé‡çš„ć•éĄŒïŒˆæœŹæ„æ˜Żć„œçš„ïŒŒäœ†ć»é€šćŸ€ćœ°ç„ïŒ‰- é èš­æƒ…æłäž‹ïŒŒć„—ä»¶ć°‡äžć†ćŸ—ćˆ°æ›Žæ–°ă€‚ćłäœżćœ˜éšŠäœżç”š `npm install` 撌 `npm update` äčŸäžæœƒçČćŸ—ä»»äœ•æ›Žæ–°ă€‚æœƒć°Žè‡Žć°ˆæĄˆç›žäŸæ–Œäžć„œçš„ć„—ä»¶ç‰ˆæœŹïŒŒæˆ–è€…æœ€ćŁžçš„æƒ…æłæ˜Żäœżç”šćˆ°ćźčæ˜“èą«æ”»æ“Šçš„çš‹ćŒă€‚çŸćœšïŒŒćœ˜éšŠäŸé é–‹ç™Œäșșć“Ąçš„ć–„æ„ć’Œèš˜æ†¶äŸ†æ‰‹ć‹•æ›Žæ–° package.json æˆ–æ‰‹ć‹•äœżç”šćƒ [ncu]((https://www.npmjs.com/package/npm-check-updates)) é€™æšŁçš„ć·„ć…·ă€‚ç„¶è€Œæ›Žé è­œçš„æ–čćŒæ˜Żè‡Ș拕çČć–ćŻé çš„ç›žäŸć„—ä»¶ç‰ˆæœŹïŒŒé›–ç„¶æČ’æœ‰æœ€äœłçš„è§Łæ±șæ–čæĄˆïŒŒäœ†ç›źć‰æœ‰ć…©çšźćŻèƒœçš„è‡Ș拕挖æ–čćŒïŒš + +(1) äœżç”š [npm outdated](https://docs.npmjs.com/cli/outdated) 或 npm-check-updates (ncu)ïŒŒç•¶æœ‰éŽæ™‚çš„ç›žäŸć„—ä»¶æ™‚ïŒŒèź“ CI 的ć»șçœźć€±æ•—ă€‚é€™æšŁćŻä»„ćŒ·ćˆ¶é–‹ç™Œäșșć“ĄäŸ†æ›Žæ–°ç›žäŸć„—ä»¶ă€‚ + +(2) äœżç”šć•†æ„­ć·„ć…·ïŒŒä»–ć€‘ćŻä»„æŽƒæçš‹ćŒäžŠè‡Șć‹•ç™Œé€æ›Žæ–°ç›žäŸć„—ä»¶çš„ PRă€‚ć‰©äž‹çš„æœ‰è¶Łć•éĄŒæ˜Żç›žäŸć„—ä»¶çš„æ›Žæ–°ç­–ç•„ — è‹„æŻć€‹èŁœäžéƒœæ›Žæ–°æœƒç”ąç”Ÿć€Șć€šçš„é–‹éŠ·ïŒŒè€Œć€§ç‰ˆæœŹç™Œäœˆæ™‚æ›Žæ–°ćŻèƒœæœƒæŒ‡ć‘äž€ć€‹äžç©©ćźšçš„ç‰ˆæœŹïŒˆèš±ć€šć„—ä»¶ćœšç™ŒäœˆćŸŒçš„ćčŸć€©ć…§èą«çˆ†ć‡șæŒæŽžïŒŒè«‹ćƒé–± [eslint-scope]((https://nodesource.com/blog/a-high-level-post-mortem-of-the-eslint-scope-security-incident/)) äș‹ä»¶ïŒ‰ă€‚ + +æœ‰æ•ˆçš„æ›Žæ–°ç­–ç•„ćŻèƒœæ˜Żć…èš±äž€äș› "ćźčćżæœŸ" — èź“çš‹ćŒćŻä»„ć»¶ćŸŒ @latest äž€æź”æ™‚é–“ć’Œç‰ˆæœŹïŒŒć†ć°‡æœŹćœ°ç«Żçš„ć‰ŻæœŹèŠ–ç‚șéŽæ™‚ïŒˆäŸ‹ćŠ‚æœŹćœ°ç‰ˆæœŹç‚ș 1.3.1 ïŒŒć­˜ć„Čćș«ç‰ˆæœŹç‚ș1.3.8ïŒ‰ă€‚ + +
    + +❌ **ćŠć‰‡ïŒš** äœ ćœš production ç’°ćąƒæ‰€äœżç”šçš„ç›žäŸć„—ä»¶ïŒŒćŻèƒœć·Čç¶“èą«è©Č䜜者暙ç€șç‚șæ˜Żæœ‰éąšéšȘ的。 + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :clap: æ­ŁäŸ‹ïŒš[ncu](https://www.npmjs.com/package/npm-check-updates) ćŻä»„æ‰‹ć‹•æˆ–ćœš CI pipeline äž­äœżç”šïŒŒä»„æȘąæžŹçš‹ćŒèœćŸŒæœ€æ–°ç‰ˆæœŹć€šć°‘。 + +![alt text](assets/bp-27-yoni-goldberg-npm.png "ncu can be used manually or within a CI pipeline to detect to which extent the code lag behind the latest versions") + +
    + +

    + +## âšȘ  5.8 ć…¶ä»–ïŒŒèˆ‡ node 無關的 CI ć»șè­° + +:white_check_mark: **ć»ș議** æœŹæ–‡çš„é‡é»žæ˜Żèˆ‡ Node æœ‰é»žé—œäż‚çš„æžŹè©Šć»șè­°ă€‚äœ†æœŹçŻ€æ•Žç†äș†äž€äș›çœŸæ‰€ć‘šçŸ„的與 Node ç„Ąé—œçš„æŠ€ć·§ïŒš + +1. äœżç”šèČæ˜ŽćŒèȘžæł•ă€‚é€™æ˜Żć€§ć€šæ•žć·„ć…·çš„ć”Żäž€éžæ“‡ïŒŒé›–ç„¶èˆŠç‰ˆæœŹçš„ Jenkins ć…èš±äœżç”šçš‹ćŒæˆ– UI。 +1. éžæ“‡ć…·æœ‰æœŹćœ°ç«Ż Docker æ”ŻæŽçš„ć·„ć…·ă€‚ +1. ç›Ąćż«ć€±æ•—ïŒŒć…ˆćŸ·èĄŒæœ€ćż«çš„æžŹè©Šă€‚èš­ç«‹äž€ć€‹"ć†’ç…™æžŹè©Š"的 step/milestoneïŒŒć°ć€šć€‹ćż«é€ŸæȘ࿟„ć·„ć…·ïŒˆćŠ‚ lintingïŒŒć–źć…ƒæžŹè©ŠïŒ‰é€ČèĄŒćˆ†ç”„ïŒŒç‚șçš‹ćŒæäș€è€…æäŸ›ćż«é€Ÿć›žé„‹ă€‚ +1. èš­æł•æ–čäŸżćœ°ç€èŠœć»șçœźçš„æ‰€æœ‰ç”ąć‡șïŒŒćŒ…æ‹ŹæžŹè©Šć ±ć‘ŠïŒŒèŠ†è“‹çŽ‡ć ±ć‘ŠïŒŒèźŠç•°ć ±ć‘ŠïŒŒlog 等。 +1. ç‚șæŻć€‹äș‹ä»¶ć‰”ć»ș怚怋 pipelines/jobsă€‚äŸ‹ćŠ‚ïŒŒç‚ș feature branch 的提äș€èš­ćźšäž€ć€‹ jobç‚ș master PR èš­ćźšćŠäž€ć€‹ă€‚ ïŒˆć€§ć€šæ•žć·„ć…·æäŸ›äș†äž€äș›çš‹ćŒé‡ç”šçš„æ©Ÿćˆ¶ïŒ‰ +1. æ°žé äžèŠćœš job ćźšçŸ©äž­ćŠ ć…„æ©ŸćŻ†äżĄæŻïŒŒćŸž secret store 或 job çš„èš­ćźšäž­çČ揖。 +1. 朹 release 䞭明çąșćźšçŸ©ç‰ˆæœŹè™Ÿă€‚ +1. 情ć»șçœźäž€æŹĄïŒŒäžŠć°æ•Žć€‹ build artifactïŒˆäŸ‹ćŠ‚ Docker imageïŒ‰ćŸ·èĄŒæ‰€æœ‰çš„æȘ࿟„。 +1. ćœšäž€ć€‹è‡šæ™‚çš„ç’°ćąƒäž­ćŸ·èĄŒæžŹè©ŠïŒŒćœšäžćŒć»șçœźäč‹é–“䞍會æ”čèźŠç‹€æ…‹ă€‚ćż«ć– node_modules ćŻèƒœæ˜Żć”Żäž€çš„äŸ‹ć€–ă€‚ + +
    + +❌ **ćŠć‰‡ïŒš** äœ æœƒéŒŻéŽć€šćčŽçš„æ™ș慧甐晶 + +

    + +## âšȘ  5.9 ć»șçœźæšĄćž‹ïŒˆMatrixïŒ‰ïŒšäœżç”šć€šć€‹ Node ç‰ˆæœŹćŸ·èĄŒćŒäž€ć€‹ CI 攁皋 + +:white_check_mark: **ć»ș議** 擁èłȘ的æȘ࿟„æ˜Żç”šæ–Œç™ŒçŸæ„ć€–ïŒŒæžŹè©ŠèŠ†è“‹çš„éƒšćˆ†è¶Šć€šïŒŒäœ ć°±è¶ŠćŻèƒœć„˜æ—©ćœ°ç™ŒçŸć•éĄŒă€‚ćœšé–‹ç™Œæœƒé‡è€‡äœżç”šçš„ć„—ä»¶æˆ–ćŸ·èĄŒć…·æœ‰ć„çšźèš­ćźšć’Œ Node ç‰ˆæœŹçš„ć€šç”šæˆ¶ç”Ÿç”ąç’°ćąƒæ™‚ïŒŒCI pipeline ćż…é ˆćœšæ‰€æœ‰èš­ćźšçš„ç”„ćˆäžŠćŸ·èĄŒæžŹè©Šă€‚äŸ‹ćŠ‚ïŒŒć‡èš­æˆ‘ć€‘çš„æŸäș›ćźąæˆ¶äœżç”š MySQLïŒŒćŠäž€æ‰čćźąæˆ¶äœżç”š Postgres。侀äș› CI ć·„ć…·æ”ŻæŒäž€çšźçš±ç‚ș"Matrix"çš„ćŠŸèƒœïŒŒè©ČćŠŸèƒœćŻä»„é‡ć° MySQL、Postgres æˆ–ć€šć€‹ Node ç‰ˆæœŹïŒˆćŠ‚8、9、10ïŒ‰çš„æ‰€æœ‰ç”„ćˆćŸ·èĄŒæžŹè©Šă€‚ćȘèŠèš­ćźšćłćŻćźŒæˆè€Œç„Ąéœ€ä»»äœ•éĄć€–ć·„äœœă€‚ć…¶ä»–äžæ”ŻæŽ Matrix 的 CI ćŻèƒœćŻä»„é€šéŽćź‰èŁć€–æŽ›æˆ–äž€ćźšçš‹ćșŠçš„èȘżæ•ŽäŸ†ćŻŠçŸé€™ć€‹ćŠŸèƒœă€‚ + +
    + +❌ **ćŠć‰‡ïŒš** 朹恚äș†é‚ŁéșŒć€šèŸ›è‹Šçš„æžŹè©Šć·„䜜äč‹ćŸŒïŒŒæ€ŽéșŒèƒœèź“éŒŻèȘ€ćƒ…ćƒ…ć› ç‚șèš­ćźšçš„ć•éĄŒè€Œć‡șçŸă€‚ + +
    + +
    ✏ çš‹ćŒçŻ„äŸ‹ + +
    + +### :clap: æ­ŁäŸ‹ïŒšäœżç”š Travis (CI äŸ›æ‡‰ć•†) 的ć»șçœźćźšçŸ©ïŒŒćœšć€šć€‹ Node ç‰ˆæœŹäžŠćŸ·èĄŒç›žćŒçš„æžŹè©Š + +
    language: node_js
    node_js:
    - "7"
    - "6"
    - "5"
    - "4"
    install:
    - npm install
    script:
    - npm run test
    +
    + +

    + +# Team + +## Yoni Goldberg + +
    + +
    + +**Role:** Writer + +**About:** I'm an independent consultant who works with Fortune 500 companies and garage startups on polishing their JS & Node.js applications. More than any other topic I'm fascinated by and aims to master the art of testing. I'm also the author of [Node.js Best Practices](https://github.com/goldbergyoni/nodebestpractices) + +**📗 Online Course:** Liked this guide and wish to take your testing skills to the extreme? Consider visiting my comprehensive course [Testing Node.js & JavaScript From A To Z](https://www.testjavascript.com) + +
    + +**Follow:** + +- [🐩 Twitter](https://twitter.com/goldbergyoni/) +- [📞 Contact](https://testjavascript.com/contact-2/) +- [✉ Newsletter](https://testjavascript.com/newsletter//) + +
    +
    +
    + +## [Bruno Scheufler](https://github.com/BrunoScheufler) + +**Role:** Tech reviewer and advisor + +Took care to revise, improve, lint and polish all the texts + +**About:** full-stack web engineer, Node.js & GraphQL enthusiast + +
    +
    + +## [Ido Richter](https://github.com/idori) + +**Role:** Concept, design and great advice + +**About:** A savvy frontend developer, CSS expert and emojis freak + +## [Kyle Martin](https://github.com/js-kyle) + +**Role:** Helps keep this project running, and reviews security related practices + +**About:** Loves working on Node.js projects and web application security. diff --git a/readme.kr.md b/readme.kr.md index a4dd1383..f2f90843 100644 --- a/readme.kr.md +++ b/readme.kr.md @@ -321,7 +321,7 @@ it("화읎튞박슀 테슀튞: 낎부 method가 VAT 0을 받윌멎 0을 반환 ```javascript it("유횚한 제품을 ì‚­ì œí•˜ë €êł  할 때, ì˜Źë°”ë„ž ì œí’ˆêłŒ ì˜Źë°”ë„ž ê”Źì„± ì •ëłŽëĄœ 데읎터 ì•Ąì„žìŠ€ DAL을 한 ëȈ 혞출했는지 확읞한닀", async () => { // ìŽëŻž 제품을 ì¶”ê°€í–ˆë‹€êł  가정 - const dataAccessMock = sinon.mock(DAL); + const dataAccessMock = sinon.mock(DAL); // ìą‹ì§€ 않음: 낎부 테슀튞는 side-effect넌 위핎서가 ìŁŒìš” ëȘ©ì ì„ 위핎서 입니닀. dataAccessMock.expects("deleteProduct").once().withArgs(DBConfig, theProductWeJustAdded, true, false); new ProductService().deletePrice(theProductWeJustAdded); @@ -448,7 +448,7 @@ describe("Product service", () => { :white_check_mark: **읎렇êȌ 핎띌:** [슀냅샷 테슀튞](https://jestjs.io/docs/en/snapshot-testing)가 필요한 êČœìš° 왞부 파음읎 아닌 테슀튞의 음부 ([읞띌읞 슀냅샷](https://jestjs.io/docs/en/snapshot-testing#inline-snapshots))에 포핹 된 ì§§êł  집쀑된 슀냅샷(3~7 띌읞)만 ì‚Źìš©í•˜ì‹­ì‹œì˜€. 읎 지ìčšì„ 따넎멎 따로 ì„€ëȘ…읎 í•„ìš”ì—†êł  잘 êčšì§€ì§€ 않는 테슀튞가 됩니닀. -반멎에, 'êł ì „ì ìž 슀냅샷' íŠœí† ëŠŹì–Œ 및 ë„ê”ŹëŠ” 왞부에 큰 파음(예: ê”Źì„± 요소 랜더링 ë§ˆíŹì—…, API JSON êČ°êłŒ)넌 ì €ìž„í•˜êł , 테슀튞넌 싀행할 때 마닀 수신된 êČ°êłŒë„Œ 저임된 ëČ„ì „êłŒ ëč„ꔐ하Ʞ넌 권임합니닀. 예넌 듀얎, 읎êČƒì€ 1,000 띌읞(ìš°ëŠŹê°€ 절대 읜지 ì•Šêł  ì¶”ëĄ í•˜ì§€ 않을 3,000개의 데읎터 값을 가진)의 윔드넌 ìš°ëŠŹ 테슀튞에 ì•”ì‹œì ìœŒëĄœ 연êȰ할 수 있슔니닀. 왜 읎êČƒìŽ 잘ëȘ» 되었을êčŒìš”? 읎렇êČŒí•˜ë©Ž 테슀튞에 싀팚할 1,000 가지 읎유가 생êčë‹ˆë‹€. 한쀄만 변êČœë˜ì–Žë„ 슀냅샷읎 유횚하지 않êȌ ë˜êł , 읎런음읎 음얎날 가늄성읎 높슔니닀. 얌마나 ìžìŁŒ? ëȘšë“  êł”ë°±, ìŁŒì„ì—ì„œ í˜č은 ì‚Źì†Œí•œ CSS/HTML 변êČœì— 대핎서. 뿐만 아니띌 테슀튞 읎늄은 1,000 띌읞읎 변êČœë˜ì§€ 않았는지넌 나타낎Ʞ 때분에, 싀팚에 대한 닚서넌 ì œêł”í•˜ì§€ 않슔니닀. 또한 테슀튞 작성자가 ꞎ ëŹžì„œ(êČ€ì‚Źí•˜êł  확읞할 수 없는)넌 받아듀읎êȌ끔 합니닀. 읎 ëȘšë“  êČƒì€ 쎈점읎 ë§žì§€ì•Šêł  너묮 많은 êČƒì„ ë‹Źì„±í•˜ë €ëŠ” ëȘší˜ží•˜êł  간절한 테슀튞 슝상입니닀. +반멎에, 'êł ì „ì ìž 슀냅샷' íŠœí† ëŠŹì–Œ 및 ë„ê”ŹëŠ” 왞부에 큰 파음(예: ê”Źì„± 요소 랜더링 ë§ˆíŹì—…, API JSON êČ°êłŒ)넌 ì €ìž„í•˜êł , 테슀튞넌 싀행할 때 마닀 수신된 êČ°êłŒë„Œ 저임된 ëČ„ì „êłŒ ëč„ꔐ하Ʞ넌 권임합니닀. 예넌 듀얎, 읎êČƒì€ 1,000 띌읞(ìš°ëŠŹê°€ 절대 읜지 ì•Šêł  ì¶”ëĄ í•˜ì§€ 않을 3,000개의 데읎터 값을 가진)의 윔드넌 ìš°ëŠŹ 테슀튞에 ì•”ì‹œì ìœŒëĄœ 연êȰ할 수 있슔니닀. 왜 읎êČƒìŽ 잘ëȘ» 되었을êčŒìš”? 읎렇êČŒí•˜ë©Ž 테슀튞에 싀팚할 1,000 가지 읎유가 생êčë‹ˆë‹€. 한쀄만 변êČœë˜ì–Žë„ 슀냅샷읎 유횚하지 않êȌ ë˜êł , 읎런음읎 음얎날 가늄성읎 높슔니닀. 얌마나 ìžìŁŒ? ëȘšë“  êł”ë°±, ìŁŒì„ì—ì„œ í˜č은 ì‚Źì†Œí•œ CSS/HTML 변êČœì— 대핎서. 뿐만 아니띌 테슀튞 읎늄은 1,000 띌읞읎 변êČœë˜ì§€ 않았는지넌 나타낎Ʞ 때분에, 싀팚에 대한 닚서넌 ì œêł”í•˜ì§€ 않슔니닀. 또한 테슀튞 작성자가 ꞎ ëŹžì„œ(êČ€ì‚Źí•˜êł  확읞할 수 없는)넌 받아듀읎êȌ끔 합니닀. 읎 ëȘšë“  êČƒì€ 쎈점읎 ë§žì§€ì•Šêł  너묮 많은 êČƒì„ ë‹Źì„±í•˜ë €ëŠ” ëȘší˜ží•˜êł  간절한 테슀튞 슝상입니닀. ꞎ 왞부 슀냅샷읎 허용되는 êČœìš°ê°€ 거의 없닀는 점은 ìŁŒëȘ©í•  가ìč˜ê°€ 있슔니닀 - 데읎터가 아닌 슀킀마넌 assert 할 때(값 추출 및 필드에 집쀑) 또는 수신된 ëŹžì„œê°€ 거의 변êČœë˜ì§€ 않는 êČœìš° @@ -468,20 +468,18 @@ describe("Product service", () => { "Examples with Jest") ```javascript -it('TestJavaScript.com 읎 ì˜Źë°”ë„ŽêȌ 랜더링 된닀.', () => { - -//Arrange - -//Act -const receivedPage = renderer -.create( Test JavaScript < /DisplayPage>) -.toJSON(); - -//Assert -expect(receivedPage).toMatchSnapshot(); -// 읎제 2,000 띌읞의 ëŹžì„œë„Œ ì•”ëŹ”ì ìœŒëĄœ 유지합니닀. -// ëȘšë“  ì€„ë°”êżˆ 또는 ìŁŒì„ìŽ 테슀튞넌 망가뜚늜니닀. - +it("TestJavaScript.com 읎 ì˜Źë°”ë„ŽêȌ 랜더링 된닀.", () => { + //Arrange + + //Act + const receivedPage = renderer + .create( Test JavaScript ) + .toJSON(); + + //Assert + expect(receivedPage).toMatchSnapshot(); + // 읎제 2,000 띌읞의 ëŹžì„œë„Œ ì•”ëŹ”ì ìœŒëĄœ 유지합니닀. + // ëȘšë“  ì€„ë°”êżˆ 또는 ìŁŒì„ìŽ 테슀튞넌 망가뜚늜니닀. }); ``` @@ -490,18 +488,18 @@ expect(receivedPage).toMatchSnapshot(); ### :clap: ì˜Źë°”ë„ž 예: expectation읎 잘 ëłŽìŽêł  집쀑된닀. ```javascript -it('TestJavaScript.com 홈페읎지넌 ë°©ëŹží•˜ë©Ž 메뉎가 ëłŽìžë‹€.', () => { -//Arrange +it("TestJavaScript.com 홈페읎지넌 ë°©ëŹží•˜ë©Ž, 메뉎가 ëłŽìžë‹€.", () => { + //Arrange -//Act -receivedPage tree = renderer -.create( Test JavaScript < /DisplayPage>) -.toJSON(); + //Act + const receivedPage = renderer + .create( Test JavaScript ) + .toJSON(); -//Assert + //Assert -const menu = receivedPage.content.menu; -expect(menu).toMatchInlineSnapshot(` + const menu = receivedPage.content.menu; + expect(menu).toMatchInlineSnapshot(`