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Ă©.
+
+
+
+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
+
+
+
+```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
+
+
+
+
+
+
+© 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
+
+ 
+
+```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
+
+ 
+
+### :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
+
+
+
+```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
+
+
+
+```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
+
+
+```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"
+
+
+
+```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
+
+
+
+```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(`
+
+- Home
+- About
+- Contact
+
+`);
+});
+```
+
+
+
+
+
+## âȘ ïž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
+
+
+
+```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
+
+
+
+```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)
+
+
+
+```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
+
+
+```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", () => {});
+ });
+});
+```
+
+
+
+
+
+### :thumbsdown: Exemple d'anti-pattern: Une liste de tests Ă plat qui rend l'identification du problĂšme difficile pour le lecteur
+
+
+
+```javascript
+test("Then the response status should decline", () => {});
+
+test("Then it should send email", () => {});
+
+test("Then there should not be a new transfer record", () => {});
+```
+
+
+
+
+
+
+
+
+
+## âȘ ïž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'
+
+
+
+âșïžExample: [YouTube: âBeyond Unit Tests: 5 Shiny Node.JS Test Types (2018)â (Yoni Goldberg)](https://www.youtube.com/watch?v=-2zP494wdUY&feature=youtu.be)
+
+
+
+
+
+
+
+
+
+## âȘ ïž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)
+
+
+
+ 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:
+
+
+
+
+
+
+
+
+
+## âȘ ïž 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
+
+
+
+```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:
+
+
+
+
+
+
+
+
+
+## âȘ ïž 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
+
+
+
+
+
+
+
+## âȘ ïž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
+
+
+
+```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
+
+ 
+
+```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
+
+
+
+```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
+
+ 
+
+```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)
+
+
+
+
+```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
+
+
+
+â
**Ă 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
+
+
+
+
+
+
+
+## âȘ ïž 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
+ 
+
+```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
+
+
+```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
+
+
+
+```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
+
+
+```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
+
+
+
+
+
+
+
+
+## âȘ ïž 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
+
+
+
+
+
+### :clap: Bien faire les choses, exemple: Configurer wraith pour capturer et comparer les snapshots de l'UI
+
+
+
+```
+â# 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
+
+ 
+
+```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
+
+
+
+
+
+### :clap: Bien faire les choses, exemple: Configurer la couverture par composant (avec 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)
+
+
+
+
+
+
+
+## âȘ ïž 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é
+
+
+
+```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)
+
+")
+
+
+
+
+
+## âȘ ïž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
+
+
+
+
+
+
+
+## âȘ ïž 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))
+
+")
+
+
+
+
+
+## âȘ ïž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
+
+```
+
+
+
+
+
+
+
+
+
+## âȘ ïž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
+
+
+
+
+
+
+
+## âȘ ïž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
+
+
+
+
+
+
+
+## âȘ ïž 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
+
+
- 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
- Choisis un fournisseur qui a une intégration Docker native
- Ă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
- Facilite le parcours des informations de build, cela inclut les rapports de tests, de couverture, de mutation, les logs ..etc
- 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)
- 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
- Augmente explicitement la version dans un build de release, ou au moins vérifie que le développeur l'a fait
- Build une fois et effectue toute les inspections sur l'artefact de build (e.g. Docker image)
- 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!
+
+
+
+
+
+
+
+
+
+
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
-
+
```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
-
+
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 ŰčۧÙÛ Ű§Ű±Ű§ŰŠÙ Ù
Û ŰŻÙÙŰŻŰ ŰšÙ ŰŻŰłŰȘ ŰąÙ۱ۯ. ÙÙŰ· ŰšÙ Ű§ÙۯۧŰČÙ ÙÛۧŰČ ŰȘŰłŰȘ Ú©ÙÛŰŻŰ ŰłŰčÛ Ú©ÙÛŰŻ ŰąÙ Ű±Ű§ ŰČÛ۱ک ÙÚŻÙ ŰŻŰ§Ű±ÛŰŻŰ ÚŻŰ§ÙÛ Ű§ÙÙۧŰȘ ŰŰȘÛ Ű§Ű±ŰČŰŽ ŰąÙ Ű±Ű§ ۯۧ۱ۯ Ú©Ù ŰšŰ±ŰźÛ Ű§ŰČ ŰȘŰłŰȘ Ùۧ ۱ۧ Ú©Ùۧ۱ ۚگ۰ۧ۱ÛŰŻ Ù ÙۧۚÙÛŰȘ ۧ۷Ù
ÛÙŰ§Ù Ű±Ű§ ŰšŰ±Ű§Û ÚŰ§ŰšÚ©Û Ù ŰłŰ§ŰŻÚŻÛ Ù
ŰčۧÙ
ÙÙ Ú©ÙÛŰŻ.
+
+
+
+ŰšÛŰŽŰȘ۱ ŰȘÙŰ”ÛÙ ÙŰ§Û ŰČÛ۱ Ù
ŰŽŰȘÙۧŰȘ ۧÛÙ Ű§Ű”Ù Ű§ŰłŰȘ.
+
+### ŰąÙ
Ű§ŰŻÙ ŰšŰ±Ű§Û ŰŽŰ±ÙŰčŰ
+
+
+
+# ۚ۟ێ 1: ŰąÙۧŰȘÙÙ
Û ŰȘŰłŰȘ
+
+
+
+## âȘ ïž 1.1 ێۧÙ
Ù 3 ÙŰłÙ
ŰȘ ۯ۱ Ù۱ ÙۧÙ
ŰąŰČÙ
ÙÙ
+
+:white_check_mark: **ۧÙۏۧÙ
ۯۧۯÙ:** ÛÚ© ÚŻŰČۧ۱ێ ŰąŰČÙ
ۧÛŰŽÛ ŰšŰ§ÛŰŻ ŰšÚŻÙÛŰŻ Ú©Ù ŰąÛۧ ۚۧŰČŰšÛÙÛ ŰšŰ±ÙۧÙ
Ù ÙŰčÙÛ Ű§ÙŰČۧÙ
ۧŰȘ ۧÙŰ±Ű§ŰŻÛ Ű±Ű§ Ú©Ù ÙŰČÙÙ
Ű§Ù ŰšŰ§ Ú©ŰŻ ۹ێÙۧ ÙÛŰłŰȘÙŰŻ ۚ۱۹ÙŰ±ŰŻÙ Ù
Û Ú©ÙŰŻ: ŰȘŰłŰȘ Ú©ÙÙŰŻÙ Ű Ù
ÙÙŰŻŰł DevOps Ú©Ù ŰŻŰ± ŰŰ§Ù Ű§ŰłŰȘÙ۱ۧ۱ ۧ۳ŰȘ Ù ŰŽÙ
ۧ ŰąÛÙŰŻÙ ŰŻÙ ŰłŰ§Ù ŰšŰčŰŻ. ۧگ۱ ŰąŰČÙ
ÙÙ Ùۧ ۯ۱ ŰłŰ·Ű Ű§ÙŰČۧÙ
ۧŰȘ Ű”ŰŰšŰȘ Ú©ÙÙŰŻ Ù ŰŽŰ§Ù
Ù 3 ۚ۟ێ ۚۧێÙŰŻŰ Ù
Û ŰȘÙŰ§Ù ŰšÙ ŰšÙŰȘ۱ÛÙ ÙŰŹÙ ŰšÙ Ű§ÛÙ Ű§Ù
۱ ŰŻŰłŰȘ ÛۧÙŰȘ:
+
+(1) ÚÙ ÚÛŰČÛ ŰŻŰ± ŰŰ§Ù ŰąŰČÙ
ۧÛŰŽ ۧ۳ŰȘŰ ŰšÙ ŰčÙÙŰ§Ù Ù
۫ۧÙŰ Ù
ŰȘŰŻ ProductsService.addNewProduct
+
+(2) ۯ۱ ÚÙ ŰŽŰ±Ű§ÛŰ· Ù ŰłÙۧ۱ÛÙÛÛŰ ŰšŰ±Ű§Û Ù
Ű«Ű§Ù ÙÛÚ ÙÛÙ
ŰȘÛ ŰšÙ Ù
ŰȘŰŻ ŰŻŰ§ŰŻÙ ÙÙ
Û ŰŽÙŰŻ
+
+(3) ÙŰȘÛŰŹÙ Û ÙŰ§ŰšÙ Ű§ÙŰȘ۞ۧ۱ ÚÛŰłŰȘŰ ŰšÙ ŰčÙÙŰ§Ù Ù
۫ۧÙŰ Ù
ŰŰ”ÙÙ ŰŹŰŻÛŰŻ ŰȘۧÛÛŰŻ ÙŰŽŰŻÙ Ű§ŰłŰȘ
+
+
+
+â **ۯ۱ ŰșÛ۱ ۧÛÙ Ű”Ù۱ŰȘ:** ÛÚ© ۧ۳ŰȘÙ۱ۧ۱ ŰšÙâŰȘۧŰČÚŻÛ ÙۧÙ
ÙÙÙ ŰšÙŰŻŰ ŰȘŰłŰȘ ۚۧ ÙۧÙ
«ۧÙŰČÙŰŻÙ Ù
ŰŰ”ÙÙ» ÙۧÙ
ÙÙÙ ŰšÙŰŻ. ŰąÛۧ ۧÛÙ ŰšÙ ŰŽÙ
ۧ Ù
Û ÚŻÙÛŰŻ Ú©Ù ŰŻÙÛÙŰ§Ù ÚÙ ÚÛŰČÛ ŰźŰ±Ű§Űš ۧ۳ŰȘŰ
+
+
+
+**đ ŰȘÙŰŹÙ ŰŻŰ§ŰŽŰȘÙ ŰšŰ§ŰŽÛŰŻ:** Ù۱ ÚŻÙÙÙÙ ŰŻŰ§Ű±Ű§Û ÙÙ
ÙÙÙ Ú©ŰŻ Ù ÚŻŰ§ÙÛ Ű§ÙÙۧŰȘ ÛÚ© ŰȘŰ”ÙÛ۱ ŰȘŰ”ÙÛŰ±Û ÙÛŰČ Ù
Û ŰšŰ§ŰŽŰŻ. ŰšŰ±Ű§Û ÚŻŰłŰȘ۱ێ Ú©ÙÛÚ© Ú©ÙÛŰŻ
+
+
+â ÙÙ
ÙÙÙ Ú©ŰŻ
+
+
+
+### :clap: Ù
Ű«Ű§Ù ŰŻŰ±ŰłŰȘ: ÙۧÙ
ŰȘŰłŰȘÛ Ú©Ù Ű§ŰČ 3 ÙŰłÙ
ŰȘ ŰȘŰŽÚ©ÛÙ ŰŽŰŻÙ Ű§ŰłŰȘ
+
+
+
+```javascript
+//1. ÙۧŰŰŻ ۯ۱ ŰŰ§Ù ŰȘŰłŰȘ
+describe('۟ۯÙ
ۧŰȘ Ù
ŰŰ”ÙÙۧŰȘ', function() {
+ describe('ۧÙŰČÙŰŻÙ Ù
ŰŰ”ÙÙ ŰŹŰŻÛŰŻ', function() {
+ //2. ŰłÙۧ۱ÛÙ Ù 3. ۧÙŰȘ۞ۧ۱
+ it('ÙÙگۧÙ
Û Ú©Ù ÙÛÙ
ŰȘÛ Ù
ێ۟۔ ÙŰŽŰŻÙ Ű§ŰłŰȘŰ Ù۶ŰčÛŰȘ Ù
ŰŰ”ÙÙ ŰŻŰ± ۧÙŰȘ۞ۧ۱ ŰȘۧÛÛŰŻ ۧ۳ŰȘ', ()=> {
+ const newProduct = new ProductService().add(...);
+ expect(newProduct.status).to.equal('pendingApproval');
+ });
+ });
+});
+
+```
+
+
+
+### :clap: Ù
Ű«Ű§Ù ŰŻŰ±ŰłŰȘ: ÙۧÙ
ŰąŰČÙ
ۧÛŰŽÛ Ú©Ù Ű§ŰČ 3 ÙŰłÙ
ŰȘ ŰȘŰŽÚ©ÛÙ ŰŽŰŻÙ Ű§ŰłŰȘ
+
+
+
+
+
+
+## âȘ ïž 1.2 ŰȘŰłŰȘ ÙŰ§Û ŰłŰ§ŰźŰȘۧ۱ ۚۧ ۧÙÚŻÙÛ AAA
+
+:white_check_mark: **ۧÙۏۧÙ
ۯۧۯÙ:** ۳ۧ۟ŰȘۧ۱ ŰȘŰłŰȘ ÙŰ§Û ŰźÙŰŻ ۱ۧ ۚۧ 3 ۚ۟ێ ŰšÙ ŰźÙŰšÛ ŰŹŰŻŰ§ ŰŽŰŻÙ Ù
Ùۯۧ۱ ŰŻÙÛ Ú©ÙÛŰŻŰ Ű§ŰŹŰ±Ű§ Ú©ÙÛŰŻ Ù Ù
ÙۧÛŰłÙ Ú©ÙÛŰŻ (AAA). ÙŸÛ۱ÙÛ Ű§ŰČ Ű§ÛÙ ŰłŰ§ŰźŰȘۧ۱ ŰȘ۶Ù
ÛÙ Ù
Û Ú©ÙŰŻ Ú©Ù ŰźÙۧÙÙŰŻÙ ÙÛÚ CPU Ù
ŰșŰČÛ Ű±Ű§ ŰšŰ±Ű§Û ŰŻŰ±Ú© ۚ۱ÙۧÙ
Ù ŰąŰČÙ
ۧÛŰŽÛ ŰźŰ±ŰŹ ÙÙ
Û Ú©ÙŰŻ:
+
+ۧÙÙ A - Ù
Ùۯۧ۱ ۯۧۯÙ: ŰȘÙ
ۧÙ
Ú©ŰŻÙŰ§Û Ű±Ű§Ù Ű§ÙۯۧŰČÛ ŰšŰ±Ű§Û Ű±ŰłŰ§ÙŰŻÙ ŰłÛŰłŰȘÙ
ŰšÙ ŰłÙۧ۱ÛÙÛÛ Ú©Ù ŰȘŰłŰȘ ÙŰŻÙ ŰąÙ ŰŽŰšÛÙ ŰłŰ§ŰČÛ Ű§ŰłŰȘ. ۧÛÙ Ù
Ù
Ú©Ù Ű§ŰłŰȘ ێۧÙ
Ù ÙÙ
ÙÙÙ ŰłŰ§ŰČÛ ÙۧŰŰŻ ۯ۱ ŰŰ§Ù ŰąŰČÙ
ۧÛŰŽ ۳ۧŰČÙŰŻÙŰ Ű§Ű¶Ű§ÙÙ Ú©Ű±ŰŻÙ Ű±Ú©Ù۱ۯÙŰ§Û DBŰ mocking/stubbing ۧێÛۧ Ù Ù۱ Ú©ŰŻ ŰąÙ
Ű§ŰŻÙ ŰłŰ§ŰČÛ ŰŻÛگ۱ ۚۧێۯ.
+
+ŰŻÙÙ
A - ۧۏ۱ۧ: ÙۧŰŰŻ ŰȘŰŰȘ ŰȘŰłŰȘ ۱ۧ ۧۏ۱ۧ Ú©ÙÛŰŻ. Ù
ŰčÙ
ÙÙۧ 1 ۟۷ Ú©ŰŻ
+
+ŰłÙÙ
A - Ù
ÙۧÛŰłÙ: ۧ۷Ù
ÛÙŰ§Ù ŰŰ§Ű”Ù Ú©ÙÛŰŻ Ú©Ù Ű§Ű±ŰČŰŽ ۯ۱ÛۧÙŰȘÛ Ű§ÙŰȘ۞ۧ۱ۧŰȘ ۱ۧ ۚ۱۹ÙŰ±ŰŻÙ Ù
Û Ú©ÙŰŻ. Ù
ŰčÙ
ÙÙۧ 1 ۟۷ Ú©ŰŻ
+
+
+
+â **ۯ۱ ŰșÛ۱ ۧÛÙ Ű”Ù۱ŰȘ:** ÙÙ ŰȘÙÙۧ ۳ۧŰčŰȘ Ùۧ ŰšŰ±Ű§Û ŰŻŰ±Ú© Ú©ŰŻ ۧ۔ÙÛ ÙÙŰȘ Ù
Û ÚŻŰ°Ű§Ű±ÛŰŻŰ ŰšÙÚ©Ù ÚÛŰČÛ Ú©Ù ŰšŰ§ÛŰŻ ŰłŰ§ŰŻÙ ŰȘ۱ÛÙ ÙŰłÙ
ŰȘ ۱ÙŰČ ŰšŰ§ŰŽŰŻ (ŰȘŰłŰȘ) Ù
ŰșŰČ ŰŽÙ
ۧ ۱ۧ Ú©ŰŽ Ù
Û ŰŻÙŰŻ.
+
+
+
+â ÙÙ
ÙÙÙ Ú©ŰŻ
+
+
+
+### :clap: Ù
Ű«Ű§Ù ŰŻŰ±ŰłŰȘ: ŰȘŰłŰȘÛ Ú©Ù ŰšŰ§ ۧÙÚŻÙÛ AAA ۳ۧ۟ŰȘۧ۱ ÛۧÙŰȘÙ Ű§ŰłŰȘ
+
+ 
+
+```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 ŰȘŰČŰŠÛÙ Ù
Û Ú©ÙŰŻ()
+
+
+
+â ÙÙ
ÙÙÙ Ú©ŰŻ
+
+ 
+
+### :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: Ù
Ű«Ű§Ù Ű¶ŰŻ ۧÙÚŻÙ: ÛÚ© Ù
Ù۱ۯ ŰȘŰłŰȘÛ ŰšŰŻÙÙ ÙÛÚ ŰŻÙÛÙ Ù
ÙŰŹÙÛ ÙŰ·ŰčۧŰȘ ۯۧ۟ÙÛ Ű±Ű§ ŰȘŰłŰȘ Ù
Û Ú©ÙŰŻ
+
+
+
+```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 Ùۧ ۚ۱ ۱ÙÛ ŰŻŰ§ŰźÙÛ ŰȘÙ
۱کŰČ Ù
Û Ú©ÙÙŰŻ
+
+
+
+```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: Ù
Ű«Ű§Ù Ű¶ŰŻ ۧÙÚŻÙ: Ù
ŰŹÙ
ÙŰčÙ ŰąŰČÙ
ۧÛŰŽÛ Ú©Ù ŰšÙ ŰŻÙÛÙ ŰŻŰ§ŰŻÙ ÙŰ§Û ŰșÛ۱ÙۧÙŰčÛ ÙŰšÙÙ Ù
Û ŰŽÙŰŻ
+
+
+
+```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: Ù
Ű«Ű§Ù ŰŻŰ±ŰłŰȘ: ŰąŰČÙ
ۧÛŰŽ ۚ۳ÛŰ§Ű±Û Ű§ŰČ ŰŹŰ§ÛÚŻŰŽŰȘ ÙŰ§Û Ù۱ÙŰŻÛ ŰšŰ§ "ŰšŰ±Ű±ŰłÛ ŰłŰ±ÛŰč"
+
+
+
+```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 ۟۷ Ú©ŰŻ
+
+
+
+```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(`
+
+- Home
+- About
+- Contact
+
+`);
+});
+```
+
+
+
+
+
+## âȘ ïžÚ©ŰŻ Ű±Ű§ Ú©ÙŸÛ Ú©ÙÛŰŻŰ Ű§Ù
ۧ ÙÙŰ· ŰąÙÚÙ ÙۧŰČÙ
ۧ۳ŰȘ
+
+:white_check_mark: **ۧÙۏۧÙ
ۯۧۯÙ:** ŰȘÙ
ۧÙ
ŰŹŰČŰŠÛۧŰȘ ÙۧŰČÙ
۱ۧ Ú©Ù ŰšŰ± ÙŰȘÛŰŹÙ ŰąŰČÙ
ۧÛŰŽ ŰȘŰŁŰ«Û۱ Ù
Û ÚŻŰ°Ű§Ű±ŰŻŰ Ùۧ۱ۯ Ú©ÙÛŰŻŰ Ű§Ù
ۧ ÙÙ ŰšÛŰŽŰȘ۱. ŰšÙ ŰčÙÙŰ§Ù Ù
۫ۧÙŰ ŰȘŰłŰȘÛ Ű±Ű§ ۯ۱ Ù۞۱ ŰšÚŻÛ۱ÛŰŻ Ú©Ù ŰšŰ§ÛŰŻ 100 ۟۷ Ù۱ÙŰŻÛ JSON ۱ۧ ۯ۱ Ù۞۱ ŰšÚŻÛ۱ÛŰŻâ-âÚ۳ۚۧÙŰŻÙ ŰąÙ ŰŻŰ± Ù۱ ŰȘŰłŰȘ ۟۳ŰȘÙ Ú©ÙÙŰŻÙ Ű§ŰłŰȘ. ۧ۳ŰȘ۟۱ۧۏ ŰąÙ Ű§ŰČ ŰźŰ§Ű±ŰŹ ŰšÙ transferFactory.getJSON() ۚۧŰčŰ« Ù
ÛâŰŽÙŰŻ ŰȘŰłŰȘ Ù
ŰšÙÙ
ۚۧÙÛ ŰšÙ
ۧÙŰŻâ-âۚۯÙÙ ŰŻŰ§ŰŻÙŰ Ű§Ű±ŰȘۚۧ۷ ÙŰȘÛŰŹÙ ŰąŰČÙ
ۧÛŰŽ ۚۧ ŰčÙŰȘ ŰŻŰŽÙۧ۱ ۧ۳ŰȘ ("Ú۱ۧ Ù۱ۧ۱ ۧ۳ŰȘ Ù۶ŰčÛŰȘ 400 ۱ۧ ۚ۱گ۱ۯۧÙŰŻŰ"). Ú©ŰȘۧۚ Ú©Ùۧ۳ÛÚ© ۧÙÚŻÙÙŰ§Û ÙۧŰŰŻ x ÙۧÙ
ۧÛÙ Ű§ÙÚŻÙ Ű±Ű§ «Ù
ÙÙ
Ű§Ù Ű§ŰłŰ±Ű§Ű±ŰąÙ
ÛŰČ» گ۰ۧێŰȘÙŰŻ - âÚÛŰČÛ ÙۧۯÛŰŻÙ ŰšŰ± ÙŰȘۧÛŰŹ ŰąŰČÙ
ۧÛŰŽ Ù
ۧ ŰȘŰŁŰ«Û۱ گ۰ۧێŰȘŰ Ù
ۧ ŰŻÙÛÙŰ§Ù ÙÙ
ÛâۯۧÙÛÙ
ÚÙ ÚÛŰČÛ. Ù
ۧ Ù
ÛâŰȘÙۧÙÛÙ
ۚۧ ۧ۳ŰȘ۟۱ۧۏ ÙŰ·ŰčۧŰȘ Ű·ÙÙۧÙÛ ÙŰ§ŰšÙ ŰȘک۱ۧ۱ ۯ۱ ۟ۧ۱ۏ Ù ŰšÙ Ű”Ű±Ű§ŰŰȘ Ű§ŰŽŰ§Ű±Ù Ú©ÙÛÙ
Ú©Ù Ú©ŰŻŰ§Ù
ŰŹŰČŰŠÛۧŰȘ ۟ۧ۔ ŰšŰ±Ű§Û ŰąŰČÙ
ۧÛŰŽ Ù
ÙÙ
ÙŰłŰȘÙŰŻŰ ŰšÙŰȘ۱ ŰčÙ
Ù Ú©ÙÛÙ
. ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ Ű§ŰČ Ù
Ű«Ű§Ù ŰšŰ§ÙŰ§Ű ŰąŰČÙ
ÙÙ Ù
ÛâŰȘÙۧÙŰŻ ÙŸŰ§Ű±Ű§Ù
ŰȘ۱ÙۧÛÛ Ű±Ű§ ÙŸŰ§Űł Ú©ÙŰŻ Ú©Ù ŰąÙÚÙ Ù
ÙÙ
ۧ۳ŰȘ ۱ۧ ۚ۱ۏ۳ŰȘÙ Ù
ÛâÚ©ÙŰŻ: transferFactory.getJSON({sender: undefined}). ۯ۱ ۧÛÙ Ù
۫ۧÙŰ ŰźÙۧÙÙŰŻÙ ŰšŰ§ÛŰŻ ÙÙŰ±Ű§Ù Ű§ŰłŰȘÙۚۧ۷ Ú©ÙŰŻ Ú©Ù ÙŰłÙ
ŰȘ ۟ۧÙÛ Ù۱۳ŰȘÙŰŻÙ ŰŻÙÛÙÛ Ű§ŰłŰȘ Ú©Ù ŰąŰČÙ
ÙÙ ŰšŰ§ÛŰŻ Ù
ÙŰȘ۞۱ ŰźŰ·Ű§Û Ű§ŰčŰȘۚۧ۱۳ÙŰŹÛ Ûۧ Ù۱ ÙŰȘÛŰŹÙ Ú©Ű§ÙÛ Ù
ŰŽŰ§ŰšÙ ŰŻÛÚŻŰ±Û ŰšŰ§ŰŽŰŻ.
+
+
+â **ۯ۱ ŰșÛ۱ ۧÛÙ Ű”Ù۱ŰȘ:** Ú©ÙŸÛ Ú©Ű±ŰŻÙ 500 ۟۷ JSON ۚۧŰčŰ« Ù
ÛâŰŽÙŰŻ Ú©Ù ŰȘŰłŰȘâÙŰ§Û ŰŽÙ
ۧ ÙŰ§ŰšÙ ÙÚŻÙŰŻŰ§Ű±Û Ù ŰźÙۧÙŰŻÙ ÙۚۧێÙŰŻ. ۏۧۚۏۧÛÛ ÙÙ
Ù ÚÛŰČ ŰšÙ ŰšÛ۱ÙÙ ŰšŰ§ ŰąŰČÙ
ÙÙ ÙŰ§Û Ù
ŰšÙÙ
Û Ú©Ù ŰŻŰ±Ú© ŰąÙÙۧ ۳۟ŰȘ ۧ۳ŰȘ ŰšÙ ÙŸŰ§ÛŰ§Ù Ù
Û Ű±ŰłŰŻ
+
+
+
+â ÙÙ
ÙÙÙ Ú©ŰŻ
+
+
+
+### :thumbsdown: Ù
Ű«Ű§Ù Ű¶ŰŻ ۧÙÚŻÙ: ŰŽÚ©ŰłŰȘ ŰȘŰłŰȘ ÙۧÙ
ێ۟۔ ۧ۳ŰȘ ŰČÛ۱ۧ ÙÙ
Ù ŰčÙŰȘ ŰźŰ§Ű±ŰŹÛ Ű§ŰłŰȘ Ù ŰŻŰ± JSON ŰšŰČ۱گ ÙŸÙÙŰ§Ù Ù
Û ŰŽÙŰŻ
+
+
+
+```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: Ù
Ű«Ű§Ù Ű¶ŰŻ ۧÙÚŻÙ: ÛÚ© Ù
Ù۱ۯ ŰȘŰłŰȘ Ű·ÙÙۧÙÛ Ú©Ù ŰłŰčÛ Ù
ÛâÚ©ÙŰŻ ÙŰŹÙŰŻ ۟۷ۧ ۱ۧ ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ Ű§ŰČ ŰąŰČÙ
ÙÙ ŰȘŰŁÛÛŰŻ Ú©ÙŰŻ.
+
+
+
+```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 ۧÙۏۧÙ
ÙÙ
Û ŰŻÙÙŰŻ Ù ŰŰȘÛ ŰČÙ
ۧÙÛ Ú©Ù ŰȘÙŰłŰčÙ ŰŻÙÙŰŻÙ ŰŻŰ± ŰŰ§Ù ŰȘۧÛÙŸ Ú©Ű±ŰŻÙ Ű§ŰłŰȘ Ù
Û ŰȘÙۧÙÙŰŻ Ù
ک۱۱ۧ ۧۏ۱ۧ ŰŽÙÙŰŻ)
+
+
+
+```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: Ù
Ű«Ű§Ù ŰŻŰ±ŰłŰȘ: Ù
ŰŹÙ
ÙŰčÙ ŰłŰ§ŰźŰȘŰ§Ű±Û ŰšŰ§ ÙۧÙ
ÙۧŰŰŻ ŰȘŰŰȘ ŰąŰČÙ
ۧÛŰŽ Ù ŰłÙۧ۱ÛÙÙۧ ŰšÙ ÚŻŰČۧ۱ێ Ù
ÙŰ§ŰłŰšÛ Ù
Ùۏ۱ Ù
Û ŰŽÙŰŻ Ú©Ù ŰŻŰ± ŰČÛ۱ ÙŰŽŰ§Ù ŰŻŰ§ŰŻÙ ŰŽŰŻÙ Ű§ŰłŰȘ.
+
+
+
+```javascript
+// ÙۧŰŰŻ ۯ۱ ŰŰ§Ù ŰȘŰłŰȘ
+describe("۳۱ÙÛŰł ۧÙŰȘÙۧÙ", () => {
+ //ŰłÙۧ۱ÛÙ
+ describe("ŰČÙ
ۧÙÛ Ú©Ù Ű§ŰčŰȘŰšŰ§Ű±Û ÙŰŹÙŰŻ Ùۯۧ۱ۯ", () => {
+ //ۧÙŰȘ۞ۧ۱
+ test("ŰłÙŸŰł Ù۶ŰčÛŰȘ ÙŸŰ§ŰłŰź ۚۧÛŰŻ کۧÙŰŽ Ûۧۚۯ", () => {});
+
+ //ۧÙŰȘ۞ۧ۱
+ test("ŰłÙŸŰł ۚۧÛŰŻ ŰšŰ±Ű§Û Ù
ŰŻÛ۱ ۧÛÙ
ÛÙ Ű§Ű±ŰłŰ§Ù ŰŽÙŰŻ", () => {});
+ });
+});
+```
+
+
+
+
+
+### :thumbsdown: Ù
Ű«Ű§Ù Ű¶ŰŻ ۧÙÚŻÙ: ÛÚ© ÙÛŰłŰȘ Ù
ŰłŰ·Ű Ű§ŰČ ŰȘŰłŰȘ Ùۧ ŰŽÙۧ۳ۧÛÛ ŰŻŰ§ŰłŰȘŰ§Ù ÙŰ§Û Ú©Ű§Ű±ŰšŰ± Ù Ű§Ű±ŰȘۚۧ۷ ŰšÛÙ ŰȘŰłŰȘ ÙŰ§Û ŰŽÚ©ŰłŰȘ ŰźÙŰ±ŰŻÙ Ű±Ű§ ŰšŰ±Ű§Û ŰźÙۧÙÙŰŻÙ ŰłŰźŰȘ ŰȘ۱ Ù
Û Ú©ÙŰŻ.
+
+
+
+```javascript
+test("ŰłÙŸŰł Ù۶ŰčÛŰȘ ÙŸŰ§ŰłŰź ۚۧÛŰŻ کۧÙŰŽ Ûۧۚۯ", () => {});
+
+test("ŰłÙŸŰł ۚۧÛŰŻ ۧÛÙ
ÛÙ ŰšÙ۱۳ŰȘŰŻ", () => {});
+
+test("ۯ۱ ۧÛÙ Ű”Ù۱ŰȘ ÙۚۧÛŰŻ ۳ۧۚÙÙ ÙÙÙ Ù Ű§ÙŰȘÙۧÙۧŰȘ ŰŹŰŻÛŰŻÛ ÙŰŹÙŰŻ ۯۧێŰȘÙ ŰšŰ§ŰŽŰŻ", () => {});
+```
+
+
+
+
+
+
+
+
+
+## âȘ ïž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: Ù
Ű«Ű§Ù ŰŻŰ±ŰłŰȘ: ŰłÛÙŰŻÛ ŰłŰ±ÛŰŻÙŰ§Ù ÛÚ© ÙÙ
ÙÙÙ Ú©Ű§Ű± ŰąŰČÙ
ۧÛŰŽÛ ŰșÙÛ Ű±Ű§ ۯ۱ ÙŸŰłŰȘ ŰŽÚŻÙŰȘâۧÙÚŻÛŰČ ŰźÙŰŻ «ŰȘŰłŰȘ Ú©Ű±ŰŻÙ Ù
Ûک۱Ù۳۱ÙÛŰłâÙۧ» ÙŸÛŰŽÙÙۧۯ Ù
ÛâÚ©ÙŰŻ - ŰšÙ ÙÙ
ÛÙ Ű±ÙŰŽ.
+
+
+
+âșïžExample: [YouTube: âBeyond Unit Tests: 5 Shiny Node.JS Test Types (2018)â (Yoni Goldberg)](https://www.youtube.com/watch?v=-2zP494wdUY&feature=youtu.be)
+
+
+
+
+
+
+
+
+
+## âȘ ïž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 ۯ۱ Ù۱۹ÛÙŰŻ (۳۱ÛŰč Ù ÙŸÙŰŽŰŽ ÚÙŰŻÛÙ ÙۧÛÙ)
+
+
+
+ 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: Ù
ÙŰ§Ù ŰŻŰ±ŰłŰȘ:
+
+
+
+
+
+
+
+
+
+## âȘ ïž 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
+
+
+
+```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Ű ÛÚ© ۧۚŰČۧ۱ ŰȘŰŹŰ§Ű±Û Ű§ŰłŰȘ Ú©Ù Ù
Û ŰȘÙۧÙŰŻ Ù
ŰȘŰŻ ÙŰ§Û ÙŸÛÚÛŰŻÙ Ű±Ű§ ŰŽÙۧ۳ۧÛÛ Ú©ÙŰŻ:
+
+
+
+
+
+
+
+
+
+## âȘ ïž 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 ۱ۧ ۧÛۏۧۯ Ú©ÙŰŻŰ ŰšÙۧۚ۱ۧÛÙ Ù
ÛâŰȘÙۧÙÛŰŻ Ù
ÛŰČŰ§Ù Ű§ÙŰč۷ۧÙâÙŸŰ°ÛŰ±Û ŰšŰ±ÙۧÙ
Ù ŰŽÙ
ۧ ۯ۱ ۚ۱ۧۚ۱ Ù۱ۏ Ù Ù
۱ۏ ۱ۧ ŰąŰČÙ
ۧÛŰŽ Ú©ÙÛŰŻ.
+
+
+
+
+
+
+
+## âȘ ïž2.7 ۧۏŰȘÙۧۚ ۧŰČ ŰȘŰŹÙÛŰČۧŰȘ ŰȘŰłŰȘ ŰŹÙۧÙÛ Ù ŰŻŰ§ÙÙŰ Ű§Ű¶Ű§ÙÙ Ú©Ű±ŰŻÙ ŰŻŰ§ŰŻÙ Ùۧ ۯ۱ Ù۱ ŰąŰČÙ
ÙÙ
+
+:white_check_mark: **ۧÙۏۧÙ
ۯۧۯÙ:** ۚۧ ŰȘÙŰŹÙ ŰšÙ ÙۧÙÙÙ Ű·ÙۧÛÛ (ÚŻÙÙÙÙ 0)Ű Ù۱ ŰȘŰłŰȘ ۚۧÛŰŻ Ù
ŰŹÙ
ÙŰčÙ Ű§Û Ű§ŰČ Ű±ŰŻÛÙ ÙŰ§Û DB ŰźÙŰŻ ۱ۧ ۧ۶ۧÙÙ Ú©Ű±ŰŻÙ Ù Ű±ÙÛ ŰąÙ ŰčÙ
Ù Ú©ÙŰŻ ŰȘۧ ۧŰČ ŰŹÙŰȘ ŰŽŰŻÙ ŰŹÙÙÚŻÛŰ±Û Ú©ÙŰŻ Ù ŰšÙ Ű±Ű§ŰŰȘÛ ŰŻŰ± Ù
Ù۱ۯ ۏ۱ÛŰ§Ù ŰȘŰłŰȘ ۧ۳ŰȘŰŻÙŰ§Ù Ú©ÙŰŻ. ۯ۱ ÙۧÙŰčŰ Ű§ÛÙ Ű§Ù
۱ ۧŰșÙŰš ŰȘÙ۳۷ ŰąŰČÙ
ۧÛŰŽâÚ©ÙÙۯگۧÙÛ Ú©Ù ÙŰšÙ Ű§ŰČ Ű§ŰŹŰ±Ű§Û ŰąŰČÙ
ۧÛŰŽâÙۧ (Ú©Ù ŰšÙ ŰčÙÙŰ§Ù Â«ŰȘŰłŰȘ ÙÛÚ©ŰłÚ۱» ÙÛŰČ ŰŽÙۧ۟ŰȘÙ Ù
ÛâŰŽÙŰŻ) ۱ۧ ۚۧ ۯۧۯÙâÙۧ ŰšÙ Ù
ÙŰžÙ۱ ŰšÙŰšÙŰŻ ŰčÙ
Ùک۱ۯ Ù
ÛâۯۧÙÙŰŻŰ ÙÙ۶ Ù
ÛâŰŽÙŰŻ. ۯ۱ ŰۧÙÛ Ú©Ù ŰčÙ
Ùک۱ۯ ۯ۱ ÙۧÙŰč ÛÚ© Ùگ۱ۧÙÛ Ù
ŰčŰȘۚ۱ ۧ۳ŰȘ. ŰčÙ
ÙۧÙŰ Ù۱ Ù
Ù۱ۯ ŰąŰČÙ
ۧÛŰŽÛ Ű±Ű§ ŰšÙ Ű”Ű±Ű§ŰŰȘ ŰłÙŰ§ŰšÙ DB Ù
Ù۱ۯ ÙÛۧŰČ ŰźÙŰŻ ۱ۧ ۧ۶ۧÙÙ Ú©ÙÛŰŻ Ù ÙÙŰ· ۱ÙÛ ŰąÙ Ű±Ú©Ù۱ۯÙۧ ŰčÙ
Ù Ú©ÙÛŰŻ. ۧگ۱ ŰčÙ
Ùک۱ۯ ŰšÙ ÛÚ© Ùگ۱ۧÙÛ ŰÛۧŰȘÛ ŰȘۚۯÛÙ ŰŽÙŰŻ - ÛÚ© Ù
۔ۧÙŰÙ Ù
ŰȘÙۧŰČÙ Ù
Ù
Ú©Ù Ű§ŰłŰȘ ŰšÙ ŰŽÚ©Ù Ú©Ű§ŰŽŰȘ ŰȘÙÙۧ Ù
ŰŹÙ
ÙŰčÙ ŰąŰČÙ
ۧÛŰŽâÙۧÛÛ ŰšŰ§ŰŽŰŻ Ú©Ù ŰŻŰ§ŰŻÙâÙۧ ۱ۧ ŰȘŰșÛÛ۱ ÙÙ
ÛâŰŻÙÙŰŻ (Ù
Ű«ÙŰ§Ù ŰŻŰ±ŰźÙۧ۳ŰȘâÙۧ)
+
+
+â **ۯ۱ ŰșÛ۱ ۧÛÙ Ű”Ù۱ŰȘ:** ŰȘŰčۯۧۯ Ú©Ù
Û Ű§ŰČ ŰȘŰłŰȘ Ùۧ ŰŽÚ©ŰłŰȘ Ù
Û ŰźÙ۱ÙŰŻŰ Ű§ŰłŰȘÙ۱ۧ۱ Ù
ŰȘÙÙÙ Ù
Û ŰŽÙŰŻŰ ŰȘÛÙ
Ù
ۧ ۧکÙÙÙ ŰČÙ
Ű§Ù ÚŻŰ±Ű§ÙŰšÙۧÛÛ Ű±Ű§ Ű”Ű±Ù Ù
Û Ú©ÙŰŻŰ ŰąÛۧ Ù
ۧ ۚۧگ ۯۧ۱ÛÙ
Ű ŰšÛۧÛÛŰŻ ŰšŰ±Ű±ŰłÛ Ú©ÙÛÙ
Ű Ű§ÙÙ ÙÙâŰšÙ Ù۞۱ Ù
Ûâ۱۳ۯ ŰŻÙ ŰȘŰłŰȘ ۯۧۯÙâÙŰ§Û Ű§ÙÙÛÙ Ù
ێۧۚÙÛ Ű±Ű§ ŰŹÙŰŽ Ù
ÛâۯۧۯÙŰŻ
+
+
+
+â ÙÙ
ÙÙÙ Ú©ŰŻ
+
+
+
+### :thumbsdown: Ù
Ű«Ű§Ù Ű¶ŰŻ ۧÙÚŻÙ: ŰȘŰłŰȘâÙۧ Ù
ŰłŰȘÙÙ ÙÛŰłŰȘÙŰŻ Ù ŰšŰ±Ű§Û ŰȘŰș۰ÛÙ ŰŻŰ§ŰŻÙâÙŰ§Û DB ŰŹÙۧÙÛ ŰšÙ ÚÙŰŻ ÙÙۧۚ ŰŹÙۧÙÛ Ù
ŰȘÚ©Û ÙŰłŰȘÙŰŻ.
+
+
+
+```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
+
+ 
+
+```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: ۧÙۏۧÙ
ۯ۱۳ŰȘ Ù
۫ۧÙ: ÙŸŰ±Űł Ù ŰŹÙ Ű§ŰČ ÛÚ© ŰčÙ۔۱ ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ Ű§ŰČ ÛÚ© ÙÛÚÚŻÛ Ű§ŰźŰȘŰ”Ű§Ű”Û ŰšŰ±Ű§Û ŰȘŰłŰȘ
+
+
+
+```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: ۧÙۏۧÙ
ۯ۱۳ŰȘ ŰąÙ Ù
۫ۧÙ: کۧ۱ ŰšÙ Ű”Ù۱ŰȘ ÙۧÙŰč ŰšÛÙۧÙÙ ŰšŰ§ ÛÚ© ŰŹŰČŰĄ کۧÙ
ÙŰ§Ù Ű±Ùۯ۱ ŰŽŰŻÙ
+
+ 
+
+```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)
+
+
+
+
+```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 Ù
۱ۧÙŰš ÙŰÙÙ Ű§Ű±Ű§ŰŠÙ Ù
ŰŰȘÙۧ ۧŰČ Ű·Ű±ÛÙ ŰŽŰšÚ©Ù ŰšŰ§ŰŽÛŰŻ
+
+
+
+â
**ۧÙۏۧÙ
ۯۧۯÙ:** ŰšŰ±ŰźÛ Ű§ŰČ Ù
ۧÙÛŰȘÙ۱ÙŰ§Û ÙŰčŰ§Ù Ű±Ű§ ۧŰčÙ
Ű§Ù Ú©ÙÛŰŻ Ú©Ù ŰȘ۶Ù
ÛÙ Ù
ÛâÚ©ÙŰŻ ŰšŰ§Ű±ÚŻŰ°Ű§Ű±Û Ű”ÙŰÙ ŰŻŰ± ŰŽŰšÚ©Ù ÙۧÙŰčÛ ŰšÙÛÙÙ ŰŽŰŻÙ Ű§ŰłŰȘ - ۧÛÙ ŰŽŰ§Ù
Ù Ù۱ ÚŻÙÙÙ Ùگ۱ۧÙÛ 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: ۧÙۏۧÙ
ۯ۱۳ŰȘ Ù
۫ۧÙ: ÚŻŰČۧ۱ێ ۚۧŰČŰ±ŰłÛ ŰšŰ§Ű±ÚŻŰ°Ű§Ű±Û Ű”ÙŰÙ ÙۧÙÙŰł ۯ۱ÛۧÛÛ
+
+
+
+
+
+
+
+## âȘ ïž 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
+
+ 
+
+```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: ۧÙۏۧÙ
ۯ۱۳ŰȘ ŰąÙ Ù
۫ۧÙ: ÙŰšÙ Ű§ŰČ ÙÙ
Ù Ùۧ۱ۯ ŰŽÙÛŰŻ Ù ÙÙ ÙŰšÙ Ű§ŰČ Ù۱ کۯۧÙ
+
+
+
+```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 ۯ۱ ŰȘÙ
ۧÙ
Ű”ÙŰۧŰȘ Ű۱کŰȘ Ù
Û Ú©ÙŰŻ
+
+
+
+```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
+
+
+
+```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
+
+
+
+
+
+
+
+
+
+## âȘ ïž 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: Ù
Ű«Ű§Ù Ű¶ŰŻ ۧÙÚŻÙ: ÛÚ© ۱گ۱۳ÛÙÙ ŰšŰ”Ű±Û Ù
ŰčÙ
ÙÙÛ - Ù
ŰŰȘÙŰ§Û ŰŻŰ±ŰłŰȘÛ Ú©Ù ŰšŰŻ Ű§Ű±Ű§ŰŠÙ Ù
Û ŰŽÙŰŻ
+
+
+
+
+
+### :clap: ۧÙۏۧÙ
ۯ۱۳ŰȘ Ù
۫ۧÙ: ÙŸÛک۱ۚÙŰŻÛ wraith ŰšŰ±Ű§Û ÚŻŰ±ÙŰȘÙ Ù Ù
ÙۧÛŰłÙ ŰčÚ©Űł ÙŰ§Û ÙÙŰ±Û UI
+
+
+
+```
+â# 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 ŰšŰ±Ű§Û Ù
ÙۧÛŰłÙ ŰčÚ©Űł ÙÙŰ±Û Ù ŰłŰ§Û۱ ÙÛÚÚŻÛ ÙŰ§Û ÙŸÛێ۱ÙŰȘÙ
+
+ 
+
+```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: Ù
۫ۧÙ: ÛÚ© ÚŻŰČۧ۱ێ ÙŸÙŰŽŰŽ Ù
ŰčÙ
ÙÙÛ
+
+
+
+
+
+### :clap: ۧÙۏۧÙ
ۯ۱۳ŰȘ ŰąÙ Ù
۫ۧÙ: ŰȘÙŰžÛÙ
ÙŸÙŰŽŰŽ ŰšŰ±Ű§Û Ù۱ ŰŹŰČŰĄ (ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ Ű§ŰČ Jest)
+
+
+
+")
+
+
+
+
+
+## âȘ ïž 4.2 ÚŻŰČۧ۱ێ ÙŰ§Û ÙŸÙŰŽŰŽ ۱ۧ ŰšŰ±Ű§Û ŰŽÙۧ۳ۧÛÛ Ù
ÙŰ§Ű·Ù ŰąŰČÙ
ۧÛŰŽ ÙŰŽŰŻÙ Ù ŰłŰ§Û۱ Ù
Ùۧ۱ۯ ŰčŰŹÛŰš ۚۧŰČŰ±ŰłÛ Ú©ÙÛŰŻ
+
+:white_check_mark: **ۧÙۏۧÙ
ۯۧۯÙ:** ŰšŰ±ŰźÛ Ű§ŰČ Ù
ŰłŰ§ŰŠÙ ŰŻÙÛÙŰ§Ù ŰČÛ۱ ۱ۧۯۧ۱ ÙŸÙÙŰ§Ù Ù
Û ŰŽÙÙŰŻ Ù ŰšŰ§ ۧ۳ŰȘÙŰ§ŰŻÙ Ű§ŰČ Ű§ŰšŰČۧ۱ÙŰ§Û ŰłÙŰȘÛ ÙŸÛۯۧ Ú©Ű±ŰŻÙ ŰąÙÙۧ ÙۧÙŰčŰ§Ù ŰłŰźŰȘ ۧ۳ŰȘ. ۧÛÙÙۧ ÙۧÙŰčŰ§Ù ŰšŰ§ÚŻ ÙÛŰłŰȘÙŰŻŰ ŰšÙÚ©Ù ŰšÛŰŽŰȘ۱ ۱ÙŰȘۧ۱ÙŰ§Û ŰŽÚŻÙŰȘ ۧÙÚŻÛŰČ ŰšŰ±ÙۧÙ
Ù ÙŰłŰȘÙŰŻ Ú©Ù Ù
Ù
Ú©Ù Ű§ŰłŰȘ ŰȘŰŁŰ«Û۱ ŰŽŰŻÛŰŻÛ ŰŻŰ§ŰŽŰȘÙ ŰšŰ§ŰŽÙŰŻ. ŰšÙ ŰčÙÙŰ§Ù Ù
۫ۧÙŰ Ű§ŰșÙŰš ŰšŰ±ŰźÛ Ű§ŰČ Ù
ÙŰ§Ű·Ù Ú©ŰŻ Ù۱گŰČ Ûۧ ŰšÙ Ùۯ۱ŰȘ Ù۱ۧ۟ÙۧÙÛ ÙÙ
Û ŰŽÙÙŰŻâ-âŰŽÙ
ۧ Ùک۱ Ù
Û Ú©ÙÛŰŻ Ú©Ù Ú©Ùۧ۳ "PricingCalculator" ÙÙ
ÛŰŽÙ ÙÛÙ
ŰȘ Ù
ŰŰ”ÙÙ Ű±Ű§ ŰȘŰčÛÛÙ Ù
Û Ú©ÙŰŻŰ Ű§Ù
ۧ ŰšÙ Ù۞۱ Ù
Û Ű±ŰłŰŻ Ú©Ù ŰŻŰ± ÙۧÙŰč Ù۱گŰČ Ù۱ۧ۟ÙۧÙÛ ÙÙ
Û ŰŽÙŰŻŰ Ű§ÚŻŰ±ÚÙ Ù
ۧ 10000 Ù
ŰŰ”ÙÙ ŰŻŰ± DB ۯۧ۱ÛÙ
Ù ŰȘŰčۯۧۯ ŰČÛŰ§ŰŻÛ Ù۱ÙŰŽ... ÙŸÙŰŽŰŽ Ú©ŰŻ ÚŻŰČۧ۱ێâÙۧ ŰšÙ ŰŽÙ
ۧ Ú©Ù
Ú© Ù
ÛâÚ©ÙÙŰŻ Ù
ŰȘÙŰŹÙ ŰŽÙÛŰŻ Ú©Ù ŰąÛۧ ۚ۱ÙۧÙ
Ù ŰšÙâÚŻÙÙÙâŰ§Û Ú©Ù ŰŽÙ
ۧ Ùک۱ Ù
ÛâÚ©ÙÛŰŻ ۱ÙŰȘۧ۱ Ù
ÛâÚ©ÙŰŻ Ûۧ ŰźÛ۱. ŰšÙ ŰșÛ۱ ۧŰČ ŰąÙŰ ÙÙ
ÚÙÛÙ Ù
ÛâŰȘÙۧÙŰŻ Ù
ێ۟۔ Ú©ÙŰŻ Ú©Ù Ú©ŰŻŰ§Ù
ÙÙŰč Ú©ŰŻ ŰąŰČÙ
ۧÛŰŽ ÙŰŽŰŻÙ Ű§ŰłŰȘ - ۚۧ ۧ۷ÙۧŰč ۧŰČ Ű§ÛÙÚ©Ù 80 ۯ۱۔ۯ Ú©ŰŻ ŰąŰČÙ
ۧÛŰŽ ŰŽŰŻÙ Ű§ŰłŰȘŰ ÙÙ
ÛâÚŻÙÛŰŻ Ú©Ù ŰąÛۧ ÙŰłÙ
ŰȘâÙŰ§Û ŰÛۧŰȘÛ ÙŸÙŰŽŰŽ ŰŻŰ§ŰŻÙ ŰŽŰŻÙâۧÙŰŻ Ûۧ ŰźÛ۱. ۧÛۏۧۯ ÚŻŰČۧ۱ێ ŰąŰłŰ§Ù Ű§ŰłŰȘ - ÙÙŰ· ۚ۱ÙۧÙ
Ù ŰźÙŰŻ ۱ۧ ۯ۱ Ù
۱ŰÙÙ ŰȘÙÙÛŰŻ Ûۧ ۯ۱ ŰÛÙ ŰąŰČÙ
ۧÛŰŽ ۚۧ ۱ۯÛŰ§ŰšÛ ÙŸÙŰŽŰŽ ۧۏ۱ۧ Ú©ÙÛŰŻ Ù ŰłÙŸŰł ÚŻŰČۧ۱ێâÙŰ§Û Ű±Ùگۧ۱ÙÚŻÛ Ű±Ű§ Ù
ێۧÙŰŻÙ Ú©ÙÛŰŻ Ú©Ù ÙŰŽŰ§Ù Ù
ÛâŰŻÙŰŻ ŰȘŰčۯۧۯ ŰŻÙŰčۧŰȘ Ù۱ۧ۟ÙۧÙÛ Ù۱ ÙۧŰÛÙ Ú©ŰŻ Ù
ێ۟۔ Ù
ÛâŰŽÙŰŻ. ۧگ۱ ÙÙŰȘ ŰźÙŰŻ ۱ۧ Ű”Ű±Ù ÙگۧÙÛ Ű§ŰŹÙ
ۧÙÛ ŰšÙ Ű§ÛÙ ŰŻŰ§ŰŻÙ Ùۧ Ú©ÙÛŰŻ - Ù
Ù
Ú©Ù Ű§ŰłŰȘ ÚÙŰŻ ۧێŰȘŰšŰ§Ù ÙŸÛۯۧ Ú©ÙÛŰŻ
+
+
+â **ۯ۱ ŰșÛ۱ ۧÛÙ Ű”Ù۱ŰȘ:** ۧگ۱ ÙÙ
ÛâۯۧÙÛŰŻ کۯۧÙ
ۚ۟ێ ۧŰČ Ú©ŰŻŰȘŰ§Ù ŰąŰČÙ
ۧÛŰŽ ÙŰŽŰŻÙ Ű§ŰłŰȘŰ ÙÙ
ÛâۯۧÙÛŰŻ ۧÛÙ Ù
ŰŽÚ©ÙۧŰȘ ۧŰČ Ú©ŰŹŰ§ Ù
ÛâŰąÛÙŰŻ.
+
+
+
+â ÙÙ
ÙÙÙ Ú©ŰŻ
+
+
+
+### :thumbsdown: Ù
Ű«Ű§Ù Ű¶ŰŻ ۧÙÚŻÙ: ۧÛÙ ÚŻŰČۧ۱ێ ÙŸÙŰŽŰŽ ÚÙ Ű§ŰŽÚ©Ű§ÙÛ ŰŻŰ§Ű±ŰŻŰ
+
+ۚ۱ ۧ۳ۧ۳ ÛÚ© ŰłÙۧ۱ÛÙÛ ÙۧÙŰčÛ Ú©Ù ŰŻŰ± ŰąÙ Ù
ۧ ۧ۳ŰȘÙŰ§ŰŻÙ Ű§ŰČ ŰšŰ±ÙۧÙ
Ù ŰźÙŰŻ ۱ۧ ۯ۱ QA ۱ۯÛŰ§ŰšÛ Ú©Ű±ŰŻÛÙ
Ù Ű§ÙÚŻÙÙŰ§Û Ù۱ÙŰŻ ۏۧÙŰšÛ Ű±Ű§ ÙŸÛۯۧ ک۱ۯÛÙ
(ÙÚ©ŰȘÙ: Ù
ÛŰČŰ§Ù ŰźŰ±Ű§ŰšÛ ÙŰ§Û Ù۱ÙŰŻ ŰšÙ ŰłÛŰłŰȘÙ
ÙۧÙ
ŰȘÙۧ۳ۚ ۧ۳ŰȘŰ ÚÛŰČÛ ŰšÙ Ù۶ÙŰ Ű§ŰŽŰȘŰšŰ§Ù Ű§ŰłŰȘ. ۯ۱ ÙÙۧÛŰȘ Ù
ێ۟۔ ŰŽŰŻ Ú©Ù ŰšŰ±ŰźÛ Ű§ŰČ ŰšŰ§ÚŻ ÙŰ§Û frontend Ù
ۯۧÙ
ŰšÙ ŰłÛŰłŰȘÙ
Ùۧ۱ۯ Ù
Û ŰŽÙÙŰŻ. API Ù۱ÙŰŻ ۚۧ۷Ù)
+
+
+
+
+
+
+
+## âȘ ïž 4.3 ۧÙۯۧŰČÙ ÚŻÛŰ±Û ÙŸÙŰŽŰŽ Ù
ÙŰ·ÙÛ ŰšŰ§ ۧ۳ŰȘÙŰ§ŰŻÙ Ű§ŰČ ŰȘŰłŰȘ ŰŹÙŰŽ
+
+:white_check_mark: **ۧÙۏۧÙ
ۯۧۯÙ:** Ù
ŰčÛۧ۱ ÙŸÙŰŽŰŽ ŰłÙŰȘÛ Ű§ŰșÙŰš ۯ۱ÙŰș Ù
Û ÚŻÙÛŰŻ: Ù
Ù
Ú©Ù Ű§ŰłŰȘ ÙŸÙŰŽŰŽ 100ÙȘ Ú©ŰŻ ۱ۧ ŰšÙ ŰŽÙ
ۧ ÙŰŽŰ§Ù ŰŻÙŰŻŰ Ű§Ù
ۧ ÙÛÚ ÛÚ© ۧŰČ ŰȘÙۧۚŰč ŰŽÙ
Ű§Ű ŰŰȘÛ ÛÚ© Ù
ÙŰ±ŰŻŰ ÙŸŰ§ŰłŰź Ù
Ùۧ۳ۚ ۱ۧ ÙŰŽŰ§Ù ÙÙ
Û ŰŻÙŰŻ. ÚŰ·ÙŰ±Ű ŰąÙ Ű±Ű§ ŰšÙ ŰłŰ§ŰŻÚŻÛ Ű§ÙۯۧŰČÙ ÚŻÛŰ±Û Ù
Û Ú©ÙŰŻ Ú©Ù Ű§ŰČ Ú©ŰŻŰ§Ù
۟۷ÙŰ· Ú©ŰŻ ŰȘŰłŰȘ ۚۧŰČŰŻÛŰŻ Ú©Ű±ŰŻÙ Ű§ŰłŰȘŰ Ű§Ù
ۧ ŰšŰ±Ű±ŰłÛ ÙÙ
Û Ú©ÙŰŻ Ú©Ù ŰąÛۧ ŰąŰČÙ
ÙÙ Ùۧ ÙۧÙŰčŰ§Ù ÚÛŰČÛ Ű±Ű§ ŰąŰČÙ
ۧÛŰŽ Ú©Ű±ŰŻÙ Ű§ÙŰŻ - ŰšŰ±Ű§Û ÙŸŰ§ŰłŰź ۯ۱۳ŰȘ ۧ۞Ùۧ۱ ŰŽŰŻÙ Ű§ŰłŰȘ. Ù
ۧÙÙŰŻ Ú©ŰłÛ Ú©Ù ŰšŰ±Ű§Û ŰȘۏۧ۱ŰȘ ŰłÙ۱ Ù
Û Ú©ÙŰŻ Ù Ù
Ù۱ ÙŸŰ§ŰłÙŸÙ۱ŰȘ ŰźÙŰŻ ۱ۧ ÙŰŽŰ§Ù Ù
Û ŰŻÙŰŻ - ۧÛÙ ÙŰŽŰ§Ù ŰŻÙÙŰŻÙ Ű§ÙۏۧÙ
ÙÛÚ Ú©Ű§Ű±Û ÙÛŰłŰȘŰ ÙÙŰ· ۧÛÙÚ©Ù Ű§Ù Ű§ŰČ ŰȘŰčۯۧۯ Ú©Ù
Û Ű§ŰČ Ù۱ÙŰŻÚŻŰ§Ù Ùۧ Ù ÙŰȘÙ Ùۧ ۚۧŰČŰŻÛŰŻ Ú©Ű±ŰŻÙ Ű§ŰłŰȘ.
+
+ŰąŰČÙ
ۧÛŰŽ Ù
ŰšŰȘÙÛ ŰšŰ± ŰŹÙŰŽ ۚۧ ۧÙۯۧŰČÙâÚŻÛŰ±Û Ù
Ùۯۧ۱ Ú©ŰŻÛ Ú©Ù ÙۧÙŰčŰ§Ù ŰȘŰłŰȘ ŰŽŰŻÙ Ű§ŰłŰȘ Ù ÙÙ ÙÙŰ· ۚۧŰČŰŻÛŰŻ ŰŽŰŻÙŰ ŰšÙ ŰŽÙ
ۧ Ú©Ù
Ú© Ù
ÛâÚ©ÙŰŻ.. [Stryker](https://stryker-mutator.io/) ÛÚ© Ú©ŰȘۧۚ۟ۧÙÙ ŰŹŰ§Ùۧ ۧ۳ک۱ÛÙŸŰȘ ŰšŰ±Ű§Û ŰąŰČÙ
ۧÛŰŽ ŰŹÙŰŽ ۧ۳ŰȘ Ù ÙŸÛŰ§ŰŻÙ ŰłŰ§ŰČÛ ŰąÙ ÙۧÙŰčŰ§Ù Ù
ÙŰžÙ
ۧ۳ŰȘ:
+
+(1) ŰšÙ ŰčÙ
ŰŻ Ú©ŰŻ ۱ۧ ŰȘŰșÛÛ۱ Ù
Û ŰŻÙŰŻ Ù "ۧێکۧÙۧŰȘ ÚŻÛۧÙÛ" ۱ۧ ۧÛۏۧۯ Ù
Û Ú©ÙŰŻ. ŰšŰ±Ű§Û Ù
Ű«Ű§Ù Ú©ŰŻ newOrder.price===0 ŰšÙ newOrder.price!=0 ŰȘۚۯÛÙ Ù
Û ŰŽÙŰŻ. ۧÛÙ "ۧێکۧÙ" ŰŹÙŰŽ ÙۧÙ
ÛŰŻÙ Ù
Û ŰŽÙŰŻ
+
+(2) ŰȘŰłŰȘ Ùۧ ۱ۧ ۧۏ۱ۧ Ù
Û Ú©ÙŰŻŰ Ű§ÚŻŰ± ÙÙ
Ù Ù
ÙÙÙ ŰŽÙÙŰŻŰ Ù
ŰŽÚ©Ù ŰŻŰ§Ű±ÛÙ
- ŰąŰČÙ
ۧÛŰŽ Ùۧ ŰšÙ ÙŰŻÙ ŰźÙŰŻ ÛŰčÙÛ Ú©ŰŽÙ ŰšŰ§ÚŻ Ùۧ ŰčÙ
Ù Ùک۱ۯÙŰŻŰ ŰŹÙŰŽ Ùۧ ŰšÙ Ű§Ű”Ű·ÙŰ§Ű ŰČÙŰŻÙ Ù
Û Ù
ۧÙÙŰŻ. ۧگ۱ ŰąŰČÙ
ۧÛŰŽ Ùۧ ŰŽÚ©ŰłŰȘ ŰźÙ۱ۯÙŰŻŰ ŰčۧÙÛ Ű§ŰłŰȘŰ ŰŹÙŰŽ Ùۧ Ú©ŰŽŰȘÙ ŰŽŰŻÙŰŻ.
+
+ۯۧÙŰłŰȘÙ Ű§ÛÙÚ©Ù ÙÙ
Ù Ûۧ ŰšÛŰŽŰȘ۱ ŰŹÙŰŽâÙۧ Ú©ŰŽŰȘÙ ŰŽŰŻÙâۧÙŰŻŰ Ű§Ű·Ù
ÛÙŰ§Ù ŰšŰłÛۧ۱ ŰšÛŰŽŰȘŰ±Û Ù۳ۚŰȘ ŰšÙ ÙŸÙŰŽŰŽ ŰłÙŰȘÛ Ù
ÛâŰŻÙŰŻ Ù ŰČÙ
Ű§Ù Ű±Ű§ÙâۧÙۯۧŰČÛ Ù
ŰŽŰ§ŰšÙ Ű§ŰłŰȘ.
+
+
+â **ۯ۱ ŰșÛ۱ ۧÛÙ Ű”Ù۱ŰȘ:** ŰŽÙ
ۧ Ù۱ÛŰš ŰźÙۧÙÛŰŻ ŰźÙ۱ۯ Ú©Ù Ùک۱ Ú©ÙÛŰŻ ÙŸÙŰŽŰŽ 85ÙȘ ŰšÙ Ű§ÛÙ Ù
ŰčÙÛ Ű§ŰłŰȘ Ú©Ù ŰȘŰłŰȘ ŰŽÙ
ۧ ۧێکۧÙۧŰȘ ۱ۧ ۯ۱ 85ÙȘ ۧŰČ Ú©ŰŻ ŰŽÙ
ۧ ŰȘŰŽŰźÛŰ” Ù
Û ŰŻÙŰŻ.
+
+
+
+â ÙÙ
ÙÙÙ Ú©ŰŻ
+
+
+
+### :thumbsdown: Ù
Ű«Ű§Ù Ű¶ŰŻ ۧÙÚŻÙ: 100% ÙŸÙŰŽŰŽŰ 0% ŰȘŰłŰȘ
+
+
+
+```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Ű Ű§ŰšŰČŰ§Ű±Û ŰšŰ±Ű§Û ŰąŰČÙ
ۧÛŰŽ ŰŹÙŰŽŰ Ù
Ùۯۧ۱ Ú©ŰŻÛ Ű±Ű§ Ú©Ù ŰąŰČÙ
ۧÛŰŽ ÙŰŽŰŻÙ Ű§ŰłŰȘ ŰŽÙۧ۳ۧÛÛ Ù ŰŽÙ
ۧ۱ێ Ù
Û Ú©ÙŰŻ (ŰŹÙŰŽ)
+
+")
+
+
+
+
+
+## âȘ ïž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 ۚۧگ ŰȘÙÙÛŰŻ ŰšŰčŰŻÛ Ű±Ű§ ÙŸÛۯۧ Ù
Û Ú©ÙŰŻ
+
+
+
+
+
+
+
+## âȘ ïž 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))
+
+")
+
+
+
+
+
+## âȘ ïž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
+```
+
+
+
+
+
+
+
+
+
+## âȘ ïž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 ÙŰȘÛŰŹÙ
+
+
+
+
+
+
+
+## âȘ ïž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 ŰšŰ±Ű§Û ŰȘŰŽŰźÛŰ” Ù
ÛŰČŰ§Ù ŰčÙŰš Ù
ۧÙŰŻÚŻÛ Ú©ŰŻ ۧŰČ ŰąŰźŰ±ÛÙ ÙŰłŰźÙ Ùۧ ۧ۳ŰȘÙŰ§ŰŻÙ ŰŽÙŰŻ
+
+
+
+
+
+
+
+## âȘ ïž 5.8 ÙکۧŰȘ ŰŻÛÚŻŰ±Ű ŰșÛ۱ Ù
۱ŰȘۚ۷ ۚۧ گ۱ÙŰ CI
+
+:white_check_mark: **ۧÙۏۧÙ
ۯۧۯÙ:** ۧÛÙ ÙŸŰłŰȘ ۚ۱ ۱ÙÛ ŰȘÙŰ”ÛÙâÙŰ§Û ŰąŰČÙ
ۧÛŰŽÛ Ù
ŰȘÙ
۱کŰČ ŰŽŰŻÙ Ű§ŰłŰȘŰ Ûۧ ŰۯۧÙÙ Ù
ÛâŰȘÙŰ§Ù ŰąÙ Ű±Ű§ ۚۧ Node JS Ù
Ű«Ű§Ù ŰČŰŻ. ۚۧ ۧÛÙ ŰۧÙŰ Ű§ÛÙ ÚŻÙÙÙÙ ÚÙŰŻ ÙÚ©ŰȘÙ ŰșÛ۱ Ù
۱ŰȘۚ۷ ۚۧ Node ۱ۧ Ú©Ù ŰšÙ ŰźÙŰšÛ ŰŽÙۧ۟ŰȘÙ ŰŽŰŻÙ ÙŰłŰȘÙŰŻŰ ÚŻŰ±ÙÙ ŰšÙŰŻÛ Ù
Û Ú©ÙŰŻ
+
+ - ۧŰČ ÛÚ© ÙŰÙ Ű§ŰčÙۧÙÛ Ű§ŰłŰȘÙŰ§ŰŻÙ Ú©ÙÛŰŻ. ۧÛÙ ŰȘÙÙۧ ÚŻŰČÛÙÙ ŰšŰ±Ű§Û Ű§Ú©Ű«Ű± Ù۱ÙŰŽÙŰŻÚŻŰ§Ù Ű§ŰłŰȘ ۧÙ
ۧ ÙŰłŰźÙ ÙŰ§Û ÙŰŻÛÙ
Û Jenkins ۧÙ
Ú©Ű§Ù Ű§ŰłŰȘÙŰ§ŰŻÙ Ű§ŰČ Ú©ŰŻ Ûۧ UI ۱ۧ Ù
Û ŰŻÙŰŻ
- Ù۱ÙŰŽÙŰŻÙ Ű§Û Ű±Ű§ ۧÙŰȘ۟ۧۚ Ú©ÙÛŰŻ Ú©Ù Ű§ŰČ ÙŸŰŽŰȘÛۚۧÙÛ Docker ŰšÙÙ
Û ŰšŰ±ŰźÙ۱ۯۧ۱ ۚۧێۯ
- ŰČÙŰŻ ŰŽÚ©ŰłŰȘ ۚ۟Ù۱ÛŰŻŰ Ű§ŰšŰȘۯۧ ۳۱ÛŰč ŰȘ۱ÛÙ ŰȘŰłŰȘ ÙŰ§Û ŰźÙŰŻ ۱ۧ ۧۏ۱ۧ Ú©ÙÛŰŻ. ÛÚ© Ù
۱ŰÙÙ / ÙÙŰ·Ù ŰčŰ·Ù "ŰȘŰłŰȘ ŰŻÙŰŻ" ۧÛۏۧۯ Ú©ÙÛŰŻ Ú©Ù ÚÙŰŻÛÙ ŰšŰ§ŰČŰ±ŰłÛ ŰłŰ±ÛŰč ۱ۧ گ۱ÙÙ ŰšÙŰŻÛ Ù
Û Ú©ÙŰŻ (Ù
ۧÙÙŰŻ ÙŸŰ±ŰŻÙ ŰČŰŻÙŰ ŰȘŰłŰȘ ÙŰ§Û ÙۧŰŰŻ) Ù ŰšŰ§ŰČŰźÙ۱ۯ ۳۱ÛŰč ۱ۧ ŰšÙ committer Ú©ŰŻ Ű§Ű±Ű§ŰŠÙ Ù
Û ŰŻÙŰŻ.
- ŰšŰ±Ű±ŰłÛ ŰȘÙ
ۧÙ
Ù
Ű”ÙÙŰčۧŰȘ ۳ۧ۟ŰȘÙÛ Ű§ŰČ ŰŹÙ
ÙÙ ÚŻŰČۧ۱ێâÙŰ§Û ŰąŰČÙ
ۧÛŰŽŰ ÚŻŰČۧ۱ێâÙŰ§Û ÙŸÙŰŽŰŽŰ ÚŻŰČۧ۱ێâÙŰ§Û ŰŹÙŰŽŰ ÚŻŰČۧ۱ێâÙۧ Ù ŰșÛŰ±Ù Ű±Ű§ ŰąŰłŰ§Ù Ú©ÙÛŰŻ.
- ÚÙŰŻÛÙ ŰźŰ· ÙÙÙÙ/ŰŽŰșÙ ŰšŰ±Ű§Û Ù۱ ۱ÙÛۯۧۯ ۧÛۏۧۯ Ú©ÙÛŰŻŰ Ű§ŰČ Ù
۱ۧŰÙ ŰšÛÙ ŰąÙÙۧ ŰŻÙŰšŰ§Ű±Ù Ű§ŰłŰȘÙŰ§ŰŻÙ Ú©ÙÛŰŻ. ŰšÙ ŰčÙÙŰ§Ù Ù
۫ۧÙŰ ÛÚ© کۧ۱ ۱ۧ ŰšŰ±Ű§Û commit ÙŰ§Û ŰŽŰ§ŰźÙ ÙÛÚÚŻÛ Ù ÛÚ© کۧ۱ ŰŻÛگ۱ ۱ۧ ŰšŰ±Ű§Û PR ۧ۔ÙÛ ÙŸÛک۱ۚÙŰŻÛ Ú©ÙÛŰŻ. ŰšÙ Ù۱ Ù
ÙŰ·Ù Ű§ŰŹŰ§ŰČÙ Ű§ŰłŰȘÙŰ§ŰŻÙ Ù
ŰŹŰŻŰŻ ۱ۧ ۚۧ ۧ۳ŰȘÙŰ§ŰŻÙ Ű§ŰČ Ù
۱ۧŰÙ Ù
ŰŽŰȘ۱ک ۚۯÙÛŰŻ (ۧک۫۱ Ù۱ÙŰŽÙŰŻÚŻŰ§Ù Ù
کۧÙÛŰČÙ
Û ŰšŰ±Ű§Û Ű§ŰłŰȘÙŰ§ŰŻÙ Ù
ŰŹŰŻŰŻ ۧŰČ Ú©ŰŻ Ű§Ű±Ű§ŰŠÙ Ù
Û ŰŻÙÙŰŻ)
- Ù۱گŰČ Ű§ŰłŰ±Ű§Ű± ۱ۧ ۯ۱ ÛÚ© ۧŰčÙۧÙ
ÛÙ ŰŽŰșÙÛ Ù۱ۧ۱ ÙŰŻÙÛŰŻŰ ŰąÙÙۧ ۱ۧ ۧŰČ ÛÚ© Ù۱ÙŰŽÚŻŰ§Ù Ù
ŰźÙÛ Ûۧ ۧŰČ ÙŸÛک۱ۚÙŰŻÛ ŰŽŰșÙ ŰšÚŻÛ۱ÛŰŻ.
- ŰšÙ Ű”Ű±Ű§ŰŰȘ ÙŰłŰźÙ Ű±Ű§ ۯ۱ ÛÚ© ÙŰłŰźÙ Ù
ÙŰȘێ۱ Ú©ÙÛŰŻ Ûۧ ŰۯۧÙÙ Ű§Ű·Ù
ÛÙŰ§Ù ŰŰ§Ű”Ù Ú©ÙÛŰŻ Ú©Ù ŰȘÙŰłŰčÙ ŰŻÙÙŰŻÙ Ű§ÛÙ Ú©Ű§Ű± ۱ۧ ۧÙۏۧÙ
ŰŻŰ§ŰŻÙ Ű§ŰłŰȘ
- ÙÙŰ· ÛÚ© ۚۧ۱ ۚ۳ۧŰČÛŰŻ Ù ŰȘÙ
ۧÙ
ۚۧŰČ۱۳ÛâÙۧ ۱ۧ ۱ÙÛ ÛÚ© Ù
Ű”ÙÙŰč ۳ۧ۟ŰȘ (Ù
Ű«ÙŰ§Ù ŰȘŰ”ÙÛ۱ ۯۧک۱) ۧÙۏۧÙ
ŰŻÙÛŰŻ.
- ۯ۱ ÛÚ© Ù
ŰÛŰ· ŰČÙۯگ۰۱ Ú©Ù ŰۧÙŰȘ ۱ۧÙŰŽ ŰšÛÙ ŰłŰ§ŰźŰȘâÙۧ ۱ۧ ÙŰŻŰ§Ű±ŰŻŰ ŰȘŰłŰȘ Ú©ÙÛŰŻ. ۰۟ÛŰ±Ù 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).
+
+ĐŃĐŸĐłĐŸ ĐŒĐŸĐ¶ĐœĐŸ ĐŽĐŸŃŃĐžŃŃ ĐżŃŃĐ”ĐŒ ŃŃаŃДлŃĐœĐŸĐłĐŸ ĐČŃĐ±ĐŸŃа ŃĐżĐŸŃĐŸĐ±ĐŸĐČ, ĐžĐœŃŃŃŃĐŒĐ”ĐœŃĐŸĐČ Đž ŃДлДĐč ŃĐ”ŃŃĐžŃĐŸĐČĐ°ĐœĐžŃ, ĐșĐŸŃĐŸŃŃĐ” бŃĐŽŃŃ ĐŸĐŽĐœĐŸĐČŃĐ”ĐŒĐ”ĐœĐœĐŸ Đž ŃŃŃĐ”ĐșŃĐžĐČĐœŃĐŒĐž Đž "ĐŸĐșŃĐżĐ°Đ”ĐŒŃĐŒĐž". йДŃŃĐžŃŃĐčŃĐ” ŃĐŸĐ»ŃĐșĐŸ ŃĐŸ, ŃŃĐŸ ĐœĐ”ĐŸĐ±Ń
ĐŸĐŽĐžĐŒĐŸ. ĐĄŃаŃаĐčŃĐ”ŃŃ ŃĐČДлОŃĐžŃŃ ŃĐșĐŸŃĐŸŃŃŃ ŃазŃĐ°Đ±ĐŸŃĐșĐž. ĐĐœĐŸĐłĐŽĐ° ЎажД ŃŃĐŸĐžŃ ĐŸŃĐșазаŃŃŃŃ ĐŸŃ ĐœĐ”ĐșĐŸŃĐŸŃŃŃ
ŃĐ”ŃŃĐŸĐČ, ŃŃĐŸĐ±Ń ŃЎДлаŃŃ ĐșĐŸĐŽ Đ±ĐŸĐ»Đ”Đ” ĐżŃĐŸŃŃŃĐŒ Đž гОбĐșĐžĐŒ.
+
+
+
+ĐĐŸĐ»ŃŃĐžĐœŃŃĐČĐŸ ЎалŃĐœĐ”ĐčŃĐžŃ
ŃĐŸĐČĐ”ŃĐŸĐČ ŃĐČĐ»ŃŃŃŃŃ ĐżŃĐŸĐžĐ·ĐČĐŸĐŽĐœŃĐŒĐž ĐŸŃ ŃŃĐŸĐłĐŸ ĐżŃĐžĐœŃОпа.
+
+### ĐĐŸŃĐŸĐČŃ?
+
+
+
+# РазЎДл 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 ŃаŃŃĐž
+
+
+
+```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 ŃаŃŃĐž
+
+
+
+
+
+
+© ЧОŃаŃŃ ĐŽĐ°Đ»Đ”Đ”...
+ 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
+
+ 
+
+```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().
+
+
+
+â ĐŃĐžĐŒĐ”ŃŃ ĐșĐŸĐŽĐ°
+
+ 
+
+### :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: ĐДпŃаĐČОлŃĐœĐŸ: йДŃŃĐžŃĐŸĐČĐ°ĐœĐžĐ” ĐČĐœŃŃŃĐ”ĐœĐœĐžŃ
ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃĐŸĐČ Đ±Đ”Đ· ĐżŃĐžŃĐžĐœŃ
+
+
+
+```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: ĐДпŃаĐČОлŃĐœĐŸ: ĐĐŸĐșĐžĐœĐł ĐžŃĐżĐŸĐ»ŃĐ·ŃĐ”ŃŃŃ ĐŽĐ»Ń ŃĐ”ŃŃĐžŃĐŸĐČĐ°ĐœĐžŃ ĐČĐœŃŃŃĐ”ĐœĐœĐ”Đč ŃДалОзаŃОО
+
+
+
+```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: ĐДпŃаĐČОлŃĐœĐŸ: ĐĐ°Đ±ĐŸŃ ŃĐ”ŃŃĐŸĐČ, ĐșĐŸŃĐŸŃŃĐč ĐżŃĐŸŃ
ĐŸĐŽĐžŃ ĐžĐ·-за ĐœĐ”ŃДалОŃŃĐžŃĐœŃŃ
ĐŽĐ°ĐœĐœŃŃ
+
+
+
+```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â
+
+
+
+```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 ŃŃŃĐŸĐș ĐșĐŸĐŽĐ°
+
+
+
+```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(`
+
+- Home
+- About
+- Contact
+
+`);
+});
+```
+
+
+
+
+
+## âȘ ïžĐĐŸĐżĐžŃŃĐčŃĐ” ŃĐŸĐ»ŃĐșĐŸ ĐœĐ”ĐŸĐ±Ń
ĐŸĐŽĐžĐŒŃĐč ĐșĐŸĐŽ
+
+:white_check_mark: **ХЎДлаŃŃ:** ĐĐșĐ»ŃŃаĐčŃĐ” ĐČ ŃĐ”ŃŃ ŃĐŸĐ»ŃĐșĐŸ ĐœĐ”ĐŸĐ±Ń
ĐŸĐŽĐžĐŒĐŸĐ”, ŃŃĐŸ ĐČлОŃĐ”Ń ĐœĐ° ŃДзŃĐ»ŃŃĐ°Ń ŃĐ”ŃŃа, ĐœĐŸ ĐœĐ” Đ±ĐŸĐ»Đ”Đ” ŃĐŸĐłĐŸ. Đ ĐșаŃĐ”ŃŃĐČĐ” ĐżŃĐžĐŒĐ”Ńа, ŃаŃŃĐŒĐŸŃŃĐžĐŒ ŃĐ”ŃŃ, ĐșĐŸŃĐŸŃŃĐč ĐŽĐŸĐ»Đ¶Đ”Đœ ŃŃĐžŃŃĐČаŃŃ 100 ŃŃŃĐŸĐș JSON. йаŃĐžŃŃ Đ·Đ° ŃĐŸĐ±ĐŸĐč ŃŃĐŸĐ»ŃĐșĐŸ ŃŃŃĐŸĐș ĐČ ĐșажЎŃĐč ŃĐ”ŃŃ - ŃŃĐŸĐŒĐžŃДлŃĐœĐŸ. ĐŃлО ОзĐČлДĐșаŃŃ ŃŃŃĐŸĐșĐž за ĐżŃĐ”ĐŽĐ”Đ»Ń transferFactory.getJSON(), ŃĐŸ ŃĐ”ŃŃ ŃŃĐ°ĐœĐ”Ń ĐœĐ”ŃŃĐœŃĐŒ, ŃаĐș ĐșаĐș бДз ĐŽĐ°ĐœĐœŃŃ
ŃŃŃĐŽĐœĐŸ ŃĐŸĐŸŃĐœĐ”ŃŃĐž ŃДзŃĐ»ŃŃĐ°Ń ŃĐ”ŃŃа Đž ĐżŃĐžŃĐžĐœŃ (ĐżĐŸŃĐ”ĐŒŃ ĐŸĐœ ĐŽĐŸĐ»Đ¶Đ”Đœ ĐČĐ”ŃĐœŃŃŃ ŃŃаŃŃŃ 400?). Đ ĐșĐœĐžĐłĐ” x-unit patterns, ŃаĐșĐŸĐč паŃŃĐ”ŃĐœ ĐœĐ°Đ·ŃĐČаДŃŃŃ "ŃĐ°ĐžĐœŃŃĐČĐ”ĐœĐœŃĐč ĐłĐŸŃŃŃ" - ŃŃĐŸ-ŃĐŸ ŃĐșŃŃŃĐŸĐ” ĐżĐŸĐČлОŃĐ»ĐŸ ĐœĐ° ŃДзŃĐ»ŃŃĐ°Ń ĐœĐ°ŃĐžŃ
ŃĐ”ŃŃĐŸĐČ, ĐœĐŸ ĐŒŃ ĐœĐ” Đ·ĐœĐ°Đ”ĐŒ, ŃŃĐŸ ĐžĐŒĐ”ĐœĐœĐŸ.
+ĐĐ»Ń ŃĐ»ŃŃŃĐ”ĐœĐžŃ ŃĐžŃŃаŃОО, ĐŒŃ ĐŒĐŸĐ¶Đ”ĐŒ ОзĐČлДŃŃ ĐżĐŸĐČŃĐŸŃŃŃŃОДŃŃ ĐŽĐ”ŃалО, ĐŸŃŃаĐČĐ»ŃŃ ŃĐŸĐ»ŃĐșĐŸ ŃĐŸ, ŃŃĐŸ ĐžĐŒĐ”Đ”Ń Đ·ĐœĐ°ŃĐ”ĐœĐžĐ” ĐŽĐ»Ń ŃĐ”ŃŃа. ĐаĐș, ĐœĐ°ĐżŃĐžĐŒĐ”Ń, ĐČ ĐŽĐ°ĐœĐœĐŸĐč ŃĐžŃŃаŃОО: transferFactory.getJSON({sender: undefined}). ĐĐŽĐ”ŃŃ ĐČĐžĐŽĐœĐŸ, ŃŃĐŸ ĐżŃŃŃĐŸĐ” ĐżĐŸĐ»Đ” ĐŸŃĐżŃаĐČĐžŃĐ”Đ»Ń ŃĐČĐ»ŃĐ”ŃŃŃ ŃĐŸĐč ŃĐ°ĐŒĐŸĐč ĐżŃĐžŃĐžĐœĐŸĐč, ĐșĐŸŃĐŸŃĐ°Ń ĐżŃĐžĐČĐ”ĐŽĐ”Ń Đș ĐŸŃОбĐșĐ” ĐČалОЎаŃОО.
+
+
+â **ĐĐœĐ°ŃĐ”:** ĐĐŸĐżĐžŃĐŸĐČĐ°ĐœĐžĐ” ĐŸĐłŃĐŸĐŒĐœĐŸĐłĐŸ ĐșĐŸĐ»ĐžŃĐ”ŃŃĐČа ŃŃŃĐŸĐș JSON ĐżŃĐžĐČĐ”ĐŽĐ”Ń Đș ŃĐŸĐŒŃ, ŃŃĐŸ ĐČаŃĐž ŃĐ”ŃŃŃ ŃŃĐ°ĐœŃŃ ĐœĐ”ŃĐžŃĐ°Đ”ĐŒŃĐŒĐž Đž ŃŃŃĐŽĐœĐŸ ĐżĐŸĐŽĐŽĐ”ŃжОĐČĐ°Đ”ĐŒŃĐŒĐž.
+
+
+
+â ĐŃĐžĐŒĐ”ŃŃ ĐșĐŸĐŽĐ°
+
+
+
+### :thumbsdown: ĐДпŃаĐČОлŃĐœĐŸ: ĐąŃĐ¶Đ”Đ»ĐŸ ĐżĐŸĐœŃŃŃ ĐżŃĐžŃĐžĐœŃ ĐŸŃОбĐșĐž, ŃаĐș ĐșаĐș ĐŸĐœĐ° ŃĐșŃŃŃа ĐŸŃ ĐłĐ»Đ°Đ· ĐżĐŸĐ»ŃĐ·ĐŸĐČаŃĐ”Đ»Ń ĐČ Đ±ĐŸĐ»ŃŃĐŸĐŒ ĐșĐŸĐ»ĐžŃĐ”ŃŃĐČĐ” ŃŃŃĐŸĐș JSON
+
+
+
+```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
+
+
+
+```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 Đž ĐŒĐŸĐłŃŃ ĐČŃĐżĐŸĐ»ĐœŃŃŃŃŃ ŃаŃŃĐŸ, ЎажД ĐżĐŸĐșа ŃазŃĐ°Đ±ĐŸŃŃĐžĐș ĐœĐ°Đ±ĐžŃĐ°Đ”Ń ŃĐ”ĐșŃŃ).
+
+
+
+```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: ĐŃаĐČОлŃĐœĐŸ: Đ Đ°Đ·ĐŽĐ”Đ»Đ”ĐœĐžĐ” ĐœĐ° ŃŃŃŃĐșŃŃŃŃ ĐœĐ°Đ±ĐŸŃа ŃĐ”ŃŃĐŸĐČ ĐżŃĐžĐČĐŸĐŽĐžŃ Đș ŃĐŽĐŸĐ±ĐœĐŸĐŒŃ ĐŸŃŃĐ”ŃŃ
+
+
+
+```javascript
+// ŃĐ”ŃŃĐžŃŃĐ”ĐŒŃĐč Đ±Đ»ĐŸĐș
+describe("Transfer service", () => {
+ // ŃŃĐ”ĐœĐ°ŃĐžĐč
+ describe("When no credit", () => {
+ // ĐŸĐ¶ĐžĐŽĐ°ĐœĐžĐ”
+ test("Then the response status should decline", () => {});
+
+ // ĐŸĐ¶ĐžĐŽĐ°ĐœĐžĐ”
+ test("Then it should send email to admin", () => {});
+ });
+});
+```
+
+
+
+
+
+### :thumbsdown: ĐДпŃаĐČОлŃĐœĐŸ: ĐĐ»ĐŸŃĐșĐžĐč ŃпОŃĐŸĐș ŃĐ”ŃŃĐŸĐČ ŃŃĐ»ĐŸĐ¶ĐœŃĐ”Ń ĐżĐŸĐžŃĐș ĐŸŃОбĐșĐž
+
+
+
+```javascript
+test("Then the response status should decline", () => {});
+
+test("Then it should send email", () => {});
+
+test("Then there should not be a new transfer record", () => {});
+```
+
+
+
+
+
+
+
+
+
+## âȘ ïž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â.
+
+
+
+âșïžĐŃĐžĐŒĐ”Ń: [YouTube: âBeyond Unit Tests: 5 Shiny Node.JS Test Types (2018)â (Yoni Goldberg)](https://www.youtube.com/watch?v=-2zP494wdUY&feature=youtu.be)
+
+
+
+
+
+
+
+
+
+## âȘ ïž2.2 йДŃŃĐžŃĐŸĐČĐ°ĐœĐžĐ” ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃĐŸĐČ ĐŒĐŸĐ¶Đ”Ń ŃŃаŃŃ ĐČаŃĐžĐŒ Đ»ŃŃŃĐžĐŒ ĐŽŃŃĐłĐŸĐŒ
+
+:white_check_mark: **ХЎДлаŃŃ:** ĐажЎŃĐč ĐŒĐŸĐŽŃĐ»ŃĐœŃĐč ŃĐ”ŃŃ ĐżĐŸĐșŃŃĐČĐ°Đ”Ń ĐœĐ”Đ±ĐŸĐ»ŃŃŃŃ ŃаŃŃŃ ĐżŃĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ. ĐĐŸĐșŃŃŃŃ Đ”ĐłĐŸ ŃДлОĐșĐŸĐŒ ŃĐČĐ»ŃĐ”ŃŃŃ ĐŽĐŸŃĐŸĐłĐžĐŒ ŃĐŽĐŸĐČĐŸĐ»ŃŃŃĐČĐžĐ”ĐŒ, ĐČ ŃĐŸ ĐČŃĐ”ĐŒŃ ĐșаĐș ŃĐșĐČĐŸĐ·ĐœĐŸĐ” ŃĐ”ŃŃĐžŃĐŸĐČĐ°ĐœĐžĐ” лДгĐșĐŸ ĐżĐŸĐșŃŃĐČĐ°Đ”Ń Đ±ĐŸĐ»ŃŃŃŃ ŃаŃŃŃ ĐżŃĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ, ĐœĐŸ ŃĐČĐ»ŃĐ”ŃŃŃ ĐœĐ”ŃŃабОлŃĐœŃĐŒ Đž ĐŒĐ”ĐŽĐ»Đ”ĐœĐœŃĐŒ. ĐĐŸŃĐ”ĐŒŃ Đ±Ń ĐœĐ” ĐżŃĐžĐŒĐ”ĐœĐžŃŃ ŃĐ±Đ°Đ»Đ°ĐœŃĐžŃĐŸĐČĐ°ĐœĐœŃĐč ĐżĐŸĐŽŃ
ĐŸĐŽ, Đž ĐœĐ” пОŃаŃŃ ŃĐ”ŃŃŃ, ĐșĐŸŃĐŸŃŃĐ” ĐżĐŸ ŃĐ°Đ·ĐŒĐ”ŃŃ Đ±ĐŸĐ»ŃŃĐ”, ŃĐ”ĐŒ ĐŒĐŸĐŽŃĐ»ŃĐœŃĐ”, ĐœĐŸ ĐŒĐ”ĐœŃŃĐ”, ŃĐ”ĐŒ ŃĐșĐČĐŸĐ·ĐœĐŸĐ” ŃĐ”ŃŃĐžŃĐŸĐČĐ°ĐœĐžĐ”. ĐĐŸĐŒĐżĐŸĐœĐ”ĐœŃĐœĐŸĐ” ŃĐ”ŃŃĐžŃĐŸĐČĐ°ĐœĐžĐ” - ŃŃĐŸ ŃĐŸ, ŃŃĐŸ ĐČĐŸĐ±ŃĐ°Đ»ĐŸ ĐČ ŃĐ”Đ±Ń Đ»ŃŃŃДД Оз ĐŽĐČŃŃ
пДŃĐ”ŃĐžŃĐ»Đ”ĐœĐœŃŃ
ĐżĐŸĐŽŃ
ĐŸĐŽĐŸĐČ. ĐĐŽĐœĐŸ ŃĐŸŃĐ”ŃĐ°Đ”Ń ĐČ ŃДбД ŃазŃĐŒĐœŃŃ ĐżŃĐŸĐžĐ·ĐČĐŸĐŽĐžŃДлŃĐœĐŸŃŃŃ Đž ĐČĐŸĐ·ĐŒĐŸĐ¶ĐœĐŸŃŃŃ ĐżŃĐžĐŒĐ”ĐœĐ”ĐœĐžŃ ĐżĐ°ŃŃĐ”ŃĐœĐŸĐČ TDD, а ŃаĐșжД ŃДалОŃŃĐžŃĐœĐŸĐ” Đž Đ±ĐŸĐ»ŃŃĐŸĐ” ĐżĐŸĐșŃŃŃОД.
+
+ĐĐŸĐŒĐżĐŸĐœĐ”ĐœŃĐœŃĐ” ŃĐ”ŃŃŃ ŃĐŸĐșŃŃĐžŃŃŃŃŃŃ ĐœĐ° "Đ”ĐŽĐžĐœĐžŃĐ”" ĐŒĐžĐșŃĐŸŃĐ”ŃĐČĐžŃа, ĐŸĐœĐž ŃĐ°Đ±ĐŸŃаŃŃ Ń API, ĐœĐ” ĐžĐŒĐžŃĐžŃŃŃŃ ĐœĐžŃĐ”ĐłĐŸ, ŃŃĐŸ ĐżŃĐžĐœĐ°ĐŽĐ»Đ”Đ¶ĐžŃ ŃĐ°ĐŒĐŸĐŒŃ ĐŒĐžĐșŃĐŸŃĐ”ŃĐČĐžŃŃ (ĐœĐ°ĐżŃĐžĐŒĐ”Ń, ŃДалŃĐœŃŃ ĐРОлО, ĐżĐŸ ĐșŃаĐčĐœĐ”Đč ĐŒĐ”ŃĐ”, ДД ĐČĐ”ŃŃĐžŃ ĐČ ĐżĐ°ĐŒŃŃĐž), ĐœĐŸ заŃŃĐșаŃŃ ĐČŃĐ”, ŃŃĐŸ ŃĐČĐ»ŃĐ”ŃŃŃ ĐČĐœĐ”ŃĐœĐžĐŒ, ĐœĐ°ĐżŃĐžĐŒĐ”Ń, ĐČŃĐ·ĐŸĐČŃ ĐŽŃŃгОŃ
ĐŒĐžĐșŃĐŸŃĐ”ŃĐČĐžŃĐŸĐČ. ĐĐŸŃŃŃĐżĐ°Ń ŃаĐșĐžĐŒ ĐŸĐ±ŃĐ°Đ·ĐŸĐŒ, ĐŒŃ ŃĐ”ŃŃĐžŃŃĐ”ĐŒ ŃĐŸ, ŃŃĐŸ ŃазĐČĐ”ŃŃŃĐČĐ°Đ”ĐŒ, ĐżĐŸĐŽŃ
ĐŸĐŽĐžĐŒ Đș ĐżŃĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ ĐŸŃ ĐČĐœĐ”ŃĐœĐ”ĐłĐŸ Đș ĐČĐœŃŃŃĐ”ĐœĐœĐ”ĐŒŃ Đž ĐŸĐ±ŃĐ”ŃĐ°Đ”ĐŒ Đ±ĐŸĐ»ŃŃŃŃ ŃĐČĐ”ŃĐ”ĐœĐœĐŸŃŃŃ Đ·Đ° ŃазŃĐŒĐœĐŸĐ” ĐČŃĐ”ĐŒŃ.
+
+[ĐŁ ĐœĐ°Ń Đ”ŃŃŃ ĐżĐŸĐ»ĐœĐŸĐ” ŃŃĐșĐŸĐČĐŸĐŽŃŃĐČĐŸ, ĐșĐŸŃĐŸŃĐŸĐ” ĐżĐŸŃĐČŃŃĐ”ĐœĐŸ ĐžŃĐșĐ»ŃŃĐžŃДлŃĐœĐŸ ĐœĐ°ĐżĐžŃĐ°ĐœĐžŃ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃĐœŃŃ
ŃĐ”ŃŃĐŸĐČ ĐżŃаĐČОлŃĐœŃĐŒ ŃĐżĐŸŃĐŸĐ±ĐŸĐŒ](https://github.com/testjavascript/nodejs-integration-tests-best-practices)
+
+
+
+â **ĐĐœĐ°ŃĐ”:** ĐŃ ĐŒĐŸĐ¶Đ”ŃĐ” ĐżĐŸŃŃаŃĐžŃŃ ĐŸĐłŃĐŸĐŒĐœĐŸĐ” ĐșĐŸĐ»ĐžŃĐ”ŃŃĐČĐŸ ĐČŃĐ”ĐŒĐ”ĐœĐž ĐœĐ° ĐœĐ°ĐżĐžŃĐ°ĐœĐžĐ” ĐŒĐŸĐŽŃĐ»ŃĐœŃŃ
ŃĐ”ŃŃĐŸĐČ Đž ĐŸĐ±ĐœĐ°ŃŃжОŃŃ, ŃŃĐŸ ĐżĐŸĐșŃŃŃОД ŃĐžŃŃĐ”ĐŒŃ ŃĐŸŃŃаĐČĐ»ŃĐ”Ń ĐČŃĐ”ĐłĐŸ 20%.
+
+
+
+â ĐŃĐžĐŒĐ”ŃŃ ĐșĐŸĐŽĐ°
+
+
+
+### :clap: ĐŃаĐČОлŃĐœĐŸ: Supertest ĐżĐŸĐ·ĐČĐŸĐ»ŃĐ”Ń ĐżŃОблОжаŃŃŃŃ Đș Express API ĐČ ĐżŃĐŸŃĐ”ŃŃĐ” (Ń ĐČŃŃĐŸĐșĐŸĐč ŃĐșĐŸŃĐŸŃŃŃŃ Đž ŃĐžŃĐŸĐșĐžĐŒ ĐŸŃ
ĐČаŃĐŸĐŒ ŃŃĐŸĐČĐœĐ”Đč)
+
+
+
+ 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: ĐŃаĐČОлŃĐœĐŸ:
+
+
+
+
+
+
+
+
+
+## âȘ ïž 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 ĐżĐŸĐ»ĐœĐŸŃŃŃŃ
+
+
+
+```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 - ŃŃĐŸ ĐžĐœŃŃŃŃĐŒĐ”ĐœŃ, ĐșĐŸŃĐŸŃŃĐč ĐżŃĐ”ĐŽĐŸŃŃаĐČĐ»ŃĐ”Ń ĐșĐŸĐŒĐżĐ»Đ”ĐșŃĐœŃĐ” ĐŒĐ”ŃĐŸĐŽŃ Đ°ĐœĐ°Đ»ĐžĐ·Đ°
+
+
+
+
+
+
+
+
+
+## âȘ ïž 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-Ń
Đ°ĐŸŃа ĐŽĐ»Ń ĐżŃĐŸĐČĐ”ŃĐșĐž ŃŃŃĐŸĐčŃĐžĐČĐŸŃŃĐž ĐżŃĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ
+
+
+
+
+
+
+
+## âȘ ïž2.7 ĐзбДгаĐčŃĐ” ĐžŃĐżĐŸĐ»ŃĐ·ĐŸĐČĐ°ĐœĐžŃ ĐłĐ»ĐŸĐ±Đ°Đ»ŃĐœŃŃ
ŃĐžĐșŃŃŃŃ Đž seeds. ĐĐ»Ń ĐșĐ°Đ¶ĐŽĐŸĐłĐŸ ŃĐ”ŃŃа ĐŽĐŸĐ»Đ¶ĐœŃ Đ±ŃŃŃ ŃĐČĐŸĐž ŃĐŸĐ±ŃŃĐČĐ”ĐœĐœŃĐ” ĐŽĐ°ĐœĐœŃĐ”
+
+:white_check_mark: **ХЎДлаŃŃ:** ĐĄĐŸĐłĐ»Đ°ŃĐœĐŸ Đ·ĐŸĐ»ĐŸŃĐŸĐŒŃ ĐżŃаĐČĐžĐ»Ń (ĐżŃĐœĐșŃ 0), ĐșажЎŃĐč ŃĐ”ŃŃ ĐŽĐŸĐ»Đ¶Đ”Đœ ĐžŃĐżĐŸĐ»ŃĐ·ĐŸĐČаŃŃ ŃĐČĐŸĐč ŃĐŸĐ±ŃŃĐČĐ”ĐœĐœŃĐč ĐœĐ°Đ±ĐŸŃ ŃŃŃĐŸĐș ĐČ ĐĐ, ŃŃĐŸĐ±Ń ĐžĐ·Đ±Đ”Đ¶Đ°ŃŃ ĐżĐ”ŃĐ”ĐșŃŃŃĐžŃ ĐŽĐ°ĐœĐœŃŃ
. Đа ЎДлД жД, ĐŽĐ°ĐœĐœĐŸĐ” ĐżŃаĐČĐžĐ»ĐŸ ŃаŃŃĐŸ ĐœĐ°ŃŃŃаДŃŃŃ ŃĐ”ŃŃĐžŃĐŸĐČŃĐžĐșĐ°ĐŒĐž, ĐșĐŸŃĐŸŃŃĐ” загŃŃжаŃŃ ĐŽĐ°ĐœĐœŃĐ” ĐČ ĐРпДŃДЎ запŃŃĐșĐŸĐŒ ŃĐ”ŃŃĐŸĐČ (ŃĐžĐșŃŃŃŃŃ), ŃŃĐŸĐ±Ń ŃĐ»ŃŃŃĐžŃŃ ĐżŃĐŸĐžĐ·ĐČĐŸĐŽĐžŃДлŃĐœĐŸŃŃŃ. Đ„ĐŸŃŃ ĐżŃĐŸĐžĐ·ĐČĐŸĐŽĐžŃДлŃĐœĐŸŃŃŃ ŃĐČĐ»ŃĐ”ŃŃŃ ŃДалŃĐœĐŸĐč ĐżŃĐžŃĐžĐœĐŸĐč ĐŽĐ»Ń Đ±Đ”ŃĐżĐŸĐșĐŸĐčŃŃĐČа - ĐŸĐœĐ° ЎалДĐșĐŸ ĐœĐ” глаĐČĐœĐ°Ń (ŃĐŒ. ĐżŃĐœĐșŃ "ĐĐŸĐŒĐżĐŸĐœĐ”ĐœŃĐœĐŸĐ” ŃĐ”ŃŃĐžŃĐŸĐČĐ°ĐœĐžĐ”"). ĐĄĐ»ĐŸĐ¶ĐœĐŸŃŃŃ ŃĐ”ŃŃĐžŃĐŸĐČĐ°ĐœĐžŃ - ĐłĐŸŃĐ°Đ·ĐŽĐŸ Đ±ĐŸĐ»Đ”Đ” Đ±ĐŸĐ»Đ”Đ·ĐœĐ”ĐœĐœĐ°Ń ĐżŃĐŸĐ±Đ»Đ”ĐŒĐ°, ŃĐ”ŃĐ”ĐœĐžĐ” ĐșĐŸŃĐŸŃĐŸĐč ĐČ Đ±ĐŸĐ»ŃŃĐžĐœŃŃĐČĐ” ŃĐ»ŃŃаДĐČ ĐŸĐżŃДЎДлŃĐ”ŃŃŃ ĐŽŃŃĐłĐžĐŒĐž ŃĐŸĐŸĐ±ŃĐ°Đ¶Đ”ĐœĐžŃĐŒĐž. Đа ĐżŃаĐșŃĐžĐșĐ”, ĐœŃĐ¶ĐœĐŸ ŃЎДлаŃŃ ŃаĐș, ŃŃĐŸĐ±Ń ĐșажЎŃĐč ŃĐ”ŃŃ ŃĐČĐœŃĐŒ ĐŸĐ±ŃĐ°Đ·ĐŸĐŒ ĐŽĐŸĐ±Đ°ĐČĐ»ŃĐ”Ń ĐœŃĐ¶ĐœŃĐ” Đ”ĐŒŃ Đ·Đ°ĐżĐžŃĐž ĐČ ĐĐ Đž ŃĐ°Đ±ĐŸŃал ŃĐŸĐ»ŃĐșĐŸ Ń ĐœĐžĐŒĐž. ĐŃлО ĐżŃĐŸĐžĐ·ĐČĐŸĐŽĐžŃДлŃĐœĐŸŃŃŃ ŃŃĐ°ĐœĐŸĐČĐžŃŃŃ ĐșŃĐžŃĐžŃĐ”ŃĐșĐŸĐč ĐżŃĐŸĐ±Đ»Đ”ĐŒĐŸĐč - ĐșĐŸĐŒĐżŃĐŸĐŒĐžŃŃĐŸĐŒ ĐŒĐŸĐ¶Đ”Ń Đ±ŃŃŃ ŃДалОзаŃĐžŃ ŃĐ”ŃŃĐŸĐČ, ĐșĐŸŃĐŸŃŃĐ” ĐœĐ” ĐžĐ·ĐŒĐ”ĐœŃŃŃ ĐŽĐ°ĐœĐœŃĐ” (ĐœĐ°ĐżŃĐžĐŒĐ”Ń, запŃĐŸŃŃ).
+
+
+â **ĐĐœĐ°ŃĐ”:** ĐаĐșОД-ŃĐŸ ŃĐ”ŃŃŃ ĐœĐ” ŃŃĐ°Đ±ĐŸŃалО, ŃазĐČĐ”ŃŃŃĐČĐ°ĐœĐžĐ” ĐżŃĐŸĐ”ĐșŃа ĐżŃĐ”ŃĐČĐ°ĐœĐŸ Đž ŃазŃĐ°Đ±ĐŸŃŃĐžĐșĐž ŃŃаŃŃŃ ĐČŃĐ”ĐŒŃ ĐœĐ° ĐČŃŃŃĐœĐ”ĐœĐžĐ” ĐŸŃОбĐșĐž. Đ Đ”ŃŃŃ Đ»Đž ĐŸĐœĐ°? ĐажДŃŃŃ, ŃŃĐŸ ĐœĐ”Ń, ĐČĐ”ĐŽŃ ĐŽĐČа ŃĐ”ŃŃа ĐżŃĐŸŃŃĐŸ ĐžĐ·ĐŒĐ”ĐœĐžĐ»Đž ĐŸĐŽĐœĐž Đž ŃĐ” жД seed-ĐŽĐ°ĐœĐœŃĐ”.
+
+
+
+â ĐŃĐžĐŒĐ”ŃŃ ĐșĐŸĐŽĐ°
+
+
+
+### :thumbsdown: ĐДпŃаĐČОлŃĐœĐŸ: ŃĐ”ŃŃŃ ĐœĐ” ŃĐČĐ»ŃŃŃŃŃ ĐœĐ”Đ·Đ°ĐČĐžŃĐžĐŒŃĐŒĐž Đž ĐżĐŸĐ»Đ°ĐłĐ°ŃŃŃŃ ĐœĐ° ĐœĐ”ĐșĐžĐč ĐłĐ»ĐŸĐ±Đ°Đ»ŃĐœŃĐč Ń
ŃĐș ĐŽĐ»Ń ĐżĐŸĐ»ŃŃĐ”ĐœĐžŃ ĐłĐ»ĐŸĐ±Đ°Đ»ŃĐœŃŃ
ĐŽĐ°ĐœĐœŃŃ
+
+
+
+```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: ĐŃаĐČОлŃĐœĐŸ: Đ Đ°Đ·ĐŽĐ”Đ»Đ”ĐœĐžĐ” ĐżĐŸĐ»ŃĐ·ĐŸĐČаŃДлŃŃĐșĐŸĐłĐŸ ĐžĐœŃĐ”ŃŃĐ”ĐčŃа
+
+ 
+
+```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: ĐŃаĐČОлŃĐœĐŸ: ĐапŃĐŸŃ ŃĐ»Đ”ĐŒĐ”ĐœŃа Ń ĐžŃĐżĐŸĐ»ŃĐ·ĐŸĐČĐ°ĐœĐžĐ”ĐŒ ŃпДŃОалŃĐœĐŸĐłĐŸ аŃŃОбŃŃа ĐŽĐ»Ń ŃĐ”ŃŃĐžŃĐŸĐČĐ°ĐœĐžŃ
+
+
+
+```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: ĐŃаĐČОлŃĐœĐŸ: ĐĐŸĐ»ĐœĐŸŃŃŃŃ ŃДалОŃŃĐžŃĐœŃĐ” ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃŃ ĐżĐŸŃлД ŃĐ”ĐœĐŽĐ”ŃĐžĐœĐłĐ°
+
+ 
+
+```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)
+
+
+
+
+```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 ĐаблŃЎаĐčŃĐ” за ŃĐ”ĐŒ, ĐșаĐș ĐșĐŸĐœŃĐ”ĐœŃ ĐżĐ”ŃДЎаДŃŃŃ ĐżĐŸ ŃĐ”ŃĐž
+
+
+
+â
**ХЎДлаŃŃ:** ĐŃĐžĐŒĐ”ĐœĐžŃĐ” ĐșаĐșĐŸĐč-ĐœĐžĐ±ŃĐŽŃ Đ°ĐșŃĐžĐČĐœŃĐč ĐŒĐŸĐœĐžŃĐŸŃ, ĐșĐŸŃĐŸŃŃĐč ĐŸĐ±Đ”ŃпДŃĐžŃ ĐŸĐżŃĐžĐŒĐžĐ·Đ°ŃĐžŃ Đ·Đ°ĐłŃŃĐ·ĐșĐž ŃŃŃĐ°ĐœĐžŃŃ Đ° ŃĐ”ŃĐž - ŃŃĐŸ ĐČĐșĐ»ŃŃĐ°Đ”Ń Đ»ŃбŃĐ” ĐżŃĐŸĐ±Đ»Đ”ĐŒŃ 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 ĐŽĐ»Ń ĐżŃĐŸĐČĐ”ŃĐșĐž загŃŃĐ·ĐșĐž ŃŃŃĐ°ĐœĐžŃŃ
+
+
+
+
+
+
+
+## âȘ ïž 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
+
+ 
+
+```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
+
+
+
+```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-ŃĐ”ŃŃа ĐżĐŸ ĐČŃĐ”Đč ŃŃŃĐ°ĐœĐžŃĐ”
+
+
+
+```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
+
+
+
+```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
+
+
+
+
+
+
+
+
+
+## âȘ ïž 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: ĐДпŃаĐČОлŃĐœĐŸ: йОпОŃĐœĐ°Ń ĐČОзŃалŃĐœĐ°Ń ŃДгŃĐ”ŃŃĐžŃ - ĐșĐŸĐœŃĐ”ĐœŃ, ĐșĐŸŃĐŸŃŃĐč ĐżĐ»ĐŸŃ
ĐŸ ĐŸĐ±Đ»ŃжОĐČаДŃŃŃ
+
+
+
+
+
+### :clap: ĐŃаĐČОлŃĐœĐŸ: ĐаŃŃŃĐŸĐčĐșа ĐŽĐ»Ń ĐżĐŸĐ»ŃŃĐ”ĐœĐžŃ Đž ŃŃаĐČĐœĐ”ĐœĐžŃ ŃĐœĐžĐŒĐșĐŸĐČ UI
+
+
+
+```
+ # ĐĐŸĐ±Đ°ĐČŃŃĐ” ŃŃĐŸĐ»ŃĐșĐŸ ĐŽĐŸĐŒĐ”ĐœĐŸĐČ, ŃĐșĐŸĐ»ŃĐșĐŸ ĐœŃĐ¶ĐœĐŸ. ĐĐ»ŃŃ ĐŽĐ”ĐčŃŃĐČŃĐ”Ń, ĐșаĐș ĐŒĐ”ŃĐșа
+
+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 ĐŽĐ»Ń ĐżĐŸĐ»ŃŃĐ”ĐœĐžŃ ŃДзŃĐ»ŃŃаŃа ŃŃаĐČĐœĐ”ĐœĐžŃ ŃĐœĐžĐŒĐșĐŸĐČ Đž ĐŽŃŃгОŃ
ĐČĐŸĐ·ĐŒĐŸĐ¶ĐœĐŸŃŃĐ”Đč
+
+ 
+
+```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: ĐŃĐžĐŒĐ”Ń: ĐŃŃĐ”Ń ĐŸ ĐżĐŸĐșŃŃŃОО ŃĐ”ŃŃĐ°ĐŒĐž
+
+
+
+
+
+### :clap: ĐŃаĐČОлŃĐœĐŸ: ĐаŃŃŃĐŸĐčĐșа ĐżĐŸĐșŃŃŃĐžŃ ĐșĐ°Đ¶ĐŽĐŸĐłĐŸ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃа, ĐžŃĐżĐŸĐ»ŃĐ·ŃŃ Jest
+
+
+
+")
+
+
+
+
+
+## âȘ ïž 4.2 ĐŃĐŸŃĐŒĐŸŃŃĐžŃĐ” ĐŸŃŃĐ”ŃŃ ĐŸ ĐżĐŸĐșŃŃŃОО, ŃŃĐŸĐ±Ń ĐœĐ°ĐčŃĐž ĐœĐ”ĐżŃĐŸŃĐ”ŃŃĐžŃĐŸĐČĐ°ĐœĐœŃĐ” ŃŃаŃŃĐž ĐșĐŸĐŽĐ°
+
+:white_check_mark: **ХЎДлаŃŃ:** ĐĐ”ĐșĐŸŃĐŸŃŃĐ” ĐżŃĐŸĐ±Đ»Đ”ĐŒŃ Ń ĐșĐŸĐŽĐŸĐŒ ĐžĐœĐŸĐłĐŽĐ° ĐŸŃŃаŃŃŃŃ ĐœĐ”Đ·Đ°ĐŒĐ”ŃĐ”ĐœĐœŃĐŒĐž. ĐŃ
ĐŽĐ”ĐčŃŃĐČĐžŃДлŃĐœĐŸ ŃŃŃĐŽĐœĐŸ ĐŸĐ±ĐœĐ°ŃŃжОŃŃ ĐŽĐ°Đ¶Đ” Ń ĐżĐŸĐŒĐŸŃŃŃ ŃŃаЎОŃĐžĐŸĐœĐœŃŃ
ĐžĐœŃŃŃŃĐŒĐ”ĐœŃĐŸĐČ. ĐĐœĐž ĐœĐ” ĐČŃДгЎа ŃĐČĐ»ŃŃŃŃŃ ĐŸŃОбĐșĐ°ĐŒĐž, а ĐžĐœĐŸĐłĐŽĐ° ŃŃĐŸ ŃĐșĐŸŃДД ĐœĐ”ĐŸĐ¶ĐžĐŽĐ°ĐœĐœĐŸĐ” ĐżĐŸĐČĐ”ĐŽĐ”ĐœĐžĐ” ĐżŃĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ, ĐșĐŸŃĐŸŃĐŸĐ” ĐŒĐŸĐ¶Đ”Ń ĐžĐŒĐ”ŃŃ ŃĐ”ŃŃĐ”Đ·ĐœŃĐ” ĐżĐŸŃлДЎŃŃĐČĐžŃ. ĐапŃĐžĐŒĐ”Ń, ŃаŃŃĐŸ ĐœĐ”ĐșĐŸŃĐŸŃŃĐ” ĐŸĐ±Đ»Đ°ŃŃĐž ĐșĐŸĐŽĐ° ŃДЎĐșĐŸ ОлО ĐżĐŸŃŃĐž ĐœĐžĐșĐŸĐłĐŽĐ° ĐœĐ” ĐČŃĐ·ŃĐČаŃŃŃŃ - ĐČŃ ĐŽŃĐŒĐ°Đ»Đž, ŃŃĐŸ ĐșлаŃŃ 'PricingCalculator' ĐČŃДгЎа ŃŃŃĐ°ĐœĐ°ĐČлОĐČĐ°Đ”Ń ŃĐ”ĐœŃ ĐżŃĐŸĐŽŃĐșŃа, ĐœĐŸ ĐŸĐșĐ°Đ·Đ°Đ»ĐŸŃŃ, ŃŃĐŸ ĐŸĐœ ŃаĐșŃĐžŃĐ”ŃĐșĐž ĐœĐžĐșĐŸĐłĐŽĐ° ĐœĐ” ĐČŃĐ·ŃĐČаДŃŃŃ, Ń
ĐŸŃŃ Ń ĐœĐ°Ń 10000 ĐżŃĐŸĐŽŃĐșŃĐŸĐČ ĐČ ĐĐ Đž ĐŒĐœĐŸĐłĐŸ ĐżŃĐŸĐŽĐ°Đ¶... ĐŃŃĐ”ŃŃ ĐŸ ĐżĐŸĐșŃŃŃОО ĐșĐŸĐŽĐ° ĐżĐŸĐŒĐŸĐłŃŃ ĐČĐ°ĐŒ ĐżĐŸĐœŃŃŃ, ĐČĐ”ĐŽĐ”Ń Đ»Đž ĐżŃĐžĐ»ĐŸĐ¶Đ”ĐœĐžĐ” ŃĐ”Đ±Ń ŃаĐș, ĐșаĐș ĐČŃ ŃŃĐžŃаДŃĐ”. ĐŃĐŸĐŒĐ” ŃĐŸĐłĐŸ, ĐŸĐœĐž ĐŒĐŸĐłŃŃ ĐżĐŸĐșазаŃŃ, ĐșаĐșОД ŃĐžĐżŃ ĐșĐŸĐŽĐ° ĐœĐ” ĐżŃĐŸŃĐ”ŃŃĐžŃĐŸĐČĐ°ĐœŃ - Đ”ŃлО ĐČĐ°ĐŒ ŃĐŸĐŸĐ±ŃаŃŃ, ŃŃĐŸ 80% ĐșĐŸĐŽĐ° ĐżŃĐŸŃĐ”ŃŃĐžŃĐŸĐČĐ°ĐœĐŸ, ŃŃĐŸ Đ”ŃĐ” ĐœĐ” ĐłĐŸĐČĐŸŃĐžŃ ĐŸ ŃĐŸĐŒ, ĐżĐŸĐșŃŃŃŃ Đ»Đž ĐșŃĐžŃĐžŃĐ”ŃĐșОД ŃаŃŃĐž. ĐĐ»Ń ĐłĐ”ĐœĐ”ŃаŃОО ĐŸŃŃĐ”ŃĐŸĐČ ĐœŃĐ¶ĐœĐŸ ĐżŃĐŸŃŃĐŸ запŃŃŃĐžŃŃ ĐżŃĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ Đ»ĐžĐ±ĐŸ ĐČ ĐżŃĐŸĐŽĐ°ĐșŃĐœ, Đ»ĐžĐ±ĐŸ ĐČĐŸ ĐČŃĐ”ĐŒŃ ŃĐ”ŃŃĐžŃĐŸĐČĐ°ĐœĐžŃ, а заŃĐ”ĐŒ ĐžŃ
ĐżŃĐŸŃĐŒĐŸŃŃĐ”ŃŃ. ĐĐœĐž ĐżĐŸĐșазŃĐČаŃŃ, ĐșаĐș ŃаŃŃĐŸ ĐČŃĐ·ŃĐČаДŃŃŃ, ĐșĐ°Đ¶ĐŽĐ°Ń ĐŸĐ±Đ»Đ°ŃŃŃ ĐșĐŸĐŽĐ° Đž, Đ”ŃлО ĐČŃ ĐżĐŸŃŃаŃĐžŃĐ” ĐČŃĐ”ĐŒŃ ĐœĐ° ĐžŃ
ОзŃŃĐ”ĐœĐžĐ”, ŃĐŸ ŃĐŒĐŸĐ¶Đ”ŃĐ” ĐŸĐ±ĐœĐ°ŃŃжОŃŃ ĐœĐ”ĐșĐŸŃĐŸŃŃĐ” ĐżŃĐŸĐ±Đ»Đ”ĐŒŃ Ń ĐșĐŸĐŽĐŸĐŒ.
+
+
+â **ĐĐœĐ°ŃĐ”:** ĐŃлО ĐČŃ ĐœĐ” Đ·ĐœĐ°Đ”ŃĐ”, ĐșаĐșĐ°Ń ŃаŃŃŃ ĐșĐŸĐŽĐ° Ń ĐČĐ°Ń ĐœĐ” ĐżŃĐŸŃĐ”ŃŃĐžŃĐŸĐČĐ°ĐœĐ°, ŃĐŸ ĐČŃ ĐœĐ” ŃĐŒĐŸĐ¶Đ”ŃĐ” ŃĐ·ĐœĐ°ŃŃ, ĐŸŃĐșŃЎа ĐŒĐŸĐłŃŃ ĐČĐŸĐ·ĐœĐžĐșĐœŃŃŃ ĐżŃĐŸĐ±Đ»Đ”ĐŒŃ
+
+
+
+â ĐŃĐžĐŒĐ”ŃŃ ĐșĐŸĐŽĐ°
+
+
+
+### :thumbsdown: ĐДпŃаĐČОлŃĐœĐŸ: ЧŃĐŸ ĐœĐ” ŃаĐș Ń ŃŃĐžĐŒ ĐŸŃŃĐ”ŃĐŸĐŒ?
+
+Đа ĐŸŃĐœĐŸĐČĐ” ŃДалŃĐœĐŸĐłĐŸ ŃŃĐ”ĐœĐ°ŃĐžŃ, ĐșĐŸĐłĐŽĐ° ĐŒŃ ĐŸŃŃлДжОĐČалО ĐžŃĐżĐŸĐ»ŃĐ·ĐŸĐČĐ°ĐœĐžĐ” ĐœĐ°ŃĐ”ĐłĐŸ ĐżŃĐžĐ»ĐŸĐ¶Đ”ĐœĐžŃ ĐČ QA Đž ĐŸĐ±ĐœĐ°ŃŃжОлО ĐžĐœŃĐ”ŃĐ”ŃĐœŃĐ” заĐșĐŸĐœĐŸĐŒĐ”ŃĐœĐŸŃŃĐž ĐČŃ
ĐŸĐŽĐ° ĐČ ŃĐžŃŃĐ”ĐŒŃ (ĐżĐŸĐŽŃĐșазĐșа: ĐșĐŸĐ»ĐžŃĐ”ŃŃĐČĐŸ ĐŸŃĐșĐ°Đ·ĐŸĐČ ĐČŃ
ĐŸĐŽĐ° ĐœĐ”ĐżŃĐŸĐżĐŸŃŃĐžĐŸĐœĐ°Đ»ŃĐœĐŸ, ŃŃĐŸ-ŃĐŸ ŃĐČĐœĐŸ ĐœĐ” ŃаĐș. Đ ĐžŃĐŸĐłĐ” ĐČŃŃŃĐœĐžĐ»ĐŸŃŃ, ŃŃĐŸ ĐșаĐșаŃ-ŃĐŸ ĐŸŃОбĐșа ĐČĐŸ ŃŃĐŸĐœŃĐ”ĐœĐŽĐ” ĐżĐŸŃŃĐŸŃĐœĐœĐŸ бŃĐ”Ń ĐżĐŸ API ĐČŃ
ĐŸĐŽĐ° ĐČ Đ±ŃĐșĐ”ĐœĐŽ).
+
+
+
+
+
+
+
+## âȘ ïž 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% ĐżŃĐŸŃĐ”ŃŃĐžŃĐŸĐČĐ°ĐœĐŸ
+
+
+
+```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, ĐžĐœŃŃŃŃĐŒĐ”ĐœŃ ĐŽĐ»Ń ŃĐ”ŃŃĐžŃĐŸĐČĐ°ĐœĐžŃ ĐŒŃŃаŃĐžĐč, ĐŸĐ±ĐœĐ°ŃŃжОĐČĐ°Đ”Ń Đž ĐżĐŸĐŽŃŃĐžŃŃĐČĐ°Đ”Ń ĐșĐŸĐ»ĐžŃĐ”ŃŃĐČĐŸ ĐșĐŸĐŽĐ°, ĐșĐŸŃĐŸŃŃĐč ĐœĐ” ĐżŃĐŸŃДл ĐżŃĐŸĐČĐ”ŃĐșŃ (ĐŃŃаŃОО)
+
+")
+
+
+
+
+
+## âȘ ïž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 ДД ĐŸŃлаĐČлОĐČаДŃ
+
+
+
+
+
+
+
+## âȘ ïž 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))
+
+")
+
+
+
+
+
+## âȘ ïž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
+
+```
+
+
+
+
+
+
+
+
+
+## âȘ ïž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
+
+
+
+
+
+
+
+## âȘ ïž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 ĐŽĐ»Ń ĐŸĐżŃĐ”ĐŽĐ”Đ»Đ”ĐœĐžŃ ŃŃĐ”ĐżĐ”ĐœĐž ĐŸŃŃŃаĐČĐ°ĐœĐžŃ ĐșĐŸĐŽĐ° ĐŸŃ ĐżĐŸŃĐ»Đ”ĐŽĐœĐžŃ
ĐČĐ”ŃŃĐžĐč
+
+
+
+
+
+
+
+## âȘ ïž 5.8 ĐŃŃгОД, ĐœĐ” ŃĐČŃĐ·Đ°ĐœĐœŃĐ” Ń Node, ŃĐŸĐČĐ”ŃŃ ĐżĐŸ CI
+
+:white_check_mark: **ХЎДлаŃŃ:** ĐŃа Đ·Đ°ĐŒĐ”ŃĐșа ĐżĐŸŃĐČŃŃĐ”ĐœĐ° ŃĐŸĐČĐ”ŃĐ°ĐŒ ĐżĐŸ ŃĐ”ŃŃĐžŃĐŸĐČĐ°ĐœĐžŃ, ĐșĐŸŃĐŸŃŃĐ” ŃĐČŃĐ·Đ°ĐœŃ Ń Node JS ОлО, ĐżĐŸ ĐșŃаĐčĐœĐ”Đč ĐŒĐ”ŃĐ”, ĐŒĐŸĐłŃŃ Đ±ŃŃŃ ĐżŃĐŸĐžĐ»Đ»ŃŃŃŃĐžŃĐŸĐČĐ°ĐœŃ ĐœĐ° Đ”ĐłĐŸ ĐżŃĐžĐŒĐ”ŃĐ”. ĐĐŽĐœĐ°ĐșĐŸ ĐČ ŃŃĐŸĐŒ ĐżŃĐœĐșŃĐ” ŃĐłŃŃппОŃĐŸĐČĐ°ĐœĐŸ ĐœĐ”ŃĐșĐŸĐ»ŃĐșĐŸ ŃĐŸĐČĐ”ŃĐŸĐČ, ĐœĐ” ŃĐČŃĐ·Đ°ĐœĐœŃŃ
Ń Node, ĐșĐŸŃĐŸŃŃĐ” Ń
ĐŸŃĐŸŃĐŸ ОзĐČĐ”ŃŃĐœŃ
+
+ - ĐŃĐżĐŸĐ»ŃĐ·ŃĐčŃĐ” ĐŽĐ”ĐșлаŃаŃĐžĐČĐœŃĐč ŃĐžĐœŃаĐșŃĐžŃ. ĐŃĐŸ Đ”ĐŽĐžĐœŃŃĐČĐ”ĐœĐœŃĐč ĐČаŃĐžĐ°ĐœŃ ĐŽĐ»Ń Đ±ĐŸĐ»ŃŃĐžĐœŃŃĐČа ĐżĐŸŃŃаĐČŃĐžĐșĐŸĐČ, ĐœĐŸ ŃŃаŃŃĐ” ĐČĐ”ŃŃОО Jenkins ĐżĐŸĐ·ĐČĐŸĐ»ŃŃŃ ĐžŃĐżĐŸĐ»ŃĐ·ĐŸĐČаŃŃ ĐșĐŸĐŽ ОлО UI
- ĐŃбОŃаĐčŃĐ” ĐżŃĐŸĐŽŃĐșŃ, ĐșĐŸŃĐŸŃŃĐč ĐžĐŒĐ”Đ”Ń ĐČŃŃŃĐŸĐ”ĐœĐœŃŃ ĐżĐŸĐŽĐŽĐ”ŃжĐșŃ Docker
- ĐапŃŃĐșаĐčŃĐ” ŃĐœĐ°Ńала ŃĐ°ĐŒŃĐ” бŃŃŃŃŃĐ” ŃĐ”ŃŃŃ. ĐĄĐŸĐ·ĐŽĐ°ĐčŃĐ” Ńаг/ŃŃап "Smoke testing", ĐșĐŸŃĐŸŃŃĐč ĐłŃŃппОŃŃĐ”Ń ĐœĐ”ŃĐșĐŸĐ»ŃĐșĐŸ бŃŃŃŃŃŃ
ĐżŃĐŸĐČĐ”ŃĐŸĐș (ĐœĐ°ĐżŃĐžĐŒĐ”Ń, Đ»ĐžĐœŃĐžĐœĐł, ĐŒĐŸĐŽŃĐ»ŃĐœŃĐ” ŃĐ”ŃŃŃ) Đž ĐŸĐ±Đ”ŃпДŃĐžĐČĐ°Đ”Ń Đ±ŃŃŃŃŃŃ ĐŸĐ±ŃаŃĐœŃŃ ŃĐČŃĐ·Ń Ń ĐșĐŸĐŒĐŒĐžŃŃĐ”ŃĐŸĐŒ ĐșĐŸĐŽĐ°.
- ĐŁĐżŃĐŸŃŃĐžŃŃ ĐżŃĐŸŃĐŒĐŸŃŃ ŃĐ±ĐŸŃĐșĐž, ĐČĐșĐ»ŃŃĐ°Ń ĐŸŃŃĐ”ŃŃ ĐŸ ŃĐ”ŃŃĐžŃĐŸĐČĐ°ĐœĐžĐž, ĐŸŃŃĐ”ŃŃ ĐŸ ĐżĐŸĐșŃŃŃОО, ĐŸŃŃĐ”ŃŃ ĐŸ ĐŒŃŃаŃĐžŃŃ
, жŃŃĐœĐ°Đ»Ń Đž Ń.ĐŽ.
- ĐĄĐŸĐ·ĐŽĐ°ĐčŃĐ” ĐœĐ”ŃĐșĐŸĐ»ŃĐșĐŸ Đ·Đ°ĐŽĐ°ĐœĐžĐč ĐŽĐ»Ń ĐșĐ°Đ¶ĐŽĐŸĐłĐŸ ŃĐŸĐ±ŃŃĐžŃ, ĐżĐŸĐČŃĐŸŃĐœĐŸ ĐžŃĐżĐŸĐ»ŃĐ·ŃŃ ŃагО ĐŒĐ”Đ¶ĐŽŃ ĐœĐžĐŒĐž. ĐапŃĐžĐŒĐ”Ń, ĐœĐ°ŃŃŃĐŸĐčŃĐ” ĐŸĐŽĐœĐŸ Đ·Đ°ĐŽĐ°ĐœĐžĐ” ĐŽĐ»Ń ĐșĐŸĐŒĐŒĐžŃĐŸĐČ ĐČĐ”ŃĐșĐž ŃŃĐœĐșŃĐžĐč Đž ĐŽŃŃĐłĐŸĐ” - ĐŽĐ»Ń PR ĐŒĐ°ŃŃĐ”Ńа. ĐŃŃŃŃ ĐșажЎŃĐč Оз ĐœĐžŃ
ĐżĐŸĐČŃĐŸŃĐœĐŸ ĐžŃĐżĐŸĐ»ŃĐ·ŃĐ”Ń Đ»ĐŸĐłĐžĐșŃ, ĐžŃĐżĐŸĐ»ŃĐ·ŃŃ ĐŸĐ±ŃОД ŃагО (Đ±ĐŸĐ»ŃŃĐžĐœŃŃĐČĐŸ ĐżŃĐŸĐŽŃĐșŃĐŸĐČ ĐżŃĐ”ĐŽĐŸŃŃаĐČĐ»ŃŃŃ ĐœĐ”ĐșĐŸŃĐŸŃŃĐ” ĐŒĐ”Ń
Đ°ĐœĐžĐ·ĐŒŃ ĐŽĐ»Ń ĐżĐŸĐČŃĐŸŃĐœĐŸĐłĐŸ ĐžŃĐżĐŸĐ»ŃĐ·ĐŸĐČĐ°ĐœĐžŃ ĐșĐŸĐŽĐ°).
- ĐĐžĐșĐŸĐłĐŽĐ° ĐœĐ” ĐČŃŃаĐČĐ»ŃĐčŃĐ” ŃĐ”ĐșŃĐ”ŃŃ ĐČ ĐŸĐ±ŃŃĐČĐ»Đ”ĐœĐžĐ” Đ·Đ°ĐŽĐ°ĐœĐžŃ, бДŃĐžŃĐ” ĐžŃ
Оз Ń
ŃĐ°ĐœĐžĐ»ĐžŃа ŃĐ”ĐșŃĐ”ŃĐŸĐČ ĐžĐ»Đž Оз ĐșĐŸĐœŃОгŃŃаŃОО Đ·Đ°ĐŽĐ°ĐœĐžŃ
- ĐŻĐČĐœĐŸ ĐżĐŸĐČŃŃаĐčŃĐ” ĐČĐ”ŃŃĐžŃ ĐČ ŃĐ±ĐŸŃĐșĐ” ŃДлОза ОлО, ĐżĐŸ ĐșŃаĐčĐœĐ”Đč ĐŒĐ”ŃĐ”, ŃбДЎОŃĐ”ŃŃ, ŃŃĐŸ ŃазŃĐ°Đ±ĐŸŃŃĐžĐș ŃŃĐŸ ŃЎДлал
- ĐĄĐ±ĐŸŃĐșа ŃĐŸĐ»ŃĐșĐŸ ĐŸĐŽĐžĐœ Ńаз Đž ĐČŃĐżĐŸĐ»ĐœĐ”ĐœĐžĐ” ĐČŃĐ”Ń
ĐżŃĐŸĐČĐ”ŃĐŸĐș ĐœĐ°ĐŽ Đ”ĐŽĐžĐœŃŃĐČĐ”ĐœĐœŃĐŒ аŃŃĐ”ŃаĐșŃĐŸĐŒ ŃĐ±ĐŸŃĐșĐž (ĐœĐ°ĐżŃĐžĐŒĐ”Ń, ĐŸĐ±ŃĐ°Đ·ĐŸĐŒ Docker)
- йДŃŃĐžŃŃĐčŃĐ” ĐČ ŃŃĐ”ĐŒĐ”ŃĐœĐŸĐč ŃŃДЎД, ĐșĐŸŃĐŸŃĐ°Ń ĐœĐ” пДŃĐ”ĐœĐŸŃĐžŃ ŃĐŸŃŃĐŸŃĐœĐžĐ” ĐŒĐ”Đ¶ĐŽŃ ŃĐ±ĐŸŃĐșĐ°ĐŒĐž. ĐŃŃĐžŃĐŸĐČĐ°ĐœĐžĐ” ĐŒĐŸĐŽŃлДĐč 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!
+
+
+
+
+
+
+
+
+
+
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). йДŃŃŃĐčŃĐ” лОŃĐ” ŃŃŃĐ»ŃĐșĐž, ŃĐșŃĐ»ŃĐșĐž ĐżĐŸŃŃŃĐ±ĐœĐŸ, ĐœĐ°ĐŒĐ°ĐłĐ°ĐčŃĐ”ŃŃ ĐżŃĐŽŃŃĐžĐŒŃĐČаŃĐž ĐčĐŸĐłĐŸ ŃĐżŃĐžŃĐœŃŃŃŃ, ŃĐœĐșĐŸĐ»Đž ĐœĐ°ĐČŃŃŃ ĐČаŃŃĐŸ ĐČŃĐŽĐŒĐŸĐČĐžŃĐžŃŃ ĐČŃĐŽ ĐŽĐ”ŃĐșĐžŃ
ŃĐ”ŃŃŃĐČ Ń ĐżĐŸĐŒŃĐœŃŃĐž ĐœĐ°ĐŽŃĐčĐœŃŃŃŃ ĐœĐ° ŃĐČОЎĐșŃŃŃŃ Ń ĐżŃĐŸŃŃĐŸŃŃ.
+
+
+
+ĐŃĐ»ŃŃŃŃŃŃ ĐœĐ°ĐČĐ”ĐŽĐ”ĐœĐžŃ
ĐœĐžĐ¶ŃĐ” ĐżĐŸŃаЎ Ń ĐżĐŸŃ
ŃĐŽĐœĐžĐŒĐž ĐČŃĐŽ ŃŃĐŸĐłĐŸ ĐżŃĐžĐœŃОпŃ.
+
+### ĐĐŸŃĐŸĐČŃ ĐżĐŸŃаŃĐž?
+
+
+
+# Đ ĐŸĐ·ĐŽŃĐ» 1: ĐĐœĐ°ŃĐŸĐŒŃŃ ŃĐ”ŃŃŃ
+
+
+
+## âȘ ïž 1.1 ĐĐŸĐŽĐ°ĐčŃĐ” 3 ŃаŃŃĐžĐœĐž ĐŽĐŸ ĐșĐŸĐ¶ĐœĐŸŃ ĐœĐ°Đ·ĐČĐž ŃĐ”ŃŃŃ
+
+:white_check_mark: **Đ ĐŸĐ±Đž:** ĐŁ Đ·ĐČŃŃŃ ĐżŃĐŸ ŃĐ”ŃŃŃĐČĐ°ĐœĐœŃ ĐŒĐ°Ń Đ±ŃŃĐž ĐČĐșĐ°Đ·Đ°ĐœĐŸ, ŃĐž Đ·Đ°ĐŽĐŸĐČĐŸĐ»ŃĐœŃŃ ĐżĐŸŃĐŸŃĐœĐ° ĐČĐ”ŃŃŃŃ ĐżŃĐŸĐłŃĐ°ĐŒĐž ĐČĐžĐŒĐŸĐłĐž ĐŽĐŸ Đ»ŃĐŽĐ”Đč, ŃĐșŃ ĐœĐ” ĐŸĐ±ĐŸĐČâŃĐ·ĐșĐŸĐČĐŸ Đ·ĐœĐ°ĐčĐŸĐŒŃ Đ· ĐșĐŸĐŽĐŸĐŒ: ŃĐ”ŃŃŃĐČалŃĐœĐžĐșа, ŃĐœĐ¶Đ”ĐœĐ”Ńа DevOps, ŃĐșĐžĐč ŃĐŸĐ·ĐłĐŸŃŃаŃ, Ń ĐŒĐ°ĐčбŃŃĐœŃĐŸĐłĐŸ ĐČĐ°Ń ŃĐ”ŃДз ĐŽĐČа ŃĐŸĐșĐž. ĐŠŃĐŸĐłĐŸ ĐŒĐŸĐ¶ĐœĐ° ĐŽĐŸŃŃĐłŃĐž ĐœĐ°ĐčĐșŃаŃĐ”, ŃĐșŃĐŸ ŃĐ”ŃŃĐž ŃĐșлаЎаŃĐžĐŒŃŃŃŃŃ ĐœĐ° ŃŃĐČĐœŃ ĐČĐžĐŒĐŸĐł Ń ĐČĐșĐ»ŃŃаŃĐžĐŒŃŃŃ 3 ŃаŃŃĐžĐœĐž:
+
+(1) Đ©ĐŸ пДŃĐ”ĐČŃŃŃŃŃŃŃŃ? ĐапŃĐžĐșлаЎ, ĐŒĐ”ŃĐŸĐŽ ProductsService.addNewProduct
+
+(2) Đа ŃĐșĐžŃ
ĐŸĐ±ŃŃаĐČĐžĐœ Ń ŃŃĐ”ĐœĐ°ŃŃŃ? ĐапŃĐžĐșлаЎ, Ń ĐŒĐ”ŃĐŸĐŽ ĐœĐ” пДŃДЎаŃŃŃŃŃ ŃŃĐœĐ°
+
+(3) ĐŻĐșĐžĐč ŃДзŃĐ»ŃŃĐ°Ń ĐŸŃŃĐșŃŃŃŃŃŃ? ĐапŃĐžĐșлаЎ, ĐœĐŸĐČĐžĐč ĐżŃĐŸĐŽŃĐșŃ ĐœĐ” заŃĐČĐ”ŃĐŽĐ¶Đ”ĐœĐŸ
+
+
+
+â **ĐĐœĐ°ĐșŃĐ”:** Đ ĐŸĐ·ĐłĐŸŃŃĐ°ĐœĐœŃ ŃĐŸĐčĐœĐŸ ĐœĐ” ĐČĐŽĐ°Đ»ĐŸŃŃ, ŃĐ”ŃŃ ŃĐ· ĐœĐ°Đ·ĐČĐŸŃ Â«Add product» ĐœĐ” ĐČЎаĐČŃŃ. ЧО ŃĐ” ĐłĐŸĐČĐŸŃĐžŃŃ ĐČĐ°ĐŒ ĐżŃĐŸ ŃĐ”, ŃĐŸ ŃĐ°ĐŒĐ” ĐœĐ”ŃĐżŃаĐČĐœĐŸ?
+
+
+
+**đ ĐŃĐžĐŒŃŃĐșа:** ĐĐŸĐ¶Đ”Đœ ĐżŃĐœĐșŃ ĐŒĐ°Ń ĐżŃĐžĐșлаЎО ĐșĐŸĐŽŃ, а ŃĐœĐșĐŸĐ»Đž ŃаĐșĐŸĐ¶ Đ·ĐŸĐ±ŃĐ°Đ¶Đ”ĐœĐœŃ. ĐаŃĐžŃĐœŃŃŃ, ŃĐŸĐ± ŃĐŸĐ·ĐłĐŸŃĐœŃŃĐž
+
+
+â ĐŃĐžĐșлаЎО ĐșĐŸĐŽŃ
+
+
+
+### :clap: Đ ĐŸĐ±Đž ŃĐ” ĐżŃаĐČОлŃĐœĐŸ. ĐŃĐžĐșлаЎ: ŃĐŒâŃ ŃĐ”ŃŃŃ, ŃĐșĐ” ŃĐșлаЎаŃŃŃŃŃ Đ· 3 ŃаŃŃĐžĐœ
+
+
+
+```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 ŃаŃŃĐžĐœ
+
+
+
+
+
+
+© 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
+
+ 
+
+```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()
+
+
+
+â ĐŃĐžĐșлаЎО ĐșĐŸĐŽŃ
+
+ 
+
+### :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: ĐŃĐžĐșлаЎ Đ°ĐœŃĐžŃĐ°Đ±Đ»ĐŸĐœŃ: ŃĐ”ŃŃĐŸĐČĐžĐč ĐżŃĐžĐșлаЎ ŃĐ”ŃŃŃŃ ĐČĐœŃŃŃŃŃĐœŃ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃĐž бДз ĐČĐ°ĐłĐŸĐŒĐŸŃ ĐżŃĐžŃĐžĐœĐž
+
+
+
+```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 ŃĐŒŃŃŃŃ ĐČĐœŃŃŃŃŃĐœŃ ŃДалŃзаŃŃŃ
+
+
+
+```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: ĐŃĐžĐșлаЎ Đ°ĐœŃĐžŃĐ°Đ±Đ»ĐŸĐœŃ: ĐœĐ°Đ±ŃŃ ŃĐ”ŃŃŃĐČ, ŃĐșĐžĐč ĐżŃĐŸŃ
ĐŸĐŽĐžŃŃ ŃĐ”ŃДз ĐœĐ”ŃДалŃŃŃĐžŃĐœŃ ĐŽĐ°ĐœŃ
+
+
+
+```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â
+
+
+
+```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 ŃŃĐŽĐșĐ°ĐŒĐž ĐșĐŸĐŽŃ
+
+
+
+```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(`
+
+- Home
+- About
+- Contact
+
+`);
+});
+```
+
+
+
+
+
+## âȘ ïžĐĄĐșĐŸĐżŃŃĐčŃĐ” ĐșĐŸĐŽ, алД ŃŃĐ»ŃĐșĐž ĐœĐ”ĐŸĐ±Ń
ŃĐŽĐœĐžĐč
+
+:white_check_mark: **Đ ĐŸĐ±Đž:** ĐĐșĐ»ŃŃŃŃŃ ŃŃŃ ĐœĐ”ĐŸĐ±Ń
ŃĐŽĐœŃ ĐŽĐ”ŃалŃ, ŃĐșŃ ĐČплОĐČаŃŃŃ ĐœĐ° ŃДзŃĐ»ŃŃĐ°Ń ŃĐ”ŃŃŃ, алД ĐœĐ” бŃĐ»ŃŃĐ” ŃĐŸĐłĐŸ. ĐŻĐș ĐżŃĐžĐșлаЎ, ŃĐŸĐ·ĐłĐ»ŃĐœĐ”ĐŒĐŸ ŃĐ”ŃŃ, ŃĐșĐžĐč ĐŒĐ°Ń ŃĐŸĐ·ŃаŃ
ĐŸĐČŃĐČаŃĐž 100 ŃŃĐŽĐșŃĐČ ĐČŃ
ŃĐŽĐœĐŸĐłĐŸ JSONâ-âĐŃŃаĐČĐ»ŃŃĐž ŃĐ” ĐČ ĐșĐŸĐ¶Đ”Đœ ŃĐ”ŃŃ ŃŃĐŸĐŒĐ»ĐžĐČĐŸ. ĐŻĐșŃĐŸ ĐČĐžŃŃĐłŃĐž ĐčĐŸĐłĐŸ за ĐŒĐ”Đ¶Ń transferFactory.getJSON(), ŃĐ”ŃŃ Đ·Đ°Đ»ĐžŃĐžŃŃŃŃ ĐœĐ”ĐČĐžĐ·ĐœĐ°ŃĐ”ĐœĐžĐŒâ-âĐДз ĐŽĐ°ĐœĐžŃ
ĐČажĐșĐŸ ŃĐżŃĐČĐČŃĐŽĐœĐ”ŃŃĐž ŃДзŃĐ»ŃŃĐ°Ń ŃĐ”ŃŃŃ Đ· ĐżŃĐžŃĐžĐœĐŸŃ («ŃĐŸĐŒŃ ĐČŃĐœ ĐŒĐ°Ń ĐżĐŸĐČĐ”ŃŃаŃĐž ŃŃаŃŃŃ 400?»). ĐлаŃĐžŃĐœŃ ĐșĐœĐžĐ¶ĐșĐŸĐČŃ ŃĐ°Đ±Đ»ĐŸĐœĐž x-unit ĐœĐ°Đ·ĐČалО ŃĐ”Đč ŃĐ°Đ±Đ»ĐŸĐœ «ŃаŃĐŒĐœĐžŃĐžĐŒ ĐłĐŸŃŃĐ”ĐŒÂ»â-âĐ©ĐŸŃŃ ĐœĐ”ĐČĐžĐŽĐžĐŒĐ” ĐČĐżĐ»ĐžĐœŃĐ»ĐŸ ĐœĐ° ĐœĐ°ŃŃ ŃДзŃĐ»ŃŃаŃĐž ŃĐ”ŃŃŃĐČĐ°ĐœĐœŃ, ĐŒĐž ĐœĐ” Đ·ĐœĐ°ŃĐŒĐŸ, ŃĐŸ ŃĐ°ĐŒĐ”. ĐĐž ĐŒĐŸĐ¶Đ”ĐŒĐŸ ĐŽĐŸŃŃĐłŃĐž ĐșŃаŃĐžŃ
ŃДзŃĐ»ŃŃаŃŃĐČ, ĐČĐžŃŃĐłĐœŃĐČŃĐž ĐżĐŸĐČŃĐŸŃŃĐČĐ°ĐœŃ ĐŽĐŸĐČĐłŃ ŃаŃŃĐžĐœĐž ĐœĐ°Đ·ĐŸĐČĐœŃ Đ ŃŃŃĐșĐŸ Đ·Đ°Đ·ĐœĐ°ŃĐžĐČŃĐž, ŃĐșŃ ĐșĐŸĐœĐșŃĐ”ŃĐœŃ ĐŽĐ”ŃĐ°Đ»Ń ĐŒĐ°ŃŃŃ Đ·ĐœĐ°ŃĐ”ĐœĐœŃ ĐŽĐ»Ń ŃĐ”ŃŃŃ. ĐĐ”ŃĐ”Ń
ĐŸĐŽŃŃĐž ĐŽĐŸ ĐœĐ°ĐČĐ”ĐŽĐ”ĐœĐŸĐłĐŸ ĐČĐžŃĐ” ĐżŃĐžĐșлаЎŃ, ŃĐ”ŃŃ ĐŒĐŸĐ¶Đ” пДŃДЎаĐČаŃĐž паŃĐ°ĐŒĐ”ŃŃĐž, ŃĐșŃ ĐżŃĐŽĐșŃĐ”ŃĐ»ŃŃŃŃ ĐČажлОĐČŃŃŃŃ: transferFactory.getJSON({sender: undefined}). ĐŁ ŃŃĐŸĐŒŃ ĐżŃĐžĐșĐ»Đ°ĐŽŃ ŃĐžŃĐ°Ń ĐżĐŸĐČĐžĐœĐ”Đœ ĐœĐ”ĐłĐ°ĐčĐœĐŸ Đ·ŃĐŸĐ±ĐžŃĐž ĐČĐžŃĐœĐŸĐČĐŸĐș, ŃĐŸ ĐżĐŸŃĐŸĐ¶ĐœŃ ĐżĐŸĐ»Đ” ĐČŃĐŽĐżŃаĐČĐœĐžĐșа Ń ĐżŃĐžŃĐžĐœĐŸŃ, ŃĐŸĐŒŃ ŃĐ”ŃŃ ĐżĐŸĐČĐžĐœĐ”Đœ ĐŸŃŃĐșŃĐČаŃĐž ĐżĐŸĐŒĐžĐ»ĐșŃ ĐżĐ”ŃĐ”ĐČŃŃĐșĐž Đ°Đ±ĐŸ бŃĐŽŃ-ŃĐșĐžĐč ŃĐœŃĐžĐč ĐżĐŸĐŽŃĐ±ĐœĐžĐč аЎДĐșĐČаŃĐœĐžĐč ŃДзŃĐ»ŃŃаŃ.
+
+
+â **ĐĐœĐ°ĐșŃĐ”:** ĐĐŸĐżŃŃĐČĐ°ĐœĐœŃ 500 ŃŃĐŽĐșŃĐČ JSON Đ·ŃĐŸĐ±ĐžŃŃ ĐČаŃŃ ŃĐ”ŃŃĐž ĐœĐ”ĐżŃОЎаŃĐœĐžĐŒĐž ĐŽĐ»Ń ĐŸĐ±ŃĐ»ŃĐłĐŸĐČŃĐČĐ°ĐœĐœŃ Ńа ŃĐžŃĐ°ĐœĐœŃ. ĐĐ”ŃĐ”ĐœĐ”ŃĐ”ĐœĐœŃ ĐČŃŃĐŸĐłĐŸ ĐœĐ°Đ·ĐŸĐČĐœŃ Đ·Đ°ĐșŃĐœŃĐžŃŃŃŃ ĐœĐ”ŃŃŃĐșĐžĐŒĐž ŃĐ”ŃŃĐ°ĐŒĐž, ŃĐșŃ ĐČажĐșĐŸ Đ·ŃĐŸĐ·ŃĐŒŃŃĐž
+
+
+
+â ĐŃĐžĐșлаЎО ĐșĐŸĐŽŃ
+
+
+
+### :thumbsdown: ĐŃĐžĐșлаЎ Đ°ĐœŃĐžŃĐ°Đ±Đ»ĐŸĐœŃ: ĐĐŸĐŒĐžĐ»Đșа ŃĐ”ŃŃŃ ĐœĐ”Đ·ŃĐŸĐ·ŃĐŒŃла, ĐŸŃĐșŃĐ»ŃĐșĐž ĐČŃŃ ĐżŃĐžŃĐžĐœĐ° Đ·ĐŸĐČĐœŃŃĐœŃ Ń Ń
ĐŸĐČаŃŃŃŃŃ Ń ĐČДлОŃĐ”Đ·ĐœĐŸĐŒŃ JSON
+
+
+
+```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
+
+
+
+```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) Ńа ĐŒĐŸĐ¶ŃŃŃ ĐČĐžĐșĐŸĐœŃĐČаŃĐžŃŃ ŃаŃŃĐŸ, ĐœĐ°ĐČŃŃŃ ĐșĐŸĐ»Đž ŃĐŸĐ·ŃĐŸĐ±ĐœĐžĐș ĐČĐČĐŸĐŽĐžŃŃ ŃĐ”ĐșŃŃ)
+
+
+
+```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: ĐŃĐžĐșлаЎ ĐżŃаĐČОлŃĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: ĐĄŃŃŃĐșŃŃŃŃĐČĐ°ĐœĐœŃ ĐœĐ°Đ±ĐŸŃŃ Đ· ĐœĐ°Đ·ĐČĐŸŃ Đ±Đ»ĐŸĐșŃ, ŃĐŸ ŃĐ”ŃŃŃŃŃŃŃŃ, Ń ŃŃĐ”ĐœĐ°ŃŃŃĐŒĐž ĐżŃОзĐČДЎД ĐŽĐŸ Đ·ŃŃŃĐœĐŸĐłĐŸ Đ·ĐČŃŃŃ, ŃĐșĐžĐč ĐżĐŸĐșĐ°Đ·Đ°ĐœĐŸ ĐœĐžĐ¶ŃĐ”
+
+
+
+```javascript
+// йДŃŃŃŃĐŒĐ° ĐŸĐŽĐžĐœĐžŃŃ
+describe("Transfer service", () => {
+ // ĐĄŃĐ”ĐœĐ°ŃŃĐč
+ describe("When no credit", () => {
+ // ĐŃŃĐșŃĐČĐ°ĐœĐœŃ
+ test("Then the response status should decline", () => {});
+
+ // ĐŃŃĐșŃĐČĐ°ĐœĐœŃ
+ test("Then it should send email to admin", () => {});
+ });
+});
+```
+
+
+
+
+
+### :thumbsdown: ĐŃĐžĐșлаЎ Đ°ĐœŃĐžŃĐ°Đ±Đ»ĐŸĐœŃ: ĐĐ»ĐŸŃĐșĐžĐč ŃпОŃĐŸĐș ŃĐ”ŃŃŃĐČ ŃŃĐșĐ»Đ°ĐŽĐœĐžŃŃ ŃĐžŃаŃĐ”ĐČŃ ŃĐŽĐ”ĐœŃĐžŃŃĐșаŃŃŃ ŃŃŃĐŸŃŃĐč ĐșĐŸŃĐžŃŃŃĐČаŃŃĐČ Ń ŃĐżŃĐČĐČŃĐŽĐœĐ”ŃĐ”ĐœĐœŃ ĐœĐ”ĐČЎалОŃ
ŃĐ”ŃŃŃĐČ
+
+
+
+```javascript
+test("Then the response status should decline", () => {});
+
+test("Then it should send email", () => {});
+
+test("Then there should not be a new transfer record", () => {});
+```
+
+
+
+
+
+
+
+
+
+## âȘ ïž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 ĐżŃĐŸĐżĐŸĐœŃŃ Đ±Đ°ĐłĐ°ŃĐ” ĐżĐŸŃŃŃĐŸĐ»ŃĐŸ ŃĐ”ŃŃŃĐČĐ°ĐœĐœŃ Ń ŃĐČĐŸŃĐč ĐŽĐžĐČĐŸĐČĐžĐ¶ĐœŃĐč ĐżŃблŃĐșаŃŃŃ Â«ĐąĐ”ŃŃŃĐČĐ°ĐœĐœŃ ĐŒŃĐșŃĐŸŃĐ”ŃĐČŃŃŃĐČ â ŃаĐșĐžĐŒ жД ŃĐžĐœĐŸĐŒÂ»
+
+
+
+âșïžĐŃĐžĐșлаЎ: [YouTube: âĐа ĐŒĐ”Đ¶Đ°ĐŒĐž ĐŒĐŸĐŽŃĐ»ŃĐœĐžŃ
ŃĐ”ŃŃŃĐČ: 5 блОŃĐșŃŃĐžŃ
ŃОпŃĐČ ŃĐ”ŃŃŃĐČ Node.JS (2018)â (Yoni Goldberg)](https://www.youtube.com/watch?v=-2zP494wdUY&feature=youtu.be)
+
+
+
+
+
+
+
+
+
+## âȘ ïž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 ĐČ ĐżŃĐŸŃĐ”ŃŃ (ŃĐČОЎĐșĐŸ Ńа ĐŸŃ
ĐŸĐżĐ»ŃŃ Đ±Đ°ĐłĐ°ŃĐŸ ŃŃĐČĐœŃĐČ)
+
+
+
+ ĐŽĐŸĐ·ĐČĐŸĐ»ŃŃ ĐżŃĐŽŃ
ĐŸĐŽĐžŃĐž ĐŽĐŸ Express API ĐČ ĐżŃĐŸŃĐ”ŃŃ (ŃĐČОЎĐșĐŸ Ńа ĐŸŃ
ĐŸĐżĐ»ŃŃ Đ±Đ°ĐłĐ°ŃĐŸ ŃŃĐČĐœŃĐČ)")
+
+
+
+
+
+## âȘ ïž2.3 ĐĐ”ŃĐ”ĐșĐŸĐœĐ°ĐčŃĐ”ŃŃ, ŃĐŸ ĐœĐŸĐČŃ ŃДлŃĐ·Đž ĐœĐ” ĐżĐŸŃŃŃŃŃŃŃ API, ĐČĐžĐșĐŸŃĐžŃŃĐŸĐČŃŃŃĐž ŃĐ”ŃŃĐž ĐșĐŸĐœŃŃаĐșŃŃ
+
+:white_check_mark: **Đ ĐŸĐ±Đž:** ĐŃжД, ĐČĐ°Ń ĐŒŃĐșŃĐŸŃĐ”ŃĐČŃŃ ĐŒĐ°Ń ĐșŃĐ»ŃĐșа ĐșĐ»ŃŃĐœŃŃĐČ, Ń ĐČĐž запŃŃĐșаŃŃĐ” ĐșŃĐ»ŃĐșа ĐČĐ”ŃŃŃĐč ŃĐ”ŃĐČŃŃŃ Đ· ĐŒŃŃĐșŃĐČĐ°ĐœŃ ŃŃĐŒŃŃĐœĐŸŃŃŃ (ŃĐŸĐ± ŃŃŃ Đ±ŃлО Đ·Đ°ĐŽĐŸĐČĐŸĐ»Đ”ĐœŃ). ĐĐŸŃŃĐŒ ĐČĐž Đ·ĐŒŃĐœŃŃŃĐ” ŃĐșĐ”ŃŃ ĐżĐŸĐ»Đ” Ń Â«Đ±ŃĐŒ!», ŃĐșĐžĐčŃŃ ĐČажлОĐČĐžĐč ĐșĐ»ŃŃĐœŃ, ŃĐșĐžĐč ĐżĐŸĐșлаЎаŃŃŃŃŃ ĐœĐ° ŃĐ” ĐżĐŸĐ»Đ”, ŃĐ”ŃĐŽĐžŃŃŃŃ. ĐŠĐ” 22-Đč ĐżŃĐŽŃŃŃĐż ŃĐČŃŃŃ ŃĐœŃДгŃаŃŃŃ: ŃĐ”ŃĐČĐ”ŃĐœŃĐč ŃŃĐŸŃĐŸĐœŃ ĐŽŃжД ŃĐșĐ»Đ°ĐŽĐœĐŸ ĐČŃаŃ
ŃĐČаŃĐž ĐČŃŃ ŃĐžŃĐ»Đ”ĐœĐœŃ ĐŸŃŃĐșŃĐČĐ°ĐœĐœŃ ĐșĐ»ŃŃĐœŃŃĐČâââĐ ŃĐœŃĐŸĐłĐŸ Đ±ĐŸĐșŃ, ĐșĐ»ŃŃĐœŃĐž ĐœĐ” ĐŒĐŸĐ¶ŃŃŃ ĐżŃĐŸĐČĐŸĐŽĐžŃĐž Đ¶ĐŸĐŽĐœĐŸĐłĐŸ ŃĐ”ŃŃŃĐČĐ°ĐœĐœŃ, ĐŸŃĐșŃĐ»ŃĐșĐž ŃĐ”ŃĐČĐ”Ń ĐșĐŸĐœŃŃĐŸĐ»ŃŃ ĐŽĐ°ŃĐž ĐČОпŃŃĐșŃ. ĐŃĐœŃŃ ŃŃлОĐč ŃпДĐșŃŃ ĐŒĐ”ŃĐŸĐŽŃĐČ, ŃĐșŃ ĐŒĐŸĐ¶ŃŃŃ ĐżĐŸĐŒÊŒŃĐșŃĐžŃĐž ĐżŃĐŸĐ±Đ»Đ”ĐŒŃ ĐșĐŸĐœŃŃаĐșŃŃ, ĐŽĐ”ŃĐșŃ Đ· ĐœĐžŃ
ĐżŃĐŸŃŃŃ, ŃĐœŃŃ Ń Đ±Đ°ĐłĐ°ŃŃĐžĐŒĐž ĐœĐ° ŃŃĐœĐșŃŃŃ Ńа ĐČĐžĐŒĐ°ĐłĐ°ŃŃŃ ĐșŃŃŃŃŃĐŸŃ ĐșŃĐžĐČĐŸŃ ĐœĐ°ĐČŃĐ°ĐœĐœŃ. ĐŁ ĐżŃĐŸŃŃĐŸĐŒŃ Ńа ŃĐ”ĐșĐŸĐŒĐ”ĐœĐŽĐŸĐČĐ°ĐœĐŸĐŒŃ ĐżŃĐŽŃ
ĐŸĐŽŃ ĐżĐŸŃŃаŃалŃĐœĐžĐș API ĐżŃблŃĐșŃŃ ĐżĐ°ĐșĐ”Ń npm ŃĐ· ŃĐžĐżĐŸĐŒ API (ĐœĐ°ĐżŃĐžĐșлаЎ, JSDoc, TypeScript). ĐąĐŸĐŽŃ ŃĐżĐŸĐ¶ĐžĐČаŃŃ Đ·ĐŒĐŸĐ¶ŃŃŃ ĐŸŃŃĐžĐŒĐ°ŃĐž ŃŃ Đ±ŃблŃĐŸŃĐ”ĐșŃ Ńа ĐŸŃŃĐžĐŒĐ°ŃĐž ĐČĐžĐłĐŸĐŽŃ ĐČŃĐŽ Đ°ĐœĐ°Đ»ŃĐ·Ń ŃаŃŃ ĐșĐŸĐŽŃĐČĐ°ĐœĐœŃ Ńа пДŃĐ”ĐČŃŃĐșĐž. ĐОгаЎлОĐČŃŃĐžĐč ĐżŃĐŽŃ
ŃĐŽ â ĐČĐžĐșĐŸŃĐžŃŃĐ°ĐœĐœŃ [PACT](https://docs.pact.io/), ŃŃĐČĐŸŃĐ”ĐœĐŸĐłĐŸ ĐŽĐ»Ń ŃĐŸŃĐŒĐ°Đ»ŃзаŃŃŃ ŃŃĐŸĐłĐŸ ĐżŃĐŸŃĐ”ŃŃ Đ·Đ° ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃ ĐŽŃжД ŃŃĐčĐœŃĐČĐœĐŸĐłĐŸ ĐżŃĐŽŃ
ĐŸĐŽŃââÂ«ĐœĐ” ŃĐ”ŃĐČĐ”Ń ŃĐ°ĐŒ ĐČĐžĐ·ĐœĐ°ŃĐ°Ń ĐżĐ»Đ°Đœ ŃĐ”ŃŃŃĐČĐ°ĐœĐœŃ, а ĐșĐ»ŃŃĐœŃ ĐČĐžĐ·ĐœĐ°ŃĐ°Ń ŃĐ”ŃŃĐž. PACT ĐŒĐŸĐ¶Đ” запОŃŃĐČаŃĐž ĐŸŃŃĐșŃĐČĐ°ĐœĐœŃ ĐșĐ»ŃŃĐœŃа Ńа ŃĐŸĐ·ĐŒŃŃŃĐČаŃĐž ĐČ ŃĐżŃĐ»ŃĐœĐŸĐŒŃ ĐŒŃŃŃŃ, Â«ĐżĐŸŃĐ”ŃĐ”ĐŽĐœĐžĐșŃ», ŃĐŸĐ± ŃĐ”ŃĐČĐ”Ń ĐŒŃĐł ĐŸŃŃĐžĐŒĐ°ŃĐž ĐŸŃŃĐșŃĐČĐ°ĐœĐœŃ Ńа запŃŃĐșаŃĐž ĐșĐŸĐ¶ĐœŃ Đ·Đ±ŃŃĐșŃ Đ·Đ° ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃ Đ±ŃблŃĐŸŃĐ”ĐșĐž PACT ĐŽĐ»Ń ĐČĐžŃĐČĐ»Đ”ĐœĐœŃ ĐżĐŸŃŃŃĐ”ĐœĐžŃ
ĐșĐŸĐœŃŃаĐșŃŃĐČâââĐŸŃŃĐșŃĐČĐ°ĐœĐœŃ ĐșĐ»ŃŃĐœŃа, ŃĐșĐ” ĐœĐ” ĐČŃĐŽĐżĐŸĐČŃЎаŃŃŃ. ĐаĐČĐŽŃĐșĐž ŃŃĐŸĐŒŃ ĐČŃŃ ĐœĐ”ĐČŃĐŽĐżĐŸĐČŃĐŽĐœĐŸŃŃŃ API ŃĐ”ŃĐČĐ”Ńа Ńа ĐșĐ»ŃŃĐœŃа ĐČĐžŃĐČĐ»ŃŃŃŃŃŃ ĐœĐ° ŃĐ°ĐœĐœŃĐč ŃŃаЎŃŃ Đ·Đ±ĐžŃĐ°ĐœĐœŃ/CI, ŃĐŸ ĐŒĐŸĐ¶Đ” ĐżĐŸĐ·Đ±Đ°ĐČĐžŃĐž ĐČĐ°Ń ĐČŃĐŽ ŃĐŸĐ·ŃаŃŃĐČĐ°ĐœĐœŃ
+
+
+â **ĐĐœĐ°ĐșŃĐ”:** ĐĐ»ŃŃĐ”ŃĐœĐ°ŃĐžĐČĐ°ĐŒĐž Ń ĐČĐžŃĐœĐ°Đ¶Đ»ĐžĐČĐ” ŃŃŃĐœĐ” ŃĐ”ŃŃŃĐČĐ°ĐœĐœŃ Đ°Đ±ĐŸ ŃŃŃаŃ
ŃĐŸĐ·ĐłĐŸŃŃĐ°ĐœĐœŃ
+
+
+
+â ĐŃĐžĐșлаЎО ĐșĐŸĐŽŃ
+
+
+
+### :clap: ĐŃĐžĐșлаЎ ĐżŃаĐČОлŃĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ:
+
+
+
+
+
+
+
+
+
+## âȘ ïž 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
+
+
+
+```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, ĐșĐŸĐŒĐ”ŃŃŃĐčĐœĐžĐč ŃĐœŃŃŃŃĐŒĐ”ĐœŃ, ŃĐșĐžĐč ĐŒĐŸĐ¶Đ” ŃĐŽĐ”ĐœŃĐžŃŃĐșŃĐČаŃĐž ŃĐșĐ»Đ°ĐŽĐœŃ ĐŒĐ”ŃĐŸĐŽĐž:
+
+
+
+
+
+
+
+
+
+## âȘ ïž 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, ŃĐŸĐ± ĐČĐž ĐŒĐŸĐłĐ»Đž пДŃĐ”ĐČŃŃĐžŃĐž, ĐœĐ°ŃĐșŃĐ»ŃĐșĐž ĐČаŃа ĐżŃĐŸĐłŃĐ°ĐŒĐ° ŃŃŃĐčĐșа ĐŽĐŸ Ń
Đ°ĐŸŃŃ
+
+
+
+
+
+
+
+## âȘ ïž2.7 ĐŁĐœĐžĐșаĐčŃĐ” ĐłĐ»ĐŸĐ±Đ°Đ»ŃĐœĐžŃ
ŃŃĐșŃŃŃŃ ŃŃĐŽŃĐČ, ĐŽĐŸĐŽĐ°ĐČаĐčŃĐ” ĐŽĐ°ĐœĐœŃ ĐŽĐ»Ń ĐșĐŸĐ¶ĐœĐŸĐłĐŸ ŃĐ”ŃŃŃ
+
+:white_check_mark: **Đ ĐŸĐ±Đž:** ĐĐŸŃŃĐžĐŒŃŃŃĐžŃŃ Đ·ĐŸĐ»ĐŸŃĐŸĐłĐŸ ĐżŃаĐČОла (Đ ĐŸĐ·ĐŽŃĐ» 0), ĐșĐŸĐ¶Đ”Đœ ŃĐ”ŃŃ ĐżĐŸĐČĐžĐœĐ”Đœ ĐŽĐŸĐŽĐ°ĐČаŃĐž ĐČлаŃĐœĐžĐč ĐœĐ°Đ±ŃŃ ŃŃĐŽĐșŃĐČ ĐĐ Ń ĐŽŃŃŃĐž ĐœĐ° ĐœĐžŃ
, ŃĐŸĐ± Đ·Đ°ĐżĐŸĐ±ŃĐłŃĐž Đ·ŃĐ”ĐżĐ»Đ”ĐœĐœŃ Ńа лДгĐșĐŸ ĐŸĐ±ÒŃŃĐœŃŃĐČаŃĐž ĐżĐŸŃŃĐș ŃĐ”ŃŃŃ. ĐаŃĐżŃаĐČĐŽŃ ŃĐ” ŃаŃŃĐŸ ĐżĐŸŃŃŃŃŃŃŃŃŃ ŃĐ”ŃŃŃĐČалŃĐœĐžĐșĐ°ĐŒĐž, ŃĐșŃ Đ·Đ°ĐżĐŸĐČĐœŃŃŃŃ ĐĐ ĐŽĐ°ĐœĐžĐŒĐž пДŃДЎ запŃŃĐșĐŸĐŒ ŃĐ”ŃŃŃĐČ (ŃаĐșĐŸĐ¶ ĐČŃĐŽĐŸĐŒĐžŃ
ŃĐș «test fixture») заŃаЎО ĐżŃĐŽĐČĐžŃĐ”ĐœĐœŃ ĐżŃĐŸĐŽŃĐșŃĐžĐČĐœĐŸŃŃŃ. Đ„ĐŸŃа ĐżŃĐŸĐŽŃĐșŃĐžĐČĐœŃŃŃŃ ŃĐżŃаĐČĐŽŃ Ń ĐŸĐ±ÒŃŃĐœŃĐŸĐČĐ°ĐœĐŸŃ ĐżŃĐŸĐ±Đ»Đ”ĐŒĐŸŃââ«ŃŃ ĐŒĐŸĐ¶ĐœĐ° ĐżĐŸŃŃĐșŃĐžŃĐž (ĐŽĐžĐČ. ĐżŃĐœĐșŃ Â«ĐąĐ”ŃŃŃĐČĐ°ĐœĐœŃ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃŃĐČ»), ĐŸĐŽĐœĐ°Đș ŃĐșĐ»Đ°ĐŽĐœŃŃŃŃ ŃĐ”ŃŃŃ Ń ĐŽŃжД Đ±ĐŸĐ»ŃŃĐœĐžĐŒ ŃŃĐŒĐŸĐŒ, ŃĐșĐžĐč Ń Đ±ŃĐ»ŃŃĐŸŃŃŃ ĐČОпаЎĐșŃĐČ ĐżĐŸĐČĐžĐœĐ”Đœ ĐșĐ”ŃŃĐČаŃĐž ŃĐœŃĐžĐŒĐž ĐŒŃŃĐșŃĐČĐ°ĐœĐœŃĐŒĐž. Đа ĐżŃаĐșŃĐžŃŃ Đ·ŃĐŸĐ±ŃŃŃ ŃаĐș, ŃĐŸĐ± ĐșĐŸĐ¶Đ”Đœ ŃĐ”ŃŃĐŸĐČĐžĐč ĐżŃĐžĐșлаЎ ŃĐČĐœĐŸ ĐŽĐŸĐŽĐ°ĐČаĐČ ĐœĐ”ĐŸĐ±Ń
ŃĐŽĐœŃ Đ·Đ°ĐżĐžŃĐž ĐĐ Ń ĐŽŃŃĐČ Đ»ĐžŃĐ” Đ· ŃĐžĐŒĐž запОŃĐ°ĐŒĐž. ĐŻĐșŃĐŸ ĐżŃĐŸĐŽŃĐșŃĐžĐČĐœŃŃŃŃ ŃŃĐ°Ń ĐșŃĐžŃĐžŃĐœĐŸŃ ĐżŃĐŸĐ±Đ»Đ”ĐŒĐŸŃâââĐ·Đ±Đ°Đ»Đ°ĐœŃĐŸĐČĐ°ĐœĐžĐč ĐșĐŸĐŒĐżŃĐŸĐŒŃŃ ĐŒĐŸĐ¶Đ” ĐżŃĐžĐčŃĐž Ń ŃĐŸŃĐŒŃ Đ·Đ°ĐżĐŸĐČĐœĐ”ĐœĐœŃ ŃĐŽĐžĐœĐŸĐłĐŸ ĐœĐ°Đ±ĐŸŃŃ ŃĐ”ŃŃŃĐČ, ŃĐșŃ ĐœĐ” Đ·ĐŒŃĐœŃŃŃŃ ĐŽĐ°ĐœŃ (ĐœĐ°ĐżŃĐžĐșлаЎ, запОŃĐž)
+
+
+â **ĐĐœĐ°ĐșŃĐ”:** ĐŃĐ»ŃĐșа ŃĐ”ŃŃŃĐČ Đ·Đ°Đ·ĐœĐ°ŃŃŃ ĐœĐ”ĐČЎаŃ, ŃĐŸĐ·ĐłĐŸŃŃĐ°ĐœĐœŃ ĐżĐ”ŃĐ”ŃĐČĐ°ĐœĐŸ, ĐœĐ°Ńа ĐșĐŸĐŒĐ°ĐœĐŽĐ° ĐČĐžŃŃаŃаŃĐžĐŒĐ” ĐŽĐŸŃĐŸĐłĐŸŃŃĐœĐœĐžĐč ŃĐ°Ń Đ·Đ°Ńаз, Ń ĐœĐ°Ń Ń ĐżĐŸĐŒĐžĐ»Đșа? ĐŽĐŸŃĐ»ŃĐŽŃĐŒ, ĐŸ ĐœŃâââŃŃ
ĐŸĐ¶Đ”, ŃĐŸ ĐŽĐČа ŃĐ”ŃŃĐž ĐŒŃŃŃĐČалО ĐŸĐŽĐœĐ°ĐșĐŸĐČŃ ĐČĐžŃ
ŃĐŽĐœŃ ĐŽĐ°ĐœŃ
+
+
+
+â ĐŃĐžĐșлаЎО ĐșĐŸĐŽŃ
+
+
+
+### :thumbsdown: ĐŃĐžĐșлаЎ Đ°ĐœŃĐžŃĐ°Đ±Đ»ĐŸĐœŃ: ŃĐ”ŃŃĐž ĐœĐ” Ń ĐœĐ”Đ·Đ°Đ»Đ”Đ¶ĐœĐžĐŒĐž Ń ĐżĐŸĐșлаЎаŃŃŃŃŃ ĐœĐ° ŃĐșĐžĐčŃŃ ĐłĐ»ĐŸĐ±Đ°Đ»ŃĐœĐžĐč Ń
ŃĐș ĐŽĐ»Ń ĐżĐ”ŃДЎаŃŃ ĐłĐ»ĐŸĐ±Đ°Đ»ŃĐœĐžŃ
ĐŽĐ°ĐœĐžŃ
ĐĐ
+
+
+
+```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: ĐŃĐžĐșлаЎ ĐżŃаĐČОлŃĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: ĐŃĐŽĐŸĐșŃĐ”ĐŒĐ»Đ”ĐœĐœŃ ĐŽĐ”ŃалДĐč ŃĐœŃĐ”ŃŃĐ”ĐčŃŃ
+
+ 
+
+```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: ĐŃĐžĐșлаЎ ĐżŃаĐČОлŃĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: ĐĐ°ĐżĐžŃ Đ”Đ»Đ”ĐŒĐ”ĐœŃа за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃ ŃпДŃŃалŃĐœĐŸĐłĐŸ аŃŃОбŃŃа ĐŽĐ»Ń ŃĐ”ŃŃŃĐČĐ°ĐœĐœŃ
+
+
+
+```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: ĐŃĐžĐșлаЎ ĐżŃаĐČОлŃĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: РДалŃŃŃĐžŃĐœĐ° ŃĐŸĐ±ĐŸŃа Đ· ĐżĐŸĐČĐœŃŃŃŃ ĐČŃĐŽŃĐ”ĐœĐŽĐ”ŃĐ”ĐœĐžĐŒ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃĐŸĐŒ
+
+ 
+
+```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)
+
+
+
+
+```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 ĐĐŸĐŽĐžĐČŃŃŃŃŃ, ŃĐș ĐČĐŒŃŃŃ ĐżĐŸĐŽĐ°ŃŃŃŃŃ ĐČ ĐŒĐ”ŃДжŃ
+
+
+
+â
**Đ ĐŸĐ±Đž:** ĐаŃŃĐŸŃŃĐčŃĐ” ĐŽĐ”ŃĐșĐžĐč аĐșŃĐžĐČĐœĐžĐč ĐŒĐŸĐœŃŃĐŸŃ, ŃĐșĐžĐč гаŃĐ°ĐœŃŃŃ ĐŸĐżŃĐžĐŒŃзаŃŃŃ Đ·Đ°ĐČĐ°ĐœŃĐ°Đ¶Đ”ĐœĐœŃ ŃŃĐŸŃŃĐœĐșĐž ĐČ ŃДалŃĐœŃĐč ĐŒĐ”ŃĐ”Đ¶Ń - ŃĐ” ŃŃĐŸŃŃŃŃŃŃŃ Đ±ŃĐŽŃ-ŃĐșĐžŃ
ĐżŃĐŸĐ±Đ»Đ”ĐŒ Đ· 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
+
+
+
+
+
+
+
+## âȘ ïž 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
+
+ 
+
+```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: ĐŃĐžĐșлаЎ ĐżŃаĐČОлŃĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: ĐŃ
ŃĐŽ пДŃДЎ ŃŃŃĐŒĐ°, а ĐœĐ” пДŃДЎ ĐșĐŸĐ¶ĐœĐžĐŒ
+
+
+
+```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" ŃĐ”ŃŃ ĐżĐŸ ĐČŃŃŃ
ŃŃĐŸŃŃĐœĐșаŃ
+
+
+
+```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
+
+
+
+```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
+
+
+
+
+
+
+
+
+
+## âȘ ïž 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: ĐŃĐžĐșлаЎ Đ°ĐœŃĐžŃĐ°Đ±Đ»ĐŸĐœŃ: ĐąĐžĐżĐŸĐČа ĐČŃĐ·ŃалŃĐœĐ° ŃДгŃĐ”ŃŃŃ â ĐżŃаĐČОлŃĐœĐžĐč ĐșĐŸĐœŃĐ”ĐœŃ, ŃĐșĐžĐč ĐżĐŸĐŽĐ°ŃŃŃŃŃ ĐżĐŸĐłĐ°ĐœĐŸ
+
+
+
+
+
+### :clap: ĐŃĐžĐșлаЎ ĐżŃаĐČОлŃĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: ĐалаŃŃŃĐČĐ°ĐœĐœŃ 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 ĐŽĐ»Ń ĐżĐŸŃŃĐČĐœŃĐœĐœŃ Đ·ĐœŃĐŒĐșŃĐČ Ńа ŃĐœŃĐžŃ
ŃĐŸĐ·ŃĐžŃĐ”ĐœĐžŃ
ŃŃĐœĐșŃŃĐč
+
+ 
+
+```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: ĐŃĐžĐșлаЎ: ĐąĐžĐżĐŸĐČĐžĐč Đ·ĐČŃŃ ĐżŃĐŸ ĐżĐŸĐșŃĐžŃŃŃ
+
+
+
+
+
+### :clap: ĐŃĐžĐșлаЎ ĐżŃаĐČОлŃĐœĐŸĐłĐŸ ĐČĐžĐșĐŸĐœĐ°ĐœĐœŃ: ĐалаŃŃŃĐČĐ°ĐœĐœŃ ĐżĐŸĐșŃĐžŃŃŃ ĐŽĐ»Ń ĐșĐŸĐ¶ĐœĐŸĐłĐŸ ĐșĐŸĐŒĐżĐŸĐœĐ”ĐœŃа (за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃ Jest)
+
+
+
+")
+
+
+
+
+
+## âȘ ïž 4.2 ĐĐ”ŃĐ”ĐČŃŃŃĐ” Đ·ĐČŃŃĐž ĐżŃĐŸ ĐżĐŸĐșŃĐžŃŃŃ, ŃĐŸĐ± ĐČĐžŃĐČĐžŃĐž ĐœĐ”ĐżĐ”ŃĐ”ĐČŃŃĐ”ĐœŃ ĐŸĐ±Đ»Đ°ŃŃŃ Ńа ŃĐœŃŃ ĐŽĐžĐČаŃŃĐČа
+
+:white_check_mark: **Đ ĐŸĐ±Đž:** ĐĐ”ŃĐșŃ ĐżŃĐŸĐ±Đ»Đ”ĐŒĐž ĐœĐ”ĐżĐŸĐŒŃŃĐ”ĐœŃ, Ń ŃŃ
ĐŽŃжД ĐČажĐșĐŸ Đ·ĐœĐ°ĐčŃĐž за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃ ŃŃаЎОŃŃĐčĐœĐžŃ
ŃĐœŃŃŃŃĐŒĐ”ĐœŃŃĐČ. ĐаŃĐżŃаĐČĐŽŃ ŃĐ” ĐœĐ” ĐżĐŸĐŒĐžĐ»ĐșĐž, а ŃĐșĐŸŃŃŃĐ” ĐŽĐžĐČĐŸĐČĐžĐ¶ĐœĐ° ĐżĐŸĐČДЎŃĐœĐșа ĐżŃĐŸĐłŃĐ°ĐŒĐž, ŃĐșа ĐŒĐŸĐ¶Đ” ĐŒĐ°ŃĐž ŃĐ”ŃĐčĐŸĐ·ĐœŃ ĐœĐ°ŃĐ»ŃĐŽĐșĐž. ĐапŃĐžĐșлаЎ, ŃаŃŃĐŸ ĐŽĐ”ŃĐșŃ ĐŸĐ±Đ»Đ°ŃŃŃ ĐșĐŸĐŽŃ ĐœŃĐșĐŸĐ»Đž Đ°Đ±ĐŸ ŃŃĐŽĐșĐŸ ĐČĐžĐșлОĐșаŃŃŃŃŃâââĐČĐž ĐŽŃĐŒĐ°Đ»Đž, ŃĐŸ ĐșĐ»Đ°Ń Â«PricingCalculator» заĐČжЎО ĐČŃŃĐ°ĐœĐŸĐČĐ»ŃŃ ŃŃĐœŃ ĐżŃĐŸĐŽŃĐșŃŃ, алД ĐČĐžŃĐČĐžĐ»ĐŸŃŃ, ŃĐŸ ĐČŃĐœ ĐœĐ°ŃĐżŃаĐČĐŽŃ ĐœŃĐșĐŸĐ»Đž ĐœĐ” ĐČĐžĐșлОĐșаŃŃŃŃŃ, Ń
ĐŸŃа ĐŒĐž ĐŒĐ°ŃĐŒĐŸ 10000 ĐżŃĐŸĐŽŃĐșŃŃĐČ Ń ĐĐ Ń Đ±Đ°ĐłĐ°ŃĐŸ ĐżŃĐŸĐŽĐ°Đ¶ŃĐČ⊠ĐĐŸĐșŃĐžŃŃŃ ĐșĐŸĐŽŃ Đ·ĐČŃŃĐž ĐŽĐŸĐżĐŸĐŒĐ°ĐłĐ°ŃŃŃ Đ·ŃĐŸĐ·ŃĐŒŃŃĐž, ŃĐž ĐżŃаŃŃŃ ĐżŃĐŸĐłŃĐ°ĐŒĐ° ŃаĐș, ŃĐș ĐČĐž ĐŽŃĐŒĐ°ŃŃĐ”. ĐĐșŃŃĐŒ ŃŃĐŸĐłĐŸ, ĐČŃĐœ ŃаĐșĐŸĐ¶ ĐŒĐŸĐ¶Đ” ĐżŃĐŽĐșŃĐ”ŃлОŃĐž, ŃĐșŃ ŃОпО ĐșĐŸĐŽŃ ĐœĐ” пДŃĐ”ĐČŃŃĐ”ĐœĐŸââ«ŃĐœŃĐŸŃĐŒĐ°ŃŃŃ ĐżŃĐŸ ŃĐ”, ŃĐŸ 80% ĐșĐŸĐŽŃ ĐżĐ”ŃĐ”ĐČŃŃĐ”ĐœĐŸ, ĐœĐ” ĐłĐŸĐČĐŸŃĐžŃŃ ĐżŃĐŸ ŃĐ”, ŃĐž ĐŸŃ
ĐŸĐżĐ»Đ”ĐœĐŸ ĐșŃĐžŃĐžŃĐœŃ ŃаŃŃĐžĐœĐž. ĐĄŃĐČĐŸŃŃĐČаŃĐž Đ·ĐČŃŃĐž лДгĐșĐŸâââĐżŃĐŸŃŃĐŸ запŃŃŃŃŃŃ ŃĐČĐŸŃ ĐżŃĐŸĐłŃĐ°ĐŒŃ ĐČ ŃĐŸĐ±ĐŸŃĐŸĐŒŃ ŃŃĐ°ĐœŃ Đ°Đ±ĐŸ ĐżŃĐŽ ŃĐ°Ń ŃĐ”ŃŃŃĐČĐ°ĐœĐœŃ Đ· ĐČŃĐŽŃŃĐ”Đ¶Đ”ĐœĐœŃĐŒ ĐżĐŸĐșŃĐžŃŃŃ, а ĐżĐŸŃŃĐŒ пДŃДглŃĐœŃŃĐ” ĐșĐŸĐ»ŃĐŸŃĐŸĐČŃ Đ·ĐČŃŃĐž, Ń ŃĐșĐžŃ
ĐżĐŸĐșĐ°Đ·Đ°ĐœĐŸ, ŃĐș ŃаŃŃĐŸ ĐČĐžĐșлОĐșаŃŃŃŃŃ ĐșĐŸĐ¶ĐœĐ° ĐŸĐ±Đ»Đ°ŃŃŃ ĐșĐŸĐŽŃ. ĐŻĐșŃĐŸ ĐČĐž ĐœĐ” ĐżĐŸŃĐżŃŃаŃŃĐ” зазОŃĐœŃŃĐž ĐČ ŃŃ ĐŽĐ°ĐœŃâââĐČĐž ĐŒĐŸĐ¶Đ”ŃĐ” Đ·ĐœĐ°ĐčŃĐž ĐŽĐ”ŃĐșŃ ĐżŃĐŸĐ±Đ»Đ”ĐŒĐž
+
+
+â **ĐĐœĐ°ĐșŃĐ”:** ĐŻĐșŃĐŸ ĐČĐž ĐœĐ” Đ·ĐœĐ°ŃŃĐ”, ŃĐșŃ ŃаŃŃĐžĐœĐž ĐČаŃĐŸĐłĐŸ ĐșĐŸĐŽŃ Đ·Đ°Đ»ĐžŃОлОŃŃ ĐœĐ”ĐŸĐ±ŃĐŸĐ±Đ»Đ”ĐœĐžĐŒĐž, ĐČĐž ĐœĐ” Đ·ĐœĐ°ŃŃĐ”, Đ·ĐČŃĐŽĐșĐž ĐŒĐŸĐ¶ŃŃŃ ĐČĐžĐœĐžĐșĐœŃŃĐž ĐżŃĐŸĐ±Đ»Đ”ĐŒĐž
+
+
+
+â ĐŃĐžĐșлаЎО ĐșĐŸĐŽŃ
+
+
+
+### :thumbsdown: ĐŃĐžĐșлаЎ Đ°ĐœŃĐžŃĐ°Đ±Đ»ĐŸĐœŃ: Đ©ĐŸ ĐœĐ” ŃаĐș ŃĐ· ŃĐžĐŒ Đ·ĐČŃŃĐŸĐŒ ĐżŃĐŸ ĐżĐŸĐșŃĐžŃŃŃ?
+
+Đа ĐŸŃĐœĐŸĐČŃ ŃДалŃĐœĐŸĐłĐŸ ŃŃĐ”ĐœĐ°ŃŃŃ, ĐșĐŸĐ»Đž ĐŒĐž ĐČŃĐŽŃŃДжŃĐČалО ĐČĐžĐșĐŸŃĐžŃŃĐ°ĐœĐœŃ ĐœĐ°ŃĐŸŃ ĐżŃĐŸĐłŃĐ°ĐŒĐž ĐČ QA Ńа Đ·ĐœĐ°Ń
ĐŸĐŽĐžĐ»Đž ŃŃĐșаĐČŃ ŃĐ°Đ±Đ»ĐŸĐœĐž ĐČŃ
ĐŸĐŽŃ (ĐŃĐŽĐșазĐșа: ĐșŃĐ»ŃĐșŃŃŃŃ ĐżĐŸĐŒĐžĐ»ĐŸĐș ĐČŃ
ĐŸĐŽŃ ĐœĐ”ĐżŃĐŸĐżĐŸŃŃŃĐčĐœĐ°, ŃĐŸŃŃ ŃĐČĐœĐŸ ĐœĐ” ŃаĐș. ĐаŃĐ”ŃŃŃ ĐČĐžŃĐČĐžĐ»ĐŸŃŃ, ŃĐŸ ŃĐșаŃŃ ĐżĐŸĐŒĐžĐ»Đșа ŃĐœŃĐ”ŃŃĐ”ĐčŃŃ ĐżĐŸŃŃŃĐčĐœĐŸ ĐČŃĐ°Đ¶Đ°Ń ŃĐ”ŃĐČĐ”ŃĐœĐžĐč API ĐČŃ
ĐŸĐŽŃ)
+
+
+
+
+
+
+
+## âȘ ïž 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% ŃĐ”ŃŃŃĐČĐ°ĐœĐœŃ
+
+
+
+```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, ŃĐœŃŃŃŃĐŒĐ”ĐœŃ ĐŽĐ»Ń ĐżĐ”ŃĐ”ĐČŃŃĐșĐž ĐŒŃŃаŃŃĐč, ĐČĐžŃĐČĐ»ŃŃŃŃ Ń ĐżŃĐŽŃаŃ
ĐŸĐČŃŃŃŃ ĐșŃĐ»ŃĐșŃŃŃŃ ĐșĐŸĐŽŃ, ŃĐșĐžĐč ĐœĐ” пДŃĐ”ĐČŃŃŃŃŃŃŃŃ (ĐŃŃаŃŃŃ)
+
+")
+
+
+
+
+
+## âȘ ïž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 ĐČĐžŃĐČĐ»ŃŃ ĐœĐ°ŃŃŃĐżĐœŃ ĐČĐžŃĐŸĐ±ĐœĐžŃŃ ĐżĐŸĐŒĐžĐ»ĐșŃ
+
+
+
+
+
+
+
+## âȘ ïž 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))
+
+
+
+
+
+
+
+## âȘ ïž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
+
+```
+
+
+
+
+
+
+
+
+
+## âȘ ïž5.6 ĐĐŸŃŃŃĐčĐœĐŸ пДŃĐ”ĐČŃŃŃĐčŃĐ” ĐœĐ°ŃĐČĐœŃŃŃŃ ĐČŃазлОĐČĐžŃ
Đ·Đ°Đ»Đ”Đ¶ĐœĐŸŃŃĐ”Đč
+
+:white_check_mark: **Đ ĐŸĐ±Đž:** ĐаĐČŃŃŃ ĐœĐ°ĐčаĐČŃĐŸŃĐžŃĐ”ŃĐœŃŃŃ Đ·Đ°Đ»Đ”Đ¶ĐœĐŸŃŃŃ, ŃаĐșŃ ŃĐș Express, ĐŒĐ°ŃŃŃ ĐČŃĐŽĐŸĐŒŃ ĐČŃазлОĐČĐŸŃŃŃ. ĐŠĐ” ĐŒĐŸĐ¶ĐœĐ° лДгĐșĐŸ ĐżŃĐžĐ±ĐŸŃĐșаŃĐž за ĐŽĐŸĐżĐŸĐŒĐŸĐłĐŸŃ ŃĐœŃŃŃŃĐŒĐ”ĐœŃŃĐČ ŃĐżŃĐ»ŃĐœĐŸŃĐž, ŃаĐșĐžŃ
ŃĐș [npm audit](https://docs.npmjs.com/getting-started/running-a-security-audit), Đ°Đ±ĐŸ ĐșĐŸĐŒĐ”ŃŃŃĐčĐœĐžŃ
ŃĐœŃŃŃŃĐŒĐ”ĐœŃŃĐČ, ŃаĐșĐžŃ
ŃĐș [snyk](https:// snyk.io/) (ĐżŃĐŸĐżĐŸĐœŃŃĐŒĐŸ ŃаĐșĐŸĐ¶ бДзĐșĐŸŃŃĐŸĐČĐœŃ ĐČĐ”ŃŃŃŃ ŃĐżŃĐ»ŃĐœĐŸŃĐž). ĐбОЎĐČа ĐŒĐŸĐ¶ĐœĐ° ĐČĐžĐșлОĐșаŃĐž Đ· ĐČаŃĐŸĐłĐŸ ĐĐ ĐżŃĐŽ ŃĐ°Ń ĐșĐŸĐ¶ĐœĐŸŃ Đ·Đ±ŃŃĐșĐž
+
+â **ĐĐœĐ°ĐșŃĐ”:** Đ©ĐŸĐ± заŃ
ĐžŃŃĐžŃĐž ĐČĐ°Ń ĐșĐŸĐŽ ĐČŃĐŽ ŃŃазлОĐČĐŸŃŃĐ”Đč бДз ŃпДŃŃалŃĐœĐžŃ
ŃĐœŃŃŃŃĐŒĐ”ĐœŃŃĐČ, ĐżĐŸŃŃŃĐ±ĐœĐŸ ĐżĐŸŃŃŃĐčĐœĐŸ ŃŃДжОŃĐž за ĐżŃблŃĐșаŃŃŃĐŒĐž ĐČ ĐĐœŃĐ”ŃĐœĐ”ŃŃ ĐżŃĐŸ ĐœĐŸĐČŃ Đ·Đ°ĐłŃĐŸĐ·Đž. ĐĐŸŃĐžŃŃ ĐœŃĐŽĐœĐŸ
+
+
+
+â ĐŃĐžĐșлаЎО ĐșĐŸĐŽŃ
+
+
+
+### :clap: ĐŃĐžĐșлаЎ: РДзŃĐ»ŃŃĐ°Ń Đ°ŃĐŽĐžŃŃ 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, ŃĐŸĐ± ĐČĐžĐ·ĐœĐ°ŃĐžŃĐž, ĐœĐ°ŃĐșŃĐ»ŃĐșĐž ĐșĐŸĐŽ ĐČŃĐŽŃŃĐ°Ń ĐČŃĐŽ ĐŸŃŃĐ°ĐœĐœŃŃ
ĐČĐ”ŃŃŃĐč
+
+
+
+
+
+
+
+## âȘ ïž 5.8 ĐĐœŃŃ ĐżĐŸŃаЎО CI, ĐœĐ” ĐżĐŸĐČâŃĐ·Đ°ĐœŃ Đ· Node
+
+:white_check_mark: **Đ ĐŸĐ±Đž:** ĐŠŃ ĐżŃблŃĐșаŃŃŃ Đ·ĐŸŃĐ”ŃĐ”ĐŽĐ¶Đ”ĐœĐ° ĐœĐ° ĐżĐŸŃаЎаŃ
ŃĐŸĐŽĐŸ ŃĐ”ŃŃŃĐČĐ°ĐœĐœŃ, ŃĐșŃ ĐżĐŸĐČâŃĐ·Đ°ĐœŃ Đ· Node JS Đ°Đ±ĐŸ, ĐżŃĐžĐœĐ°ĐčĐŒĐœŃ, ĐŒĐŸĐ¶ŃŃŃ Đ±ŃŃĐž ĐżŃДЎŃŃаĐČĐ»Đ”ĐœŃ ĐœĐ° ĐżŃĐžĐșĐ»Đ°ĐŽŃ Node JS. ĐĐŽĐœĐ°Đș Ń ŃŃĐŸĐŒŃ ŃĐŸĐ·ĐŽŃĐ»Ń Đ·ĐłŃŃĐżĐŸĐČĐ°ĐœĐŸ ĐșŃĐ»ŃĐșа ĐŽĐŸĐ±ŃĐ” ĐČŃĐŽĐŸĐŒĐžŃ
ĐżĐŸŃаЎ, ĐœĐ” ĐżĐŸĐČâŃĐ·Đ°ĐœĐžŃ
Đ· Node
+
+ - Use a declarative syntax. This is the only option for most vendors but older versions of Jenkins allows using code or UI
- Opt for a vendor that has native Docker support
- 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
- Make it easy to skim-through all build artifacts including test reports, coverage reports, mutation reports, logs, etc
- 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)
- Never embed secrets in a job declaration, grab them from a secret store or from the jobâs configuration
- Explicitly bump version in a release build or at least ensure the developer did so
- Build only once and perform all the inspections over the single build artifact (e.g. Docker image)
- 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 Ń Đ±Đ”Đ·ĐżĐ”ĐșĐŸŃ ĐČДб-ĐŽĐŸĐŽĐ°ŃĐșŃĐČ.
+
+## ĐĐČŃĐŸŃĐž âš
+
+ĐŃĐșŃŃĐŒĐŸ ŃĐžĐŒ ŃŃĐŽĐŸĐČĐžĐŒ Đ»ŃĐŽŃĐŒ, ŃĐșŃ Đ·ŃĐŸĐ±ĐžĐ»Đž ĐČĐœĐ”ŃĐŸĐș Ń ŃĐ” ŃŃ
ĐŸĐČĐžŃĐ”!
+
+
+
+
+
+
+
+
+
+
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ïŒé«çææŻăć·„ć
·ä»„ćæ”èŻćŻčè±Ąăä»
æ”èŻéèŠçć
ćźčïŒćȘćäżæć
¶ç”掻æ§ïŒæäșæ¶ćçèłćŒćŸć»èćŒäžäșæ”èŻæ„æąćç”æŽ»æ§ćçźæŽæ§ă
-
+
äžéąç性éšćć»șèźźèĄçèȘèżäžććă
@@ -129,7 +129,7 @@ describe('Products Service', function() {
### :clap: æŁäŸ: äžäžȘć
ć«äžéšćççšäŸć
-
+
@@ -732,7 +732,7 @@ describe('Order service', function() {
### :clap: æŁäŸ: Cindy Sridharan ćšć„čçæç« âæ”èŻćŸźæćĄââçæșçæčćŒâäžæćșäșäžäžȘäž°ćŻçæ”èŻç»ć
-
+
âșïž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() {

- allows approaching Express API in-process (fast and cover many layers)")
+ allows approaching Express API in-process (fast and cover many layers)")
@@ -797,7 +797,7 @@ describe('Order service', function() {

-
+
@@ -1625,7 +1625,7 @@ describe('visual validation', () => {
### :thumbsdown: ćäŸ: èżä»œèŠççæ„ćæä»äčéźéąïŒćșäșäžäžȘçćźçćșæŻïŒæä»Źè·èžȘäș QA äžçćșçšçšćșäœżçšæ
ć”ïŒćč¶ćç°äșäžäșæè¶Łçç»ćœæšĄćŒ(æç€ș:ç»ćœć€±èŽ„çæ°éæŻäžææŻäŸçïŒæäșć°æčæŸç¶æéźéąăæç»èĄšç°äžșäžäșć端ç bug äžæè§Šćć端ç»ćœAPI)
-
+
@@ -1674,7 +1674,7 @@ it("Test addNewOrder, don't use such test names", () => {
### :clap: æŁäŸ: Stryker æ„ćïŒäžäžȘçŒèŻæ”èŻć·„ć
·ïŒćç°ćč¶ç»èźĄæČĄæè૿”èŻć°ç代ç ïŒććŒïŒ
-")
+")
@@ -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)ă
+
+çșäșéć°éćçźæšïŒæććŻä»„éžæć
·æææŹæçć髿èłć ±é
ŹçççæèĄăć·„ć
·ćæžŹè©ŠçźæšăćȘæžŹè©ŠéèŠçć
§ćźčïŒćȘćäżæä»çéæŽ»æ§ïŒæäșæćçèłćŸæšæŁäžäșæžŹè©ŠäŸæćéæŽ»æ§ćç°Ąæœæ§ă
+
+
+
+仄äžć€§éšćçć»șè°èĄçèȘéäžććă
+
+### æșćć„œäșćïŒ
+
+
+
+# 珏 1 ç« ïŒæžŹè©Šćæ
+
+
+
+## âȘ ïž 1.1 æŻćæžŹè©Šçćçš±èŠć
ć«çäžćéšć
+
+:white_check_mark: **ć»șè°ïŒ** äžä»œæžŹè©Šć ±ćæè©Čć蚎éŁäșäžäžćźçæçšćŒçäșșïŒçźćæçšçšćŒçäżźèšçæŹæŻćŠçŹŠćä»ćçèŠæ±ïŒć
æŹïŒæžŹè©ŠäșșćĄăDevOps ć·„çšćž«ćć
©ćčŽćŸçäœ ăćŠææžŹè©Šèœć
ć«éäžćéæ±éąçæèż°ïŒć°±èœćŸć„œç毊çŸéäžé»ïŒ
+
+(1) æžŹè©Šçć°è±ĄæŻä»éșŒïŒ äŸćŠïŒProductsService.addNewProduct éćæčæłă
+
+(2) ćšä»éșŒæ
æłæć ŽæŻäžïŒ äŸćŠïŒćčæ ŒæČæćłç”Šè©Čæčæłă
+
+(3) é æçç”ææŻä»éșŒïŒ äŸćŠïŒæ°ççąćæČæè૿čćă
+
+
+
+â **ćŠćïŒ** äžććć«"æ°ćąçąć"çæžŹè©Šć€±æäșăéæçąșćć°ćèšŽäœ ć°ćșæŻä»éșŒć°æčćșćéĄćïŒ
+
+
+
+**đ Note:** æŻćé
çźéœææäžćçšćŒçŻäŸïŒææćéææé
ćçă
+
+
+â çšćŒçŻäŸ
+
+
+
+### :clap: æŁäŸïŒäžćć
ć«éäžéšćçæžŹè©Šćçš±
+
+
+
+```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: æŁäŸïŒäžćć
ć«éäžéšćçæžŹè©Šćçš±
+
+
+
+
+
+
+© 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 æšĄćŒäŸć»șæ§æžŹè©Š
+
+ 
+
+```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() æèšćçæžŹè©Šç„éă
+
+
+
+â çšćŒçŻäŸ
+
+ 
+
+### :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: ćäŸïŒäžćçĄè
ŠæžŹè©Šć
§éšæčæłçæžŹè©Š
+
+
+
+```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
+
+
+
+```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: ćäŸ: äžćæžŹè©ŠæĄäŸäœżçšéçćŻŠèłæć»ééæžŹè©Š
+
+
+
+```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 äŸæžŹè©Šèš±ć€ç茞ć
„ç”ć
+
+
+
+```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 èĄçšćŒèŠćéČæćçæžŹè©ŠæĄäŸäž
+
+
+
+```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(`
+
+- Home
+- About
+- Contact
+
+`);
+});
+```
+
+
+
+
+
+## âȘ ïž1.9 éżć
äœżçšć
šćç test fixtures æ seedsïŒèæŻæŸéČæŻćæžŹè©Šäž
+
+:white_check_mark: **ć»șè°ïŒ** ćç
§é»éććïŒæŻćæžŹè©ŠéèŠćšćźèȘć·±ç DB äžéČèĄæäœéżć
äșçžæ±ĄæăäœçŸćŻŠäžïŒéæąèŠćç¶ćžžèą«æç ŽïŒçșäșæ§èœçæćèćšć·èĄæžŹè©Šććć§ćć
šćèłæćș« (äčèą«çš±çș"[test fixture](https://en.wikipedia.org/wiki/Test_fixture)")ăć知æ§èœćŸéèŠïŒäœæŻćźćŻä»„ééćŸéąèŹçăç”ä»¶æžŹè©ŠăäŸććæšăçșäșæžèŒè€éćșŠïŒæććŻä»„ćšæŻćæžŹè©ŠäžćȘćć§ćèȘć·±éèŠçæžæăé€éæ§èœćéĄççéćžžćŽéïŒéŁéæŻćŻä»„ćäžćźçšćșŠç抄ć - ć
ćšć
šćæŸäžææčèźçæžæ (æŻćŠ query)ă
+
+
+
+â **ćŠćïŒ** æäžäșæžŹè©Š fail äșïŒćéè±äșèš±ć€æéćŸçŒçŸïŒćȘæŻć çșć
©ćæžŹè©Šćææčèźäșćäžć seedă
+
+
+
+â çšćŒçŻäŸ
+
+
+
+### :thumbsdown: ćäŸïŒæžŹè©ŠæĄäŸäčéäžæŻçšç«çăèæŻçžäŸæŒć
šćç DB èłæ
+
+
+
+```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``` äŸæ·èšéŻèȘ€
+
+
+
+```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 çćż«éæžŹè©ŠïŒçèłćŻä»„ćšéçŒäșșćĄæćæé »çčć°ć·èĄ)
+
+
+
+```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: æŁäŸïŒć©çšæžŹè©ŠæĄäŸçćçš±ćæ
ćąäŸç”çčïŒćŻä»„çąçèŻć„œçæžŹè©Šć ±ćïŒćŠäžæç€ș
+
+
+
+```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", () => {});
+ });
+});
+```
+
+
+
+
+
+### :thumbsdown: ćäŸïŒæćčłçæžŹè©ŠćèĄšæäœżèźè
ćŸéŁć»çæ user story ć怱æçæžŹè©Šäčéçéäż
+
+
+
+```javascript
+test("Then the response status should decline", () => {});
+
+test("Then it should send email", () => {});
+
+test("Then there should not be a new transfer record", () => {});
+```
+
+
+
+
+
+
+
+
+
+## âȘ ïž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" äžæćșäșäžćè±ćŻçæžŹè©Šç”ć
+
+
+
+âșïžExample: [YouTube: âBeyond Unit Tests: 5 Shiny Node.JS Test Types (2018)â (Yoni Goldberg)](https://www.youtube.com/watch?v=-2zP494wdUY&feature=youtu.be)
+
+
+
+
+
+
+
+
+
+## âȘ ïž2.2 ç”ä»¶ćæžŹè©ŠćŻèœæŻæææçć©ćš
+
+:white_check_mark: **ć»șè°ïŒ** æçšçšćŒäžçæŻććźć
æžŹè©Šć
èœèŠèæŽćçšćŒçäžć°éšćïŒèŠèŠèć
šéšæéćžžéș»ç
©ïŒè端ć°ç«ŻæžŹè©ŠćŻä»„ćŸèŒéŹć°èŠè性éććïŒäœæŻæŻèŒèćŒ±èäžćŸæ
ąăäœäžæŸäžććčłèĄĄé»ïŒćŻ«äžäșæŻćźć
æžŹè©Šć€§ïŒäœæŻæŻç«Żć°ç«ŻæžŹè©Šć°çæžŹè©Šăç”ä»¶æžŹè©ŠæŻæžŹè©ŠäžççäžéĄéșç â ćźæŸć°äșć
©ćæšĄćŒçæäœłćčłèĄĄé»ïŒäžéŻçæ§èœćäœżçš TDD æšĄćŒçćŻèœæ§èç毊äžćŒ·ć€§çèŠèçă
+
+ç”ä»¶æžŹè©ŠéæłšæŒćŸźæć"ćźć
"ïŒä»ćéć° API äŸćäșïŒäž mock ä»»äœć±ŹæŒćŸźæćæŹèș«çæ±è„żïŒćæŻç毊ç DBïŒçèłæŻè©Č DB ç in-memory çæŹïŒäœæŻ stub ææć€éšçæ±è„żïŒćæŻćŒć«ć
¶ä»çćŸźæćăèç±éçšźæčćŒïŒæććŻä»„æžŹè©ŠæćéšçœČçéšćïŒç±ć€èć
§ć°èŠèæçšçšćŒïŒćŻä»„çŻç性éæé䞊çČćŸäżĄćżă
+
+
+
+â **ćŠćïŒ** äœ ćŻèœè±äșć„œćčŸć€©äŸćŻ«ćźć
æžŹè©ŠïŒć»çŒçŸćȘćŸć°äș 20% çèŠèçă
+
+
+
+â çšćŒçŻäŸ
+
+
+
+### :clap: æŁäŸïŒäœżçš Supertest äŸæžŹè©Š Express API (ćż«éäžèŠèć€ćć±€æŹĄ)
+
+
+
+ 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: æŁäŸïŒ
+
+
+
+
+
+
+
+
+
+## âȘ ïž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 äŒșæćš
+
+
+
+```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ïŒäžććŻä»„çŒçŸè€éæčæłç㿄㷄ć
·
+
+
+
+
+
+
+
+
+
+## âȘ ïž 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 ćéĄïŒć æ€æšćŻä»„æžŹè©Šæšçæçšçšćșć°æŒæžŸæČç驿èœć
+
+
+
+
+
+
+
+# 珏 3 ç« ïŒćç«ŻæžŹè©Š
+
+## âȘ ïž 3.1 ć° UI èćèœćéą
+
+:white_check_mark: **ć»șè°ïŒ** ç¶ć°æłšæŒæžŹè©Šç”ä»¶éèŒŻæïŒUI ç现çŻć°±èźæäșæè©Čèą«ćé€çééłïŒéæšŁæšçæžŹè©Šçźæšć°±ćŻä»„éäžćšèłæéąäžă毊éäžïŒæććșçšćŒäžæéçèłæïŒć°éäœèç«éąçèŠćïŒć
ć°ćźçŽçèłæ (è HTML/CSS çććœąçŽ°çŻçžæŻ) éČèĄæ·èšïŒäžŠćçšæææ
ąéćșŠçćç«ăæšæè©ČèŠè©Šèéżć
ç«éąçæžČæïŒć
æžŹè©Š UI ćŸéąçéšć (äŸćŠïŒæćăćäœăććČ)ïŒäœéć°ć°èŽæžŹè©Šè毊éæ
æłäžçžçŹŠïŒăæŁçąșçèłææ čæŹçĄæłćçŸćš UI äžăéçšźćéĄć°±çĄæłçŒçŸă
+
+
+
+â **ćŠćïŒ** äœ çæžŹè©ŠćŻèœè±äș 10ms ć°±æșćć„œèłæïŒäœć çșäžäșçĄéç·èŠçè±äżćç«ïŒèźæŽćæžŹè©ŠæĄäŸæçșäș 500msă(100ćæžŹè©Š = 1ćé)
+
+
+
+â çšćŒçŻäŸïŒ
+
+
+
+### :clap: æŁäŸïŒćéą UI ç现çŻ
+
+ 
+
+```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 äŸæ„è©ąć
çŽ äŸéČèĄæžŹè©Š
+
+
+
+```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: æŁäŸïŒ æäœäžćć
ćæžČæçç毊ç”ä»¶
+
+ 
+
+```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)
+
+
+
+
+```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 è§ćŻèłæșç¶ç±ç¶Čè·Żèą«æäŸçæ
æł
+
+
+
+â
**ć»șè°ïŒ** äœżçšäžäș掻ćçŁèŠćšïŒä»„çąșäżćšç毊ç¶Čè·Żäžçé éąèŒć
„æ
æłæŻæäœłç â éć
ć«äžäșäœżçšè
é«é©çćéĄïŒćæŻç·©æ
ąçé éąèŒć
„æéææȘç¶ćŁçžźçèłæșăćžéąäžæćŸè±ćŻçæȘ࿄㷄ć
·ïŒć [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 çé éąèŒć
„æȘąæžŹć ±ć
+
+
+
+
+
+
+
+## âȘ ïž 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 çćŒć«
+
+ 
+
+```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
+
+
+
+```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: æŁäŸïŒäžćè·éææé éąçćç
æžŹè©Š
+
+
+
+```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 仄äșșéĄćŻé±èźçèȘèšäŸæèż°æžŹè©Š
+
+
+
+```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 äŸć±ç€șç”ä»¶ççäžćçæ
ć茞ć
„
+
+
+
+
+
+
+
+
+
+## âȘ ïž 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ïŒćłćŽć
§ćźč饯ç€șç°ćžž
+
+
+
+
+
+### :clap: æŁäŸïŒèšćź wraith äŸæć䞊æŻć° UI æȘć
+
+
+
+```
+â# 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 äŸçČćŸæȘćçæŻć°ç”æä»„ćć
¶ä»éČéćèœ
+
+ 
+
+```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: æŁäŸïŒäžćć
žćçèŠèçć ±ć
+
+
+
+
+
+### :clap: æŁäŸïŒçșæŻćç”ä»¶èšćźèŠèç (äœżçš Jest)
+
+
+
+")
+
+
+
+
+
+## âȘ ïž 4.2 æȘąæ„æžŹè©ŠèŠèççć ±ćäŸçŒçŸæČæè૿žŹè©Šçććæć„æȘçć°æč
+
+:white_check_mark: **ć»șè°ïŒ** æäșćéĄé±èćšé·éäčäžïŒäœżçšćłç”±çć·„ć
·ćŸéŁçŒçŸć°ćźćăćźćéćžžäžæŻçæŁç bugïŒć€§ć€æžæ
æłäžæŻæçšçšćŒçㄿȘèĄçșïŒèéäșèĄçșćŻèœæé æćŽéćœ±éżăäŸćŠïŒäžäșçšćŒćććčŸäčäžææćŸć°èą«ćŒć« â äœ ä»„çș "PricingCalculator" éć class ćȘæèšćźçąććčæ ŒïŒç”æä»ćčŸäčäžæèą«ćŒć«ïŒćłäœżæćçèłæćș«äžæ 10000 ä»¶ćć仄ććŸć€çäș€æâŠâŠ æžŹè©ŠèŠèçć ±ććŻä»„ćč«ć©äœ çŒçŸæçšçšćŒæŻćŠæç
§äœ çææćšć·èĄă逿€äčć€ïŒćźæ highlight ćșćȘäșéĄćççšćŒæČæè૿žŹè©Šć°ïŒ80% ççšćŒè૿žŹè©ŠäžŠäžèœä»ŁèĄšçšćŒäžéé”çéšćæèą«èŠèć°ăçąçć ±ććŸç°ĄćźïŒćȘéèŠćšć·èĄæžŹè©ŠçæćéćèŠèçèżœèč€çćèœïŒç¶ćŸèźéŁäșè±è±ç¶ ç¶ çć ±ćäŸćèšŽäœ æŻćçšćŒććĄèą«ćŒć«çé »çăćŠæäœ è±æéć»çéäșæžæïŒäœ ćŻèœæçŒçŸäžäșćéĄă
+
+
+
+â **ćŠćïŒ** ćŠæäœ äžç„éäœ ççšćŒèŁĄéąæćȘäșć°æčæČæè૿žŹè©Šć°ïŒäœ ć°çĄæłç„éćéĄçäŸæșă
+
+
+
+â çšćŒçŻäŸ
+
+
+
+### :thumbsdown: ćäŸïŒéćæžŹè©ŠèŠèççć ±ććșäșä»éșŒćéĄïŒ
+
+ćșæŒäžćç毊äžççæ
ćąïŒæććš QA äžèżœèč€äșæćæçšçšćŒçäœżçšæ
æłïŒäžŠçŒçŸäșéćæè¶Łçç»éæšĄćŒ (æç€șïŒç»ć
„怱æçæžéäžææŁæŻçïŒéĄŻç¶æŻæćéĄçăæćŸçŒçŸïŒæäžäșć端ç bug äžçŽćšæćŸç«Żçç»ć
„ API)
+
+
+
+
+
+
+
+## âȘ ïž 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
+
+
+
+```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 çć ±ćïŒäžćèźç°æžŹè©Šçć·„ć
·ïŒć”æžŹäžŠç”±èšæČæèą«æžŹè©Šć°ççšćŒ (èźç°)
+
+")
+
+
+
+
+
+## âȘ ïž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ă
+
+
+
+
+
+
+
+## âȘ ïž 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))
+
+")
+
+
+
+
+
+## âȘ ïž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
+
+```
+
+
+
+
+
+
+
+
+
+## âȘ ïž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 çç”æ
+
+
+
+
+
+
+
+## âȘ ïž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 äžäœżçšïŒä»„æȘąæžŹçšćŒèœćŸææ°çæŹć€ć°ă
+
+
+
+
+
+
+
+## âȘ ïž 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(`
- Home
- About
@@ -595,12 +593,12 @@ it("ìŹìŽíž ìŽëŠì ì
ë°ìŽíž í ë, ì±êł”ì íìžíë€.", async ()
"Examples with Jest")
```javascript
-it("ì íëȘ
ìŽ ììŒë©Ž 400 ì€ë„넌 ëì§ë€.", async() => {
+it("ì íëȘ
ìŽ ììŒë©Ž, 400 ì€ë„넌 ëì§ë€.", async() => {
let errorWeExceptFor = null;
try {
- const result = await addNewProduct({name:'nest'});}
- catch (error) {
- expect(error.code).to.equal('InvalidInput');
+ const result = await addNewProduct({});
+ } catch (error) {
+ expect(error.code).to.equal("InvalidInput");
errorWeExceptFor = error;
}
expect(errorWeExceptFor).not.to.be.null;
@@ -614,8 +612,10 @@ it("ì íëȘ
ìŽ ììŒë©Ž 400 ì€ë„넌 ëì§ë€.", async() => {
### :clap: ìŹë°ë„ž ì: QAë PMìŽëŒë ìœêČ ìŽíŽí ì ìêł ìœêž° ìŹìŽ expectation
```javascript
-it.only("ì íëȘ
ìŽ ììŒë©Ž 400 ì€ë„넌 ëì§ë€.", async() => {
- expect(addNewProduct)).to.eventually.throw(AppError).with.property('code', "InvalidInput");
+it("ì íëȘ
ìŽ ììŒë©Ž, 400 ì€ë„넌 ëì§ë€.", async () => {
+ await expect(addNewProduct({}))
+ .to.eventually.throw(AppError)
+ .with.property("code", "InvalidInput");
});
```
@@ -658,7 +658,7 @@ describe('ìŁŒëŹž ìëčì€', function() {
## âȘ ïž 1.12 ìŒë°ì ìž ìąì í
ì€íž êž°ëČë€
-
+
:white_check_mark: **ìŽë êČ íŽëŒ:** ìŽ êžì Node.jsì êŽë šìŽ ìê±°ë ì”ìí Node.jsëĄ ì넌 ë€ ì ìë í
ì€íž ìĄ°ìžì ì€ì ìëêł ìì”ëë€. ê·žëŹë ìŽëČìë Node.jsê° ìëì§ë§ ì ìë €ì§ íë€ì íŹíšíêł ìì”ëë€.
[TDD ììč](https://www.sm-cloud.com/book-review-test-driven-development-by-example-a-tldr/)ì ë°°ì°êł ì°ì”íììì€ - ë§ì ìŹëë€ìêČ ë§€ì° ê°ìčê° ìì§ë§, ìì ì ì€íìŒì ë§ì§ ìì ì ìì”ëë€. [ì€íš-ì±êł”-늏íí ë§ ì€íìŒ](https://blog.cleancoder.com/uncle-bob/2014/12/17/TheCyclesOfTDD.html)ëĄ ìœë ìì± ì ì í
ì€ížë„Œ ìì±íë êČì êł ë €íììì€. ëČ귞넌 ë°êČŹí멎 ê° í
ì€ížìì ì íí í ê°ì§ë§ íìžíëëĄ íììì€. ìì íêž° ì ì ììŒëĄ ìŽ ëČ귞넌 ë°êČŹ í í
ì€ížë„Œ ìì±íììì€. í
ì€ížê° ì±êł”íêž° ì ì ê° í
ì€ížê° íëČ ìŽì ì€íšíëëĄ íììì€. í
ì€ížë„Œ ë§ìĄ±ìí€ë ê°ëší ìœë넌 ìì±íìŹ ëč 넎êČ ëȘšëì ììíììì€ - ì ì ì ìŒëĄ 늏íí ë§íìŹ íëĄëì
ë±êžì ìì€ìŒëĄ ê°ì žê°ììì€. íêČœ(êČœëĄ, OS ë±)ì ëí ìą
ìì±ì íŒíììì€.
@@ -879,7 +879,7 @@ Credit:: {
+before(async () => {
// DBì ìŹìŽížì ìŽëëŻŒ ë°ìŽí°ë„Œ ì¶ê°í©ëë€. ë°ìŽí°ë ìŽëì ìëì? ìžë¶ì ìì”ëë€. ìžë¶ json íìŒìŽë ë§ìŽê·žë ìŽì
íë ììíŹì ìì”ëë€.
await DB.AddSeedDataFromJson('seed.json');
});
@@ -889,8 +889,8 @@ it("ìŹìŽíž ìŽëŠì ëłêČœí멎, ì±êł” êČ°êłŒê°ì ë°ììšë€", async (
const updateNameResult = await SiteService.changeName(siteToUpdate, "newName");
expect(updateNameResult).to.be(true);
});
-it("ìŹìŽíž ìŽëŠìŒëĄ ìĄ°ííìë, íŽëč ìŹìŽížë„Œ ê°ì žìšë€", async () => {
- //"portal"ìŽëŒë ìŽëŠì ìŹìŽížê° ìë€ë êČì ìêł ìì”ëë€. - ìšë íìŒìì 뎀ì”ëë€.
+it("ìŹìŽíž ìŽëŠìŒëĄ ìĄ°í íì ë, íŽëč ìŹìŽížë„Œ ê°ì žìšë€", async () => {
+ //"portal"ìŽëŒë ìŽëŠì ìŹìŽížê° ìë€ë êČì ìêł ìì”ëë€. - ìšë íìŒìì 뎀ì”ëë€.
const siteToCheck = await SiteService.getSiteByName("Portal");
expect(siteToCheck.name).to.be.equal("Portal"); //ì€íš! ìŽì í
ì€ížìì ìŽëŠìŽ ëłêČœëìì”ëë€ :[
});
@@ -941,7 +941,7 @@ it("ìŹìŽíž ìŽëŠì ëłêČœí멎, ì±êł” êČ°êłŒê°ì ë°ììšë€", async (
"Examples with react-testing-library")
```javascript
-test('ì€ì§ VIP넌 ëłŽêž°ìíŽ ìŹì©ìëȘ©ëĄì íìíìë, ì€ì§ VIP ë©€ëČë€ë§ 볎ìŹì žìŒ íë€', () => {
+test('ì€ì§ VIP넌 ëłŽêž°ìíŽ ìŹì©ì ëȘ©ëĄì íì íì ë, ì€ì§ VIP ë©€ëČë€ë§ 볎ìŹì žìŒ íë€', () => {
// Arrange
const allUsers = [
{ id: 1, name: 'Yoni Goldberg', vip: false },
@@ -963,7 +963,7 @@ test('ì€ì§ VIP넌 ëłŽêž°ìíŽ ìŹì©ìëȘ©ëĄì íìíìë, ì€ì§ VIP
### :thumbsdown: ìëȘ»ë ì: í멎 ìžë¶ìŹíë€êłŒ ë°ìŽí°ë„Œ ììŽì êČìŠ
```javascript
-test('ì€ì§ VIP넌 ëłŽêž°ìíŽ ìŹì©ìëȘ©ëĄì íìíìë, ì€ì§ VIP ë©€ëČë€ë§ 볎ìŹì žìŒ íë€', () => {
+test('ì€ì§ VIP넌 ëłŽêž°ìíŽ ìŹì©ì ëȘ©ëĄì íì íì ë, ì€ì§ VIP ë©€ëČë€ë§ 볎ìŹì žìŒ íë€', () => {
// Arrange
const allUsers = [
{id: 1, name: 'Yoni Goldberg', vip: false },
@@ -1006,44 +1006,45 @@ test('ì€ì§ VIP넌 ëłŽêž°ìíŽ ìŹì©ìëȘ©ëĄì íìíìë, ì€ì§ VIP

-```html
+```jsx
// the markup code (part of React component)
- {value}
+ {value}
+ {/* data-test-id ìì± ì°žêł */}
```
```javascript
// react-testing-library넌 ìŹì©í ìì
- test('metricì ë°ìŽí°ê° ì ëŹëì§ ììŒë©Ž, 0ì êž°ëłžê°ìŒëĄ 볎ìŹì€ë€', () => {
- // Arrange
- const metricValue = undefined;
+test("metricì ë°ìŽí°ê° ì ëŹëì§ ììŒë©Ž, 0ì êž°ëłžê°ìŒëĄ 볎ìŹì€ë€", () => {
+ // Arrange
+ const metricValue = undefined;
- // Act
- const { getByTestId } = render();
-
- expect(getByTestId('errorsLabel')).text()).toBe("0");
- });
+ // Act
+ const { getByTestId } = render();
+ expect(getByTestId("errorsLabel").text()).toBe("0");
+});
```
### :thumbsdown: ìëȘ»ë ì: CSS ììë€ì ììĄŽ
-```html
-
-{value}
+```jsx
+// the markup code (part of React component)
+{value}
+// ë§ìœ ëììŽëê° íŽëì€ë„Œ ëłêČœíë€ë©Ž?
```
```javascript
-// enzymeì ìŹì©í ìì
-test('ë°ìŽí°ê° ì ëŹëì§ ììŒë©Ž, 0ì 볎ìŹì€ë€', () => {
- // ...
-
- expect(wrapper.find("[className='d-flex-column']").text()).toBe("0");
- });
+// this exammple is using enzyme
+test("ë°ìŽí°ê° ì ëŹëì§ ììŒë©Ž, 0ì 볎ìŹì€ë€", () => {
+ // ...
+
+ expect(wrapper.find("[className='d-flex-column']").text()).toBe("0");
+});
```
@@ -1078,50 +1079,50 @@ test('ë°ìŽí°ê° ì ëŹëì§ ììŒë©Ž, 0ì 볎ìŹì€ë€', () => {
```javascript
class Calendar extends React.Component {
- static defaultProps = {showFilters: false}
-
+ static defaultProps = { showFilters: false };
+
render() {
return (
A filters panel with a button to hide/show filters
-
+
- )
+ );
}
}
-//Examples use React & Enzyme
-test('ì€ì ì ìž ì ê·Œ: íí°ë€ì íŽëŠí멎, íí°ë€ìŽ í멎ì íìëë€', () => {
- // Arrange
- const wrapper = mount()
-
- // Act
- wrapper.find('button').simulate('click');
+//React & Enzyme ìŹì© ì
+test("ì€ì ì ìž ì ê·Œ: íí°ë€ì íŽëŠí멎, íí°ë€ìŽ í멎ì íìëë€", () => {
+ // Arrange
+ const wrapper = mount();
- // Assert
- expect(wrapper.text().includes('Choose Filter'));
- // ìŹì©ìê° ììì ì ê·Œíë ë°©ëČ: í
ì€ížë„Œ ìŽì©
-})
+ // Act
+ wrapper.find("button").simulate("click");
+ // Assert
+ expect(wrapper.text().includes("Choose Filter"));
+ // ìŹì©ìê° ììì ì ê·Œíë ë°©ëČ: í
ì€ížë„Œ ìŽì©
+});
```
### :thumbsdown: ìëȘ»ë ì: ìì ë ëë§êłŒ íšê» ì€ì 넌 ëȘ©íč
```javascript
+test("ìì/ëȘ©íč ì ê·Œ: íí°ë€ì íŽëŠí멎, íí°ë€ìŽ í멎ì íìëë€", () => {
+ // Arrange
+ const wrapper = shallow();
-test('ìì/ëȘ©íč ì ê·Œ: íí°ë€ì íŽëŠí멎, íí°ë€ìŽ í멎ì íìëë€', () => {
- // Arrange
- const wrapper = shallow()
-
- // Act
- wrapper.find('filtersPanel').instance().showFilters();
- // ëŽë¶ë„Œ ííêł , í멎ì 돎ìíì± ë©ìë넌 ížì¶. íìŽížë°ì€ ì ê·Œ
-
- // Assert
- expect(wrapper.find('Filter').props()).toEqual({title: 'Choose Filter'});
- // nameì ëłêČœíê±°ë, êŽë šë ë€ë„ž êČë€ì ì ëŹíì§ ìëë€ë©Ž ìŽë»êČ ë êč?
-})
-
+ // Act
+ wrapper
+ .find("filtersPanel")
+ .instance()
+ .showFilters();
+ // ëŽë¶ë„Œ ííêł , í멎ì 돎ìíì± ë©ìë넌 ížì¶. íìŽížë°ì€ ì ê·Œ
+
+ // Assert
+ expect(wrapper.find("Filter").props()).toEqual({ title: "Choose Filter" });
+ // nameì ëłêČœíê±°ë, êŽë šë ë€ë„ž êČë€ì ì ëŹíì§ ìëë€ë©Ž ìŽë»êČ ë êč?
+});
```
@@ -1161,40 +1162,36 @@ cy.wait('@products')// wait for route to appear
```javascript
// @testing-library/dom
-test('movie title appears', async () => {
- // ììë ìŽêž°ì ìĄŽìŹ íì§ ìì...
-
- // ì¶íì ëêž°
- await wait(() => {
- expect(getByText('the lion king')).toBeInTheDocument()
- })
+test("ìí ì ëȘ©ìŽ ëíëë€", async () => {
+ // ììë ìŽêž°ì ìĄŽìŹ íì§ ìì...
- // ì¶íì êž°ë€ëа í ìì넌 늏íŽ
- const movie = await waitForElement(() => getByText('the lion king'))
-})
+ // ì¶íì ëêž°
+ await wait(() => {
+ expect(getByText("the lion king")).toBeInTheDocument();
+ });
+ // ì¶íì êž°ë€ëа í ìì넌 늏íŽ
+ const movie = await waitForElement(() => getByText("the lion king"));
+});
```
### :thumbsdown: ìëȘ»ë ì: ìŹì©ì ì ì ìŹëŠœ ìœë
```javascript
+test("ìí ì ëȘ©ìŽ ëíëë€", async () => {
+ // ìŽêž°ì ììê° ìĄŽìŹ íì§ ìì...
+
+ // ìŹì©ì ì ì ëêž° ëĄì§ (ìŁŒì: ë§€ì° ëšì, íììììŽ ìë)
+ const interval = setInterval(() => {
+ const found = getByText("the lion king");
+ if (found) {
+ clearInterval(interval);
+ expect(getByText("the lion king")).toBeInTheDocument();
+ }
+ }, 100);
-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'))
-})
-
+ // ì¶íì êž°ë€ëа í ìì넌 늏íŽ
+ const movie = await waitForElement(() => getByText("the lion king"));
+});
```
@@ -1247,38 +1244,36 @@ test('movie title appears', async () => {
"Examples with React") 
-```javascript
-
+```jsx
// unit under test
-export default function ProductsList() {
- const [products, setProducts] = useState(false)
+export default function ProductsList() {
+ const [products, setProducts] = useState(false);
- const fetchProducts = async() => {
- const products = await axios.get('api/products')
- setProducts(products);
- }
+ const fetchProducts = async () => {
+ const products = await axios.get("api/products");
+ setProducts(products);
+ };
- useEffect(() => {
- fetchProducts();
- }, []);
+ useEffect(() => {
+ fetchProducts();
+ }, []);
- return products ? {products}
: No products
+ return products ? {products}
: No products
;
}
// test
-test('productsê° ìë êČœì°, ì ì í ë©ìì§ íìíêž°', () => {
- // Arrange
- nock("api")
- .get(`/products`)
- .reply(404);
+test("productsê° ìë êČœì°, ì ì í ë©ìì§ íìíë€", () => {
+ // Arrange
+ nock("api")
+ .get(`/products`)
+ .reply(404);
- // Act
- const {getByTestId} = render();
+ // Act
+ const { getByTestId } = render();
- // Assert
- expect(getByTestId('no-products-message')).toBeTruthy();
+ // Assert
+ expect(getByTestId("no-products-message")).toBeTruthy();
});
-
```
@@ -1360,7 +1355,7 @@ beforeEach(setUser => () {

```javascript
-it('ëȘšë íìŽì§ë„Œ smoke í
ì€íž í ë, íìŽì§ë€ìŽ ì ìì ìŒëĄ ëĄë ëìŽìŒ íë€', () => {
+it('ëȘšë íìŽì§ë„Œ smoke í
ì€íž í ë, íìŽì§ë€ìŽ ì ìì ìŒëĄ ëĄëëìŽìŒ íë€', () => {
// Cypress넌 ìŽì©í ìì ì
ëë€
// ë€ë„ž E2E ëê”ŹëĄë ìœêČ ê”ŹíìŽ ê°ë„í©ëë€
cy.visit('https://mysite.com/home');
@@ -1480,44 +1475,21 @@ paths:
"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');
-
-});
-
+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");
+ });
});
```
@@ -1603,7 +1575,7 @@ mutation êž°ë°ì í
ì€ížë ëšìí '방돞'ìŽ ìë ì€ì ëĄ í
ì€íž
-â **ê·žë ì§ ììŒë©Ž:** 85%ì 컀ëČ늏ì§ë í
ì€ížìì ìœëì 85%ìì ëČ귞넌 ê°ì§íë€ë ì믞ì
ëë€.
+â **ê·žë ì§ ììŒë©Ž:** 85%ì 컀ëČ늏ì§ë í
ì€ížìì ìœëì 85%ìì ëČ귞넌 ê°ì§íë€ë ì믞ì
ëë€.
@@ -1611,22 +1583,22 @@ mutation êž°ë°ì í
ì€ížë ëšìí '방돞'ìŽ ìë ì€ì ëĄ í
ì€íž
-### :thumbsdown: ìŹë°ë„Žì§ ìì ì: 100% 컀ëČ늏ì§, 0% í
ì€íž
+### :thumbsdown: ìŹë°ë„Žì§ ìì ì: 100% 컀ëČ늏ì§, 0% í
ì€íž

```javascript
function addNewOrder(newOrder) {
- logger.log(`Adding new order ${newOrder}`);
- DB.save(newOrder);
- Mailer.sendMail(newOrder.assignee, `A new order was places ${newOrder}`);
+ logger.log(`Adding new order ${newOrder}`);
+ DB.save(newOrder);
+ Mailer.sendMail(newOrder.assignee, `A new order was places ${newOrder}`);
- return {approved: true};
+ return { approved: true };
}
-it("Test addNewOrder, don't use such test names", () => {
- addNewOrder({asignee: "John@mailer.com",price: 120});
+it("addNewOrder넌 í
ì€ížíêł , ìŽëŹí í
ì€íž ìŽëŠì ìŹì©íì§ ë§ììì€.", () => {
+ addNewOrder({ assignee: "John@mailer.com", price: 120 });
}); // 100% 컀ëČ늏ì§ê° ëì€ì§ë§ ì돎êČë íìžíì§ ìì”ëë€.
```
@@ -1718,7 +1690,7 @@ it("Test name", () => {*//error:no-identical-title. Assign unique titles to test
### :clap: ìŹë°ë„ž ì: ìœë íì§ êČìŹë„Œ ìííë npm ì€íŹëŠœížë ììČ ì ëë ê°ë°ìê° ì ìœë넌 ížìíë €êł í ë ëȘšë ëłë ŹëĄ ì€íë©ëë€.
-```javascript
+```json
"scripts": {
"inspect:sanity-testing": "mocha **/**--test.js --grep \"sanity\"",
"inspect:lint": "eslint .",
diff --git a/readme.md b/readme.md
index 55a639b7..211643f3 100644
--- a/readme.md
+++ b/readme.md
@@ -1,30 +1,29 @@
+
# đ Why this guide can take your testing skills to the next level
-## đ 46+ best practices: Super-comprehensive and exhaustive
+## đ 50+ best practices: Super-comprehensive and exhaustive
-This is a guide for JavaScript & Node.js reliability from A-Z. It summarizes and curates for you dozens of the best blog posts, books and tools the market has to offer
+This is a guide for JavaScript & Node.js reliability from A-Z. It summarizes and curates for you dozens of the best blog posts, books, and tools the market has to offer
## đą Advanced: Goes 10,000 miles beyond the basics
-Hop into a journey that travels way beyond the basics into advanced topics like testing in production, mutation testing, property-based testing and many other strategic & professional tools. Should you read every word in this guide your testing skills are likely to go way above the average
+Hop into a journey that travels way beyond the basics into advanced topics like testing in production, mutation testing, property-based testing, and many other strategic & professional tools. Should you read every word in this guide your testing skills are likely to go way above the average
## đ Full-stack: front, backend, CI, anything
-Start by understanding the ubiquitous testing practices that are the foundation for any application tier. Then, delve into your area of choice: frontend/UI, backend, CI or maybe all of them?
+Start by understanding the ubiquitous testing practices that are the foundation for any application tier. Then, delve into your area of choice: frontend/UI, backend, CI, or maybe all of them?
-### Written By Yoni Goldberg
+### Written By Yoni Goldberg - A JavaScript & Node.js consultant
-- 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/)
+### đšâđ« Exciting news: I've just released my super-comprehensive testing course after two years of recording and editing. [Less than 48 hours left for the đ special launch deal](https://testjavascript.com/)
@@ -35,6 +34,13 @@ Start by understanding the ubiquitous testing practices that are the foundation
- đ”đ±[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)
+- đŻđ”[Japanese (draft)](https://github.com/yuichkun/javascript-testing-best-practices/blob/master/readme-jp.md) - Courtesy of [Yuichi Yogo](https://github.com/yuichkun) and [ryo](https://github.com/kawamataryo)
+- đčđŒ[Traditional Chinese](readme-zh-TW.md) - Courtesy of [Yubin Hsu](https://github.com/yubinTW)
+- đșđŠ[Ukrainian](readme-ua.md) - Courtesy of [Serhii Shramko](https://github.com/Shramkoweb)
+- đźđ·[Persian](readme-pr-fr.md) - Courtesy of [Ali Azmoodeh](https://github.com/TREER00T)
+- đ·đș[Russian](readme-ru.md) - Courtesy of [Alex Popov](https://github.com/Saimon398)
+
- Want to translate to your own language? please open an issue đ
@@ -51,7 +57,7 @@ The foundation - structuring clean tests (12 bullets)
#### [`Section 2: Backend`](#section-2ïžâŁ-backend-testing)
-Writing backend and Microservices tests efficiently (8 bullets)
+Writing backend and Microservices tests efficiently (13 bullets)
#### [`Section 3: Frontend`](#section-3ïžâŁ-frontend-testing)
@@ -74,13 +80,13 @@ Guidelines for CI in the JS world (9 bullets)
## âȘïž 0 The Golden Rule: Design for lean testing
:white_check_mark: **Do:**
-Testing code is not like production-code - design it to be dead-simple, short, abstraction-free, flat, delightful to work with, lean. One should look at a test and get the intent instantly.
+Testing code is not production-code - Design it to be short, dead-simple, flat, and delightful to work with. One should look at a test and get the intent instantly.
-Our minds are full with the main production code, we don't have 'headspace' for additional complexity. Should we try to squeeze yet another challenging code into our poor brain it will slow the team down which works against the reason we do testing. Practically this is where many teams just abandon testing.
+See, our minds are already occupied with our main job - the production code. There is no 'headspace' for additional complexity. Should we try to squeeze yet another sus-system into our poor brain it will slow the team down which works against the reason we do testing. Practically this is where many teams just abandon testing.
-The tests are an opportunity for something else - a friendly and smiley assistant, one that it's delightful to work with and delivers great value for such a small investment. Science tells us that we have two brain systems: system 1 is used for effortless activities like driving a car on an empty road and system 2 which is meant for complex and conscious operations like solving a math equation. Design your test for system 1, when looking at test code it should _feel_ as easy as modifying an HTML document and not like solving 2X(17 Ă 24).
+The tests are an opportunity for something else - a friendly assistant, co-pilot, that delivers great value for a small investment. Science tells us that we have two brain systems: system 1 is used for effortless activities like driving a car on an empty road and system 2 is meant for complex and conscious operations like solving a math equation. Design your test for system 1, when looking at test code it should _feel_ as easy as modifying an HTML document and not like solving 2X(17 Ă 24).
-This can be achieved by selectively cherry-picking techniques, tools and test targets that are cost-effective and provide great ROI. Test only as much as needed, strive to keep it nimble, sometimes it's even worth dropping some tests and trade reliability for agility and simplicity.
+This can be achieved by selectively cherry-picking techniques, tools, and test targets that are cost-effective and provide great ROI. Test only as much as needed, and strive to keep it nimble, sometimes it's even worth dropping some tests and trading reliability for agility and simplicity.

@@ -154,7 +160,7 @@ describe('Products Service', function() {
:white_check_mark: **Do:** Structure your tests with 3 well-separated sections Arrange, Act & Assert (AAA). Following this structure guarantees that the reader spends no brain-CPU on understanding the test plan:
-1st A - Arrange: All the setup code to bring the system to the scenario the test aims to simulate. This might include instantiating the unit under test constructor, adding DB records, mocking/stubbing on objects and any other preparation code
+1st A - Arrange: All the setup code to bring the system to the scenario the test aims to simulate. This might include instantiating the unit under test constructor, adding DB records, mocking/stubbing on objects, and any other preparation code
2nd A - Act: Execute the unit under test. Usually 1 line of code
@@ -162,7 +168,7 @@ describe('Products Service', function() {
-â **Otherwise:** Not only do you spend hours understanding the main code, but what should have been the simplest part of the day (testing) stretches your brain
+â **Otherwise:** Not only do you spend hours understanding the main code but what should have been the simplest part of the day (testing) stretches your brain
@@ -294,7 +300,7 @@ class ProductService {
//public method
getPrice(productId) {
const desiredProduct = DB.getProduct(productId);
- finalPrice = this.calculateVATAdd(desiredProduct.price).finalPrice;
+ const finalPrice = this.calculateVATAdd(desiredProduct.price).finalPrice;
return finalPrice;
}
}
@@ -313,7 +319,7 @@ it("White-box test: When the internal methods get 0 vat, it return 0 response",
:white_check_mark: **Do:** Test doubles are a necessary evil because they are coupled to the application internals, yet some provide immense value ([Read here a reminder about test doubles: mocks vs stubs vs spies](https://martinfowler.com/articles/mocksArentStubs.html)).
-Before using test doubles, ask a very simple question: Do I use it to test functionality that appears, or could appear, in the requirements document? If no, itâs a white-box testing smell.
+Before using test doubles, ask a very simple question: Do I use it to test functionality that appears, or could appear, in the requirements document? If not, itâs a white-box testing smell.
For example, if you want to test that your app behaves reasonably when the payment service is down, you might stub the payment service and trigger some âNo Responseâ return to ensure that the unit under test returns the right value. This checks our application behavior/response/outcome under certain scenarios. You might also use a spy to assert that an email was sent when that service is downâââthis is again a behavioral check which is likely to appear in a requirements doc (âSend an email if payment couldnât be savedâ). On the flip side, if you mock the Payment service and ensure that it was called with the right JavaScript typesâââthen your test is focused on internal things that have nothing to do with the application functionality and are likely to change frequently
@@ -370,7 +376,7 @@ it("When a valid product is about to be deleted, ensure an email is sent", async
## âȘ ïž1.6 Donât âfooâ, use realistic input data
-:white_check_mark: **Do:** Often production bugs are revealed under some very specific and surprising inputâââthe more realistic the test input is, the greater the chances are to catch bugs early. Use dedicated libraries like [Faker](https://www.npmjs.com/package/faker) to generate pseudo-real data that resembles the variety and form of production data. For example, such libraries can generate realistic phone numbers, usernames, credit card, company names, and even âlorem ipsumâ text. You may also create some tests (on top of unit tests, not as a replacement) that randomize fakers data to stretch your unit under test or even import real data from your production environment. Want to take it to the next level? See the next bullet (property-based testing).
+:white_check_mark: **Do:** Often production bugs are revealed under some very specific and surprising inputâââthe more realistic the test input is, the greater the chances are to catch bugs early. Use dedicated libraries like [Chance](https://github.com/chancejs/chancejs) or [Faker](https://www.npmjs.com/package/faker) to generate pseudo-real data that resembles the variety and form of production data. For example, such libraries can generate realistic phone numbers, usernames, credit cards, company names, and even âlorem ipsumâ text. You may also create some tests (on top of unit tests, not as a replacement) that randomize fakers' data to stretch your unit under test or even import real data from your production environment. Want to take it to the next level? See the next bullet (property-based testing).
â **Otherwise:** All your development testing will falsely show green when you use synthetic inputs like âFooâ, but then production might turn red when a hacker passes-in a nasty string like â@3e2ddsf . ##â 1 fdsfds . fds432 AAAAâ
@@ -424,7 +430,7 @@ it("Better: When adding new valid product, get successful confirmation", async (
## âȘ ïž 1.7 Test many input combinations using Property-based testing
-:white_check_mark: **Do:** Typically we choose a few input samples for each test. Even when the input format resembles real-world data (see bullet âDonât fooâ), we cover only a few input combinations (method(ââ, true, 1), method(âstringâ , falseâ , 0)), However, in production, an API that is called with 5 parameters can be invoked with thousands of different permutations, one of them might render our process down ([see Fuzz Testing](https://en.wikipedia.org/wiki/Fuzzing)). What if you could write a single test that sends 1000 permutations of different inputs automatically and catches for which input our code fails to return the right response? Property-based testing is a technique that does exactly that: by sending all the possible input combinations to your unit under test it increases the serendipity of finding a bug. For example, given a methodâââaddNewProduct(id, name, isDiscount)âââthe supporting libraries will call this method with many combinations of (number, string, boolean) like (1, âiPhoneâ, false), (2, âGalaxyâ, true). You can run property-based testing using your favorite test runner (Mocha, Jest, etc) using libraries like [js-verify](https://github.com/jsverify/jsverify) or [testcheck](https://github.com/leebyron/testcheck-js) (much better documentation). Update: Nicolas Dubien suggests in the comments below to [checkout fast-check](https://github.com/dubzzz/fast-check#readme) which seems to offer some additional features and also to be actively maintained
+:white_check_mark: **Do:** Typically we choose a few input samples for each test. Even when the input format resembles real-world data (see bullet [âDonât fooâ](https://github.com/goldbergyoni/javascript-testing-best-practices#-%EF%B8%8F16-dont-foo-use-realistic-input-data)), we cover only a few input combinations (method(ââ, true, 1), method(âstringâ , false , 0)), However, in production, an API that is called with 5 parameters can be invoked with thousands of different permutations, one of them might render our process down ([see Fuzz Testing](https://en.wikipedia.org/wiki/Fuzzing)). What if you could write a single test that sends 1000 permutations of different inputs automatically and catches for which input our code fails to return the right response? Property-based testing is a technique that does exactly that: sending all the possible input combinations to your unit under test it increases the serendipity of finding a bug. For example, given a methodâââaddNewProduct(id, name, isDiscount)âââthe supporting libraries will call this method with many combinations of (number, string, boolean) like (1, âiPhoneâ, false), (2, âGalaxyâ, true). You can run property-based testing using your favorite test runner (Mocha, Jest, etc) using libraries like [js-verify](https://github.com/jsverify/jsverify) or [testcheck](https://github.com/leebyron/testcheck-js) (much better documentation). Update: Nicolas Dubien suggests in the comments below to [checkout fast-check](https://github.com/dubzzz/fast-check#readme) which seems to offer some additional features and also to be actively maintained
â **Otherwise:** Unconsciously, you choose the test inputs that cover only code paths that work well. Unfortunately, this decreases the efficiency of testing as a vehicle to expose bugs
@@ -463,12 +469,12 @@ describe("Product service", () => {
:white_check_mark: **Do:** When there is a need for [snapshot testing](https://jestjs.io/docs/en/snapshot-testing), use only short and focused snapshots (i.e. 3-7 lines) that are included as part of the test ([Inline Snapshot](https://jestjs.io/docs/en/snapshot-testing#inline-snapshots)) and not within external files. Keeping this guideline will ensure your tests remain self-explanatory and less fragile.
-On the other hand, âclassic snapshotsâ tutorials and tools encourage to store big files (e.g. component rendering markup, API JSON result) over some external medium and ensure each time when the test run to compare the received result with the saved version. This, for example, can implicitly couple our test to 1000 lines with 3000 data values that the test writer never read and reasoned about. Why is this wrong? By doing so, there are 1000 reasons for your test to fail - itâs enough for a single line to change for the snapshot to get invalid and this is likely to happen a lot. How frequently? for every space, comment or minor CSS/HTML change. Not only this, the test name wouldnât give a clue about the failure as it just checks that 1000 lines didnât change, also it encourages to the test writer to accept as the desired true a long document he couldnât inspect and verify. All of these are symptoms of obscure and eager test that is not focused and aims to achieve too much
+On the other hand, âclassic snapshotsâ tutorials and tools encourage storing big files (e.g. component rendering markup, API JSON result) over some external medium and ensure each time when the test runs to compare the received result with the saved version. This, for example, can implicitly couple our test to 1000 lines with 3000 data values that the test writer never read and reasoned about. Why is this wrong? By doing so, there are 1000 reasons for your test to fail - itâs enough for a single line to change for the snapshot to get invalid and this is likely to happen a lot. How frequently? for every space, comment, or minor CSS/HTML change. Not only this, the test name wouldnât give a clue about the failure as it just checks that 1000 lines didnât change, also it encourages the test writer to accept as the desired true a long document he couldnât inspect and verify. All of these are symptoms of obscure and eager test that is not focused and aims to achieve too much
Itâs worth noting that there are few cases where long & external snapshots are acceptable - when asserting on schema and not data (extracting out values and focusing on fields) or when the received document rarely changes
-â **Otherwise:** A UI test fails. The code seems right, the screen renders perfect pixels, what happened? your snapshot testing just found a difference from the origin document to current received one - a single space character was added to the markdown...
+â **Otherwise:** A UI test fails. The code seems right, the screen renders perfect pixels, what happened? your snapshot testing just found a difference from the original document to the current received one - a single space character was added to the markdown...
@@ -526,12 +532,12 @@ it("When visiting TestJavaScript.com home page, a menu is displayed", () => {
-## âȘ ïž1.9 Avoid global test fixtures and seeds, add data per-test
+## âȘ ïž 1.9 Copy code, but only what's neccessary
-:white_check_mark: **Do:** Going by the golden rule (bullet 0), each test should add and act on its own set of DB rows to prevent coupling and easily reason about the test flow. In reality, this is often violated by testers who seed the DB with data before running the tests ([also known as âtest fixtureâ](https://en.wikipedia.org/wiki/Test_fixture)) for the sake of performance improvement. While performance is indeed a valid concernâââit can be mitigated (see âComponent testingâ bullet), however, test complexity is a much painful sorrow that should govern other considerations most of the time. Practically, make each test case explicitly add the DB records it needs and act only on those records. If performance becomes a critical concernâââa balanced compromise might come in the form of seeding the only suite of tests that are not mutating data (e.g. queries)
+:white_check_mark: **Do:** Include all the necessary details that affect the test result, but nothing more. As an example, consider a test that should factor 100 lines of input JSONâ-âPasting this in every test is tedious. Extracting it outside to transferFactory.getJSON() will leave the test vagueâ-âWithout data, it's hard to correlate the test result with the cause ("why is it supposed to return 400 status?"). The classic book x-unit patterns named this pattern 'the mystery guest'â-âSomething unseen affected our test results, we don't know what exactly. We can do better by extracting repeatable long parts outside AND mentioning explicitly which specific details matter to the test. Going with the example above, the test can pass parameters that highlight what is important: transferFactory.getJSON({sender: undefined}). In this example, the reader should immediately infer that the empty sender field is the reason why the test should expect a validation error or any other similar adequate outcome.
-â **Otherwise:** Few tests fail, a deployment is aborted, our team is going to spend precious time now, do we have a bug? letâs investigate, oh noâââit seems that two tests were mutating the same seed data
+â **Otherwise:** Copying 500 JSON lines in will leave your tests unmaintainable and unreadable. Moving everything outside will end with vague tests that are hard to understand
@@ -539,49 +545,46 @@ it("When visiting TestJavaScript.com home page, a menu is displayed", () => {
-### :thumbsdown: Anti-Pattern Example: tests are not independent and rely on some global hook to feed global DB data
+### :thumbsdown: Anti-Pattern Example: The test failure is unclear because all the cause is external and hides within huge JSON

```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 :[
-});
+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: Doing It Right Example: We can stay within the test, each test acts on its own set of data
+### :clap: Doing It Right Example: The test highlights what is the cause of the test result
```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 Donât catch errors, expect them
@@ -668,11 +671,11 @@ describe("Order service", function() {
## âȘ ïž 1.12 Categorize tests under at least 2 levels
-:white_check_mark: **Do:** Apply some structure to your test suite so an occasional visitor could easily understand the requirements (tests are the best documentation) and the various scenarios that are being tested. A common method for this is by placing at least 2 'describe' blocks above your tests: the 1st is for the name of the unit under test and the 2nd for additional level of categorization like the scenario or custom categories (see code examples and print screen below). Doing so will also greatly improve the test reports: The reader will easily infer the tests categories, delve into the desired section and correlate failing tests. In addition, it will get much easier for a developer to navigate through the code of a suite with many tests. There are multiple alternative structures for test suite that you may consider like [given-when-then](https://github.com/searls/jasmine-given) and [RITE](https://github.com/ericelliott/riteway)
+:white_check_mark: **Do:** Apply some structure to your test suite so an occasional visitor could easily understand the requirements (tests are the best documentation) and the various scenarios that are being tested. A common method for this is by placing at least 2 'describe' blocks above your tests: the 1st is for the name of the unit under test and the 2nd for an additional level of categorization like the scenario or custom categories (see code examples and the print screen below). Doing so will also greatly improve the test reports: The reader will easily infer the test categories, delve into the desired section and correlate failing tests. In addition, it will get much easier for a developer to navigate through the code of a suite with many tests. There are multiple alternative structures for the test suite that you may consider like [given-when-then](https://github.com/searls/jasmine-given) and [RITE](https://github.com/ericelliott/riteway)
-â **Otherwise:** When looking at a report with flat and long list of tests, the reader have to skim-read through long texts to conclude the major scenarios and correlate the commonality of failing tests. Consider the following case: When 7/100 tests fail, looking at a flat list will demand reading the failing tests text to see how they relate to each other. However, in a hierarchical report all of them could be under the same flow or category and the reader will quickly infer what or at least where is the root failure cause
+â **Otherwise:** When looking at a report with a flat and long list of tests, the reader has to skim-read through long texts to conclude the major scenarios and correlate the commonality of failing tests. Consider the following case: When 7/100 tests fail, looking at a flat list will demand reading the text of the failing to see how they relate to each other. However, in a hierarchical report, all of them could be under the same flow or category and the reader will quickly infer what or at least where is the root failure cause
@@ -724,9 +727,9 @@ test("Then there should not be a new transfer record", () => {});
## âȘ ïž1.13 Other generic good testing hygiene
-:white_check_mark: **Do:** This post is focused on testing advice that is related to, or at least can be exemplified with Node JS. This bullet, however, groups few non-Node related tips that are well-known
+:white_check_mark: **Do:** This post is focused on testing advice that is related to or at least can be exemplified with Node JS. This bullet, however, groups a few non-Node related tips that are well-known
-Learn and practice [TDD principles](https://www.sm-cloud.com/book-review-test-driven-development-by-example-a-tldr/)âââthey are extremely valuable for many but donât get intimidated if they donât fit your style, youâre not the only one. Consider writing the tests before the code in a [red-green-refactor style](https://blog.cleancoder.com/uncle-bob/2014/12/17/TheCyclesOfTDD.html), ensure each test checks exactly one thing, when you find a bugâââbefore fixing write a test that will detect this bug in the future, let each test fail at least once before turning green, start a module by writing a quick and simplistic code that satisfies the test - then refactor gradually and take it to a production grade level, avoid any dependency on the environment (paths, OS, etc)
+Learn and practice [TDD principles](https://www.sm-cloud.com/book-review-test-driven-development-by-example-a-tldr/)âââthey are extremely valuable for many but donât get intimidated if they donât fit your style, youâre not the only one. Consider writing the tests before the code in a [red-green-refactor style](https://blog.cleancoder.com/uncle-bob/2014/12/17/TheCyclesOfTDD.html), ensure each test checks exactly one thing, when you find a bugâââbefore fixing write a test that will detect this bug in the future, and let each test fail at least once before turning green, start a module by writing a quick and simplistic code that satisfies the test - then refactor gradually and take it to a production grade level, avoid any dependency on the environment (paths, OS, etc)
â **Otherwise:** Youâll miss pearls of wisdom that were collected for decades
@@ -737,13 +740,13 @@ Learn and practice [TDD principles](https://www.sm-cloud.com/book-review-test-dr
## âȘ ïž2.1 Enrich your testing portfolio: Look beyond unit tests and the pyramid
-:white_check_mark: **Do:** The [testing pyramid](https://martinfowler.com/bliki/TestPyramid.html), though 10> years old, is a great and relevant model that suggests three testing types and influences most developersâ testing strategy. At the same time, more than a handful of shiny new testing techniques emerged and are hiding in the shadows of the testing pyramid. Given all the dramatic changes that weâve seen in the recent 10 years (Microservices, cloud, serverless), is it even possible that one quite-old model will suit *all* types of applications? shouldnât the testing world consider welcoming new testing techniques?
+:white_check_mark: **Do:** The [testing pyramid](https://martinfowler.com/bliki/TestPyramid.html), though 10> years old, is a great and relevant model that suggests three testing types and influences most developersâ testing strategies. At the same time, more than a handful of shiny new testing techniques emerged and are hiding in the shadows of the testing pyramid. Given all the dramatic changes that weâve seen in the recent 10 years (Microservices, cloud, serverless), is it even possible that one quite-old model will suit *all* types of applications? shouldnât the testing world consider welcoming new testing techniques?
-Donât get me wrong, in 2019 the testing pyramid, TDD and unit tests are still a powerful technique and are probably the best match for many applications. Only like any other model, despite its usefulness, [it must be wrong sometimes](https://en.wikipedia.org/wiki/All_models_are_wrong). For example, consider an IoT application that ingests many events into a message-bus like Kafka/RabbitMQ, which then flow into some data-warehouse and are eventually queried by some analytics UI. Should we really spend 50% of our testing budget on writing unit tests for an application that is integration-centric and has almost no logic? As the diversity of application types increase (bots, crypto, Alexa-skills) greater are the chances to find scenarios where the testing pyramid is not the best match.
+Donât get me wrong, in 2019 the testing pyramid, TDD, and unit tests are still a powerful technique and are probably the best match for many applications. Only like any other model, despite its usefulness, [it must be wrong sometimes](https://en.wikipedia.org/wiki/All_models_are_wrong). For example, consider an IoT application that ingests many events into a message-bus like Kafka/RabbitMQ, which then flow into some data-warehouse and are eventually queried by some analytics UI. Should we really spend 50% of our testing budget on writing unit tests for an application that is integration-centric and has almost no logic? As the diversity of application types increases (bots, crypto, Alexa-skills) greater are the chances to find scenarios where the testing pyramid is not the best match.
-Itâs time to enrich your testing portfolio and become familiar with more testing types (the next bullets suggest few ideas), mind models like the testing pyramid but also match testing types to real-world problems that youâre facing (âHey, our API is broken, letâs write consumer-driven contract testing!â), diversify your tests like an investor that build a portfolio based on risk analysisâââassess where problems might arise and match some prevention measures to mitigate those potential risks
+Itâs time to enrich your testing portfolio and become familiar with more testing types (the next bullets suggest a few ideas), mind models like the testing pyramid but also match testing types to real-world problems that youâre facing (âHey, our API is broken, letâs write consumer-driven contract testing!â), diversify your tests like an investor that builds a portfolio based on risk analysisâââassess where problems might arise and match some prevention measures to mitigate those potential risks
-A word of caution: the TDD argument in the software world takes a typical false-dichotomy face, some preach to use it everywhere, others think itâs the devil. Everyone who speaks in absolutes is wrong :]
+A word of caution: the TDD argument in the software world takes a typical false-dichotomy face, some preach to use it everywhere, and others think itâs the devil. Everyone who speaks in absolutes is wrong :]
@@ -771,9 +774,12 @@ A word of caution: the TDD argument in the software world takes a typical false-
## âȘ ïž2.2 Component testing might be your best affair
-:white_check_mark: **Do:** Each unit test covers a tiny portion of the application and itâs expensive to cover the whole, whereas end-to-end testing easily covers a lot of ground but is flaky and slower, why not apply a balanced approach and write tests that are bigger than unit tests but smaller than end-to-end testing? Component testing is the unsung song of the testing worldâââthey provide the best from both worlds: reasonable performance and a possibility to apply TDD patterns + realistic and great coverage.
+:white_check_mark: **Do:** Each unit test covers a tiny portion of the application and itâs expensive to cover the whole, whereas end-to-end testing easily covers a lot of ground but is flaky and slower, why not apply a balanced approach and write tests that are bigger than unit tests but smaller than end-to-end testing? Component testing is the unsung song of the testing worldâââthey provide the best of both worlds: reasonable performance and a possibility to apply TDD patterns + realistic and great coverage.
+
+Component tests focus on the Microservice âunitâ, they work against the API and donât mock anything which belongs to the Microservice itself (e.g. real DB, or at least the in-memory version of that DB) but stub anything that is external like calls to other Microservices. By doing so, we test what we deploy, approach the app from outward to inward and gain great confidence in a reasonable amount of time.
+
+[We have a full guide that is solely dedicated to writing component tests in the right way](https://github.com/testjavascript/nodejs-integration-tests-best-practices)
-Component tests focus on the Microservice âunitâ, they work against the API, donât mock anything which belongs to the Microservice itself (e.g. real DB, or at least the in-memory version of that DB) but stub anything that is external like calls to other Microservices. By doing so, we test what we deploy, approach the app from outwards to inwards and gain great confidence in a reasonable amount of time.
â **Otherwise:** You may spend long days on writing unit tests to find out that you got only 20% system coverage
@@ -796,7 +802,7 @@ Component tests focus on the Microservice âunitâ, they work against the API,
## âȘ ïž2.3 Ensure new releases donât break the API using contract tests
-:white_check_mark: **Do:** So your Microservice has multiple clients, and you run multiple versions of the service for compatibility reasons (keeping everyone happy). Then you change some field and âboom!â, some important client who relies on this field is angry. This is the Catch-22 of the integration world: Itâs very challenging for the server side to consider all the multiple client expectationsâââOn the other hand, the clients canât perform any testing because the server controls the release dates. [Consumer-driven contracts and the framework PACT](https://docs.pact.io/) were born to formalize this process with a very disruptive approachââânot the server defines the test plan of itself rather the client defines the tests of the⊠server! PACT can record the client expectation and put in a shared location, âbrokerâ, so the server can pull the expectations and run on every build using PACT library to detect broken contractsâââa client expectation that is not met. By doing so, all the server-client API mismatches are caught early during build/CI and might save you a great deal of frustration
+:white_check_mark: **Do:** So your Microservice has multiple clients, and you run multiple versions of the service for compatibility reasons (keeping everyone happy). Then you change some field and âboom!â, some important client who relies on this field is angry. This is the Catch-22 of the integration world: Itâs very challenging for the server side to consider all the multiple client expectationsâââOn the other hand, the clients canât perform any testing because the server controls the release dates. There is a spectrum of techniques that can mitigate the contract problem, some are simple, other are more feature-rich and demand a steeper learning curve. In a simple and recommended approach, the API provider publishes npm package with the API typing (e.g. JSDoc, TypeScript). Then the consumers can fetch this library and benefit from codign time intellisense and validation. A fancier approach is to use [PACT](https://docs.pact.io/) which was born to formalize this process with a very disruptive approachââânot the server defines the test plan itself rather the client defines the tests of the⊠server! PACT can record the client expectation and put it in a shared location, âbrokerâ, so the server can pull the expectations and run on every build using the PACT library to detect broken contractsâââa client expectation that is not met. By doing so, all the server-client API mismatches are caught early during build/CI and might save you a great deal of frustration
â **Otherwise:** The alternatives are exhausting manual testing or deployment fear
@@ -957,6 +963,164 @@ it("When updating site name, get successful confirmation", async () => {
+
+
+## âȘ ïž2.8 Choose a clear data clean-up strategy: After-all (recommended) or after-each
+
+:white_check_mark: **Do:** The timing when the tests clean the database determines the way the tests are being written. The two most viable options are cleaning after all the tests vs cleaning after every single test. Choosing the latter option, cleaning after every single test guarantees clean tables and builds convenient testing perks for the developer. No other records exist when the test starts, one can have certainty which data is being queried and even might be tempted to count rows during assertions. This comes with severe downsides: When running in a multi-process mode, tests are likely to interfere with each other. While process-1 purges tables, at the very moment process-2 queries for data and fail (because the DB was suddenly deleted by process-1). On top of this, It's harder to troubleshoot failing tests - Visiting the DB will show no records.
+
+The second option is to clean up after all the test files have finished (or even daily!). This approach means that the same DB with existing records serves all the tests and processes. To avoid stepping on each other's toes, the tests must add and act on specific records that they have added. Need to check that some record was added? Assume that there are other thousands of records and query for records that were added explicitly. Need to check that a record was deleted? Can't assume an empty table, check that this specific record is not there. This technique brings few powerful gains: It works natively in multi-process mode, when a developer wishes to understand what happened - the data is there and not deleted. It also increases the chance of finding bugs because the DB is full of records and not artificially empty. [See the full comparison table here](https://github.com/testjavascript/nodejs-integration-tests-best-practices/blob/master/graphics/db-clean-options.png).
+
+
+â **Otherwise:** Without a strategy to separate records or clean - Tests will step on each other toes; Using transactions will work only for relational DB and likely to get complicated once there are inner transactions
+
+
+
+â Code Examples
+
+
+
+### :clap: Cleaning after ALL the tests. Not neccesserily after every run. The more data we have while the tests are running - The more it resembles the production perks
+
+```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 Isolate the component from the world using HTTP interceptor
+
+:white_check_mark: **Do:** Isolate the component under test by intercepting any outgoing HTTP request and providing the desired response so the collaborator HTTP API won't get hit. Nock is a great tool for this mission as it provides a convenient syntax for defining external services behavior. Isolation is a must to prevent noise and slow performance but mostly to simulate various scenarios and responses - A good flight simulator is not about painting clear blue sky rather bringing safe storms and chaos. This is reinforced in a Microservice architecture where the focus should always be on a single component without involving the rest of the world. Though it's possible to simulate external service behavior using test doubles (mocking), it's preferable not to touch the deployed code and act on the network level to keep the tests pure black-box. The downside of isolation is not detecting when the collaborator component changes and not realizing misunderstandings between the two services - Make sure to compensate for this using a few contract or E2E tests
+
+
+â **Otherwise:** Some services provide a fake version that can be deployed by the caller locally, usually using Docker - This will ease the setup and boost the performance but won't help with simulating various responses; Some services provide 'sandbox' environment, so the real service is hit but no costs or side effects are triggered - This will cut down the noise of setting up the 3rd party service but also won't allow simulating scenarios
+
+
+
+â Code Examples
+
+
+
+### :clap: Preventing network calls to externous components allows simulating scenarios and minimizing the noise
+
+```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 Test the response schema, mostly when there are auto-generated fields
+
+:white_check_mark: **Do:** When it is impossible to assert for specific data, check for mandatory field existence and types. Sometimes, the response contains important fields with dynamic data that can't be predicted when writing the test, like dates and incrementing numbers. If the API contract promises that these fields won't be null and hold the right types, it's imperative to test it. Most assertion libraries support checking types. If the response is small, check the return data and type together within the same assertion (see code example). One more option is to verify the entire response against an OpenAPI doc (Swagger). Most test runners have community extensions that validate API responses against their documentation.
+
+
+
+
+â **Otherwise:** Although the code/API caller relies on some field with dynamic data (e.g., ID, date), it will not come in return and break the contract
+
+
+
+â Code Examples
+
+
+
+### :clap: Asserting that fields with dynamic value exist and have the right type
+
+```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 Check integrations corner cases and chaos
+
+:white_check_mark: **Do:** When checking integrations, go beyond the happy and sad paths. Check not only error responses (e.g. HTTP 500 error) but also network-level anomalies like slow and timed-out responses. This will prove that the code is resilient and can handle various network scenarios like taking the right path after a timeout, has no fragile race conditions, and contains a circuit breaker for retries. Reputable interceptor tools can easily simulate various network behaviors like hectic service that occasionally fail. It can even realize when the default HTTP client timeout value is longer than the simulated response time and throw a timeout exception right away without waiting
+
+
+
+
+â **Otherwise:** All your tests pass, it's only the production who will crash or won't report errors correctly when 3rd parties send exceptional responses
+
+
+
+â Code Examples
+
+
+
+### :clap: Ensuring that on network failures, the circuit breaker can save the day
+
+```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 Test the five potential outcomes
+
+:white_check_mark: **Do:** When planning your tests, consider covering the five typical flow's outputs. When your test is triggering some action (e.g., API call), a reaction is happening, something meaningful occurs and calls for testing. Note that we don't care about how things work. Our focus is on outcomes, things that are noticeable from the outside and might affect the user. These outcomes/reactions can be put in 5 categories:
+
+âą Response - The test invokes an action (e.g., via API) and gets a response. It's now concerned with checking the response data correctness, schema, and HTTP status
+
+âą A new state - After invoking an action, some **publicly accessible** data is probably modified
+
+âą External calls - After invoking an action, the app might call an external component via HTTP or any other transport. For example, a call to send SMS, email or charge a credit card
+
+âą Message queues - The outcome of a flow might be a message in a queue
+
+âą Observability - Some things must be monitored, like errors or remarkable business events. When a transaction fails, not only we expect the right response but also correct error handling and proper logging/metrics. This information goes directly to a very important user - The ops user (i.e., production SRE/admin)
+
+
# Section 3ïžâŁ: Frontend Testing
@@ -1033,7 +1197,7 @@ test("When flagging to show only VIP, should display only VIP members", () => {

-```html
+```jsx
// the markup code (part of React component)
@@ -1060,7 +1224,7 @@ test("Whenever no data is passed to metric, show 0 as default", () => {
### :thumbsdown: Anti-Pattern Example: Relying on CSS attributes
-```html
+```jsx
{value}
@@ -1081,7 +1245,7 @@ test("Whenever no data is passed, error metric shows zero", () => {
## âȘ ïž 3.3 Whenever possible, test with a realistic and fully rendered component
-:white_check_mark: **Do:** Whenever reasonably sized, test your component from outside like your users do, fully render the UI, act on it and assert that the rendered UI behaves as expected. Avoid all sort of mocking, partial and shallow rendering - this approach might result in untrapped bugs due to lack of details and harden the maintenance as the tests mess with the internals (see bullet 'Favour blackbox testing'). If one of the child components is significantly slowing down (e.g. animation) or complicating the setup - consider explicitly replacing it with a fake
+:white_check_mark: **Do:** Whenever reasonably sized, test your component from outside like your users do, fully render the UI, act on it and assert that the rendered UI behaves as expected. Avoid all sort of mocking, partial and shallow rendering - this approach might result in untrapped bugs due to lack of details and harden the maintenance as the tests mess with the internals (see bullet ['Favour blackbox testing'](https://github.com/goldbergyoni/javascript-testing-best-practices#-%EF%B8%8F-14-stick-to-black-box-testing-test-only-public-methods)). If one of the child components is significantly slowing down (e.g. animation) or complicating the setup - consider explicitly replacing it with a fake
With all that said, a word of caution is in order: this technique works for small/medium components that pack a reasonable size of child components. Fully rendering a component with too many children will make it hard to reason about test failures (root cause analysis) and might get too slow. In such cases, write only a few tests against that fat parent component and more tests against its children
@@ -1295,7 +1459,7 @@ test("When no products exist, show the appropriate message", () => {
## âȘ ïž 3.7 Have very few end-to-end tests that spans the whole system
-:white_check_mark: **Do:** Although E2E (end-to-end) usually means UI-only testing with a real browser (See bullet 3.6), for other they mean tests that stretch the entire system including the real backend. The latter type of tests is highly valuable as they cover integration bugs between frontend and backend that might happen due to a wrong understanding of the exchange schema. They are also an efficient method to discover backend-to-backend integration issues (e.g. Microservice A sends the wrong message to Microservice B) and even to detect deployment failures - there are no backend frameworks for E2E testing that are as friendly and mature as UI frameworks like [Cypress](https://www.cypress.io/) and [Puppeteer](https://github.com/GoogleChrome/puppeteer). The downside of such tests is the high cost of configuring an environment with so many components, and mostly their brittleness - given 50 microservices, even if one fails then the entire E2E just failed. For that reason, we should use this technique sparingly and probably have 1-10 of those and no more. That said, even a small number of E2E tests are likely to catch the type of issues they are targeted for - deployment & integration faults. It's advisable to run those over a production-like staging environment
+:white_check_mark: **Do:** Although E2E (end-to-end) usually means UI-only testing with a real browser (See [bullet 3.6](https://github.com/goldbergyoni/javascript-testing-best-practices#-%EF%B8%8F-36-stub-flaky-and-slow-resources-like-backend-apis)), for other they mean tests that stretch the entire system including the real backend. The latter type of tests is highly valuable as they cover integration bugs between frontend and backend that might happen due to a wrong understanding of the exchange schema. They are also an efficient method to discover backend-to-backend integration issues (e.g. Microservice A sends the wrong message to Microservice B) and even to detect deployment failures - there are no backend frameworks for E2E testing that are as friendly and mature as UI frameworks like [Cypress](https://www.cypress.io/) and [Puppeteer](https://github.com/GoogleChrome/puppeteer). The downside of such tests is the high cost of configuring an environment with so many components, and mostly their brittleness - given 50 microservices, even if one fails then the entire E2E just failed. For that reason, we should use this technique sparingly and probably have 1-10 of those and no more. That said, even a small number of E2E tests are likely to catch the type of issues they are targeted for - deployment & integration faults. It's advisable to run those over a production-like staging environment
@@ -1305,7 +1469,7 @@ test("When no products exist, show the appropriate message", () => {
## âȘ ïž 3.8 Speed-up E2E tests by reusing login credentials
-:white_check_mark: **Do:** In E2E tests that involve a real backend and rely on a valid user token for API calls, it doesn't payoff to isolate the test to a level where a user is created and logged-in in every request. Instead, login only once before the tests execution start (i.e. before-all hook), save the token in some local storage and reuse it across requests. This seem to violate one of the core testing principle - keep the test autonomous without resources coupling. While this is a valid worry, in E2E tests performance is a key concern and creating 1-3 API requests before starting each individual tests might lead to horrible execution time. Reusing credentials doesn't mean the tests have to act on the same user records - if relying on user records (e.g. test user payments history) than make sure to generate those records as part of the test and avoid sharing their existence with other tests. Also remember that the backend can be faked - if your tests are focused on the frontend it might be better to isolate it and stub the backend API (see bullet 3.6).
+:white_check_mark: **Do:** In E2E tests that involve a real backend and rely on a valid user token for API calls, it doesn't payoff to isolate the test to a level where a user is created and logged-in in every request. Instead, login only once before the tests execution start (i.e. before-all hook), save the token in some local storage and reuse it across requests. This seem to violate one of the core testing principle - keep the test autonomous without resources coupling. While this is a valid worry, in E2E tests performance is a key concern and creating 1-3 API requests before starting each individual tests might lead to horrible execution time. Reusing credentials doesn't mean the tests have to act on the same user records - if relying on user records (e.g. test user payments history) than make sure to generate those records as part of the test and avoid sharing their existence with other tests. Also remember that the backend can be faked - if your tests are focused on the frontend it might be better to isolate it and stub the backend API (see [bullet 3.6](https://github.com/goldbergyoni/javascript-testing-best-practices#-%EF%B8%8F-36-stub-flaky-and-slow-resources-like-backend-apis)).
@@ -1337,14 +1501,13 @@ before(() => {
})
// happens before EACH test
-beforeEach(setUser => () {
- cy.visit('/home', {
- onBeforeLoad (win) {
+beforeEach(setUser => {
+ cy.visit('/home', () => {
+ onBeforeLoad (win => {
win.localStorage.setItem('token', JSON.stringify(authenticationToken))
- },
+ })
})
})
-
```
@@ -1375,9 +1538,9 @@ it("When doing smoke testing over all page, should load them all successfully",
// using any E2E suite
cy.visit("https://mysite.com/home");
cy.contains("Home");
- cy.contains("https://mysite.com/Login");
+ cy.visit("https://mysite.com/Login");
cy.contains("Login");
- cy.contains("https://mysite.com/About");
+ cy.visit("https://mysite.com/About");
cy.contains("About");
});
```
@@ -1402,8 +1565,8 @@ it("When doing smoke testing over all page, should load them all successfully",

-```javascript
-// this is how one can describe tests using cucumber: plain language that allows anyone to understand and collaborate
+```text
+This is how one can describe tests using cucumber: plain language that allows anyone to understand and collaborate
Feature: Twitter new tweet
@@ -1416,7 +1579,6 @@ Feature: Twitter new tweet
Given I type "Hello followers!" in the textbox
Given I click on "Submit" button
Then I see message "Tweet saved"
-
```
### :clap: Doing It Right Example: Visualizing our components, their various states and inputs using Storybook
@@ -1638,7 +1800,7 @@ it.skip("Test name", () => {// *error:no-skipped-tests, error:error:no-global-te
expect("somevalue"); // error:no-assert
});
-it("Test name", () => {*//error:no-identical-title. Assign unique titles to tests
+it("Test name", () => {// *error:no-identical-title. Assign unique titles to tests
});
```
@@ -1688,14 +1850,14 @@ Practically, some CI vendors (Example: [CircleCI local CLI](https://circleci.com
### :clap: Doing It Right Example: npm scripts that perform code quality inspection, all are run in parallel on demand or when a developer is trying to push new code
-```javascript
-"scripts": {
+```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": {
@@ -1703,8 +1865,8 @@ Practically, some CI vendors (Example: [CircleCI local CLI](https://circleci.com
"precommit": "npm run inspect:all",
"prepush": "npm run inspect:all"
}
+ }
}
-
```
@@ -1768,13 +1930,12 @@ The huge Kubernetes ecosystem is yet to formalize a standard convenient tool for
### :clap: Doing It Right Example:
-```javascript
-//install license-checker in your CI environment or also locally
+```shell
+# 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
+# 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
-
```
@@ -1920,55 +2081,76 @@ Thanks goes to these wonderful people who have contributed to this repository!