diff --git a/.all-contributorsrc b/.all-contributorsrc
new file mode 100644
index 00000000..db0972e7
--- /dev/null
+++ b/.all-contributorsrc
@@ -0,0 +1,487 @@
+{
+ "files": [
+ "readme.md"
+ ],
+ "imageSize": 100,
+ "contributorsPerLine": 7,
+ "badgeTemplate": "[](#contributors)",
+ "skipCi": true,
+ "contributors": [
+ {
+ "login": "stdavis",
+ "name": "Scott Davis",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/1326248?v=4",
+ "profile": "http://geospatialscott.blogspot.com/",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "AdrienRedon",
+ "name": "Adrien REDON",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/5978436?v=4",
+ "profile": "https://github.com/AdrienRedon",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "NoriSte",
+ "name": "Stefano Magni",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/173663?v=4",
+ "profile": "https://twitter.com/NoriSte",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "yjoer",
+ "name": "Yeoh Joer",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/47742486?v=4",
+ "profile": "https://www.joer.im",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "jhonnymoreira",
+ "name": "Jhonny Moreira",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/2177742?v=4",
+ "profile": "http://jhonnymoreira.dev",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "Germanika",
+ "name": "Ian Germann",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/8846678?v=4",
+ "profile": "https://github.com/Germanika",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "AbdelrahmanHafez",
+ "name": "Hafez",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/19984935?v=4",
+ "profile": "https://github.com/AbdelrahmanHafez",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "ruxandrafed",
+ "name": "Ruxandra Fediuc",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/11021586?v=4",
+ "profile": "http://www.ruxandrafediuc.com",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "jacklee814",
+ "name": "Jack",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/9951291?v=4",
+ "profile": "https://github.com/jacklee814",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "aloyr",
+ "name": "Peter Carrero",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/231727?v=4",
+ "profile": "https://www.petercarrero.com",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "huhgawz",
+ "name": "Huhgawz",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/369338?v=4",
+ "profile": "https://github.com/huhgawz",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "haakonmb",
+ "name": "Haakon Borch",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/7099302?v=4",
+ "profile": "https://github.com/haakonmb",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "jaimemendozadev",
+ "name": "Jaime Mendoza",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/5395811?v=4",
+ "profile": "https://jaimemendoza.com/",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "camerondunford",
+ "name": "Cameron Dunford",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/840612?v=4",
+ "profile": "https://github.com/camerondunford",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "shadowspawn",
+ "name": "John Gee",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/15719847?v=4",
+ "profile": "https://github.com/shadowspawn",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "aurelijusrozenas",
+ "name": "Aurelijus Rožėnas",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/3273544?v=4",
+ "profile": "https://github.com/aurelijusrozenas",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "aaronshivers",
+ "name": "Aaron",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/42848750?v=4",
+ "profile": "http://aaronshivers.com",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "tomanagle",
+ "name": "Tom Nagle",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/8683577?v=4",
+ "profile": "https://tomdoes.tech/",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "yvesyao",
+ "name": "Yves yao",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/7723729?v=4",
+ "profile": "https://github.com/yvesyao",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "Userbit",
+ "name": "Userbit",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/34487074?v=4",
+ "profile": "https://github.com/Userbit",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "glaucia86",
+ "name": "Glaucia Lemos",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/1631477?v=4",
+ "profile": "https://glaucialemos.netlify.com/",
+ "contributions": [
+ "maintenance"
+ ]
+ },
+ {
+ "login": "koooge",
+ "name": "koooge",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/7419215?v=4",
+ "profile": "https://twitter.com/koooge",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "mbiesiad",
+ "name": "Michal",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/18367606?v=4",
+ "profile": "https://twitter.com/michalbiesiada",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "roywalker",
+ "name": "roywalker",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/611846?v=4",
+ "profile": "http://roywalker.me",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "dangen-effy",
+ "name": "dangen",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/23185799?v=4",
+ "profile": "https://dangen-effy.github.io/",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "biesiadamich",
+ "name": "biesiadamich",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/60202305?v=4",
+ "profile": "https://dev.to/mbiesiad",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "cncolder",
+ "name": "Yanlin Jiang",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/127009?v=4",
+ "profile": "https://tarojsx.github.io",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "sanguino",
+ "name": "sanguino",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/2077168?v=4",
+ "profile": "https://github.com/sanguino",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "MorganGeek",
+ "name": "Morgan",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/3721240?v=4",
+ "profile": "https://github.com/MorganGeek",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "lukasbischof",
+ "name": "Lukas Bischof",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/8350985?v=4",
+ "profile": "https://luk4s.dev",
+ "contributions": [
+ "test",
+ "content"
+ ]
+ },
+ {
+ "login": "JuanMaRuiz",
+ "name": "JuanMa Ruiz",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/1837650?v=4",
+ "profile": "https://juanmaruiz.surge.sh",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "luisangelorjr",
+ "name": "Luís Ângelo Rodrigues Jr.",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/22268900?v=4",
+ "profile": "https://luisangelorjr.com.br",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "jfernandezpe",
+ "name": "José Fernández",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/12046620?v=4",
+ "profile": "https://jfernandezpe.wordpress.com/",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "AlejandroGutierrezB",
+ "name": "Alejandro Gutierrez Barcenilla",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/56408597?v=4",
+ "profile": "http://www.linkedin.com/in/AlejandroGutierrezB",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "jasonandmonte",
+ "name": "Jason",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/30088000?v=4",
+ "profile": "https://github.com/jasonandmonte",
+ "contributions": [
+ "content"
+ ]
+ },
+ {
+ "login": "otavionetoca",
+ "name": "Otavio Araujo",
+ "avatar_url": "https://avatars.githubusercontent.com/u/11263232?v=4",
+ "profile": "https://github.com/otavionetoca",
+ "contributions": [
+ "test",
+ "content"
+ ]
+ },
+ {
+ "login": "contributorpw",
+ "name": "Alex Ivanov",
+ "avatar_url": "https://avatars.githubusercontent.com/u/5027939?v=4",
+ "profile": "https://contributor.pw",
+ "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",
+ "commitConvention": "angular"
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..e43b0f98
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+.DS_Store
diff --git a/.operations/questions-answers.md b/.operations/questions-answers.md
new file mode 100644
index 00000000..2f71c638
--- /dev/null
+++ b/.operations/questions-answers.md
@@ -0,0 +1,19 @@
+# Common questions and answers
+
+## Q: How do I start a new translation?
+
+**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
+
+Before you start with this, I've prepared some basic workflow guidelines:
+
+**Where to do the translation?** - Fork and work your own copy, create a readme-{language}.md file (e.g. readme-fr.md) and do the translation work over there
+
+**How to push changes?** - I will create a dedicated branch for you translations-{language}-staging (e.g. translations-fr-staging), whenever you want to save some changes or share with the team - just PR to this branch
+
+**How & when to publish to master?** - The content can be published once it's 70% translated and 100% language proofed. Kindly run it through spell checker. Whenever you feel that the content stands to these guidelines, just a raise a flag and I'll merge the language branch into the master
+
+**Will I get credit for the translation work?** - Obviously! Your name will appear nearby the language flag in the main readme.md, added to the repo team, appear boldly at the top of the translation page - 'Translated, adapted and reviewed by {Your name}'. We will also publish a medium article with the translation with your name at the top
+
+Looking forward and excited to work on this!
diff --git a/assets/flat-report.png b/assets/flat-report.png
new file mode 100644
index 00000000..67e6e156
Binary files /dev/null and b/assets/flat-report.png differ
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/headspace.png b/assets/headspace.png
index 324d8204..83ed85d7 100644
Binary files a/assets/headspace.png and b/assets/headspace.png differ
diff --git a/assets/hierarchical-report.png b/assets/hierarchical-report.png
new file mode 100644
index 00000000..72ece98c
Binary files /dev/null and b/assets/hierarchical-report.png differ
diff --git a/assets/uml.png b/assets/uml.png
new file mode 100644
index 00000000..088a9bd4
Binary files /dev/null and b/assets/uml.png 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/assets/~$header.pptx b/assets/~$header.pptx
deleted file mode 100644
index 1a939757..00000000
Binary files a/assets/~$header.pptx and /dev/null differ
diff --git a/index.js b/index.js
new file mode 100644
index 00000000..e3c1c215
--- /dev/null
+++ b/index.js
@@ -0,0 +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
new file mode 100644
index 00000000..daf57f48
--- /dev/null
+++ b/package.json
@@ -0,0 +1,33 @@
+{
+ "name": "javascript-testing-best-practices",
+ "version": "2.0.0",
+ "description": "📗🌐 🚢 Comprehensive and exhaustive JavaScript & Node.js testing best practices (April 2020) https://testjavascript.com/",
+ "main": "index.js",
+ "scripts": {
+ "test": "t"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/i0natan/javascript-testing-best-practices.git"
+ },
+ "keywords": [
+ "test",
+ "tdd",
+ "javascript",
+ "mocha",
+ "jest",
+ "aaa",
+ "coverage",
+ "confidence",
+ "unit",
+ "test",
+ "integration",
+ "e2e"
+ ],
+ "author": "Yoni Goldberg",
+ "license": "ISC",
+ "bugs": {
+ "url": "https://github.com/i0natan/javascript-testing-best-practices/issues"
+ },
+ "homepage": "https://github.com/i0natan/javascript-testing-best-practices#readme"
+}
diff --git a/readme-es.md b/readme-es.md
new file mode 100644
index 00000000..a2b4ebda
--- /dev/null
+++ b/readme-es.md
@@ -0,0 +1,1991 @@
+
+
+
+
+# 👇 Por qué esta guía puede hacerte llevar tus habilidades de testing al siguiente nivel
+
+
+
+## 📗 46+ buenas practicas: Súper comprensiva y exhaustiva
+
+Esta es una guía completa para JavaScript y Node.js de la A a la Z. Resume y selecciona docenas de los mejores post de blogs, libros, y herramientas ofrecidas en el mercado
+
+## 🚢 Avanzado: Va 10.000 kilómetros más allá de lo básico
+
+Súbete a un viaje que va más allá de lo básico, llegando a temas avanzados como testeando en producción, mutation testing, property-based testing y muchas otras herramientas estratégicas y profesionales. Si lees esta guía completamente es probable que tus habilidades de testing acaben por encima de la media
+
+## 🌐 Full-stack: front, backend, CI, cualquier cosa
+
+Empieza por comprender las técnicas de testing ubicuas que son la base de cualquier nivel de aplicación. Luego, profundiza en tu área de elección: frontend/UI, backend, CI o tal vez todos
+
+
+
+### Escrita por Yoni Goldberg
+
+- Consultor JavaScript & Node.js
+- 📗 [Testing Node.js & JavaScript de la A a la Z](https://www.testjavascript.com) - Mi curso completamente online con más de [10 horas de video](https://www.testjavascript.com), 14 tipos de test y más de 40 buenas practicas
+- [Sígueme en Twitter ](https://twitter.com/goldbergyoni/)
+
+
+
+### Traducciones - leelas en tu propio idioma
+
+- 🇨🇳[Chino](readme-zh-CN.md) - cortesía de [Yves yao](https://github.com/yvesyao)
+- 🇰🇷[Coreano](readme.kr.md) - cortesía de [Rain Byun](https://github.com/ragubyun)
+- 🇵🇱[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 💜
+
+
+
+## `Tabla de Contenidos`
+
+#### [`Sección 0: La Regla de Oro`](#sección-0️⃣-la-regla-de-oro)
+
+Un solo consejo que inspira a todos los demás (1 apartado especial)
+
+#### [`Sección 1: La Anatomía de un Test`](#sección-1-la-anatomía-de-un-test-1)
+
+La base - estructurando test claros (12 apartados)
+
+#### [`Sección 2: Backend`](#sección-2️⃣-backend-testing)
+
+Escribiendo test de backend y microservicios eficientemente (8 apartados)
+
+#### [`Sección 3: Frontend`](#sección-3️⃣-frontend-testing)
+
+Escribiendo test para web UI incluyendo test de componente y E2E (11 apartados)
+
+#### [`Sección 4: Midiendo la Efectividad de los Test`](#sección-4️⃣-midiendo-la-efectividad-de-los-test)
+
+Vigilando al vigilante - midiendo la calidad de los test (4 apartados)
+
+#### [`Sección 5: Integración Continua`](#sección-5️⃣-ci-y-otras-medidas-de-calidad)
+
+Pautas para Integración Continua en el mundo de JS (9 apartados)
+
+
+
+# Sección 0️⃣: La Regla de Oro
+
+
+
+## ⚪️ 0 La Regla de Oro: Diseñando testing ligero
+
+:white_check_mark: **Haz:**
+El código de los test no es como el código de producción - diséñalo para que sea simple, corto, sin abstracciones, plano, agradable de trabajar, ligero. Uno debe mirar un test y entender la intención al instante
+
+Nuestras mentes están llenas por el código de producción, no tenemos espacio de cabeza para añadir más cosas complejas. Si intentamos que introducir otro código desafiante en nuestro cerebro, ralentizará todo el equipo, lo que va en contra de la razón por la que hacemos testing. Prácticamente esta una de las razones por la que muchos equipos simplemente abandonan el testing
+
+Los test son una oportunidad de tener algo más: un asistente amable y sonriente, con el que es un placer trabajar y da mucho valor a cambio de una inversión muy pequeña. La ciencia nos dice que tenemos dos sistemas cerebrales: el sistema 1 se usa para actividades sin esfuerzo como conducir un automóvil en una carretera vacía y el sistema 2, que está destinado a operaciones complejas y conscientes como resolver una ecuación matemática. Diseña tus test para el sistema 1, cuando observes el código de un test, debería parecer tan fácil como modificar un documento HTML y no como resolver 2X(17 × 24)
+
+Esto se puede lograr mediante la selección de técnicas cuidadosamente, herramientas y objetivos de test que son rentables y proporcionan un gran retorno de la inversión. Testea solo lo que sea necesario, esfuérzate por mantenerlo ágil, a veces incluso vale la pena abandonar algunos test y cambiar la confiabilidad por agilidad y simplicidad
+
+
+
+La mayoría de los siguientes consejos son derivados de este principio
+
+### ¿Listo para empezar?
+
+
+
+# Sección 1: La Anatomía de un Test
+
+
+
+## ⚪ ️ 1.1 Incluye 3 partes en los nombres de tus test
+
+:white_check_mark: **Haz:** El reporte de un test debe indicar si la revisión de la aplicación actual cumple los requisitos para las personas que no están necesariamente familiarizadas con el código: el tester, el DevOps que está desplegángolo y el futuro tú de dentro de dos años. Esto se puede lograr si los test hablan al nivel de los requisitos e incluyen 3 partes:
+
+(1) ¿Qué se está testeando? Por ejemplo, el método ProductsService.addNewProduct
+
+(2) ¿Bajo qué escenario y circunstancias? Por ejemplo, no se pasa ningún precio al método
+
+(3) ¿Cuál es el resultado esperado? Por ejemplo, el nuevo producto no está aprobado
+
+
+
+❌ **De lo contrario:** Un despliegue falla, un test llamado "Agregar producto" ha fallado. ¿Esto te dice exactamente qué está funcionando mal?
+
+
+
+**👇 Nota:** Cada apartado tiene ejemplos de código y, en ocasiones, también una imagen ilustrativa. Haga clic para ampliar
+
+
+✏ Código de Ejemplo
+
+
+
+### :clap: Ejemplo de cómo hacerlo correctamente: Un nombre de test que consta de 3 partes
+
+
+
+```javascript
+//1. unidad que esta siendo testeada
+describe('Products Service', function() {
+ describe('Add new product', function() {
+ //2. escenario y 3. quá se espera
+ 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: Ejemplo de cómo hacerlo correctamente: Un nombre de test que consta de 3 partes
+
+
+
+
+
+
+© Créditos y más información
+ 1. Roy Osherove - Naming standards for unit tests
+
+
+
+
+## ⚪ ️ 1.2 Estructura tus test con el patron AAA
+
+:white_check_mark: **Haz:** Estructura tus test con 3 secciones bien separadas Ajustar, Actuar y Afirmar (AAA en inglés Arrange, Act & Assert). Seguir esta estructura garantiza que quien lea nuestro test no se estruje el cerebro en comprender los test:
+
+1a A - Ajustar: configura el código, crea el escenario que el test pretende simular. Esto podría incluir crear instancias de la unidad bajo el constructor del test, agregar registros de base de datos, mocks y stubs de objetos y cualquier otro código necesario
+
+2a A - Actuar: Ejecuta la unidad en test. Normalmente 1 línea de código
+
+3a A - Afirmar: Comprobar que el valor recibido satisface las expectativas. Normalmente 1 línea de código
+
+
+
+❌ **De lo contrario:** No solo emplearas horas comprendiendo el código principal, si no que lo que debería haber sido la parte más simple del día (testing) te ha estrujado el cerebro
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :clap: Ejemplo de cómo hacerlo correctamente: Un test estructurado con el patron AAA
+
+ 
+
+```javascript
+describe("Customer classifier", () => {
+ test("When customer spent more than 500$, should be classified as premium", () => {
+ //Ajustar
+ const customerToClassify = { spent: 505, joined: new Date(), id: 1 };
+ const DBStub = sinon.stub(dataAccess, "getCustomer").reply({ id: 1, classification: "regular" });
+
+ //Actuar
+ const receivedClassification = customerClassifier.classifyCustomer(customerToClassify);
+
+ //Afirmar
+ expect(receivedClassification).toMatch("premium");
+ });
+});
+```
+
+
+
+### :thumbsdown: Ejemplo Anti Patrón: Sin separación, un bloque, más dificil de interpretar
+
+```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 Describe las expectativas en el lenguaje del producto: usa aserciones estilo BDD
+
+:white_check_mark: **Haz:** Escribir el código de tus test de forma declarativa permite que aquel que lo lea tenga al instante la idea sin tener que estrujarse el cerebro. Cuando escribes el código de los test de forma imperativa estará lleno de condiciones lógicas y obligas al que lo lee a gastar mucho más tiempo en comprenderlo. En este caso, escribe el código de la forma más humana, de forma declarativa con BDD, usando `expect` o `should` y sin usar código personalizado. Si Chai o Jest no incluyen las aserciones que deseas y se hace muy repetitivo, siempre puedes [extender Jest con Jest matcher](https://jestjs.io/docs/en/expect#expectextendmatchers) o escribir un [plugin de Chai](https://www.chaijs.com/guide/plugins/)
+
+
+❌ **De lo contrario:** El equipo escribirá menos test y acabará marcando los test más molestos con .skip()
+
+
+
+✏ Código de Ejemplo
+
+ 
+
+### :thumbsdown: Ejemplo Anti Patrón: quien lea nuestro test deberá enfrentarse a un código largo e imperativo para conocer la historia del test
+
+```javascript
+test("When asking for an admin, ensure only ordered admins in results", () => {
+ //presuponiendo que hayamos agregado aquí dos administradores "admin1", "admin2" y "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: Ejemplo de cómo hacerlo correctamente: Comprender el siguiente test declarativo es muy sencillo
+
+```javascript
+it("When asking for an admin, ensure only ordered admins in results", () => {
+ //presuponiendo que hayamos agregado aquí dos administradores
+ const allAdmins = getUsers({ adminOnly: true });
+
+ expect(allAdmins)
+ .to.include.ordered.members(["admin1", "admin2"])
+ .but.not.include.ordered.members(["user1"]);
+});
+```
+
+
+
+
+
+## ⚪ ️ 1.4 Acercarse al testing caja-negra: Testea solo métodos públicos
+
+:white_check_mark: **Haz:** Testear las partes internas suele traer un gasto extra enorme para obtener muy poco beneficio. Si tu código/API comprueba todos los resultado posibles correctamente, ¿deberías perder las próximas 3 horas en comprobar como esta funcionando internamente y después mantener esos test tan frágiles? Cada vez que se comprueba un comportamiento público, la implementación privada es implícitamente testeada y tus test se romperán sólo si hay un problema concreto (por ejemplo una salida incorrecta). Este enfoque también es conocido como `behavioral testing` (testing de comportamiento). Por otro lado, si se testean las partes internas (caja-blanca) tu enfoque cambia de planificar la salida del componente a detalles minúsculos, y tus test pueden romperse debido a refactors de código menores sin que se rompan los test de salida, por lo que aumenta tremendamente el mantenimiento de los mismos
+
+❌ **De lo contrario:** Tus test se comportaran como la fabula de [que viene el lobo](https://es.wikipedia.org/wiki/El_pastor_mentiroso): gritando falsos positivos (por ejemplo, un test falla porque se cambio el nombre a una variable probada). Como es de esperar, la gente empezara a ignorar estos test hasta que un día ignoren un test de verdad...
+
+
+✏ Código de Ejemplo
+
+
+
+### :thumbsdown: Ejemplo Anti Patrón: Un test esta testeando la parte interna sin ninguna razón aparente
+
+
+
+```javascript
+class ProductService {
+ //este método es usado solo internamente
+ //Cambiarle el nombre causara que el test falle
+ calculateVATAdd(priceWithoutVAT) {
+ return { finalPrice: priceWithoutVAT * 1.2 };
+ //Cambiar el formatos de salida o nombre de la clave a continuación hará que el test falle
+ }
+ //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 () => {
+ //No hay ningún requisito para permitir a los usuarios calcular el IVA, solo existe el de mostrar el precio final al usuario. Sin embargo, falsamente insistimos en testear las partes privadas de la clase
+ expect(new ProductService().calculateVATAdd(0).finalPrice).to.equal(0);
+});
+```
+
+
+
+
+
+## ⚪ ️ ️1.5 Eligiendo los dobles de los test correctamente: Evita mocks en favor de stubs y spies
+
+:white_check_mark: **Haz:** Los dobles de los test son un mal necesario porque están acoplados a las tripas de la aplicación, sin embargo, algunos proporcionan un valor inmenso ([Aquí tienes un bueno recordatorio de que son los dobles de los test: mocks vs stubs vs spies](https://martinfowler.com/articles/mocksArentStubs.html))
+
+Antes de usar un doble, hazte una simple pregunta: ¿Lo voy a usar para testear una funcionalidad que aparece, o puede aparecer, en el documento de requisitos? Si no, eso huele que estas testeando partes privadas
+
+Por ejemplo, si quieres testear que tu app se comporta razonablemente cuando el servicio de pagos está caído, deberías hacer un stub del servicio de pagos y devolver un 'Sin respuesta' para asegurar que la unidad que está siendo testeada devuelve el valor correcto. Esto verifica que el comportamiento/respuesta/resultado de nuestra app en ciertos escenarios. También podrías usar un spy para asegurar que un email ha sido enviado cuando este servicio está caído — esto es nuevamente una verificación de comportamiento que probablemente aparezca en el documento de requisitos ("Enviar un correo electrónico si no se pudo guardar el pago"). En el lado opuesto, si se mockea el servicio de pagos y se asegura que se haya llamado con el tipado correcto — entonces tu test esta comprobando cosas internas que no tienen nada que ver con la funcionalidad de la app y es muy probable que cambien con frecuencia
+
+
+❌ **De lo contrario:** Cualquier refactor de código te exigirá buscar todos los mocks que tengas y tendrás que actualizarlos en consecuencia. Los test pasan de ser un amigo útil a una carga más
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :thumbsdown: Ejemplo Anti Patrón: Mocks centrados en la parte interna
+
+
+
+```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 () => {
+ //Asumimos que ya hemos añadido un producto
+ const dataAccessMock = sinon.mock(DAL);
+ //hmmm MAL: testear las partes internas está siendo nuestro principal objetivo, en vez de ser un efecto secundario
+ dataAccessMock
+ .expects("deleteProduct")
+ .once()
+ .withArgs(DBConfig, theProductWeJustAdded, true, false);
+ new ProductService().deletePrice(theProductWeJustAdded);
+ dataAccessMock.verify();
+});
+```
+
+
+
+### :clap: Ejemplo de cómo hacerlo correctamente: los spies se centran en testear los requisitos pero como efecto secundario inevitable se están testeando las partes internas
+
+```javascript
+it("When a valid product is about to be deleted, ensure an email is sent", async () => {
+ //Asumimos que ya hemos añadido un producto
+ const spy = sinon.spy(Emailer.prototype, "sendEmail");
+ new ProductService().deletePrice(theProductWeJustAdded);
+ //hmmm OK: ¿Testeamos las partes internas? Si, pero como efecto secundario de hacer test de los requisitos (enviar un email)
+ expect(spy.calledOnce).to.be.true;
+});
+```
+
+
+
+
+
+## 📗 ¿Quieres aprender todas esto con video en directo?
+
+### Visita el curso online [Testing Node.js & JavaScript From A To Z](https://www.testjavascript.com)
+
+
+
+## ⚪ ️1.6 No uses “foo”, usa datos realistas
+
+:white_check_mark: **Haz:** A menudo, los bugs de producción se revelan bajo una entrada muy específica y sorprendente: cuanto más realista sea la entrada de un test, mayores serán las posibilidades de detectar bugs temprano. Utiliza librerías dedicadas como [Faker] (https://www.npmjs.com/package/faker) para generar datos pseudo-reales que se asemejan en variedad y forma a los datos de producción. Por ejemplo, dichas librerías pueden generar números de teléfono realistas, nombres de usuario, tarjetas de crédito, nombres de empresas e incluso texto "lorem ipsum". También puedes crear algunos test (además de los test unitarios, no como un reemplazo) que aleatorizan los datos falsos para forzar la unidad que estamos testeando o incluso importar datos reales de su entorno de producción. ¿Quieres llevarlo al siguiente nivel? Ve la próxima sección (test basados en propiedades)
+
+
+❌ **De lo contrario:** Todo tus test de desarrollo estarán en verde falsamente cuando uses datos sintéticos como "Foo", pero luego en producción pueden ponerse en rojo cuando un hacker use cadenas extrañas como “@3e2ddsf . ##’ 1 fdsfds . fds432 AAAA”
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :thumbsdown: Ejemplo Anti Patrón: Un conjunto de test que dan ok debido a datos no realistas
+
+
+
+```javascript
+const addProduct = (name, price) => {
+ const productNameRegexNoSpace = /^\S*$/; //no se admiten espacios
+
+ if (!productNameRegexNoSpace.test(name)) return false; //esta rama nunca se testeara debido a inputs sintéticos
+
+ //algo de lógica aquí
+ return true;
+};
+
+test("Wrong: When adding new product with valid properties, get successful confirmation", async () => {
+ //La cadena "Foo" que es usada en todo los test, nunca provocará un resultado false
+ const addProductResult = addProduct("Foo", 5);
+ expect(addProductResult).toBe(true);
+ //Falso positivo: la operación tuvo éxito porque nunca lo intentamos con un
+ //nombre de producto largo que incluya espacios
+});
+```
+
+
+
+### :clap:Ejemplo de cómo hacerlo correctamente: Generando datos de entrada realistas
+
+```javascript
+it("Better: When adding new valid product, get successful confirmation", async () => {
+ const addProductResult = addProduct(faker.commerce.productName(), faker.random.number());
+ //Datos de entrada generados aleatoriamente: {'Sleek Cotton Computer', 85481}
+ expect(addProductResult).to.be.true;
+ //El test falla, El valor de entrada random ha provocado que se vaya por un camino que nunca planeamos
+ //!Hemos descubierto un bug muy pronto!
+});
+```
+
+
+
+
+
+## ⚪ ️ 1.7 Testea muchas combinaciones de entrada utilizando test basados en propiedades
+
+:white_check_mark: **Haz:** Por lo general elegimos pocos datos de entrada por cada test. Incluso cuando el formato de entrada se parece a datos reales (ver sección "no uses foo"), cubrimos solo unas pocas combinaciones de datos de entrada (method(‘’, true, 1), method(“string” , false” , 0)), Sin embargo, en producción, un API que es llamada con 5 parámetros puede ser invocada por miles de combinaciones diferentes, una sola puede hacer que nuestro proceso falle ([ver Fuzz Testing](https://en.wikipedia.org/wiki/Fuzzing)). ¿Qué tal si pudieras escribir un solo test que envíe 1000 combinaciones de diferentes entradas automáticamente y capture qué entrada hace que código no devuelva la respuesta correcta? Los test basados en propiedades son una técnica que hace exactamente eso: al enviar todas las combinaciones de entrada posibles a la unidad que está siendo testada, aumenta la probabilidad de encontrar un bug. Por ejemplo, dado un método — addNewProduct(id, name, isDiscount) — las librerías compatibles llamaran a ese método con muchas combinaciones (números, textos y booleanos) como (1, “iPhone”, false), (2, “Galaxy”, true). Puedes ejecutar test basados en propiedades usando tu librería de test favorita (Mocha, Jest, etc) como [js-verify](https://github.com/jsverify/jsverify) o [testcheck](https://github.com/leebyron/testcheck-js) (mucho mejor documentada). Actualizado: Nicolas Dubien sugiere en los comentarios [checkout fast-check](https://github.com/dubzzz/fast-check#readme) que parece ofrecer características adicionales y es activamente mantenida
+
+
+❌ **De lo contrario:** Inconscientemente, eliges los datos de entrada para tus test que cubren solo las ramas de código que funcionan bien. Desafortunadamente, esto disminuye la eficiencia de los test como vehículo para detectar bugs
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :clap: Ejemplo de cómo hacerlo correctamente: Testear muchos datos de entrada permutados con “fast-check”
+
+
+
+```javascript
+import fc from "fast-check";
+
+describe("Product service", () => {
+ describe("Adding new", () => {
+ //esto ejecutara 100 veces con diferentes propiedades al azar
+ 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 lo necesitas, usa solo snapshots cortos y en el propio test
+
+:white_check_mark: **Haz:** Cuando hay necesidad de usar [snapshot testing](https://jestjs.io/docs/es-ES/snapshot-testing), usa solo snapshots cortos y bien enfocados (por ejemplo 3-7 lineas) y que estén incluidos en el propio test ([Inline Snapshot](https://jestjs.io/docs/es-ES/snapshot-testing#inline-snapshots)) y no como ficheros externos. Mantener esta dirección te garantiza que tus test se explican por si mismos y a la vez que sean menos frágiles
+
+Por otro lado, los tutoriales y herramientas basados en ‘classic snapshots’ tienden a guardar ficheros muy grandes en medios externos (por ejemplo component rendering markup, API JSON result) cada vez que se ejecutan los test para comparar los resultados recibidos con la versión guardada. Esto, por ejemplo, puede asociar nuestro test a 1000 lineas con 3000 valores de datos que quien este escribiendo test jamas leerá ni razonará. ¿Por qué está mal esto? Al hacerlo, hay 1000 razones para que tu test falle - tan solo el cambio de una linea de código es suficiente para que el snapshoot se invalide y es muy probable que esto ocurra a menudo. ¿Como de frecuente? cada espacio, comentario o pequeño cambio de css/html. Y no solo eso, el nombre del test no nos va a dar ni una sola pista de que está fallando, solo verifica que esas 1000 lineas han cambiado. Además obliga a quien escribe los test a asumir como correcto un fichero enorme que no ha podido inspeccionar y corroborar. Todo estos son síntomas de un test oscuro que no está bien enfocado y trata de cubrir demasiadas cosas a la vez
+
+Vale la pena señalar que hay algunos casos en los que los snapshoots grandes y externos son buenos - cuando comprobamos el esquema y no los datos (ignorando los valores y centrándonos en los campos) o en los casos en el que el documento no va a cambiar apenas en el tiempo
+
+
+❌ **De lo contrario:** Un test UI falla. El código parece correcto, la pantalla esta pintando todos los pixels correctamente, ¿que ha pasado? tu test de snapshoot ha encontrado una diferencia entre el origen y lo que ha recibido al ejecutarse: simplemente hay un espacio añadido en cualquier parte...
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :thumbsdown: Ejemplo Anti Patrón: Acoplando nuestro test a un fichero no revisado de 2000 lineas de código
+
+
+
+```javascript
+it("TestJavaScript.com is renderd correctly", () => {
+ //Ajustar
+
+ //Actuar
+ const receivedPage = renderer
+ .create( Test JavaScript )
+ .toJSON();
+
+ //Afirmar
+ expect(receivedPage).toMatchSnapshot();
+ //Ahora nosotros implícitamente mantenemos un fichero de 2000 lineas
+ //cada salto de linea o comentario añadido van a romper nuestro test
+});
+```
+
+
+
+### :clap: Ejemplo de cómo hacerlo correctamente: Lo esperado es visible y esta enfocado
+
+```javascript
+it("When visiting TestJavaScript.com home page, a menu is displayed", () => {
+ //Ajustar
+
+ //Actuar
+ const receivedPage = renderer
+ .create( Test JavaScript )
+ .toJSON();
+
+ //Afirmar
+
+ const menu = receivedPage.content.menu;
+ expect(menu).toMatchInlineSnapshot(`
+
+- Home
+- About
+- Contact
+
+`);
+});
+```
+
+
+
+
+
+## ⚪ ️1.9 Evitar fixtures globales y seeds, añade datos por cada test
+
+:white_check_mark: **Haz:** Siguiendo la regla de oro (sección 0), cada test debe añadir y actuar en su propio conjunto de filas en base de datos para evitar el acoplamiento y poder explicar fácilmente el flujo del test. En realidad muchos testers se saltan esta regla al añadir datos a la DB solo una vez antes de ejecutar los test ([también conocido como ‘test fixture’](https://en.wikipedia.org/wiki/Test_fixture)) a favor de mejorar el rendimiento. Si bien el rendimiento es una preocupación válida — puede mitigarse de otras formas (consulte la sección "test de componentes"), sin embargo, la complejidad del test es más dolorosa que otras consideraciones la mayoría de las veces. De manera practica, haz que cada test añada explícitamente los registros que necesitas y actúe sobre ellos. Si el rendimiento se convierte en algo critico — se puede llegar al compromiso de utilizar los mismos datos en un conjunto de test siempre que no se muten los datos (por ejemplo en queries)
+
+
+
+❌ **De lo contrario:** Muchos test fallaran, un despliegue se aborta, nuestro equipo perderá mucho tiempo, ¿tenemos un bug? vamos a investigar, ah no — parece que dos test están mutando el mismo dato
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :thumbsdown: Ejemplo Anti Patrón: los test no son independientes y dependen de una inserción global de datos en la base de datos
+
+
+
+```javascript
+before(async () => {
+ //añadiendo datos de sites y admin a nuestra base de datos. ¿Donde están los datos? fuera. En un json externo o en un modelo da migración
+ await DB.AddSeedDataFromJson('seed.json');
+});
+it("When updating site name, get successful confirmation", async () => {
+ //Se que el nombre de site "portal" existe, — lo he visto en seed.json
+ 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 () => {
+ //Se que el nombre de site "portal" existe, — lo he visto en seed.json
+ const siteToCheck = await SiteService.getSiteByName("Portal");
+ expect(siteToCheck.name).to.be.equal("Portal"); //Fallo! El test anterior ha cambiado el nombre :[
+});
+
+```
+
+
+
+### :clap: Ejemplo de cómo hacerlo correctamente: Podemos permanecer dentro de nuestro test, cada test actúa sobre su propio conjunto de datos
+
+```javascript
+it("When updating site name, get successful confirmation", async () => {
+ //el test esta añadiendo registors nuevos cada vez y actuando solo en esos registros
+ const siteUnderTest = await SiteService.addSite({
+ name: "siteForUpdateTest"
+ });
+
+ const updateNameResult = await SiteService.changeName(siteUnderTest, "newName");
+
+ expect(updateNameResult).to.be(true);
+});
+```
+
+
+
+
+
+## ⚪ ️ 1.10 No captures errores, esperalos
+
+:white_check_mark: **Haz:** Cuando queremos comprobar que una entrada lanza un error, nos puede parecer correcto usar try-catch-finally y asertar que se entra por el catch. El resultado es un test incomodo y verboso (ejemplo a continuación) que nos oculta la intención de un test muy simple y las expectativas del resultado
+
+Una alternativa más elegante seria usar solo la aserción de una sola linea que tiene Chai: expect(method).to.throw (o en Jest: expect(method).toThrow()). Es totalmente obligatorio también asegurarse de que la excepción contenga una propiedad que indique el tipo de error, de lo contrario, lanzando solo un error genérico, la app no podrá hacer mucho más que mostrarle un error decepcionante para el usuario
+
+
+
+❌ **De lo contrario:** Sería muy difícil deducir a partir de los reportes de test (por ejemplo, reporte de CI) que es lo que ha salido mal
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :thumbsdown: Ejemplo Anti Patrón: Un test largo que trata de asertar la existencia de un error con 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;
+ //si esta aserción falla, el resultado/reporte del test solo mostrará
+ //que algunos valores son null, no se mostrara que falta una excepción
+});
+```
+
+
+
+### :clap: Ejemplo de cómo hacerlo correctamente: Una afirmación legible para una persona puede ser comprendida fácilmente, tanto por el QA como por el 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 Etiqueta tus test
+
+:white_check_mark: **Haz:** Deben ejecutarse diferentes test en diferentes escenarios: quick smoke, IO-less, los test deben ejecutarse cuando un desarrollador guarda o hace commit de un fichero, los test end-to-end suelen ejecutarse cuando un nuevo pull request es añadido, etc. Esto se puede lograr etiquetando los test con tags como #cold #api #sanity para que se pueda filtrar e invocar solo el subconjunto deseado. Por ejemplo, así es como se ejecutan solo el grupo sanity test con Mocha: mocha — grep ‘sanity’
+
+
+
+❌ **De lo contrario:** Ejecutar todos los test, incluidos los test que realizan docenas de queries a base de datos, cada vez que un desarrollador hace un pequeño cambio, puede ser extremadamente lento y provocar que los desarrolladores ignoren correr los test
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :clap: Ejemplo de cómo hacerlo correctamente: Etiquetar los test como ‘#cold-test’ permite que el test runner ejecute solo los test más rápidos (Cold === 'test rápidos' que no están haciendo operaciones de IO y que pueden ser ejecutados con frecuencia incluso mientras el desarrollador está escribiendo código)
+
+
+
+```javascript
+//este test es rápido (sin DB) y lo estamos etiquetando correctamente
+//ahora el usuario/CI puede ejecutarlo con frecuencia
+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() {
+ //lógica aquí
+ });
+ });
+});
+```
+
+
+
+
+
+## ⚪ ️ 1.12 Categoriza los test en al menos 2 niveles
+
+:white_check_mark: **Haz:** Aplica cierta estructura a tu conjunto de test para que un visitante ocasional pueda comprender fácilmente los requisitos (los test siempre son la mejor documentación) y los diversos escenarios que estamos testeando. Una practica común para esto es crear al menos 2 bloques 'describe' antes de tus test: el primero es para el nombre de la unidad que está siendo testeada y el segundo es para un nivel adicional de categorización como el escenario o las categorías personalizadas (ver ejemplos de código y pantallazos más abajo). Hacerlo también mejorará los reportes de los test: quien los lea deducirá fácilmente las categorías de los test, profundizará en aquellas que lo desee y podrá relacionar mejor los test fallidos. Además, será mucho más fácil para un desarrollador navegar a través del código de un conjunto de test amplio. Existen múltiples formas de estructurar tus test que deben ser consideradas, como [given-when-then](https://github.com/searls/jasmine-given) y [RITE](https://github.com/ericelliott/riteway)
+
+
+
+❌ **De lo contrario:** Cuando nos enfrentamos a un reporte con una lista plana de test, tendremos que leer rápidamente textos largos para determinar los escenarios principales y relacionar los test fallidos. Considera el siguiente caso: Cuando 7/100 test fallan, revisar una lista plana te exige leer el texto de los test que fallan para ver como se relacionan entre ellos y que tienen en común. Sin embargo, en un reporte jerarquizado, si los 7 están bajo un mismo flujo o categoría, puedes saber rápidamente cual o donde puede estar la causa raíz del fallo
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :clap: Ejemplo de cómo hacerlo correctamente: Estructurando un conjunto de test con el nombre de la unidad testada y los escenarios nos conducirá al reporte que se muestra a continuación
+
+
+
+```javascript
+// Unidad que se está testeando
+describe("Transfer service", () => {
+ //Escenario
+ describe("When no credit", () => {
+ //Esperado
+ test("Then the response status should decline", () => {});
+
+ //Esperado
+ test("Then it should send email to admin", () => {});
+ });
+});
+```
+
+
+
+
+
+### :thumbsdown: Ejemplo Anti Patrón: Una lista plana de test hará más difícil a quien lo lea el identificar las historias de usuario y detectar los test que están fallando
+
+
+
+```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 Otras buenas practicas genéricas sobre higiene de los test
+
+:white_check_mark: **Haz:** Esta publicación se centra en consejos de test relacionados con, o al menos, que se pueden ejemplificar en Node JS. Sin embargo, esta sección agrupa algunos consejos no relacionados con Node que son bien conocidos
+
+Aprenda y practique [principios TDD](https://www.sm-cloud.com/book-review-test-driven-development-by-example-a-tldr/) — son extremadamente valiosos para muchos pero no te dejes intimidar si no se ajustan a tu estilo, no eres el único. Considera escribir los test antes que el código con el [estilo rojo-verde-refactor](https://blog.cleancoder.com/uncle-bob/2014/12/17/TheCyclesOfTDD.html), te asegura que cada test chequea exactamente una cosa, cuando encuentras un bug — antes de corregirlo escribe un test que lo detecte como bug en el futuro, dejando que cada test falle al menos una vez antes de convertirlo en un verde, comienza el inicio de un modulo escribiendo código muy simple y rápidamente, que satisfaga el test, luego lo refatorizamos gradualmente hasta que nuestro código tenga el nivel deseado en producción, evitando siempre cualquier dependencia con el entorno (rutas en disco, sistema operativo, etc)
+
+
+❌ **De lo contrario:** Echarás de menos las perlas de sabiduría que se han ido recolectando durante décadas
+
+
+
+# Sección 2️⃣: Backend Testing
+
+## ⚪ ️2.1 Enriquece tu abanico de test: mira más allá de los test unitarios y la pirámide
+
+:white_check_mark: **Haz:** La [pirámide de test](https://martinfowler.com/bliki/TestPyramid.html), con 10> años de antigüedad, es un modelo excelente y relevante que sugiere tres tipos de test e influye en la estrategia de testeo de la mayoría de los desarrolladores. Al mismo tiempo, surgieron un puñado de nuevas y brillantes técnicas de testeo que se esconden en las sombras de la pirámide de test. Dados todos los cambios que hemos visto en los últimos 10 años (microservicios, cloud, serverless), ¿es posible que un modelo algo antiguo se adapte a *todos* los tipos de aplicaciones? ¿No debería el mundo del testing considerar aceptar nuevas técnicas?
+
+No me malinterpretes, en 2019 la pirámide de test, el TDD y los test unitarios siguen siendo una técnica buena y probablemente sean la mejor combinación para muchas aplicaciones. Sólo como cualquier otro modelo, a pesar de su utilidad, [a veces debe estar equivocado] (https://en.wikipedia.org/wiki/All_models_are_wrong). Por ejemplo, considera una aplicación IOT que ingiere muchos eventos en un bus de mensajes como Kafka / RabbitMQ, que luego fluyen a algún data-warehouse y finalmente son consultados por alguna UI de análisis. ¿Realmente deberíamos gastar el 50% de nuestro presupuesto para test en escribir test unitarios para una aplicación que esté centrada en la integración y apenas tenga lógica? A medida que aumenta la diversidad de tipos de aplicaciones (bots, criptografía, Alexa-skills), aumentan las posibilidades de encontrar escenarios en los que la pirámide de test no sea la mejor opción
+
+Es hora de enriquecer el abanico de test y familiarizarse con más tipos de test (las siguientes secciones sugieren algunas ideas), modelos como la pirámide de test, pero también hacer coincidir los tipos de test con los problemas del mundo real al que te enfrentas ('Hola, nuestra API está rota, ¡escribamos contract testing dirigidos al consumidor!'), diversifica tus test como un inversor que construye una cartera de inversión basada en el análisis de riesgos — evalúa dónde pueden surgir problemas y combina algunas medidas de prevención para mitigar esos riesgos potenciales
+
+Una advertencia: el TDD en el mundo del software adopta una cara de falsa dicotomía, algunos predican que debemos usarlo en todas partes, otros piensan que es el diablo. Todos los que hablan en absoluto están equivocados:]
+
+
+
+❌ **De lo contrario:** Te perderás algunas herramientas con un ROI increíble, algunas como Fuzz, lint y mutation pueden proporcionar valor en 10 minutos
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :clap: Ejemplo de cómo hacerlo correctamente: Cindy Sridharan sugiere un porfolio de test amplio en su increíble publicación ‘Testing Microservices — the same way’
+
+
+
+☺️Ejemplo: [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 Los test de Componentes pueden ser tu mejor amigo
+
+:white_check_mark: **Haz:**
+
+Cada test unitario cubre una pequeña parte de la aplicación y cubrirla totalmente cuesta muchísimo, mientras que los test end-to-end cubren fácilmente mucho terreno, pero son costosos y más lentos, ¿por qué no aplicar un enfoque equilibrado y escribir test más grandes que test unitarios pero más pequeños que los test end-to-end? Los test de componente es la canción no cantada del mundo del testing — proporcionan lo mejor de ambos mundos: rendimiento razonable y la posibilidad de aplicar patrones TDD + cobertura realista
+
+Los test de componente se centran en la 'unidad' de microservicios, funcionan contra la API, no mockean nada que pertenezca al microservicio en sí (por ejemplo, base de datos real, o al menos la versión en memoria de esa base de datos) pero hace stub de cualquier cosa que sea externa como llamadas a otros microservicios. Al hacerlo, probamos lo que desplegamos, nos acercamos a la aplicación de fuera a dentro y obtenemos una gran confianza en un período de tiempo razonable
+
+
+
+❌ **De lo contrario:** Puedes pasar muchos días escribiendo test unitarios para descubrir que solo tiene un 20% de cobertura del sistema
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :clap: Ejemplo de cómo hacerlo correctamente: Supertest permite acercarse al API Express (rápido y cubre muchas capas)
+
+
+
+
+ permite acercarse al API Express (rápido y cubre muchas capas)")
+
+
+
+
+
+## ⚪ ️2.3 Asegúrate de que las nuevas versiones no rompan el API usando tests de contrato
+
+:white_check_mark: **Haz:** Pongamos que tu microservicio tiene múltiples consumidores, y tenemos en ejecución diferentes versiones del servicio por compatibilidad (para que todos estén contentos). Luego cambias un campo y "¡boom!", uno de los consumidores que necesita ese campo se cabrea. Este es el Catch-22 del mundo de la integración: es muy difícil para el lado del servidor considerar todas las expectativas de todos los consumidores. Por otro lado, los consumidores no pueden realizar ningún test porque el servidor controla las fechas de release. [Los contratos dirigidos por el consumidor y el framework PACT] (https://docs.pact.io/) nacieron para regularizar este proceso con un enfoque muy disruptivo: no es el servidor quien define los test de sí mismo, sino que son los consumidores quienes definen los test de ¡el servidor! PACT puede registrar las expectativas del consumidor y dejarlas en una ubicación compartida, "broker", para que el servidor pueda cogerlas y cumplir con las expectativas y ejecutar cada construcción utilizando la librería PACT para detectar contratos incumplidos — una expectativa de consumidor no cumplida. Al hacerlo, todos los desajustes de la API cliente-servidor se detectan muy pronto durante la construcción / CI y pueden ahorrarte mucha frustración
+
+
+
+❌ **De lo contrario:** Las alternativas son test manuales agotadores o miedo al despliegue
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :clap: Ejemplo de cómo hacerlo correctamente:
+
+
+
+
+
+
+
+
+
+## ⚪ ️ 2.4 Testea tus middlewares aisladamente
+
+:white_check_mark: **Haz:** Muchos evitan los test de middleware porque representan una pequeña porción del sistema y requieren ejecutar un servidor Express. Ambas razones son incorrectas — los middlewares son pequeños pero afectan a todas o la mayoría de las solicitudes y pueden testearse fácilmente como funciones puras que obtienen {req, res} objetos JS. Para testear una función de middleware se debe invocar y usar spy ([usando Sinon, por ejemplo] (https://www.npmjs.com/package/sinon)) sobre la interacción con los objetos {req, res} para garantizar que nuestra función middleware realiza la acción correcta. La librería [node-mock-http] (https://www.npmjs.com/package/node-mocks-http) lo lleva aún más lejos y factoriza los objetos {req, res} ademas de añadir el spy. Por ejemplo, puede asertar si el estado http que se estableció en el objeto res coincide con el esperado (consulta el ejemplo a continuación)
+
+
+
+❌ **De lo contrario:** Un bug en un middleware de Express === un bug todas o casi todas las peticiones
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :clap:Ejemplo de cómo hacerlo correctamente: Probar un middleware de forma aislada sin emitir llamadas de red y evitar tener que levantar Express
+
+
+
+```javascript
+//el middleware que queremos testear
+const unitUnderTest = require("./middleware");
+const httpMocks = require("node-mocks-http");
+//Sintaxis de Jest, equivalente a describe() e it() en 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 Mide y refactoriza utilizando herramientas de análisis estático
+
+:white_check_mark: **Haz:** El uso de herramientas de análisis estático ayuda proporcionando formas objetivas para mejorar la calidad del código y a tener el código mantenible. Puede agregar herramientas de análisis estático a su pipeline de CI para abortar cuando encuentre code smells. Sus principales beneficios sobre el linter plano son la habilidad de analizar la calidad en el contexto de múltiples ficheros (por ejemplo encontrar duplicados), realizando análisis avanzados (por ejemplo complejidad del código) y siguiendo el historial y el progreso de cada problema. Dos ejemplos de herramientas que puedes usar son [Sonarqube](https://www.sonarqube.org/) (2,600+ [estrellas](https://github.com/SonarSource/sonarqube)) y [Code Climate](https://codeclimate.com/) (1,500+ [estrellas](https://github.com/codeclimate/codeclimate))
+
+Crédito: [Keith Holliday](https://github.com/TheHollidayInn)
+
+
+
+❌ **De lo contrario:** Con una mala calidad de código, los bugs y el rendimiento siempre serán un problema que ninguna librería completamente nueva o características punteras van a poder solucionar
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :clap: Ejemplo de cómo hacerlo correctamente: CodeClimate, una herramienta comercial que puede identificar métodos complejos:
+
+
+
+
+
+
+
+
+
+## ⚪ ️ 2.6 Comprueba tu predisposición al caos relacionado con Node
+
+:white_check_mark: **Haz:** Extrañamente, la mayoría de los test de software son acerca de lógica y datos, pero los errores más graves (y que son muy difíciles de resolver) son problemas de infraestructura. Por ejemplo, ¿alguna vez has testeado que pasa cuando se sobrecarga la memoria? ¿o cuando el servidor/proceso muere? ¿o tu sistema de monitorización es capaz de darse cuenta cuando la API es un 50% más lenta de lo normal? Para probar y evitar este tipo de problemas — [Chaos engineering](https://principlesofchaos.org/) fue creado por Netflix. Su objetivo es proporcionar conciencia, frameworks, y herramientas para testear la resiliencia de nuestras aplicaciones en problemas caóticos. Por ejemplo, una de las herramientas más conocidas [the chaos monkey](https://github.com/Netflix/chaosmonkey), mata servidores de forma aleatoria para comprobar si nuestro servicio aun puede dar servicio a los usuarios y asegurar que no depende de un solo servidor (hay también una versión para kubernetes, [kube-monkey](https://github.com/asobti/kube-monkey), que mata pods en vez de servidores. Todas estas herramientas funcionan a nivel de hosting/plataforma, pero ¿qué pasa si deseas probar y generar caos a nivel Node puramente como comprobar como tu proceso Node hace frente a errores no controlados, o a rejects de promesas no capturados, o sobrecarga de la memoria de v8 por encima del máximo de 1.7GB o si la UX sigue siendo buena si se satura el event loop? Para todo esto he escrito [node-chaos](https://github.com/i0natan/node-chaos-monkey) (alpha) que proporciona todo tipo de formas de crear el caos en Node
+
+
+❌ **De lo contrario:** No hay escapatoria, la ley de Murphy afectará a producción sin piedad
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :clap: Ejemplo de cómo hacerlo correctamente: : Node-chaos puede generar todo tipo de problemas en Node.js de tal manera que puedas probar la resiliencia de tu app al caos
+
+
+
+
+
+
+
+## ⚪ ️2.7 Evitar fixtures globales y seeds, añade datos por cada test
+
+:white_check_mark: **Haz:** Siguiendo la regla de oro (sección 0), cada test debe añadir y actuar en su propio conjunto de filas en base de datos para evitar el acoplamiento y poder explicar fácilmente el flujo del test. En realidad muchos testers se saltan esta regla al añadir datos a la DB solo una vez antes de ejecutar los test ([también conocido como ‘test fixture’](https://en.wikipedia.org/wiki/Test_fixture)) a favor de mejorar el rendimiento. Si bien el rendimiento es una preocupación válida — puede mitigarse de otras formas (consulte la sección "test de componentes"), sin embargo, la complejidad del test es más dolorosa que otras consideraciones la mayoría de las veces. De manera practica, haz que cada test añada explícitamente los registros que necesitas y actúe sobre ellos. Si el rendimiento se convierte en algo critico — se puede llegar al compromiso de utilizar los mismos datos en un conjunto de test siempre que no se muten los datos (por ejemplo en queries)
+
+
+
+❌ **De lo contrario:** Muchos test fallaran, un despliegue se aborta, nuestro equipo perderá mucho tiempo, ¿tenemos un bug? vamos a investigar, ah no — parece que dos test están mutando el mismo dato
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :thumbsdown: Ejemplo Anti Patrón: test no son independientes y dependen de una inserción global de datos en la base de datos
+
+
+
+```javascript
+before(async () => {
+ //añadiendo datos de sites y admin a nuestra DB. ¿Donde están los datos? fuera. En un json externo o en un modelo da migración
+ await DB.AddSeedDataFromJson('seed.json');
+});
+it("When updating site name, get successful confirmation", async () => {
+ //Se que el nombre de site "portal" existe, — lo he visto en seed.json
+ 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 () => {
+ //Se que el nombre de site "portal" existe, — lo he visto en seed.json
+ const siteToCheck = await SiteService.getSiteByName("Portal");
+ expect(siteToCheck.name).to.be.equal("Portal"); //Fallo! El test anterior ha cambiado el nombre :[
+});
+
+```
+
+
+
+### :clap: Ejemplo de cómo hacerlo correctamente: Podemos permanecer dentro de nuestro test, cada test actúa sobre su propio conjunto de datos
+
+```javascript
+it("When updating site name, get successful confirmation", async () => {
+ //el test esta añadiendo registros nuevos cada vez y actuando solo en esos registros
+ const siteUnderTest = await SiteService.addSite({
+ name: "siteForUpdateTest"
+ });
+
+ const updateNameResult = await SiteService.changeName(siteUnderTest, "newName");
+
+ expect(updateNameResult).to.be(true);
+});
+```
+
+
+
+
+
+# Sección 3️⃣: Frontend Testing
+
+## ⚪ ️ 3.1 Separa la UI de la funcionalidad
+
+:white_check_mark: **Haz:** Al centrarnos en testear la lógica del componente, los detalles de la interfaz de usuario solo pueden entorpecernos, por lo qu debes abstraerte de ellos y que los test se centren en datos puros. En la practica, extrae los datos que necesites de una manera abstracta sin que este acoplada a la interfaz grafica, haz aserciones de los datos puros (vs detalles visuales en HTML/CSS) y desactiva las animaciones que pueden hacer lenta la interfaz. En este punto podrías pensar en desactivar la interfaz y solo hacer test de la parte back del UI (por ejemplo servicios, acciones, store) pero esto solo dará como resultado test ficticios, diferentes a la realidad y no desvelaran casos en los que los datos correctos no llegan a la interfaz de usuario
+
+
+
+❌ **De lo contrario:** Los datos calculados puros de tu test pueden estar listos en 10ms, pero luego todo el test tarda 500ms (100 test = 1 min) debido a alguna animación irrelevante
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :clap: Ejemplo de cómo hacerlo correctamente: Separar los detalles de UI
+
+ 
+
+```javascript
+test("When users-list is flagged to show only VIP, should display only VIP members", () => {
+ // Ajustar
+ const allUsers = [{ id: 1, name: "Yoni Goldberg", vip: false }, { id: 2, name: "John Doe", vip: true }];
+
+ // Actuar
+ const { getAllByTestId } = render();
+
+ // Afirmar - Extrae los datos de la UI primero
+ const allRenderedUsers = getAllByTestId("user").map(uiElement => uiElement.textContent);
+ const allRealVIPUsers = allUsers.filter(user => user.vip).map(user => user.name);
+ expect(allRenderedUsers).toEqual(allRealVIPUsers); //compara datos con datos, aquí no hay UI
+});
+```
+
+
+
+### :thumbsdown: Ejemplo Anti Patrón: Mezcla de datos y detalles de la UI en las aserciones
+
+```javascript
+test("When flagging to show only VIP, should display only VIP members", () => {
+ // Ajustar
+ const allUsers = [{ id: 1, name: "Yoni Goldberg", vip: false }, { id: 2, name: "John Doe", vip: true }];
+
+ // Actuar
+ const { getAllByTestId } = render();
+
+ // Afirmar - Mezcla de UI y datos en las aserciones
+ expect(getAllByTestId("user")).toEqual('[John Doe]');
+});
+```
+
+
+
+
+
+## ⚪ ️ 3.2 Consulta elementos HTML basándote en atributos que no deberían cambiar
+
+:white_check_mark: **Haz:** Consulta elementos HTML basándote en atributos que deberían permanecer intactos a cambios gráficos al contrario que selectores CSS y etiquetas de los formularios. Si el elemento designado no tiene esos atributos, crea un atributo dedicado solamente a los test como 'test-id-submit-button'. Seguir este patrón no solo asegura que tus test funcionales/lógica no se rompan nunca por cambios estéticos, sino que también queda claro a cualquier desarrollador que ese elemento y atributo están ahí por y para los test y no deben eliminarse
+
+
+
+❌ **De lo contrario:** Quieres testear el login de tu app que tiene muchos componentes, lógica, servicios, y todo esta bien configurado - stubs, spies, las llamadas Ajax están aisladas. Todo parece perfecto. Entonces el test falla porque el diseñador ha cambiado la clase de un div de 'thick-border' a 'thin-border'
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :clap: Ejemplo de cómo hacerlo correctamente: Consultar un elemento utilizando un atributo solo para testing
+
+
+
+```html
+// the markup code (part of React component)
+
+
+ {value}
+
+
+
+```
+
+```javascript
+// este ejemplo está usando react-testing-library
+test("Whenever no data is passed to metric, show 0 as default", () => {
+ // Ajustar
+ const metricValue = undefined;
+
+ // Actuar
+ const { getByTestId } = render();
+
+ expect(getByTestId("errorsLabel").text()).toBe("0");
+});
+```
+
+
+
+### :thumbsdown: Ejemplo Anti Patrón: Confiando en atributos CSS
+
+```html
+
+{value}
+
+```
+
+```javascript
+// este ejemplo está usando enzyme
+test("Whenever no data is passed, error metric shows zero", () => {
+ // ...
+
+ expect(wrapper.find("[className='d-flex-column']").text()).toBe("0");
+});
+```
+
+
+
+
+
+## ⚪ ️ 3.3 Siempre que sea posible, testea con un componente real y totalmente renderizado
+
+:white_check_mark: **Haz:** Siempre que tenga un tamaño razonable, testea tu componente como lo hacen tus usuarios, renderiza completamente la interfaz de usuario, actúa sobre ella y comprueba que la interfaz se comporta como esperabas. Evita todo tipo de mocks, partials o shadow rendering - hacerlo puede provocar bugs no detectados debido a la falta de detalles y dificultará el mantenimiento a medida que los test interfieren con las partes internas (consulte la sección 'Acercarse al testing caja-negra'). Si uno de los componentes hijos ralentiza significativamente tu test (por ejemplo por una animación) o complica el setup, considera reemplazarlo explícitamente por un fake
+
+Con todo esto también es necesario tener ciertas precauciones: esta técnica funciona para componentes pequeños / medianos que contienen un número razonable de componentes hijos. Renderizar completamente un componente con demasiados hijos hará que sea difícil analizar los fallos de los test (análisis de causa raíz) y puede ser demasiado lento. En estos casos, escribe los menos test que necesites contra ese componente principal y más test contra sus hijos
+
+
+
+❌ **De lo contrario:** Al hurgar en las partes internas de un componente, invocando sus métodos privados y verificando el estado interno - tendrás que refactorizar todos los test siempre que refactorices los componentes ¿Realmente quieres dedicar ese tiempo en hacer este mantenimiento?
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :clap: Ejemplo de cómo hacerlo correctamente: Trabajando con un componente totalmente renderizado
+
+ 
+
+```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", () => {
+ // Ajustar
+ const wrapper = mount();
+
+ // Actuar
+ wrapper.find("button").simulate("click");
+
+ // Afirmar
+ expect(wrapper.text().includes("Choose Filter"));
+ // Así es como el usuario abordará este elemento: por texto
+});
+```
+
+### :thumbsdown: Ejemplo Anti Patrón: Mockeando con renderizado superficial (shallow rendering)
+
+```javascript
+test("Shallow/mocked approach: When clicked to show filters, filters are displayed", () => {
+ // Ajustar
+ const wrapper = shallow();
+
+ // Actuar
+ wrapper
+ .find("filtersPanel")
+ .instance()
+ .showFilters();
+ // Aprovecha las partes internas, saltándote la UI e invocando el método directamente.
+ // Aproximación caja-blanca
+
+ // Afirmar
+ expect(wrapper.find("Filter").props()).toEqual({ title: "Choose Filter" });
+ // ¿Qué pasa si cambiamos el nombre de la propiedad o si no pasamos nada relevante?
+});
+```
+
+
+
+
+
+## ⚪ ️ 3.4 No pauses, usa el soporte del framewok para eventos asincronos e intenta acelerar las cosas
+
+:white_check_mark: **Haz:** En muchos casos, el tiempo que tarda una unidad bajo test es desconocido (por ejemplo, la animación elimina la visualización de un elemento) - en este caso, evita esperar (por ejemplo stTimeOut) y elige métodos mas deterministas que la mayoría de las plataformas proveen. Algunas librerías permiten esperar en operaciones (por ejemplo [Cypress cy.request('url')](https://docs.cypress.io/guides/references/best-practices.html#Unnecessary-Waiting)), otras proveen un API para esperar como [@testing-library/dom method wait(expect(element))](https://testing-library.com/docs/guide-disappearance). A veces, una forma más elegante es hacer stub del recurso lento, como una API por ejemplo, con lo que el momento de respuesta se vuelve determinista, y volvemos a poder renderizar el componente directamente. Cuando tengas dependencias con algún componente externo que espera, puede ser útil modificar el tiempo con librerías como [hurry-up the clock](https://jestjs.io/docs/en/timer-mocks). Esperar es algo que debemos evitar siempre, por que fuerza que tu test sea lento o tenga ciertos riesgos (cuando esperas un tiempo muy bajo). Siempre que sea inevitable esperar y hacer polling, y el framework de testing no nos da soporte, algunas librerías de npm pueden ayudar con soluciones semi-deterministas, como [wait-for-expect](https://www.npmjs.com/package/wait-for-expect)
+
+
+
+❌ **De lo contrario:** Cuando se espera mucho tiempo, los test serán un orden de magnitud más lentos. Cuando intentes esperar tiempos bajos, los test fallarán cuando la unidad bajo test no haya respondido a tiempo. Por tanto, se reduce a balancear entre puntos débiles y el rendimiento malo
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :clap: Ejemplo de cómo hacerlo correctamente: API E2E que se resuelve solo cuando se realizan las operaciones asíncronas (Cypress)
+
+
+
+
+```javascript
+// usando Cypress
+cy.get("#show-products").click(); // navegar
+cy.wait("@products"); // esperar a que la ruta aparezca
+// esta linea será ejecutada solo cuando la ruta haya terminado
+```
+
+### :clap: Ejemplo de cómo hacerlo correctamente: Liberia de testing que espera a elementos del DOM
+
+```javascript
+// @testing-library/dom
+test("movie title appears", async () => {
+ // el elemento no esta presente al comenzar...
+
+ // esperando a que este disponible
+ await wait(() => {
+ expect(getByText("the lion king")).toBeInTheDocument();
+ });
+
+ // esperando que este disponible para devolver el elemento
+ const movie = await waitForElement(() => getByText("the lion king"));
+});
+```
+
+### :thumbsdown: Ejemplo Anti Patrón: código de espera propio
+
+```javascript
+test("movie title appears", async () => {
+ // el elemento no esta presente al comenzar...
+
+ // espera con lógica propia (precaución: simplista, sin tiempo de espera)
+ const interval = setInterval(() => {
+ const found = getByText("the lion king");
+ if (found) {
+ clearInterval(interval);
+ expect(getByText("the lion king")).toBeInTheDocument();
+ }
+ }, 100);
+
+ // esperando que este disponible para devolver el elemento
+ const movie = await waitForElement(() => getByText("the lion king"));
+});
+```
+
+
+
+
+
+## ⚪ ️ 3.5 Observa como se sirve tu contenido a nivel de red
+
+
+
+✅ **Haz:** Usa un monitor que garantice que la carga de la página esté optimizada - esto incluye cualquier problema de UX como descarga lenta o un paquete no minimizado. El mercado de herramientas de este tipo no es pequeño: herramientas básicas como [pingdom](https://www.pingdom.com/), AWS CloudWatch, [gcp StackDriver](https://cloud.google.com/monitoring/uptime-checks/) se puede configurar para ver si el servidor esta corriendo y respondiendo dentro de un SLA razonable. Esto tan solo ralla la superficie, podría haber muchísimas cosas mal, por tanto es mejor optar por herramientas especializadas en frontend, (por ejemplo [lighthouse](https://developers.google.com/web/tools/lighthouse/), [pagespeed](https://developers.google.com/speed/pagespeed/insights/)) y que hagan un análisis mucho más amplio. El foco debe ponerse en los síntomas y métricas que afecten directamente a UX, como el tiempo de carga, [render mínimo](https://scotch.io/courses/10-web-performance-audit-tips-for-your-next-billion-users-in-2018/fmp-first-meaningful-paint), [tiempo hasta que la pagina es manejable (TTI)](https://calibreapp.com/blog/time-to-interactive/). Sobre todo esto, también debes estar atento a posibles causas técnicas, como garantizar que el contenido sea comprimido, tiempo hasta el primer byte, optimizar imágenes, asegurar un tamaño del DOM razonable, SSL, y muchos más. Es aconsejable tener toda esta monitorización durante el desarrollo, como parte del CI y mucho más importante - 24x7 en los servidores de producción / CDN
+
+
+
+❌ **De lo contrario:** Es muy decepcionante darse cuenta de que después de haber tenido mucho cuidado y trabajo en crear una interfaz, pasen el 100% de los test funcionales y un pipeline sofisticado - el UX es horrible y lento por culpa de un CDN mal configurado
+
+
+
+✏ Código de Ejemplo
+
+### :clap: Ejemplo de cómo hacerlo correctamente: reporte de carga de la pagina con Lighthouse
+
+
+
+
+
+
+
+## ⚪ ️ 3.6 Usa stubs para recursos lentos como el API de back-end
+
+:white_check_mark: **Haz:** Cuando programas tus test principales (no los test E2E), evita interactuar con cualquier recurso que este fuera de tu responsabilidad y control, como las API de back-end y usar stubs en su lugar (es decir, un doble). De forma practica, en vez de hacer llamadas de red reales al API, utiliza alguna librería (como [Sinon](https://sinonjs.org/), [Test dobles](https://www.npmjs.com/package/testdouble), etc) para stubear las repuestas de API. El principal beneficio es evitar la inestabilidad - testeando o suplantando APIs, por definición, no son muy estables y de vez en cuando fallarán los test, aunque TU componente se comporte bien (en producción generalmente se aceleran las respuestas, pero no esta pensado para hacer test). Hacerlo te permitirá simular varios comportamientos de API que deberían definir el comportamiento de nuestro componente para caminos no felices, por ejemplo cuando no se encuentran datos o cuando API devuelve un error. Por último, pero no por ello menos importante, las peticiones de red hacen más lentos nuestros test
+
+
+
+❌ **De lo contrario:** La media de ejecución de los test no dura más de unos pocos ms, una llamada a API estándar dura 100ms>, lo que lo hace cada test ~20x más lento
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :clap: Ejemplo de cómo hacerlo correctamente: Stubear o interceptar las llamadas a API
+
+ 
+
+```javascript
+// unidad testeada
+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", () => {
+ // Ajustar
+ nock("api")
+ .get(`/products`)
+ .reply(404);
+
+ // Actuar
+ const { getByTestId } = render();
+
+ // Afirmar
+ expect(getByTestId("no-products-message")).toBeTruthy();
+});
+```
+
+
+
+
+
+## ⚪ ️ 3.7 Haz muy pocos test end-to-end que abarquen todo el sistema
+
+:white_check_mark: **Haz:** Aunque E2E (end-to-end) para algunos significa solo hacer test de UI en navegador de verdad (ver el punto 3.6), para otros significa que impliquen todo el sistema, incluido el backend real. Esto es muy valioso ya que te cubren errores de integración entre el frontend y el backend que pueden ocurrir por diferencias de opinión en el esquema de datos. También son un método eficiente para sacar errores de integración entre backends (por ejemplo, microservicio A envía datos erróneos al microservicio B) e incluso para detectar fallos de despliegue - actualmente no hay herramientas para test E2E solo backend tan amigables y maduras como las de UI como [Cypress](https://www.cypress.io/) y [Pupeteer](https://github.com/GoogleChrome/puppeteer). La desventaja de estos test es su alto coste, tener un entorno configurado con todos los componentes, la fragilidad de los test - si tenemos 50 microservicios, solo con que falle uno, los test E2E fallan. Por estas razones tenemos que usar moderadamente esta técnica y probablemente tener entre 1 y 10 test de este tipo. Dicho esto, incluso una cantidad pequeña de test E2E es probablemente que detecten el tipo de problemas a los que están realmente dirigidos: despligue e integración. Es aconsejable que se ejecuten en un entorno lo más parecido a producción
+
+
+
+❌ **De lo contrario:** Puedes invertir mucho en testear la funcionalidad de la UI para darte cuenta demasiado tarde que el backend devuelve un contrato (el esquema de datos con el que la UI trabaja) muy diferente al esperado
+
+
+
+## ⚪ ️ 3.8 Acelera los test E2E reutilizando las credenciales de login
+
+:white_check_mark: **Haz:** En los test E2E que involucren un backend real que usa un token para identificarse en las llamadas a API, no vale la pena aislar el test tanto como para que se cree un usuario y se haga login en cada test. En vez de esto, haz login una vez antes de ejecutar todos los test (en el before-all) guarda el token de forma local y reutilizalo en cada petición. Esto parece violar unos de los principios básicos - mantén los test autónomos sin acoplamiento de recursos. Y es cierto, pero en los test E2E el rendimiento es clave, y crear 1-3 peticiones a API antes de empezar cada test individual puede llevarnos a unos tiempos de ejecución horribles. Reutilizar las credenciales no significa que los test tengan que actuar sobre los mismos registros de usuario - si se basan en ellos (por ejemplo, testeando el historial de pagos), asegurate de crear los registro como parte del test y evita compartirlos con otros test. Y siempre recuerda que el backend puede ser sustituido - si tus test están focalizados en el frontend puede ser mejor aislarlos y desconectar las API de backend (consulta el punto 3.6)
+
+
+
+❌ **De lo contrario:** Dados 200 test y asumiendo que un login son 100ms = 20 segundos solo para hacer el mismo login una y otra vez
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :clap: Ejemplo de cómo hacerlo correctamente: Logandose en before-all y no en before-each
+
+
+
+```javascript
+let authenticationToken;
+
+// ocurre antes de ejecutar TODOS los test
+before(() => {
+ cy.request('POST', 'http://localhost:3000/login', {
+ username: Cypress.env('username'),
+ password: Cypress.env('password'),
+ })
+ .its('body')
+ .then((responseFromLogin) => {
+ authenticationToken = responseFromLogin.token;
+ })
+})
+
+// ocurre antes de CADA test
+beforeEach(setUser => () {
+ cy.visit('/home', {
+ onBeforeLoad (win) {
+ win.localStorage.setItem('token', JSON.stringify(authenticationToken))
+ },
+ })
+})
+
+```
+
+
+
+
+
+## ⚪ ️ 3.9 Haz un test E2E que navegue toda la página (smoke test)
+
+:white_check_mark: **Haz:** Para el monitoreo de producción y verificar que nada se rompe en tiempo de desarrollo (sanity check), ejecuta un único test E2E que visite todas o la mayoría de las páginas y se asegure que ninguna se rompe. Este tipo de test proporciona un gran retorno de la inversión ya que es un bastante sencillo de crear y mantener y puede detecta cualquier tipo de fallo, incluido funcionales, red y despliegue. Otras formas de hacer smoke y sanity checks no son tan confiables y exhaustivas - algunos equipos de operaciones simplemente hacen ping a la página de inicio (en producción) o desarrolladores que tiene muchos test de integración que no levanta errores de construcción o de navegador. No hace falta decir que este test no sustituye los test funcionales, solo sirven como detector de humo rápido
+
+
+
+❌ **De lo contrario:** Todo puede parecer estar bien, todos los test pasan, el health-check de produción está ok también, pero el componente de pago se construyó mal y simplemente la ruta /Payment no se renderiza
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :clap: Ejemplo de cómo hacerlo correctamente: Navegando a todas las páginas
+
+
+
+```javascript
+it("When doing smoke testing over all page, should load them all successfully", () => {
+ // ejemplificado con Cypress pero puede implementarse
+ // facilmente usando cualquier herramienta E2E
+ 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 Exponer los test como un documento colaborativo vivo
+
+:white_check_mark: **Haz:** Además de aumentar la confiabilidad de la aplicación, los test te dan otra característica muy atractiva - sirven de documentación viva. Dado que los test hablan en un lenguaje menos técnico y sobre el producto y UX, usar las herramientas correctas puede servir como un artefacto de comunicación que alinea en gran medida a desarrolladores y su cliente. Por ejemplo, algunos frameworks permiten expresar el flujo y las expectativas (el test plan) utilizando un lenguaje legible para que cualquier stakeholder, incluyendo los product managers, pueden leer, aprobar y colaborar en los test convirtiéndose en el documento de requerimientos vivo. Esta técnica también se la conoce como 'test de aceptación', ya que permite al cliente definir sus criterios de aceptación en un lenguaje sencillo. Esto es [BDD (behavior-driven testing)](https://en.wikipedia.org/wiki/Behavior-driven_development) en su forma más pura. Uno de los frameworks más populares para esto es [Cucumber que tiene su sabor en JavaScript](https://github.com/cucumber/cucumber-js), ver el ejemplo más abajo. Otra forma similar pero diferente, [StoryBook](https://storybook.js.org/), permite exponer los componentes UI como un catalogo grafico, donde cualquiera puede recorrer los diferentes estados de cada componente (por ejemplo renderizar una cuadricula sin filtro, con múltiples filas o con ninguna, etc), ver como queda y como se activa ese estado - esto también puede atraer a la gente de producto pero sobre todo sirve como documentación viva para los desarrolladores que consumen esos componentes
+
+❌ **De lo contrario:** Después de invertir los mejores recursos en los test, es una pena no aprovechar ese tiempo y ganar un gran valor como es la documentación
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :clap: Ejemplo de cómo hacerlo correctamente: Describiendo test en un lenguaje para humanos usando cucumber-js
+
+
+
+```javascript
+// así es como se pueden describir los test con cucumber: lenguaje sencillo que permite a cualquiera comprenderlos y colaborar
+
+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: Ejemplo de cómo hacerlo correctamente: Visualizando nuestros componentes, sus diversos estados y entradas usando Storybook
+
+
+
+
+
+
+
+
+
+## ⚪ ️ 3.11 Detecta problemas visuales con herramientas automatizadas
+
+:white_check_mark: **Haz:** Configure herramientas automatizadas para capturar screenshoots de UI cuando se presenten cambios y detecte problemas visuales como contenido superpuesto o roto. Esto garantiza que no solo se muestren los datos correctos si no que el usuario los vea correctamente. Está técnica no es ampliamente usada, nuestra mentalidad nos lleva a los test funcionales, pero es lo visual lo que el usuario experimenta y con la cantidad de dispositivos es relativamente fácil pasar por alto algunos bugs en la UI. Algunas herramientas gratuitas pueden proporcionar lo básico - generar y guardar screenshots para la inspección manual por una persona. Mientras este enfoque podría ser suficiente para aplicaciones pequeñas, no es valido como cualquier otro test manual que exige trabajo de una persona cada vez que algo cambia. Por otro lado, es bastante difícil detectar problemas de UI automáticamente debido a que no está claramente definido - aquí es donde interviene el campo de la 'Regresión Visual' a resolver este rompecabezas de comparar la UI antigua con los últimos cambios y detectar diferencias. Algunas herramientas OSS/gratuitas pueden proporcionar parte de esta funcionalidad (por ejemplo [wraith](https://github.com/BBC-News/wraith), [PhantomCSS](<[https://github.com/HuddleEng/PhantomCSS](https://github.com/HuddleEng/PhantomCSS)>) pero podrían conllevar un tiempo de configuración muy alto. Algunas herramientas comerciales (por ejemplo [Applitools](https://applitools.com/), [Percy.io](https://percy.io/)) dan un paso más reducir la instalación y contener funciones avanzadas como interfaces de administración, alertas, captura inteligente que elimina el 'ruido visual' (por ejemplo, banners, animaciones) e incluso llegan a adelantar el análisis de la causa raíz de los cambios del DOM / css que han causado el problema
+
+
+
+❌ **De lo contrario:** ¿Como de bien está hecha una pagina que muestra buen contenido (100% test ok), carga de forma instantánea pero la mitad del área de contenido está oculto?
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :thumbsdown: Ejemplo Anti Patrón: Una regresión visual estándar - contenido correcto que se muestra mal
+
+
+
+
+
+### :clap: Ejemplo de cómo hacerlo correctamente: Configurando wraith para capturar y comparar snapshots de UI
+
+
+
+```
+# Añade tantos dominios como sea necesario. La key actuará como etiqueta
+
+domains:
+ english: "http://www.mysite.com"
+
+# escribe los anchos de pantalla a continuación, por ejemplo
+
+screen_widths:
+
+ - 600
+ - 768
+ - 1024
+ - 1280
+
+# escribe las URL de la página a continuación, por ejemplo
+paths:
+ about:
+ path: /about
+ selector: '.about'
+ subscribe:
+ selector: '.subscribe'
+ path: /subscribe
+```
+
+### :clap: Ejemplo de cómo hacerlo correctamente: Usando Applitools para hacer comparación de snapshoots y otras funciones avanzadas
+
+ 
+
+```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");
+ });
+});
+```
+
+
+
+
+
+# Sección 4️⃣: Midiendo la efectividad de los Test
+
+
+
+## ⚪ ️ 4.1 Completa una cobertura suficiente que de confianza, ~80% parece el numero de la suerte
+
+:white_check_mark: **Haz:** El propósito de los test es tener suficiente confianza para moverse rápido, obviamente cuando más código se pruebe, más confianza tendremos en el equipo. La cobertura nos mide cuantas lineas de código (y ramas, declaraciones, etc) se alcanzan mediante los test. Entonces, ¿cuanta cobertura es suficiente? Obviamente 10-30% es demasiado bajo para tener alguna idea de que puedes tener que corregir, y por otro lado el 100% es muy caro y puede cambiar el foco de los caminos críticos a rincones apenas usados del código. La respuesta larga es que depende de muchos factores como el tipo de aplicación - si estas construyendo la siguiente generación del Airbus A380 un 100% es obligatorio, pero para una web de dibujos animados, el 50% podría hasta ser demasiado. Aunque la mayoría de los entusiastas de los test dicen que el porcentaje de cobertura correcto es contextual, la mayoría de ellos comentan que el 80% como la regla correcta ([Fowler: “in the upper 80s or 90s”](https://martinfowler.com/bliki/TestCoverage.html)) que posiblemente debería satisfacer la mayoría de aplicaciones
+
+Consejos de implementación: es posible que quieras configurar la integración continua (CI) para que tenga un umbral de cobertura ([Jest link](https://jestjs.io/docs/en/configuration.html#collectcoverage-boolean)) y que pare el pipeline cuando no cumpla el estándar (también es posible configurar el porcentaje por componete, véase el ejemplo a continuación). Ademas de esto, deberías considerar detectar la bajada de cobertura (cuando un nuevo commit tiene menos cobertura que antes) - esto empujara a los desarrolladores a aumentar o al menos preservar la cantidad de código con test. Aunque también puede ser trampeado como se muestra en los siguientes puntos
+
+
+
+❌ **De lo contrario:** La confianza y los números van de la mano, sin saber realmente que se ha testeado la mayor parte del sistema - habrá algo de miedo y el miedo te retrasará
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :clap: Ejemplo: Un reporte de cobertura típico
+
+
+
+
+
+### :clap: Ejemplo de cómo hacerlo correctamente: Configurar cobertura por componente (usando Jest)
+
+
+
+")
+
+
+
+
+
+## ⚪ ️ 4.2 Inspecciona los reportes de cobertura para detectar áreas no testadas y otras cosas raras
+
+:white_check_mark: **Haz:** Algunos problemas se ocultan por debajo del radar y son realmente difíciles de encontrar utilizando herramientas tradicionales. Estos no son realmente bugs sino más bien comportamientos curiosos de la aplicación que podrían tener un gran impacto. Por ejemplo, a menudo algunas áreas de código no se invocan nunca o rara vez - puedes pensar que la clase 'PricingCalculator' siempre determina el precio del producto, pero resulta que en realidad nunca se invoca, aunque tenemos 10000 productos en base de datos y muchas ventas... Los reportes nos ayudan a darnos cuenta de si la aplicación se comporta de la manera que esperamos. Aparte de eso, también podemos resaltar qué tipos de código no se testean: que el 80% del código se testea, no nos indica si las partes críticas están cubiertas. Generar reportes es fácil: simplemente ejecute su aplicación en producción o durante test con cobertura y luego revisa los reportes que resaltan la frecuencia con la que se invoca cada parte del código. Si le dedicas un tiempo para echar un vistazo a estos datos, puedes encontrar algunas errores
+
+
+
+❌ **De lo contrario:** Si no sabes qué trozos de código no se testean, no sabes dónde pueden aparecer problemas
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :thumbsdown: Ejemplo Anti Patrón: ¿Qué está mal en este reporte de cobertura?
+
+Basado en un escenario real en el que rastreamos el uso de nuestra aplicación en el control de calidad y descubrimos patrones de login interesantes (Sugerencia: la cantidad de fallos de login no es proporcional, algo está claramente mal. Finalmente, resultó que algún error de la interfaz provocaba que se siguiera llamando al API de login en segundo plano)
+
+
+
+
+
+
+
+## ⚪ ️ 4.3 Mide la cobertura lógica usando mutation testing
+
+:white_check_mark: **Haz:** La métrica de cobertura tradicional a menudo miente: puede mostrarle una cobertura de código del 100%, pero ninguna de sus funciones, ni siquiera una, devuelve la respuesta correcta. ¿Cómo? simplemente mide sobre qué líneas de código se paso en los test, pero no verifica si los test realmente han comprobado algo - asercionando la respuesta correcta. Como alguien que viaja por negocios y muestra su pasaporte, esto no te asegura que haya realizado ningún trabajo, solo que ha visitado ciertos aeropuertos y hoteles
+
+Los test basados en mutaciones nos ayudan midiendo la cantidad de código que en realidad se TESTEÓ, no solo VISITADO. [Stryker] (https://stryker-mutator.io/) es una librería JavaScript para mutation testing y la implementación es realmente clara:
+
+(1) cambia intencionalmente el código y "planta bugs". Por ejemplo, el código newOrder.price === 0 se convierte en newOrder.price! = 0. Estos "bugs" se llaman mutaciones
+
+(2) ejecuta los test, si todo va bien, entonces tenemos un problema - los test no cumplen su propósito de descubrir bugs, las mutaciones se denominan supervivientes. Si los test fallaron, entonces genial, las mutaciones fueron destruidas
+
+Saber que todas o la mayoría de las mutaciones fueron destruidas da mucha más confianza que la cobertura tradicional y el tiempo de configuración es muy similar
+
+
+❌ **De lo contrario:** Te engañas si crees que una cobertura del 85% significa que tus test detectarán errores en el 85% de tu código
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :thumbsdown: Ejemplo Anti Patrón: 100% de cobertura, 0% testeado
+
+
+
+```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({ asignee: "John@mailer.com", price: 120 });
+}); //Genera un 100% cobertura de código, pero no comprueba nada
+```
+
+
+
+### :clap: Ejemplo de cómo hacerlo correctamente: Los reportes de Stryker, una herramienta para mutation testing, detecta y cuenta la cantidad de código que no se testea (Mutaciones)
+
+")
+
+
+
+
+
+## ⚪ ️4.4 Prevención de problemas de código de test con linters para test
+
+:white_check_mark: **Haz:** ESLint tiene un conjunto de plugins específicos para inspeccionar patrones de código de test y descubrir problemas. Por ejemplo, [eslint-plugin-mocha] (https://www.npmjs.com/package/eslint-plugin-mocha) avisará cuando un test se escriba a nivel global (no es hijo de un describe () ) o cuando se omiten los test (https://mochajs.org/#inclusive-tests), lo que puede llevar a creer erróneamente de que todos los test están ok. Del mismo modo, [eslint-plugin-jest] (https://github.com/jest-community/eslint-plugin-jest) puede, por ejemplo, advertir cuando un test no tiene aserciones (sin verificar nada)
+
+
+
+❌ **De lo contrario:** Ver un 90% de cobertura de código y 100% de test verdes te provocara una sonrisa hasta que te das cuenta de que muchos test no asercionan nada y muchos test simplemente se omitieron. Con suerte, no desplegaste nada basándote en esta falsa observación
+
+
+✏ Código de Ejemplo
+
+
+
+### :thumbsdown: Ejemplo Anti Patrón: Un test lleno de errores, afortunadamente detectados por el 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
+});
+```
+
+
+
+
+
+# Sección 5️⃣: CI y otras medidas de calidad
+
+
+
+## ⚪ ️ 5.1 Enriquece tus linters y cancela las construcciones que tienen problemas de linter
+
+:white_check_mark: **Haz:** Los linters son comida gratis, con una configuración de 5 minutos, obtienes gratis un piloto automático que vigila tu código y detecta problemas importantes mientras escribes. Atrás quedaron los días en los que el linter era solo maquillaje (¡no hay punto y coma!). Hoy en día, los linters pueden detectar problemas graves como errores que no se lanzan correctamente y perder información. Además de su conjunto básico de reglas (como [standar] (https://www.npmjs.com/package/eslint-plugin-standard) o [Airbnb] (https://www.npmjs.com/package / eslint-config-airbnb)), considera incluir algunos conjuntos de reglas especializadas como [eslint-plugin-chai-expect] (https://www.npmjs.com/package/eslint-plugin-chai-expect) que pueden descubrir test sin aserciones, [eslint-plugin-promise] (https://www.npmjs.com/package/eslint-plugin-promise?activeTab=readme) puede descubrir promesas sin resolución (tu código nunca continuará), [eslint-plugin-security] (https://www.npmjs.com/package/eslint-plugin-security?activeTab=readme) que puede descubrir expresiones regulares que podrían usarse para ataques DOS, y [eslint-plugin-you-dont-need-lodash-underscore] (https://www.npmjs.com/package/eslint-plugin-you-dont-need-lodash-underscore) es capaz de avisar cuando el código utiliza métodos de librerías de utilidades que forman parte de V8, métodos básicos como Lodash.\_map (...)
+
+
+
+❌ **De lo contrario:** Considera un día lluvioso donde producción sigue fallando pero los registros no muestran el call stack de errores. ¿Que pasa? Tu código emite por error un objeto sin error y se perdió el trazado, una buena razón para golpearse la cabeza contra una pared. Una configuración de linter de 5 minutos podría detectar este GAZAPO y salvarte el día
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :thumbsdown: Ejemplo Anti Patrón: El objeto de Error es emitido, no aparece ninguna traza para este error. Por suerte, ESLint detecta el siguiente error de producción
+
+
+
+
+
+
+
+## ⚪ ️ 5.2 Acorta el tiempo de feedback con local developer-CI
+
+:white_check_mark: **Haz:** ¿Tienes un pipeline de CI con test, linter, verificación de vulnerabilidades, etc? Ayuda a los desarrolladores a ejecutarlo también localmente para solicitar comentarios instantáneos y acortar el [ciclo de feedback] (https://www.gocd.org/2016/03/15/are-you-ready-for-continuous-delivery-part-2 -circuitos de retroalimentacion/). ¿Por qué? un proceso de testing eficiente constituye muchos bucles iterativos: (1) test -> (2) feedback -> (3) refactor. Cuanto más rápido sea el feedback, más iteraciones de mejora puede realizar un desarrollador por módulo y perfeccionar los resultados. Por otro lado, cuando el feedback tarda en llegar, se podrían realizar menos iteraciones de mejora en un solo día, el equipo podría estar ya haciendo otra cosa / tarea / módulo y podría no estar listo para refinar ese módulo
+
+En la practica, algunos proveedores de CI (Ejemplo: [CircleCI CLI local] (https://circleci.com/docs/2.0/local-cli/)) permiten ejecutar el pipeline localmente. Algunas herramientas comerciales como [wallaby proporcionan información valiosa y de test] (https://wallabyjs.com/) para el desarrollador sin coste. Alternativamente, puedes agregar scripts npm en el package.json para ejecutar todos los comandos de calidad (por ejemplo, test, linter, vulnerabilidades) - usa herramientas como [concurrently] (https://www.npmjs.com/package/concurrently) para paralelizarlas y que el código de salida sea distinto de cero si falla alguna de las herramientas. Ahora el desarrollador solo debe invocar un comando - por ejemplo "npm run quality": para obtener feedback en el acto. Considera también cancelar un commit si el control de calidad falla usando un githook ([husky puede ayudar] (https://github.com/typicode/husky))
+
+
+
+❌ **De lo contrario:** Cuando los resultados de calidad llegan un día más tarde que el código, los test no se convierten en una parte fluida del desarrollo, sino en algo formal posterior al mismo
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :clap: Ejemplo de cómo hacerlo correctamente: Scripts npm que realizan una inspección de calidad del código, todos se ejecutan en paralelo a demanda o cuando un desarrollador está tratando de hacer commit/push de código nuevo
+
+```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 Realiza test e2e sobre un espejo de producción
+
+:white_check_mark: **Haz:** Los test End to end (e2e) testing son el principal desafío de un pipeline CI - crear un entorno que sea un espejo de producción, efímero, que se genere sobre la marcha con todos los servicios necesarios puede ser tedioso y muy costoso. Buscando la mejor opción: [Docker-compose](https://docs.docker.com/compose/) permite crear un entorno dockerizado aislado con mismos contenedores usando un único fichero de texto plano, pero con la tecnología por debajo (redes, despliegues) ser diferentes a las de producción. Puedes combinarlo con [‘AWS Local’](https://github.com/localstack/localstack) para trabajar en hacer stubs de servicios AWS de verdad. Si usas [serverless](https://serverless.com/) existen multiple frameworks como serverless y [AWS SAM](https://docs.aws.amazon.com/lambda/latest/dg/serverless_app.html) que te permiten invocar código Faas en local
+
+El enorme ecosistema de Kubernetes aún no tiene una herramienta estándar para la duplicación en local y CI, aunque salen herramientas nuevas cada día. Un enfoque puede ser ejecutar implementaciones de 'kubernetes reducidos' con [Minikube](https://kubernetes.io/docs/setup/minikube/) o [MicroK8s](https://microk8s.io/) que es igual que el kubernetes completo pero con menos complejidad. Otro enfoque es tener un kubernetes de verdad remoto para test, algunos proveedores de CI (por ejemplo [Codefresh](https://codefresh.io/)) tienen integración nativa con entornos kubernetes y pueden fácilmente ejecutar el pipeline CI sobre algo más real, otros permiten ejecutar scripts customizados contra kubernetes remotos
+
+
+
+❌ **De lo contrario:** El uso de diferentes tecnologías para la producción y los test exige mantener dos modelos de implementación y mantiene a los desarrolladores y al equipo de operaciones separados
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :clap: Ejemplo: un pipeline de CI que genera un clúster de Kubernetes al vuelo ([Credito: 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 Paralelizar la ejecución de los test
+
+:white_check_mark: **Haz:** Cuando se hace correctamente, los test son tu amigo 24/7 proporcionando feedback instantáneo. En la practica, ejecutar 500 test unitarios en un solo proceso en CPU puede llevar demasiado tiempo. Afortunadamente, los test runner más modernos y las plataformas de CI (como [Jest](https://github.com/facebook/jest), [AVA](https://github.com/avajs/ava) y [Mocha extensions](https://github.com/yandex/mocha-parallel-tests)) pueden paralelizar los test en múltiples procesos y lograr una mejora importante en el tiempo en entregar feedback. Algunos proveedores de CI también paralelizan los test en contenedores (!) lo que acorta aún más la entrega de feedback. Ya sea localmente con múltiples procesos, o sobre algún CLI en cloud usando múltiples maquinas - parareliza manteniendo los test autónomos para que cada uno pueda ejecutarse en diferentes procesos
+
+❌ **De lo contrario:** Obtener el resultado de los test en 1 hora después de hacer push de código nuevo, mientras desarrollas nuevas funcionalidades, es la mejor receta para quitarle relevancia a los test
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :clap: Ejemplo de cómo hacerlo correctamente: Mocha parallel y Jest superan fácilmente al Mocha tradicional gracias a la paralelización de la ejecución de los test ([Credito: JavaScript Test-Runners Benchmark](https://medium.com/dailyjs/javascript-test-runners-benchmark-3a78d4117b4))
+
+")
+
+
+
+
+
+## ⚪ ️5.5 Mantente al margen de problemas legales usando la verificación de licencia y plagio
+
+:white_check_mark: **Haz:** Los problemas de licencias y plagio seguramente no son tu prioridad ahora mismo, pero ¿por qué no hacer check en esta tarea en solo 10 minutos? Existen muchos paquetes npm como [license check](https://www.npmjs.com/package/license-checker) y [plagiarism check](https://www.npmjs.com/package/plagiarism-checker) (commercial pero con plan gratuito) que puedes integrar fácilmente en tu pipeline CI e inspeccionar en busca de problemas como dependencias con licencias restrictivas, o código que fue copiado y pegado de stackoverflow que aparentemente viola algunos copyrights
+
+❌ **De lo contrario:** Sin querer, los desarrolladores pueden usar paquetes con licencias inapropiadas o copiar y pegar código comercial y encontrarse con problemas legales
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :clap: Ejemplo de cómo hacerlo correctamente:
+
+```javascript
+//instala license-checker en tu entorno de CI o localmente
+npm install -g license-checker
+
+//pídele que escanee todas las licencias y que termine con un código distinto de 0 si encuentra una licencia no autorizada. De esta forma el pipeline de CI puede detectarlo y detenerse
+license-checker --summary --failOn BSD
+
+```
+
+
+
+
+
+
+
+
+
+## ⚪ ️5.6 Verifica constantemente las dependencias vulnerables
+
+:white_check_mark: **Haz:** Incluso las dependencias más conocidas como express, tiene vulnerabilidades conocidas. Puedes dominarlas fácilmente usando herramientas libres como [npm audit](https://docs.npmjs.com/getting-started/running-a-security-audit), o commerciales como [snyk](https://snyk.io/) (que tiene una versión community gratuita). Ambas pueden ser ejecutadas desde tu CI en cada construcción
+
+❌ **De lo contrario:** Mantener tu código limpio de vulnerabilidades sin herramientas dedicadas requiere que estés revisando constantemente las nuevas versiones. Muy tedioso
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :clap: Ejemplo: resultado de NPM Audit
+
+
+
+
+
+
+
+## ⚪ ️5.7 Automatiza la actualización de dependencias
+
+:white_check_mark: **Haz:** La introducción en yarn y del package-lock.json de npm introduce un desafío muy importante (el camino al infierno esta lleno de buenas intenciones) — por defecto ahora los paquetes no se auto actualizan más. Incluso en un equipo que ejecute un 'npm install' limpio y 'npm update' no van a caer nuevas actualizaciones. Esto conduce a versiones de paquetes desactualizadas en el mejor de los casos, y en el peor, a código vulnerable. Los equipos pasan a depender de la buena voluntad y memoria de los desarrolladores para actualizar el package.json a mano o a utilizar herramientas como [ncu](https://www.npmjs.com/package/npm-check-updates) manualmente. Una formula mucho mejor seria automatizar el proceso de actualizar las versiones de las dependencias en las que más confiamos, pero no hay una solución perfecta, existen dos caminos para esta actualización:
+
+(1) Podemos hacer que el CI falle con dependencias obsoletas — usando herramientas como [‘npm outdated’](https://docs.npmjs.com/cli/outdated) o ‘npm-check-updates (ncu)’. Hacerlo obligará a los desarrolladores a actualizar las dependencias
+
+(2) Usar alguna de las herramientas comerciales que escanean nuestro código y envían automáticamente pull-request con actualización de dependencias. Una pregunta interesante que nos queda es cual va a ser la política de estas actualizaciones: si actualizamos cada parche se genera mucha sobrecarga, y hacerlo cuando haya una versión major nos lleva directos a usar versiones inestables o incompatibles (mucho paquetes muestran vulnerabilidades justo después de salir una versión nueva [lee sobre](https://nodesource.com/blog/a-high-level-post-mortem-of-the-eslint-scope-security-incident/) el incidente de eslint-scope)
+
+Una política de actualizaciones eficiente puede permitir cierto 'periodo de concesión' - deja pasar versiones quedándote por detrás de @latest un tiempo antes de considerar que tu versión en local está obsoleta (por ejemplo, la versión que tienes en local es 1.3.1 y la versión del repositorio del paquete es 1.3.8)
+
+
+
+❌ **De lo contrario:** Producción estará ejecutando versiones de paquetes que han sido marcadas por los propios autores como versiones con riesgos
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :clap: Ejemplo: [ncu](https://www.npmjs.com/package/npm-check-updates) puede ser usado a mano o en un pipline de CI para detectar en que medida el lag de retraso contra la versión @latest
+
+
+
+
+
+
+
+## ⚪ ️ 5.8 Otros consejos de CI no relacionados con Node
+
+:white_check_mark: **Haz:** Esta publicación se centra en los consejos sobre testing que están relacionados, o al menos pueden ejemplificarse con Node JS. Sin embargo, este punto agrupa algunos consejos no relacionados con Node que son bien conocidos
+
+
- Usa una sintaxis declarativa. Esta es la única opción para la mayoría de los pipelines de CI, pero las versiones antiguas de Jenkins permiten usar código o incluso una interfaz de usuario
- Opta por un CI que tenga soporte nativo de Docker
- Falla antes, ejecuta tus test más rápidos primero. Crea un paso / hito de 'Smoke testing' que agrupe múltiples verificaciones rápidas (por ejemplo, linter, test unitarios) y que proporcione comentarios rápidamente al desarrollador que haya commiteado
- Facilita la exploración de todas las partes de construcción, incluidos reportes de test, informes de cobertura, informes de mutación, registros, etc.
- Crea múltiples pipelines / jobs para cada evento y reutiliza los pasos entre ellos. Por ejemplo, configura un job para confirmaciones de commits a ramas de desarrollo y uno diferente para la rama master. Permite la reutilización de la lógica usando pasos compartidos en los pipelines (la mayoría de los proveedores proporcionan algún mecanismo para la reutilización de código)
- Nunca uses secretos directamente en la declaración del job, traelos de un store de secretos o de la configuración del propio job
- Suba explícitamente la versión de una release o al menos asegúrate de que el desarrollador lo hizo
- Construye solo una vez y realiza todas las inspecciones sobre el artefacto construido único (por ejemplo, imagen Docker)
- Testea en un entorno efímero que no arrastra el estado entre las construcciones. El almacenamiento en caché de node_modules podría ser la única excepción
+
+
+❌ **De lo contrario:** Echarás de menos años de sabiduría
+
+
+
+## ⚪ ️ 5.9 Build matrix: Ejecuta los mismos pasos de CI usando múltiples versiones de Node
+
+:white_check_mark: **Haz:** En control de calidad influye totalmente la casualidad, cuanto más terreno cubras, más suerte tendrás al detectar problemas temprano. Al desarrollar paquetes reutilizables o ejecutar un producto multicliente con varias configuraciones y versiones de Node, el CI debe ejecutar el pipeline de test sobre todas las combinaciones de configuraciones. Por ejemplo, suponiendo que usemos MySQL para algunos clientes y Postgres para otros, algunos proveedores de CI admiten una característica llamada 'Matrix' que permite ejecutar todos los test contra todas las combinaciones de MySQL, Postgres y versiones de Node múltiples como 8, 9 y 10. Esto se hace utilizando solo configuración, sin ningún esfuerzo adicional (suponiendo que tengas test o cualquier otro control de calidad). Otros CI que no admiten Matrix pueden tener extensiones o ajustes para permitirlo
+
+
+❌ **De lo contrario:** Entonces, después de hacer todo ese arduo trabajo de escribir los test, ¿vamos a dejar que los errores se cuelen solo por problemas de configuración?
+
+
+
+✏ Código de Ejemplo
+
+
+
+### :clap: Ejemplo: Usando la definición de construcción de Travis (proveedor de CI) para correr los mismos test en diferentes versiones de Node
+
+language: node_js
node_js:
- "7"
- "6"
- "5"
- "4"
install:
- npm install
script:
- npm run test
+
+
+
+
+# Equipo
+
+## Yoni Goldberg
+
+
+
+
+
+**Papel:** Escritor
+
+**Acerda de:** Soy un consultor independiente que trabaja con empresas de fortune 500 y startups en garajes para pulir sus aplicaciones JS & Node.js. Más que ningún otro tema me fascina y tengo como objetivo dominar el arte del testing. También soy el autor de [Node.js Buenas Practicas](https://github.com/goldbergyoni/nodebestpractices)
+
+**📗 Curso Online:** ¿Te gustó esta guía y deseas llevar tus habilidades de testing al máximo? Considera visitar mi curso completo [Testing Node.js & JavaScript From A To Z](https://www.testjavascript.com)
+
+
+
+**Sigeme:**
+
+- [🐦 Twitter](https://twitter.com/goldbergyoni/)
+- [📞 Contact](https://testjavascript.com/contact-2/)
+- [✉️ Newsletter](https://testjavascript.com/newsletter//)
+
+
+
+
+
+## [Bruno Scheufler](https://github.com/BrunoScheufler)
+
+**Rol:** Revisor técnico y asesor
+
+Se encargó de revisar, mejorar, lintear y pulir todos los textos
+
+**Acerda de:** Ingeniero full-stack web, entusiasta de Node.js y GraphQL
+
+
+
+
+## [Ido Richter](https://github.com/idori)
+
+**Rol:** Concepto, diseño y buenos consejos
+
+**Acerda de:** Un desarrollador frontend inteligente, experto en CSS y friki de los emojis
+
+## [Kyle Martin](https://github.com/js-kyle)
+
+**Rol:** Ayuda a mantener este proyecto en funcionamiento y revisa los test relacionadas con la seguridad
+
+**Acerda de:** Le encanta trabajar en proyectos Node.js y seguridad de aplicaciones web
+
+## Contribuyentes ✨
+
+¡Gracias a estas personas maravillosas que han contribuido a este repositorio!
+
+
+
+
+
+
+
+
+
diff --git a/readme-fr.md b/readme-fr.md
new file mode 100644
index 00000000..e03b122f
--- /dev/null
+++ b/readme-fr.md
@@ -0,0 +1,1967 @@
+
+
+
+
+# 👇 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
new file mode 100644
index 00000000..eb217e2d
--- /dev/null
+++ b/readme-pl.md
@@ -0,0 +1,2147 @@
+
+
+## 🎊 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
+
+
+
+## 📗 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
+
+## 🚢 Zaawansowane: przekracza 10 000 mil poza zwykłe podstawy
+
+Wskocz w podróż, która wykracza poza podstawy, podróż do zaawansowanych tematów, takich jak testowanie na produkcji, testowanie mutacji, testowanie na podstawie właściwości i wiele innych strategicznych i profesjonalnych narzędzi. Jeśli przeczytasz każde słowo w tym przewodniku, Twoje umiejętności testowania prawdopodobnie przekroczą średnią
+
+## 🌐 Full-stack: front, backend, CI, wszystko
+
+Zacznij od zrozumienia wszechobecnych praktyk testowania, które są podstawą każdej warstwy aplikacji. Następnie zagłęb się w wybrany obszar: frontend/UI, backend, CI, a może wszystkie?
+
+
+
+### Napisane przez Yoni Goldberg
+
+- Konsultant JavaScript & Node.js
+- 📗 [Testowanie Node.js i JavaScript od A do Z](https://www.testjavascript.com) - Mój kompleksowy kurs online z ponad [10 godzinami wideo](https://www.testjavascript.com), 14 typów testów i ponad 40 najlepszych praktyk
+- [Obserwuj mnie na Twitter](https://twitter.com/goldbergyoni/)
+
+
+
+### Tłumaczenia - czytaj w swoim własnym języku
+
+- 🇨🇳[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 💜
+
+
+
+## `Spis treści`
+
+#### [`Sekcja 0: Złota zasada`](#sekcja-0️⃣-złota-zasada)
+
+Jedna rada, która inspiruje wszystkie inne (1 punkt specjalny)
+
+#### [`Sekcja 1: Anatomia testu`](#sekcja-1-anatomia-testu-1)
+
+Podstawa - konstruowanie czystych testów (12 wypunktowań)
+
+#### [`Sekcja 2: Backend`](#sekcja-2️⃣-backend-testing)
+
+Pisanie backendu i wydajne testy mikroserwisów (13 wypunktowań)
+
+#### [`Sekcja 3: Frontend`](#sekcja-3️⃣-frontend-testing)
+
+Pisanie testów dla webowego interfejsu użytkownika, w tym testy komponentów i testy E2E (11 wypunktowań)
+
+#### [`Sekcja 4: Pomiary skuteczności testów`](#sekcja-4%EF%B8%8F%E2%83%A3-pomiar-skuteczno%C5%9Bci-testu)
+
+Pilnowanie strażnika - pomiar jakości testu (4 wypunktowania)
+
+#### [`Sekcja 5: Continuous Integration`](#sekcja-5️⃣-ci-oraz-inne-miary-jakości)
+
+Wytyczne dla CI w świecie JS (9 wypunktowań)
+
+
+
+# Sekcja 0️⃣: Złota zasada
+
+
+
+## ⚪️ 0 Złota zasada: Projektowanie dla lean testing
+
+:white_check_mark: **Opis:**
+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.
+
+Testy są okazją do czegoś innego - przyjaznego i uśmiechniętego asystenta, z którym przyjemnie się pracuje i zapewnia wielką wartość za tak małą inwestycję. Nauka mówi nam, że mamy dwa systemy mózgowe: system 1 służy do łatwych czynności, takich jak prowadzenie samochodu po pustej drodze, i system 2, który jest przeznaczony do złożonych i świadomych operacji, takich jak rozwiązywanie równania matematycznego. Zaprojektuj swój test dla systemu 1, gdy patrzysz na kod testowy, powinien on czuć się tak łatwo, jak modyfikacja dokumentu HTML, a nie jak rozwiązywanie 2X(17 × 24).
+
+Można to osiągnąć poprzez selektywne wybieranie technik, narzędzi i celów testowych, które są opłacalne i zapewniają duży zwrot z inwestycji. Testuj tylko tyle, ile potrzeba, staraj się, aby był zwinny, czasem warto porzucić niektóre testy i wymienić niezawodność na zwinność i prostotę.
+
+
+
+Większość poniższych porad to pochodne tej zasady.
+
+### Gotów by rozpocząć?
+
+
+
+# Sekcja 1: Anatomia testu
+
+
+
+## ⚪ ️ 1.1 Dołącz 3 części do każdej nazwy testu
+
+:white_check_mark: **Opis:** Raport z testu powinien informować, czy bieżąca wersja aplikacji spełnia wymagania osób, które niekoniecznie znają kod: testera, wdrażającego inżyniera DevOps i przyszłego ciebie za dwa lata. Można to najlepiej osiągnąć, jeśli testy są na poziomie wymagań i obejmują 3 części:
+
+(1) Co jest testowane? Na przykład, metoda ProductsService.addNewProduct
+
+(2) W jakich okolicznościach i scenariuszu? Na przykład żadna cena nie jest przekazywana do metody
+
+(3) Jaki jest oczekiwany wynik? Na przykład nowy produkt nie został zatwierdzony
+
+
+
+❌ **W przeciwnym razie:** Wdrożenie właśnie nie powiodło się, test o nazwie "Dodaj produkt" nie powiódł się. Czy to mówi ci, co dokładnie działa nieprawidłowo?
+
+
+
+**👇 Uwaga:** Każdy pocisk ma przykłady kodu, a czasem także ilustrację. Kliknij aby rozszerzyć
+
+✏ Przykłady kodu
+
+
+
+### :clap: Przykład robienia tego dobrze: nazwa testu, która składa się z 3 części
+
+
+
+```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: Przykład robienia tego dobrze: nazwa testu, która składa się z 3 części
+
+
+
+
+
+
+© Credits & read-more
+ 1. Roy Osherove - Naming standards for unit tests
+
+
+
+
+## ⚪ ️ 1.2 Struktura testów według wzorca AAA
+
+:white_check_mark: **Opis:** Ustrukturyzuj swoje testy za pomocą 3 dobrze oddzielonych sekcji: Arrange, Act & Assert (AAA). Przestrzeganie tej struktury gwarantuje, że czytelnik nie poświęci procesora mózgu na zrozumienie planu testu:
+
+1st A - Arrange: Cały kod instalacyjny, aby wprowadzić system do scenariusza, którego test ma na celu symulację. Może to obejmować tworzenie instancji testowanego konstruktora, dodawanie rekordów DB, mockowanie/usuwanie obiektów i każdy inny kod przygotowawczy
+
+2nd A - Act: Wykonaj unit pod test. Zwykle 1 linia kodu
+
+3rd A - Assert: Upewnij się, że otrzymana wartość spełnia oczekiwania. Zwykle 1 linia kodu
+
+
+
+❌ **W przeciwnym razie:** Nie tylko spędzasz godziny na zrozumieniu głównego kodu, ale to, co powinno być najprostszą częścią dnia (testowanie) obciąża Twój mózg
+
+
+
+✏ Przykłady kodu
+
+
+
+### :clap: Przykład robienia tego dobrze: test oparty na wzorcu 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: Przykład antywzorca: brak separacji, jedna masa, trudniejsza do interpretacji
+
+```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 Opisz oczekiwania w języku produktu: stosuj asercje w stylu BDD
+
+:white_check_mark: **Opis:** Kodowanie testów w stylu deklaratywnym pozwala czytelnikowi na natychmiastowe złapanie go bez wydawania nawet jednego cyklu mózg-procesor. Kiedy piszesz kod imperatywny wypełniony logiką warunkową, czytelnik jest zmuszony wywierać więcej cykli mózg-procesor. W takim przypadku zakoduj oczekiwanie w języku przypominającym język człowieka, deklaratywnym stylu BDD, używając `expect` lub `should` i nie używając niestandardowego kodu. Jeśli Chai & Jest nie zawiera żądanej asercji i jest wysoce powtarzalne, rozważ [rozszerzenie Jest matcher (Jest)](https://jestjs.io/docs/en/expect#expectextendmatchers) lub napisanie [wtyczki niestandardowej Chai](https://www.chaijs.com/guide/plugins/)
+
+
+❌ **W przeciwnym razie:** Zespół napisze mniej testów i ozdobi części irytujące z .skip()
+
+
+
+✏ Przykłady kodu
+
+ 
+
+### :thumbsdown: Przykład antywzorca: Czytelnik musi przejrzeć niezbyt krótki i imperatywny kod, aby uzyskać historię testową
+
+```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: Przykład robienia tego dobrze: Przejrzenie poniższego testu deklaratywnego to pestka
+
+```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 Trzymaj się testów czarnej skrzynki: testuj tylko metody publiczne
+
+:white_check_mark: **Opis:** Testowanie elementów wewnętrznych przynosi ogromne koszty prawie za nic. Jeśli Twój kod / interfejs API zapewnia prawidłowe wyniki, czy naprawdę warto zainwestować następne 3 godziny w testowanie JAK działało ono wewnętrznie, a następnie utrzymać te delikatne testy? Za każdym razem, gdy sprawdzane jest zachowanie publiczne, implementacja prywatna jest również domyślnie testowana, a testy zostaną przerwane tylko w przypadku wystąpienia określonego problemu (np. nieprawidłowego wyniku). Takie podejście jest również określane jako `behavioral testing`. Z drugiej strony, jeśli przetestujesz wewnętrzne elementy (podejście z białą ramką) - skupiasz się na planowaniu wyniku komponentu na drobiazgowe szczegóły, a twój test może się zepsuć z powodu drobnych refaktorów kodu, chociaż wyniki są w porządku - to dramatycznie zwiększa konserwację, obciąża
+
+
+❌ **W przeciwnym razie:** Twoje testy zachowują się jak [chłopiec, który wołał wilka](https://en.wikipedia.org/wiki/The_Boy_Who_Cried_Wolf): krzycząc false-positive (np. test kończy się niepowodzeniem, ponieważ zmieniono nazwę zmiennej prywatnej). Nic dziwnego, że ludzie wkrótce zaczną ignorować powiadomienia CI, aż pewnego dnia prawdziwy błąd zostanie zignorowany…
+
+
+✏ Przykłady kodu
+
+
+
+### :thumbsdown: Przykład antywzorca: Przypadek testowy testuje elementy wewnętrzne bez uzasadnionego powodu
+
+
+
+```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 Wybierz odpowiedni test doubles: Unikaj mockowania na rzecz stubs i spies
+
+:white_check_mark: **Opis:** Test doubles są złem koniecznym, ponieważ są sprzężone z wewnętrznymi elementami aplikacji, ale niektóre zapewniają ogromną wartość ([Przeczytaj tutaj przypomnienie o test doubles: mocks vs stubs vs spies](https://martinfowler.com/articles/mocksArentStubs.html)).
+
+Przed użyciem test doubles zadaj bardzo proste pytanie: czy używam go do testowania funkcjonalności, która pojawia się lub może pojawić się w dokumencie wymagań? Jeśli nie, jest to white-box testing smell.
+
+ Na przykład jeśli chcesz przetestować, czy aplikacja zachowuje się rozsądnie, gdy usługa płatnicza jest wyłączona, możesz zlikwidować usługę płatniczą i uruchomić niektóre zwracając ‘Brak odpowiedzi’, aby upewnić się, że testowana jednostka zwraca prawidłową wartość. To sprawdza zachowanie / odpowiedź / wynik naszej aplikacji w określonych scenariuszach. Możesz także użyć spies, aby potwierdzić, że wiadomość e-mail została wysłana, gdy ta usługa nie działa - jest to ponownie kontrola behawioralna, która prawdopodobnie pojawi się w dokumencie wymagań („Wyślij wiadomość e-mail, jeśli nie można zapisać płatności”). Z drugiej strony, jeśli mockujesz usługę płatności i upewniasz się, że została ona wywołana za pomocą odpowiednich typów JavaScript - wtedy twój test koncentruje się na wewnętrznych rzeczach, które nie mają nic z funkcjonalnością aplikacji i prawdopodobnie często się zmieniają
+
+
+❌ **W przeciwnym razie:** Wszelka refaktoryzacja kodu nakazuje wyszukiwanie wszystkich próbnych elementów w kodzie i odpowiednią aktualizację. Testy stają się ciężarem, a nie pomocnym przyjacielem
+
+
+
+✏ Przykłady kodu
+
+
+
+### :thumbsdown: Przykład antywzorca: Mocks skupiające się na elementach wewnętrznych
+
+
+
+```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:Przykład robienia tego dobrze: spies koncentrują się na testowaniu wymagań, ale jako efekt uboczny nieuchronnie dotykają elementów wewnętrznych
+
+```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;
+});
+```
+
+
+
+
+
+## 📗 Chcesz poznać wszystkie te praktyki z wideo na żywo?
+
+### Odwiedź mój kurs online [Testowanie Node.js i JavaScript od A do Z](https://www.testjavascript.com)
+
+
+
+## ⚪ ️1.6 Nie "foo", używaj realistycznych danych wejściowych
+
+:white_check_mark: **Opis:** Często błędy produkcyjne są ujawniane pod bardzo konkretnymi i zaskakującymi danymi wejściowymi - im bardziej realistyczny jest wkład testowy, tym większe są szanse na wczesne wykrycie błędów. Użyj dedykowanych bibliotek takich jak [Faker](https://www.npmjs.com/package/faker) do generowania pseudo-rzeczywistych danych, które przypominają różnorodność i formę danych produkcyjnych. Na przykład takie biblioteki mogą generować realistyczne numery telefonów, nazwy użytkowników, karty kredytowe, nazwy firm, a nawet tekst „lorem ipsum”. Możesz także utworzyć niektóre testy (oprócz testów jednostkowych, a nie zamienników), które losowo dodają fałszywych danych, aby rozciągnąć testowaną jednostkę lub nawet zaimportować prawdziwe dane ze środowiska produkcyjnego. Chcesz przenieść go na wyższy poziom? Zobacz następny punkt (testy oparte na właściwościach).
+
+
+❌ **W przeciwnym razie:** Wszystkie testy programistyczne będą fałszywie pokazywać kolor zielony, gdy użyjesz syntetycznych danych wejściowych, takich jak „Foo”, ale wtedy produkcja może zmienić kolor na czerwony, gdy haker przejdzie tak nieprzyjemny ciąg znaków, jak “@3e2ddsf . ##’ 1 fdsfds . fds432 AAAA”
+
+
+
+✏ Przykłady kodu
+
+
+
+### :thumbsdown: Przykład antywzorca: Zestaw testowy, który przechodzi z powodu nierealistycznych danych
+
+
+
+```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:Przykład robienia tego dobrze: Randomizing realistic input
+
+```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 Testowanie wielu kombinacji danych wejściowych za pomocą testów opartych na właściwościach
+
+ :white_check_mark: **Opis:** Zazwyczaj wybieramy kilka próbek wejściowych dla każdego testu. Nawet jeśli format wejściowy przypomina dane rzeczywiste (zobacz punkt ‘Nie foo’), obejmujemy tylko kilka kombinacji danych wejściowych (method(‘’, true, 1), method(“string” , false” , 0)). Jednak w produkcji interfejs API, który jest wywoływany z 5 parametrami, może być wywoływany z tysiącami różnych kombinacji, jeden z nich może spowolnić nasz proces ([zobacz Fuzz Testing](https://en.wikipedia.org/wiki/Fuzzing)). Co jeśli mógłbyś napisać pojedynczy test, który automatycznie wysyła 1000 permutacji różnych danych wejściowych i wyłapuje, dla których danych wejściowych nasz kod nie zwraca poprawnej odpowiedzi? Testowanie oparte na właściwościach jest techniką, która robi dokładnie to: wysyłając wszystkie możliwe kombinacje danych wejściowych do testowanego urządzenia, zwiększa to prawdopodobieństwo znalezienia błędu. Na przykład, biorąc pod uwagę metodę - addNewProduct (identyfikator, nazwa, isDiscount) - biblioteki obsługujące będą wywoływać tę metodę z wieloma kombinacjami (liczba, łańcuch, wartość logiczna), takich jak (1, „iPhone”, false), (2, „Galaxy ", prawdziwe). Możesz uruchomić testy oparte na właściwościach, używając swojego ulubionego test runnera (Mocha, Jest, etc) za pomocą bibliotek takich jak [js-verify](https://github.com/jsverify/jsverify) lub [testcheck](https://github.com/leebyron/testcheck-js) (znacznie lepsza dokumentacja). Aktualizacja: Nicolas Dubien sugeruje w komentarzach poniżej [sprawdzenie fast-check](https://github.com/dubzzz/fast-check#readme) który wydaje się oferować dodatkowe funkcje, a także jest aktywnie utrzymywany
+
+
+❌ **W przeciwnym razie:** Nieświadomie wybierasz wejścia testowe, które obejmują tylko dobrze działające ścieżki kodu. Niestety obniża to efektywność testowania jako pojazdu do wykrywania błędów
+
+
+
+✏ Przykłady kodu
+
+
+
+### :clap: Przykład robienia tego dobrze: Testing many input permutations with “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 Jeśli potrzeba, użyj tylko short & inline snapshots
+
+:white_check_mark: **Opis:** Gdzie jest potrzeba na [testy snapshot](https://jestjs.io/docs/en/snapshot-testing), używaj tylko krótkich i skoncentrowanych snapshotów (np. 3-7 linie), które są uwzględnione w ramach testu ([Inline Snapshot](https://jestjs.io/docs/en/snapshot-testing#inline-snapshots)) i nie w plikach zewnętrznych. Przestrzeganie tych wytycznych sprawi, że testy będą zrozumiałe i mniej kruche.
+
+Z drugiej strony, poradniki ‘klasycznych snapshotów’ i narzędzia zachęcają do przechowywania dużych plików (np. znaczników renderowania komponentu, wyniku API JSON) na jakimś zewnętrznym nośniku i zapewniają za każdym razem, gdy uruchamiany jest test w celu porównania otrzymanego wyniku z zapisaną wersją. To, na przykład, może pośrednio powiązać nasz test z 1000 liniami z 3000 wartościami danych, o których twórca testów nigdy nie czytał i nie uzasadniał. Dlaczego to źle? W ten sposób istnieje 1000 powodów niepowodzenia testu - wystarczy zmienić jedną linię, aby migawka stała się nieważna, i prawdopodobnie zdarzy się to często. Jak często? Dla każdej przestrzeni, komentarza lub drobnej zmiany CSS / HTML. Nie tylko to, nazwa testu nie dałaby informacji o niepowodzeniu, ponieważ po prostu sprawdza, czy 1000 wierszy się nie zmieniło, zachęca także twórcy testu do zaakceptowania jako pożądanego prawdziwego długiego dokumentu, którego nie mógł sprawdzić i zweryfikować. Wszystko to są objawy niejasnego i niecierpliwego testu, który nie jest skoncentrowany i ma na celu osiągnięcie zbyt wiele
+
+Warto zauważyć, że istnieje kilka przypadków, w których dopuszczalne są długie i zewnętrzne migawki - podczas potwierdzania schematu, a nie danych (wyodrębnianie wartości i skupianie się na polach) lub gdy otrzymany dokument rzadko się zmienia
+
+
+❌ **W przeciwnym razie:** Test interfejsu użytkownika kończy się niepowodzeniem. Kod wydaje się prawidłowy, ekran wyświetla idealne piksele, co się stało? Twoje testowanie migawek właśnie znalazło różnicę między dokumentem źródłowym, a aktualnie otrzymanym - do znacznika została dodana pojedyncza spacja...
+
+
+
+✏ Przykłady kodu
+
+
+
+### :thumbsdown: Przykład antywzorca: Łączymy nasz test z niewidzialnymi 2000 liniami kodu
+
+
+
+```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: Przykład robienia tego dobrze: Oczekiwania są widoczne i skoncentrowane
+
+```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 Kopiuj kod, ale tylko to, co niezbędne
+
+: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:** 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
+
+
+
+✏ Przykłady kodu
+
+
+
+### :thumbsdown: Przykład antywzorca: niepowodzenie testu jest niejasne, ponieważ cała przyczyna jest zewnętrzna i ukryta w ogromnym formacie JSON
+
+
+
+```javascript
+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 prawidłowo: Test wskazuje, co jest przyczyną wyniku testu
+
+```javascript
+
+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});
+
+ // 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
+
+:white_check_mark: **Opis:** Podczas próby stwierdzenia, że niektóre dane wejściowe powodują błąd, może być właściwe użycie try-catch-finally i zapewnienie, że wprowadzono klauzulę catch. Wynikiem jest niezręczny i pełny przypadek testowy (przykład poniżej), który ukrywa prosty cel testu i oczekiwania na wynik
+
+Bardziej elegancką alternatywą jest użycie dedykowanego asercji Chai w jednym wierszu: expect (method).to.throw (lub w Jest: expect(method).toThrow()). Absolutnie obowiązkowe jest również upewnienie się, że wyjątek zawiera właściwość określającą typ błędu, w przeciwnym razie biorąc pod uwagę tylko ogólny błąd, aplikacja nie będzie w stanie zrobić wiele, zamiast wyświetlać rozczarowujący komunikat użytkownikowi
+
+
+❌ **W przeciwnym razie:** Trudno będzie wnioskować z raportów testów (np. raportów CI), co poszło nie tak
+
+
+
+✏ Przykłady kodu
+
+
+
+### :thumbsdown: Przykład antywzorca: Długi przypadek testowy, który próbuje potwierdzić istnienie błędu z 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: Przykład robienia tego dobrze: Oczekiwanie czytelne dla człowieka, które może być łatwo zrozumiane, może nawet przez QA lub technicznego 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 Taguj twoje testy
+
+:white_check_mark: **Opis:** Różne testy muszą być uruchamiane w różnych scenariuszach: quick smoke, IO-less, testy powinny być uruchamiane, gdy programista zapisuje lub commituje plik, pełne kompleksowe testy zwykle uruchamiane są po przesłaniu nowego pull requesta itp. Można to osiągnąć poprzez oznaczenie testów słowami kluczowymi takimi jak #cold #api #sanity, aby można było grepować za pomocą uprzęży testującej i wywołać pożądany podzbiór. Na przykład w ten sposób można wywołać tylko grupę sanity test z Mocha: mocha — grep ‘sanity’
+
+
+❌ **W przeciwnym razie:** Uruchamianie wszystkich testów, w tym testów, które wykonują dziesiątki zapytań BD, za każdym razem, gdy programista wprowadzi małą zmianę, może być bardzo powolny i powstrzymuje programistów przed uruchomieniem testów
+
+
+
+✏ Przykłady kodu
+
+
+
+### :clap: Przykład robienia tego dobrze: Tagowanie testów jako ‘#cold-test’ umożliwia test runnerowi wykonywanie tylko szybkich testów (testy cold===quick które nie wykonują operacji wejścia/wyjścia i mogą być wykonywane często, nawet gdy programista pisze)
+
+
+
+```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 Kategoryzuj testy na co najmniej 2 poziomach
+
+:white_check_mark: **Opis:** Zastosuj pewną strukturę do swojego zestawu testów, aby od czasu do czasu odwiedzający mógł łatwo zrozumieć wymagania (testy to najlepsza dokumentacja) i różne testowane scenariusze. Powszechną metodą jest umieszczanie co najmniej 2 bloków 'opisz' nad testami: pierwszy to nazwa testowanej jednostki, a drugi to dodatkowy poziom kategoryzacji, taki jak scenariusz lub kategorie niestandardowe (patrz przykłady kodu i prtscn poniżej). Takie postępowanie znacznie poprawi również raporty z testów: Czytelnik łatwo wywnioskuje kategorie testów, zagłębi się w żądaną sekcję i skoreluje testy zakończone niepowodzeniem. Ponadto programistom łatwiej będzie poruszać się po kodzie pakietu z wieloma testami. Istnieje wiele alternatywnych struktur dla zestawu testów, które możesz rozważyć, jak [given-when-then](https://github.com/searls/jasmine-given) oraz [RITE](https://github.com/ericelliott/riteway)
+
+
+
+❌ **W przeciwnym razie:** Patrząc na raport z płaską i długą listą testów, czytelnik musi przejrzeć długie teksty, aby zakończyć główne scenariusze i skorelować powszechność nieudanych testów. Rozważ następujący przypadek: gdy testy 7/100 zakończą się niepowodzeniem, przeglądanie płaskiej listy będzie wymagało przeczytania tekstu testów zakończonych niepowodzeniem, aby zobaczyć, jak się ze sobą wiążą. Jednak w hierarchicznym raporcie wszystkie z nich mogą podlegać temu samemu przepływowi lub kategorii, a czytelnik szybko zorientuje się, co lub gdzie jest źródło przyczyny awarii
+
+
+
+✏ Przykłady kodu
+
+
+
+### :clap: Przykład robienia tego dobrze: Strukturyzacja pakietu z nazwą jednostki pod test i scenariuszy doprowadzi do wygodnego raportu pokazanego poniżej
+
+
+
+```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: Przykład antywzorca: Płaska lista testów utrudni czytelnikowi identyfikację historii użytkowników i skorelowanie testów zakończonych niepowodzeniem
+
+
+
+```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 Inne ogólne dobre zasady higieny testowania
+
+:white_check_mark: **Opis:** Ten post skupia się na poradach dotyczących testowania, które są związane lub przynajmniej mogą być zilustrowane przykładem Node JS. Ten punkt zawiera jednak kilka dobrze znanych wskazówek niezwiązanych z Node
+
+
+Uczyć się i ćwiczyć [zasady TDD](https://www.sm-cloud.com/book-review-test-driven-development-by-example-a-tldr/) — dla wielu są niezwykle cenne, ale nie przestrasz się, jeśli nie pasują do Twojego stylu, nie tylko tobie. Rozważ napisanie testów przed kodem w [style red-green-refactor](https://blog.cleancoder.com/uncle-bob/2014/12/17/TheCyclesOfTDD.html), upewnij się, że każdy test sprawdza dokładnie jedną rzecz, gdy znajdziesz błąd - przed naprawą napisz test, który wykryje ten błąd w przyszłości, pozwól każdemu testowi zawieść co najmniej raz, zanim zmieni kolor na zielony, uruchom moduł, pisząc szybki i uproszczony kod, który satysfakcjonuje test - następnie stopniowo refaktoryzuj i przenieś go do poziomu klasy produkcyjnej, unikaj jakiejkolwiek zależności od środowiska (ścieżki, systemu operacyjnego itp.)
+
+
+❌ **W przeciwnym razie:** Będziesz tęsknić za perłami mądrości zbieranymi przez dziesięciolecia
+
+
+
+# Sekcja 2️⃣: Backend Testing
+
+## ⚪ ️2.1 Wzbogać swoje portfolio testowe: wyjdź poza testy jednostkowe i piramidę
+
+:white_check_mark: **Opis:** [Piramida testowania](https://martinfowler.com/bliki/TestPyramid.html), pomimo że 10> lat starsza, to świetny i odpowiedni model, który sugeruje trzy typy testowania i wpływa na strategię testowania większości programistów. Jednocześnie pojawiła się ponad garstka nowych, błyszczących technik testowania, które ukrywają się w cieniu piramidy testowania. Biorąc pod uwagę wszystkie dramatyczne zmiany, które widzieliśmy w ciągu ostatnich 10 lat (Microservices, cloud, serverless), czy jest możliwe, że jeden dość stary model będzie odpowiedni _wszystkim_ typom aplikacji? Czy świat testowania nie powinien rozważyć przyjęcia nowych technik testowania?
+
+Nie zrozumcie mnie źle, w 2019 roku piramida testowania, TDD i testy jednostkowe są nadal potężną techniką i prawdopodobnie najlepiej pasują do wielu aplikacji. Tylko jak każdy inny model, pomimo swojej przydatności, [czasem musi się mylić](https://en.wikipedia.org/wiki/All_models_are_wrong). Rozważmy na przykład aplikację IOT, która pobiera wiele zdarzeń do magistrali komunikatów, takiej jak Kafka/RabbitMQ, która następnie przepływa do jakiejś hurtowni danych i jest w końcu odpytywana przez interfejs analityczny. Czy naprawdę powinniśmy wydać 50% naszego budżetu z testów na pisanie testów jednostkowych dla aplikacji, która jest zorientowana na integrację i prawie nie ma logiki? Wraz ze wzrostem różnorodności typów aplikacji (boty, krypto, Alexa-skills) rośnie szansa na znalezienie scenariuszy, w których piramida testowania nie jest najlepszym rozwiązaniem.
+
+Nadszedł czas, aby wzbogacić swoje portfolio testowania i zapoznać się z większą liczbą typów testów (następne punkty sugerują kilka pomysłów), modelami umysłu, takimi jak piramida testowania, ale także dopasować typy testowania do rzeczywistych problemów, z którymi się borykasz (‘Hej, nasz interfejs API jest zepsuty, napiszmy testowanie umów konsumenckich!’), zdywersyfikuj swoje testy jak inwestor, który buduje portfel na podstawie analizy ryzyka - oceń, gdzie mogą pojawić się problemy i dopasuj niektóre środki zapobiegawcze, aby zmniejszyć potencjalne ryzyko
+
+Słowo ostrzeżenia: argument TDD w świecie oprogramowania ma typową fałszywą dychotomię, niektórzy głoszą, że można go używać wszędzie, inni uważają, że to diabeł. Każdy, kto mówi w absolutach, jest w błędzie :]
+
+
+
+❌ **W przeciwnym razie:** Będziesz tęsknić za niektórymi narzędziami z niesamowitym ROI, takimi jak Fuzz, lint, i mutacją która może zapewnić wartość w 10 minut
+
+
+
+✏ Przykłady kodu
+
+
+
+### :clap: Przykład robienia tego dobrze: Cindy Sridharan sugeruje wzbogacić portfolio testowania w swoim niesamowitym poście 'Testowanie mikrousług - w ten sam sposób'
+
+
+
+☺️Przykład: [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 Testowanie komponentów może być Twoją najlepszą kwestią
+
+: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
+
+
+
+✏ Przykłady kodu
+
+
+
+### :clap: Przykład robienia tego dobrze: Supertest pozwala zbliżyć się do Express API w trakcie procesu (szybki i obejmuje wiele warstw)
+
+
+
+ allows approaching Express API in-process (fast and cover many layers)")
+
+
+
+
+
+## ⚪ ️2.3 Upewnij się, że nowe wersje nie psują interfejsu API
+
+:white_check_mark: **Opis:** Tak więc twój mikroserwis ma wielu klientów i uruchamiasz wiele wersji usługi ze względu na kompatybilność (aby wszyscy byli zadowoleni). Potem zmieniasz jakieś pole i 'buum!'. Jakiś ważny klient, który działa na tym, jest zły. Oto Catch-22 w świecie integracji: po stronie serwera bardzo trudne jest uwzględnienie wszystkich oczekiwań wielu klientów - z drugiej strony klienci nie mogą przeprowadzać żadnych testów, ponieważ serwer kontroluje daty wydania. [Umowy konsumenckie i framework PACT](https://docs.pact.io/) stworzone zostały, aby sformalizować ten proces z bardzo destrukcyjnym podejściem - nie serwer sam określa plan testów, a klient określa testy… serwera! PACT może rejestrować oczekiwania klienta i umieszczać je we wspólnej lokalizacji, „brokerze”, dzięki czemu serwer może wyciągać oczekiwania i uruchamiać każdą kompilację za pomocą biblioteki PACT, aby wykrywać zerwane umowy - oczekiwanie klienta, które nie jest spełnione. W ten sposób wszystkie niedopasowania API serwer-klient zostaną wykryte wcześnie podczas kompilacji / CI i mogą zaoszczędzić sporo frustracji.
+
+
+❌ **W przeciwnym razie:** Alternatywami są wyczerpujące testy ręczne lub strach przed wdrożeniem
+
+
+
+✏ Przykłady kodu
+
+
+
+### :clap: Przykład robienia tego dobrze:
+
+
+
+
+
+
+
+
+
+## ⚪ ️ 2.4 Przetestuj swoje middleware w izolacji
+
+:white_check_mark: **Opis:** Wiele osób unika testowania oprogramowania pośredniego, ponieważ stanowią one niewielką część systemu i wymagają aktywnego serwera Express. Oba powody są błędne - oprogramowanie pośrednie jest małe, ale wpływa na wszystkie lub większość żądań i można je łatwo przetestować jako dostępne funkcje {req,res} obiekty JS. Aby przetestować funkcję oprogramowania pośredniego, należy ją po prostu wywołać i szpiegować ([używając na przykład Sinon](https://www.npmjs.com/package/sinon)) w połączeniu z obiektami {req,res} aby upewnić się, że funkcja wykonała właściwą akcję.
+Biblioteka [node-mock-http](https://www.npmjs.com/package/node-mocks-http) posuwa się jeszcze dalej i uwzględnia obiekty {req, res} wraz ze szpiegowaniem ich zachowania. Na przykład można sprawdzić, czy status HTTP ustawiony w obiekcie res jest zgodny z oczekiwaniami (patrz przykład poniżej)
+
+
+❌ **W przeciwnym razie:** Błąd w middleware Express === błąd we wszystkich lub większości żądań
+
+
+
+✏ Przykłady kodu
+
+
+
+### :clap:Przykład robienia tego dobrze: Tesowanie middleware w izolacji, bez wykonywania połączeń sieciowych i budzenia całej maszyny 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 Pomiar i refaktoryzacja za pomocą narzędzi do analizy statycznej
+
+:white_check_mark: **Opis:** Korzystanie z narzędzi do analizy statycznej pomaga, zapewniając obiektywne sposoby poprawy jakości kodu i utrzymania kodu w stanie możliwym do utrzymania. Możesz dodać narzędzia analizy statycznej do kompilacji CI, aby przerwać, gdy wykryje code smells. Jego głównymi zaletami w stosunku do zwykłego lintowania jest możliwość kontroli jakości w kontekście wielu plików (np. wykrywanie duplikacji), przeprowadzania zaawansowanej analizy (np. złożoności kodu) oraz śledzenia historii i postępu problemów z kodem. Są dwa przykłady narzędzi, których możesz użyć [Sonarqube](https://www.sonarqube.org/) (2,600+ [gwiazdek](https://github.com/SonarSource/sonarqube)) oraz [Code Climate](https://codeclimate.com/) (1,500+ [gwiazdek](https://github.com/codeclimate/codeclimate))
+
+Źródło: [Keith Holliday](https://github.com/TheHollidayInn)
+
+
+
+❌ **W przeciwnym razie:** Przy złej jakości kodu błędy i wydajność zawsze będą stanowić problem, którego nie będzie w stanie naprawić żadna nowa błyszcząca biblioteka ani najnowocześniejsze funkcje
+
+
+
+✏ Przykłady kodu
+
+
+
+### :clap: Przykład robienia tego dobrze: CodeClimate, komercyjne narzędzie, które potrafi zidentyfikować złożone metody:
+
+
+
+
+
+
+
+
+
+## ⚪ ️ 2.6 Sprawdź swoją gotowość na chaos związany z Node
+
+:white_check_mark: **Opis:** Co dziwne, większość testów oprogramowania dotyczy wyłącznie logiki i danych, ale jednymi z najgorszych rzeczy, które się zdarzają (i naprawdę trudno je złagodzić) są problemy infrastrukturalne. Na przykład, czy kiedykolwiek testowałeś, co dzieje się, gdy pamięć procesowa jest przeciążona lub kiedy serwer/proces umiera, czy też twój system monitorowania zdaje sobie sprawę, kiedy API staje się o 50% wolniejsze? Aby przetestować i złagodzić tego rodzaju złe rzeczy — [Chaos engineering](https://principlesofchaos.org/) został stworzony przez Netflix. Ma na celu zapewnienie świadomości, frameworków i narzędzi do testowania odporności naszej aplikacji na chaotyczne problemy. Na przykład jedno z jego słynnych narzędzi, [the chaos monkey](https://github.com/Netflix/chaosmonkey), losowo zabija serwery, aby mieć pewność, że nasza usługa może nadal obsługiwać użytkowników i nie polegać na jednym serwerze (istnieje również wersja Kubernetes, [kube-monkey](https://github.com/asobti/kube-monkey), która zabija pods). Wszystkie te narzędzia działają na poziomie hosta / platformy, ale co zrobić, jeśli chcesz przetestować i wygenerować czysty chaos w Node, na przykład sprawdzić, jak proces Node'a radzi sobie z nieprzechwyconymi błędami, nieobsługiwanym odrzuceniem obietnicy (promise), pamięci v8 przeciążonej maksymalną dozwoloną wartością 1,7 GB lub czy Twój UX pozostaje zadowalający, gdy pętla zdarzeń jest często blokowana? Aby rozwiązać ten problem, napisałem, [node-chaos](https://github.com/i0natan/node-chaos-monkey) (alpha) który zapewnia wszelkiego rodzaju chaotyczne akty związane z Node'm.
+
+
+❌ **W przeciwnym razie:** Nie ma ucieczki, prawo Murphy'ego uderzy w twoją produkcję bez litości
+
+
+
+✏ Przykłady kodu
+
+
+
+### :clap: Przykład robienia tego dobrze: : Node-chaos może generować różnego rodzaju pranki z Node.js, dzięki czemu możesz przetestować odporność aplikacji na chaos
+
+
+
+
+
+
+
+## ⚪ ️2.7 Unikaj global test fixtures oraz seeds, dodawaj dane na test
+
+:white_check_mark: **Opis:** Przestrzeganie złotej zasady (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’) w celu poprawy wydajności. Chociaż wydajność jest istotnym problemem - można ją złagodzić (patrz punkt “Component testing”), jednak złożoność testu jest bardzo bolesnym smutkiem, który przez większość czasu powinien rządzić innymi rozważaniami. Spraw praktycznie, 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)
+
+
+❌ **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
+
+
+
+✏ Przykłady kodu
+
+
+
+### :thumbsdown: Przykład antywzorca: testy nie są niezależne i polegają na pewnym globalnym hook do zasilania globalnych danych BD
+
+
+
+```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 :[
+});
+
+```
+
+
+
+### :clap: Przykład robienia tego dobrze: Możemy pozostać w teście, każdy test działa na własny zestaw danych
+
+```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);
+});
+```
+
+
+
+
+
+## ⚪ ️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
+
+## ⚪ ️ 3.1 Oddziel interfejs użytkownika od funkcjonalności
+
+:white_check_mark: **Opis:** Podczas koncentrowania się na testowaniu logiki komponentu szczegóły interfejsu użytkownika stają się szumem, który należy wyodrębnić, aby testy mogły koncentrować się na czystych danych. Praktycznie wyodrębnij pożądane dane ze znaczników w abstrakcyjny sposób, który nie jest zbyt sprzężony z implementacją graficzną, potwierdzaj tylko na czystych danych (vs szczegóły graficzne HTML / CSS) i wyłącz spowalniające animacje. Możesz ulec pokusie unikania renderowania i testowania tylko tylnej części interfejsu użytkownika (np. usług, akcji, store), ale spowoduje to testy fikcyjne, które nie przypominają rzeczywistości i nie ujawnią przypadków, w których właściwe dane nie są nawet przybyć do interfejsu użytkownika
+
+
+
+❌ **W przeciwnym razie:** Czysto obliczone dane z testu mogą być gotowe za 10 ms, ale wtedy cały test potrwa 500 ms (100 testów = 1 minuta) z powodu jakiejś wymyślnej i nieistotnej animacji
+
+
+
+✏ Przykłady kodu
+
+
+
+### :clap: Przykład robienia tego dobrze: Oddzielanie szczegółów interfejsu użytkownika
+
+ 
+
+```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: Przykład antywzorca: asercja miesza szczegóły interfejsu użytkownika i dane
+
+```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 Zapytaj elementy HTML na podstawie atrybutów, których zmiana jest mało prawdopodobna
+
+:white_check_mark: **Opis:** Zapytaj elementy HTML na podstawie atrybutów, które prawdopodobnie przetrwają zmiany graficzne, w przeciwieństwie do selektorów CSS i podobnych etykiet formularzy. Jeśli wyznaczony element nie ma takich atrybutów, utwórz dedykowany atrybut testowy, taki jak 'test-id-submit-button'. Podążanie tą drogą nie tylko gwarantuje, że testy funkcjonalne / logiczne nigdy nie psują się z powodu zmian wyglądu i odczuć, ale także staje się jasne dla całego zespołu, że ten element i atrybut są wykorzystywane przez testy i nie należy ich usuwać
+
+
+
+❌ **W przeciwnym razie:** Chcesz przetestować funkcjonalność logowania obejmującą wiele komponentów, logikę i usługi, wszystko jest skonfigurowane idealnie - stubs, spies, połączenia Ajax są izolowane. Wszystko wydaje się idealne. Następnie test kończy się niepowodzeniem, ponieważ projektant zmienił klasę CSS div 'thick-border' do 'thin-border'
+
+
+
+✏ Przykłady kodu
+
+
+
+### :clap: Przykład robienia tego dobrze: Zapytanie o element przy użyciu dedykowanego atrybutu do testowania
+
+
+
+```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: Przykład antywzorca: Poleganie na atrybutach 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 O ile to możliwe, testuj z realistycznym i w pełni renderowanym komponentem
+
+:white_check_mark: **Opis:** Kiedy tylko rozsądny rozmiar, przetestuj komponent z zewnątrz, tak jak robią to użytkownicy, w pełni renderuj interfejs użytkownika, działaj na nim i upewnij się, że renderowany interfejs zachowuje się zgodnie z oczekiwaniami. Unikaj wszelkiego rodzaju mockowania, częściowego i płytkiego renderowania - takie podejście może skutkować niezakłóconymi błędami z powodu braku szczegółów i utrudniać konserwację, gdy testy brudzą się z elementów wewnętrznych (patrz punkt 'Preferuj testowanie czarnej skrzynki'). Jeśli jeden z elementów potomnych znacznie spowalnia (np. animacja) lub komplikuje konfigurację - zastanów się nad wyraźnym zastąpieniem go fake'm
+
+Biorąc to wszystko pod uwagę, należy zachować ostrożność: ta technika działa w przypadku małych / średnich komponentów, które pakują rozsądne rozmiary komponentów potomnych. Pełne renderowanie komponentu ze zbyt dużą liczbą potomnych utrudni rozumowanie na temat błędów testów (analiza przyczyn) i może być zbyt wolne. W takich przypadkach napisz tylko kilka testów z tym głównym składnikiem macierzystym i więcej testów z jego potomnymi.
+
+
+
+❌ **W przeciwnym razie:** Podczas wbijania się w wewnętrzne komponenty przez wywoływanie ich prywatnych metod i sprawdzania stanu wewnętrznego - podczas refaktoryzacji implementacji komponentów musiałbyś przerefakturować wszystkie testy. Czy naprawdę masz możliwości takiego poziomu konserwacji?
+
+
+
+✏ Przykłady kodu
+
+
+
+### :clap: Przykład robienia tego dobrze: Praca w trybie rzeczywistym z całkowicie renderowanym komponentem
+
+ 
+
+```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: Przykład antywzorca: Mockowanie rzeczywistości z płytkim renderowaniem
+
+```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 Nie śpij, użyj wbudowanej obsługi frameworków dla zdarzeń asynchronicznych. Spróbuj także przyspieszyć
+
+:white_check_mark: **Opis:** W wielu przypadkach czas zakończenia testu jest po prostu nieznany (np. animacja wstrzymuje wygląd elementu) - w takim przypadku unikaj spania (np. SetTimeOut) i preferuj bardziej deterministyczne metody, które zapewnia większość platform. Niektóre biblioteki pozwalają na oczekiwanie na operacje (np. [Cypress cy.request('url')](https://docs.cypress.io/guides/references/best-practices.html#Unnecessary-Waiting)), inne zapewniają API do czekania jak [@testing-library/dom method wait(expect(element))](https://testing-library.com/docs/guide-disappearance). Czasami bardziej eleganckim sposobem jest zlikwidowanie wolnego zasobu, na przykład API, a następnie, gdy moment odpowiedzi staje się deterministyczny, komponent można jawnie ponownie renderować. Gdy zależy od jakiegoś zewnętrznego komponentu, który śpi, może się przydać [hurry-up the clock](https://jestjs.io/docs/en/timer-mocks). Spanie to schemat, którego należy unikać, ponieważ wymusza powolny lub ryzykowny test (podczas oczekiwania na zbyt krótki okres). Ilekroć spanie i odpytywanie jest nieuniknione i nie ma wsparcia ze strony środowiska testowego, niektóre biblioteki npm jak [wait-for-expect](https://www.npmjs.com/package/wait-for-expect) mogą pomóc w rozwiązaniu pół-deterministycznym
+
+
+❌ **W przeciwnym razie:** Podczas snu przez długi czas testy będą o rząd wielkości wolniejsze. Podczas próby spania dla małych liczb test nie powiedzie się, gdy testowana jednostka nie zareagowała w odpowiednim czasie. Sprowadza się to zatem do kompromisu między flakiness, a złą wydajnością
+
+
+
+✏ Przykłady kodu
+
+
+
+### :clap: Przykład robienia tego dobrze: E2E API rozwiązuje to dopiero po zakończeniu operacji asynchronicznych (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: Przykład robienia tego dobrze: Biblioteka testująca, która czeka na elementy 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: Przykład antywzorca: niestandardowy sleep code
+
+```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 Zobacz, jak treść jest udostępniana przez sieć
+
+
+
+✅ **Opis:** Zastosuj aktywny monitor, który zapewnia optymalizację ładowania strony w rzeczywistej sieci - obejmuje to wszelkie problemy związane z UX, takie jak powolne ładowanie strony lub niezminimalizowany pakiet. Rynek narzędzi inspekcyjnych nie jest krótki: podstawowe narzędzia, takie jak [pingdom](https://www.pingdom.com/), AWS CloudWatch, [gcp StackDriver](https://cloud.google.com/monitoring/uptime-checks/) można łatwo skonfigurować, aby sprawdzał, czy serwer żyje i reagował na podstawie rozsądnej SLA. To tylko rysuje powierzchnię tego, co może się nie udać, dlatego lepiej wybrać narzędzia specjalizujące się we frontendzie (np. [lighthouse](https://developers.google.com/web/tools/lighthouse/), [pagespeed](https://developers.google.com/speed/pagespeed/insights/)) i wykonać bogatszą analizę. Należy skoncentrować się na objawach, wskaźnikach, które bezpośrednio wpływają na UX, takich jak czas ładowania strony, [meaningful paint](https://scotch.io/courses/10-web-performance-audit-tips-for-your-next-billion-users-in-2018/fmp-first-meaningful-paint), [czas, aż strona stanie się interaktywna (TTI)](https://calibreapp.com/blog/time-to-interactive/). Ponadto można również zwrócić uwagę na przyczyny techniczne, takie jak zapewnienie kompresji zawartości, czas do pierwszego bajtu, optymalizacja obrazów, zapewnienie rozsądnego rozmiaru DOM, SSL i wiele innych. Wskazane jest, aby mieć te bogate monitory zarówno podczas projektowania, jako część CI, a co najważniejsze - 24x7 przez serwery produkcji / CDN
+
+
+
+❌ **W przeciwnym razie:** Musi być rozczarowujące, gdy zda się sobie sprawę, że po tak wielkiej dbałości o interfejs użytkownika, 100% testy funkcjonalne zdały i wyrafinowane pakowanie - UX jest straszny i powolny z powodu błędnej konfiguracji CDN
+
+
+
+✏ Przykłady kodu
+
+### :clap: Przykład robienia tego dobrze: Lighthouse page load inspection report
+
+
+
+
+
+
+
+## ⚪ ️ 3.6 Stub dla niestabilnych i wolnych zasobów, takich jak interfejsy zaplecza API
+
+:white_check_mark: **Opis:** Kodując swoje główne testy (nie testy E2E), unikaj angażowania zasobów, które są poza twoją odpowiedzialnością i kontroluj, takie jak backend API i zamiast tego używaj stubs (np. test double). Praktycznie, zamiast prawdziwych wywołań sieciowych interfejsów API, użyj biblioteki test double (np [Sinon](https://sinonjs.org/), [Test doubles](https://www.npmjs.com/package/testdouble), etc) dla stubbingu odpowiedzi API. Główną zaletą jest zapobieganie niestabilności - testowanie lub przemieszczanie interfejsów API z definicji nie jest wysoce stabilne i od czasu do czasu zawiedzie testy, chociaż TWÓJ komponent zachowuje się dobrze (środowisko env nie było przeznaczone do testowania i zwykle ogranicza żądania). Pozwoli to na symulację różnych zachowań API, które powinny kierować zachowaniem twojego komponentu, tak jak w przypadku braku danych lub w przypadku, gdy API zgłasza błąd. Wreszcie połączenia sieciowe znacznie spowolnią testy
+
+
+
+❌ **W przeciwnym razie:** Średni test trwa nie dłużej niż kilka ms, typowe wywołanie API trwa 100 ms>, co powoduje, że każdy test ~20x wolniej
+
+
+
+
+✏ Przykłady kodu
+
+
+
+### :clap: Przykład robienia tego dobrze: Stubbing lub przechwytywanie wywołań 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 Mieć bardzo mało kompleksowych testów obejmujących cały system
+
+:white_check_mark: **Opis:** Chociaż E2E (end-to-end) zwykle oznacza testowanie tylko interfejsu użytkownika z prawdziwą przeglądarką (patrz punkt 3.6), dla innych oznacza testy rozciągające cały system, w tym prawdziwy backend. Ten ostatni rodzaj testów jest bardzo cenny, ponieważ obejmuje błędy integracyjne między frontendem a backendem, które mogą się zdarzyć z powodu niewłaściwego zrozumienia schematu wymiany. Są również skuteczną metodą wykrywania problemów z integracją backend-to-backend (np. Microservice A wysyła niewłaściwy komunikat do Microservice B), a nawet wykrywania błędów wdrażania - nie ma struktur zaplecza dla testów E2E, które byłyby tak przyjazne i dojrzałe jak frameworki UI takie jak [Cypress](https://www.cypress.io/) oraz [Pupeteer](https://github.com/GoogleChrome/puppeteer). Minusem takich testów jest wysoki koszt konfiguracji środowiska z tyloma komponentami, a przede wszystkim ich kruchość - biorąc pod uwagę 50 mikrousług, nawet jeśli jeden się nie powiedzie, cały E2E po prostu zawiódł. Z tego powodu powinniśmy stosować tę technikę oszczędnie i prawdopodobnie mieć 1-10 z nich i nie więcej. To powiedziawszy, nawet niewielka liczba testów E2E może wychwycić rodzaj problemów, do których są skierowane - błędy wdrażania i integracji. Wskazane jest, aby uruchamiać je w środowisku produkcyjnym podobnym do produkcyjnego.
+
+
+
+❌ **W przeciwnym razie:** Interfejs użytkownika może dużo zainwestować w testowanie jego funkcjonalności, aby zdać sobie sprawę bardzo późno, że backend zwrócił payload (schemat danych, z którym musi pracować interfejs użytkownika) jest bardzo różny od oczekiwanego
+
+
+
+## ⚪ ️ 3.8 Przyspiesz testy E2E poprzez ponowne użycie danych logowania
+
+:white_check_mark: **Opis:** W testach E2E, które obejmują prawdziwy backend i opierają się na prawidłowym tokenie użytkownika dla wywołań API, izolacja testu do poziomu, na którym użytkownik jest tworzony i logowany w każdym żądaniu, nie opłaca się. Zamiast tego zaloguj się tylko raz, zanim rozpocznie się wykonywanie testu (np. before-all hook), zapisz token w lokalnej pamięci i użyj go ponownie w żądaniach. Wydaje się to naruszać jedną z podstawowych zasad testowania - zachowaj autonomię testu bez łączenia zasobów. Chociaż jest to uzasadnione zmartwienie, w testach E2E kluczowe znaczenie ma wydajność, a utworzenie 1-3 zapytań API przed rozpoczęciem każdego indywidualnego testu może prowadzić do okropnego czasu wykonania. Ponowne użycie poświadczeń nie oznacza, że testy muszą działać na tych samych rekordach użytkownika - jeśli polegasz na rekordach użytkownika (np. historii płatności użytkownika testowego), to upewnij się, że wygenerujesz te rekordy w ramach testu i unikniesz dzielenia się ich istnieniem z innymi testami. Pamiętaj również, że backend może być sfałszowany - jeśli twoje testy koncentrują się na frontendzie, może lepiej być go wyodrębnić i zablokować API backendu (patrz punkt 3.6).
+
+
+
+❌ **W przeciwnym razie:** Biorąc pod uwagę 200 przypadków testowych i zakładając login = 100ms = 20 sekund tylko samego ponownego logowania
+
+
+
+✏ Przykłady kodu
+
+
+
+### :clap: Przykład robienia tego dobrze: Logging-in before-all i nie 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 Zrób jeden smoke test E2E, który podróżuje po mapie witryny
+
+
+:white_check_mark: **Opis:** W celu monitorowania produkcji i kontroli poprawności w czasie programowania uruchom pojedynczy test E2E, który odwiedzi wszystkie / większość stron witryny i zapewni, że nic się nie zepsuje. Ten rodzaj testu zapewnia duży zwrot z inwestycji, ponieważ jest bardzo łatwy do napisania i utrzymania, ale może wykryć wszelkiego rodzaju awarie, w tym problemy z funkcjonowaniem, siecią i wdrażaniem. Inne style sprawdzania smoke i sanity nie są tak niezawodne i wyczerpujące - niektóre zespoły ops po prostu pingują stronę główną (produkcję) lub programistów, którzy przeprowadzają wiele testów integracyjnych, które nie wykrywają problemów z pakowaniem i przeglądarką. Oczywiste jest, że smoke test nie zastępuje testów funkcjonalnych, a jedynie służy jako quick smoke detector
+
+
+
+❌ **W przeciwnym razie:** Wszystko może wydawać się idealne, wszystkie testy przeszły pomyślnie, kontrola kondycji produkcji również jest pozytywna, ale komponent płatności miał problem z pakowaniem i tylko trasa płatności się nie wyświetla
+
+
+
+✏ Przykłady kodu
+
+
+
+### :clap: Przykład robienia tego dobrze: Smoke podróżujący po wszystkich stronach
+
+
+
+```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 Ujawnij testy jako dokument współpracy na żywo
+
+:white_check_mark: **Opis:** Oprócz zwiększenia niezawodności aplikacji testy przynoszą kolejną atrakcyjną okazję na stół - służą jako dokumentacja aplikacji na żywo. Ponieważ testy z natury mówią w mniej technicznym i produktowym języku UX, przy użyciu odpowiednich narzędzi mogą służyć jako artefakt komunikacyjny, który w znacznym stopniu dopasowuje wszystkich współpracowników - programistów i ich klientów. Na przykład niektóre platformy umożliwiają wyrażanie przepływu i oczekiwań (np. plan testów) przy użyciu języka czytelnego dla człowieka, aby każdy interesariusz, w tym PM, mógł czytać, zatwierdzać i współpracować przy testach, które właśnie stały się dokumentem wymagań na żywo. Technikę tę określa się również mianem „testu akceptacji”, ponieważ pozwala klientowi zdefiniować kryteria akceptacji w prostym języku. To jest [BDD (behavior-driven testing)](https://en.wikipedia.org/wiki/Behavior-driven_development) w swojej najczystszej formie. Jednym z popularnych frameworków które to umożliwiają jest [Cucumber który ma aromat JavaScript](https://github.com/cucumber/cucumber-js), zobacz przykład poniżej. Kolejna podobna, ale inna możliwość, [StoryBook](https://storybook.js.org/), umożliwia eksponowanie komponentów interfejsu użytkownika jako katalogu graficznego, w którym można przechodzić przez różne stany każdego komponentu (np. renderować siatkę bez filtrów, renderować tę siatkę z wieloma wierszami lub bez, itp.), zobaczyć, jak to wygląda i jak aby wywołać ten stan - może to spodobać się także ludziom od produktu, ale służy głównie jako dokumentacja na żywo dla programistów, którzy używają tych składników.
+
+❌ **W przeciwnym razie:** Po zainwestowaniu najlepszych zasobów w testowanie, szkoda tylko nie wykorzystać tej inwestycji i zyskać świetną wartość
+
+
+
+✏ Przykłady kodu
+
+
+
+### :clap: Przykład robienia tego dobrze: Opisywanie testów w języku zrozumiałym dla człowieka używając 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: Przykład robienia tego dobrze: Wizualizacja naszych komponentów, ich różnych stanów i danych wejściowych używając Storybook
+
+
+
+
+
+
+
+
+
+## ⚪ ️ 3.11 Wykrywanie problemów wizualnych za pomocą zautomatyzowanych narzędzi
+
+:white_check_mark: **Opis:** Skonfiguruj zautomatyzowane narzędzia do przechwytywania zrzutów ekranu interfejsu użytkownika podczas prezentacji zmian i wykrywania problemów wizualnych, takich jak nakładanie się lub łamanie zawartości. Zapewnia to, że nie tylko odpowiednie dane są przygotowane, ale także użytkownik może je wygodnie zobaczyć. Ta technika nie jest powszechnie stosowana, nasze podejście do testowania skłania się ku testom funkcjonalnym, ale jest to wizualne doświadczenie użytkownika i przy tak wielu typach urządzeń bardzo łatwo jest przeoczyć jakiś paskudny błąd interfejsu użytkownika. Niektóre bezpłatne narzędzia mogą dostarczyć podstaw - generować i zapisywać zrzuty ekranu do kontroli ludzkich oczu. Chociaż takie podejście może być wystarczające w przypadku małych aplikacji, jest wadliwe, jak każde inne ręczne testowanie, które wymaga ludzkiej pracy za każdym razem, gdy coś się zmienia. Z drugiej strony automatyczne wykrywanie problemów z interfejsem jest dość trudne ze względu na brak jasnej definicji - w tym miejscu pojawia się pole „regresji wizualnej” i rozwiązuje tę zagadkę, porównując stary interfejs z najnowszymi zmianami i wykrywając różnice. Niektóre narzędzia OSS / darmowe mogą zapewniać niektóre z tych funkcji (np. [wraith](https://github.com/BBC-News/wraith), [PhantomCSS](<[https://github.com/HuddleEng/PhantomCSS](https://github.com/HuddleEng/PhantomCSS)>) ale mogą wymagać znacznego czasu instalacji. Linia narzędzi komercyjnych (np. [Applitools](https://applitools.com/), [Percy.io](https://percy.io/)) idzie o krok dalej, usprawniając instalację i pakując zaawansowane funkcje, takie jak interfejs zarządzania, alarmowanie, inteligentne przechwytywanie poprzez eliminację szumów wizualnych (np. reklamy, animacje), a nawet analizę pierwotnych przyczyn zmian DOM/CSS, które doprowadziły do problemu
+
+
+
+❌ **W przeciwnym razie:** Jak dobra jest strona z treściami, która wyświetla świetną treść (100% testów), ładuje się natychmiast, ale połowa obszaru zawartości jest ukryta?
+
+
+
+✏ Przykłady kodu
+
+
+
+### :thumbsdown: Przykład antywzorca: Typowa regresja wizualna - właściwa treść, która jest źle wyświetlana
+
+
+
+
+
+### :clap: Przykład robienia tego dobrze: Konfigurowanie Wraith do przechwytywania i porównywania migawek interfejsu użytkownika
+
+
+
+```
+# 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: Przykład robienia tego dobrze: Używanie Applitools aby uzyskać porównanie migawek i inne zaawansowane funkcje
+
+ 
+
+```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");
+ });
+});
+```
+
+
+
+
+
+# Sekcja 4️⃣: Pomiar skuteczności testu
+
+
+
+## ⚪ ️ 4.1 Zdobądź wystarczające pokrycie, aby mieć pewność siebie, ~80% wydaje się być szczęśliwą liczbą
+
+:white_check_mark: **Opis:** Celem testowania jest uzyskanie pewności siebie do szybkiego poruszania się, oczywiście im więcej kodu jest testowane, tym większa pewność zespołu. Pokrycie jest miarą tego, ile wierszy kodu (i gałęzi, instrukcji itp.) jest osiąganych przez testy. Ile wystarczy? 10–30% jest oczywiście zbyt niskie, aby mieć jakiekolwiek pojęcie o poprawności kompilacji, z drugiej strony 100% jest bardzo drogie i może przesunąć uwagę z krytycznych ścieżek na egzotyczne zakątki kodu. Długa odpowiedź jest taka, że zależy to od wielu czynników, takich jak rodzaj aplikacji - jeśli budujesz następną generację Airbusa A380, to 100% jest koniecznością, dla witryny ze zdjęciami z kreskówkami 50% może być za dużo. Chociaż większość entuzjastów testowania twierdzi, że odpowiedni próg pokrycia jest kontekstowy, większość z nich wspomina również o liczbie 80% ([Fowler: “in the upper 80s or 90s”](https://martinfowler.com/bliki/TestCoverage.html)) które prawdopodobnie powinny spełniać większość aplikacji.
+
+Wskazówki dotyczące implementacji: możesz skonfigurować ciągłą integrację (CI) tak, aby mieć próg pokrycia ([link Jest](https://jestjs.io/docs/en/configuration.html#collectcoverage-boolean)) i zatrzymać kompilację, która nie jest zgodna z tym standardem (można również skonfigurować próg na komponent, patrz przykład kodu poniżej). Ponadto rozważ wykrycie zmniejszenia pokrycia kompilacji (gdy nowo zatwierdzony kod ma mniejsze pokrycie) - spowoduje to, że programiści zwiększą lub przynajmniej zachowają ilość testowanego kodu. To powiedziawszy, pokrycie jest tylko jedną miarą, opartą na danych ilościowych, która nie wystarczy, aby powiedzieć o solidności twoich testów. Można go również oszukać, jak pokazano w następnych punktach.
+
+
+
+❌ **W przeciwnym razie:** Pewność siebie i liczby idą ze sobą w parze, tak naprawdę nie wiedząc, że przetestowałeś większość systemu - będzie też trochę strachu, a strach cię spowolni
+
+
+
+✏ Przykłady kodu
+
+
+
+### :clap: Przykład: typowy raport pokrycia
+
+
+
+
+
+### :clap: Przykład robienia tego dobrze: Konfigurowanie pokrycia dla każdego komponentu (za pomocą Jest)
+
+
+
+")
+
+
+
+
+
+## ⚪ ️ 4.2 Sprawdź raporty pokrycia, aby wykryć nietestowane obszary i inne osobliwości
+
+:white_check_mark: **Opis:** Niektóre problemy wymykają się tuż pod radarem i naprawdę trudno je znaleźć przy użyciu tradycyjnych narzędzi. To nie są tak naprawdę błędy, ale bardziej zaskakujące zachowanie aplikacji, które może mieć poważny wpływ. Na przykład często niektóre obszary kodu nie są wywoływane lub rzadko są wywoływane - myślałeś, że klasa 'PriceCalculator' zawsze ustala cenę produktu, ale okazuje się, że tak naprawdę nigdy nie jest wywoływana, chociaż mamy 10000 produktów w BD i wiele sprzedaży… Raporty pokrycia kodu pomagają zrozumieć, czy aplikacja zachowuje się tak, jak myślisz. Poza tym może także podkreślić, które typy kodu nie są testowane - informacja, że 80% kodu jest testowane, nie mówi, czy kluczowe części są objęte testem. Generowanie raportów jest łatwe - po prostu uruchom aplikację na produkcji lub podczas testowania ze śledzeniem pokrycia, a następnie wyświetl kolorowe raporty, które podkreślają częstotliwość wywoływania każdego obszaru kodu. Jeśli nie spieszysz się, aby zajrzeć do tych danych - możesz znaleźć nieco
+
+
+❌ **W przeciwnym razie:** Jeśli nie wiesz, które części kodu nie zostały przetestowane, nie wiesz, skąd mogą wynikać problemy.
+
+
+
+✏ Przykłady kodu
+
+
+
+### :thumbsdown: Przykład antywzorca: Co jest nie tak z tym raportem pokrycia?
+
+W oparciu o scenariusz z rzeczywistego świata, w którym śledziliśmy użycie naszej aplikacji w ramach kontroli jakości i znajdowaliśmy ciekawe wzorce logowania (wskazówka: liczba niepowodzeń logowania jest nieproporcjonalna, coś jest wyraźnie nie tak. W końcu okazało się, że jakiś błąd interfejsu użytkownika ciągle uderza w backend login API)
+
+
+
+
+
+
+
+## ⚪ ️ 4.3 Zmierz pokrycie logiczne za pomocą testu mutacji
+
+:white_check_mark: **Opis:** Metryka tradycyjnego pokrycia często zakłamuje, tzn. może pokazywać 100% pokrycia kodu, ale żadna z twoich funkcji, nawet jedna, nie zwraca właściwej odpowiedzi. Dlaczego? Po prostu mierzy, które wiersze kodu odwiedził test, ale nie sprawdza, czy testy faktycznie coś testowały - stwierdzając, że ma właściwą odpowiedź. Jak ktoś, kto podróżuje w interesach i pokazuje swoje znaczki paszportowe - nie świadczy to o żadnej pracy, tylko że odwiedził kilka lotnisk i hoteli.
+
+Testy oparte na mutacjach są tutaj pomocne, mierząc ilość kodu, który był TESTOWANY, a nie tylko ODWIEDZANY. [Stryker](https://stryker-mutator.io/) jest biblioteką JavaScript do testowania mutacji, a implementacja jest naprawdę fajna:
+
+(1) celowo zmienia kod i "zasadza błędy". Na przykład kod newOrder.price===0 staje się newOrder.price!=0. Te "bugi" nazywane są mutacjami
+
+(2) uruchamia testy, jeśli wszystko się powiedzie, wówczas mamy problem - testy nie służyły wykrywaniu błędów, mutacje są tzw. survived. Jeśli testy się nie powiodły, to świetnie, mutacje zostały zabite.
+
+Wiedza, że wszystkie lub większość mutacji została zabita, daje znacznie większą pewność niż tradycyjne pokrycie, a czas przygotowania jest podobny
+
+
+❌ **W przeciwnym razie:** Będziesz oszukiwany, że 85% pokrycia oznacza, że Twój test wykryje błędy w 85% twojego kodu
+
+
+
+✏ Przykłady kodu
+
+
+
+### :thumbsdown: Przykład antywzorca: 100% pokrycia, 0% testowania
+
+
+
+```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({ asignee: "John@mailer.com", price: 120 });
+}); //Triggers 100% code coverage, but it doesn't check anything
+```
+
+
+
+### :clap: Przykład robienia tego dobrze: Stryker raportuje, narzędzie do testowania mutacji, wykrywa i zlicza ilość kodu, który nie jest testowany (mutacje)
+
+")
+
+
+
+
+
+## ⚪ ️4.4 Zapobieganie problemom z kodem testowym z Test linters
+
+:white_check_mark: **Opis:** Zestaw wtyczek ESLint został zbudowany specjalnie do sprawdzania wzorców kodów testów i wykrywania problemów. Na przykład, [eslint-plugin-mocha](https://www.npmjs.com/package/eslint-plugin-mocha) ostrzeże, gdy test zostanie napisany na poziomie globalnym (nie pochodna deklaracji describe()) lub gdy testy są [pomijane](https://mochajs.org/#inclusive-tests) co może prowadzić do fałszywego przekonania, że wszystkie testy przebiegają pomyślnie. Podobnie, [eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest) może na przykład ostrzegać, gdy test nie ma żadnych asercji (niczego nie sprawdza)
+
+
+
+❌ **W przeciwnym razie:** Widok 90% pokrycia kodu i 100% zielonych testów sprawi, że twoja twarz będzie się uśmiechać tylko do momentu, gdy zdasz sobie sprawę, że wiele testów niczego nie potwierdza, a wiele pakietów testowych zostało właśnie pominiętych. Mamy nadzieję, że nie wdrożyłeś niczego w oparciu o tę fałszywą obserwację
+
+
+✏ Przykłady kodu
+
+
+
+### :thumbsdown: Przykład antywzorca: Przypadek testowy pełen błędów, na szczęście wszystkie zostają złapane przez 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
+});
+```
+
+
+
+
+
+# Sekcja 5️⃣: CI oraz inne miary jakości
+
+
+
+## ⚪ ️ 5.1 Wzbogać swoje linters i przerwij buildy, które mają problemy z linters
+
+:white_check_mark: **Opis:** Linters to bezpłatny lunch, z 5-minutową konfiguracją otrzymujesz za darmo auto-pilota pilnującego twojego kodu i wychwytującego poważny problem podczas pisania. Dawno minęły czasy, w których linting było związane z kosmetycznymi poprawkami (brak średników!). Obecnie Linters mogą wykrywać poważne problemy, takie jak błędy, które nie są poprawnie zgłaszane i gubią informacje. Oprócz podstawowego zestawu zasad (jak [ESLint standard](https://www.npmjs.com/package/eslint-plugin-standard) lub [Airbnb style](https://www.npmjs.com/package/eslint-config-airbnb)), rozważ włączenie pewnych specjalizacji Linters jak [eslint-plugin-chai-expect](https://www.npmjs.com/package/eslint-plugin-chai-expect), które mogą wykryć testy bez asercji, [eslint-plugin-promise](https://www.npmjs.com/package/eslint-plugin-promise?activeTab=readme) może odkryć obietnice (promises) bez rozwiązania (twój kod nigdy nie będzie kontynuowany), [eslint-plugin-security](https://www.npmjs.com/package/eslint-plugin-security?activeTab=readme), które mogą odkryć chciwe wyrażenia regularne, które mogą zostać wykorzystane do ataków DOS, oraz [eslint-plugin-you-dont-need-lodash-underscore](https://www.npmjs.com/package/eslint-plugin-you-dont-need-lodash-underscore) może alarmować, gdy kod korzysta z metod utility biblioteki, które są częścią podstawowych metod V8, takich jak Lodash.\_map(…)
+
+
+❌ **W przeciwnym razie:** Weź pod uwagę deszczowy dzień, w którym Twoja produkcja ulega awarii, ale dzienniki nie wyświetlają śladu stosu błędów. Co się stało? Twój kod przez pomyłkę rzucił obiekt niebędący błędem, a ślad stosu został utracony, co jest dobrym powodem uderzenia głową o ścianę z cegły. 5-minutowa konfiguracja linijek może wykryć tę LITERÓWKĘ i uratować Twój dzień
+
+
+
+✏ Przykłady kodu
+
+
+
+### :thumbsdown: Przykład antywzorca: Błędnie rzucony niewłaściwy obiekt Error, dla tego błędu nie pojawi się śledzenie stosu. Na szczęście ESLint łapie kolejny błąd produkcyjny
+
+
+
+
+
+
+
+## ⚪ ️ 5.2 Skróć pętlę sprzężenia zwrotnego z lokalnym developer-CI
+
+:white_check_mark: **Opis:** Używasz CI z błyszczącymi inspekcjami jakości, takimi jak testowanie, linting, sprawdzanie podatności itp.? Pomóż programistom uruchomić ten pipeline również lokalnie, aby uzyskać natychmiastowe informacje zwrotne i skrócić [feedback loop](https://www.gocd.org/2016/03/15/are-you-ready-for-continuous-delivery-part-2-feedback-loops/). Czemu? Wydajny proces testowania składa się z wielu iteracyjnych pętli: (1) próby -> (2) informacje zwrotne -> (3) refaktor. Im szybsze jest sprzężenie zwrotne, tym więcej iteracji ulepszeń może wykonać programista na moduł i uzyskać doskonałe wyniki. Z drugiej strony, gdy informacje zwrotne są spóźnione, mniej ulepszeń można by spakować w jeden dzień, zespół może już przejść do innego tematu / zadania / modułu i może nie być gotowy na udoskonalenie tego modułu.
+
+W praktyce, niektórzy dostawcy CI (przykład: [CircleCI local CLI](https://circleci.com/docs/2.0/local-cli/)) zezwalają na lokalne uruchomienie pipeline'a. Niektóre komercyjne narzędzia jak [wallaby zapewniają cenne i spostrzeżenia testowania](https://wallabyjs.com/) jako developer prototype (bez przynależności). Alternatywnie, możesz po prostu dodać skrypt npm do package.json który uruchamia wszystkie polecenia jakości (np. test, lint, vulnerabilities) — użyj narzędzi jak [concurrently](https://www.npmjs.com/package/concurrently) do równoległości i niezerowego kodu wyjścia, jeśli jedno z narzędzi uległo awarii. Teraz programista powinien po prostu wywołać jedno polecenie - np. ‘npm run quality’ — aby uzyskać natychmiastową informację zwrotną. Rozważ także przerwanie commita, jeśli sprawdzenie jakości nie powiodło się przy użyciu githook ([husky może pomóc](https://github.com/typicode/husky))
+
+
+❌ **W przeciwnym razie:** Gdy wyniki jakości pojawiają się następnego dnia po kodzie, testowanie nie staje się płynną częścią rozwoju, a raczej formalnym artefaktem
+
+
+
+✏ Przykłady kodu
+
+
+
+### :clap: Przykład robienia tego dobrze: skrypty npm, które przeprowadzają kontrolę jakości kodu, wszystkie są uruchamiane równolegle na żądanie lub gdy programista próbuje wypchnąć nowy kod
+
+```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 Przeprowadź testy e2e na prawdziwym production-mirror
+
+:white_check_mark: **Opis:** Testowanie end to end (e2e) są głównym wyzwaniem każdego CI pipeline — tworzenie w locie identycznego efemerycznego lustra produkcyjnego ze wszystkimi powiązanymi usługami chmurowymi może być uciążliwe i kosztowne. Znalezienie najlepszego kompromisu to Twoja gra: [Docker-compose](https://serverless.com/) umożliwia tworzenie izolowanego, zadokowanego środowiska z identycznymi kontenerami przy użyciu jednego zwykłego pliku tekstowego, ale technologia tworzenia kopii zapasowych (np. sieci, model wdrażania) różni się od rzeczywistych produkcji. Możesz to połączyć z [‘AWS Local’](https://github.com/localstack/localstack) do pracy ze stub prawdziwych usług AWS. Jeśli poszedłeś drogą [serverless](https://serverless.com/) wiele frameworków jak serverless i [AWS SAM](https://docs.aws.amazon.com/lambda/latest/dg/serverless_app.html) umożliwia lokalne wywołanie kodu Faas.
+
+Ogromny ekosystem Kubernetes ma jeszcze sformalizować standardowe wygodne narzędzie do lokalnego i CI-mirroring, choć wiele nowych narzędzi jest często uruchamianych. Jednym z podejść jest uruchomienie ‘minimized-Kubernetes’ używając narzędzi jak [Minikube](https://kubernetes.io/docs/setup/minikube/) i [MicroK8s](https://microk8s.io/) które przypominają prawdziwe, pochodzą tylko z mniejszym nakładem. Innym podejściem jest testowanie za pomocą zdalnego ‘real-Kubernetes’, niektórzy CI dostawcy (np. [Codefresh](https://codefresh.io/)) mają natywną integrację ze środowiskiem Kubernetes i ułatwiają uruchamianie pipeline CI w rzeczywistości, inne pozwalają na niestandardowe skrypty na zdalnym Kubernetes.
+
+
+❌ **W przeciwnym razie:** Korzystanie z różnych technologii do produkcji i testowania wymaga utrzymania dwóch modeli wdrażania oraz oddzielenia programistów i zespołu Ops.
+
+
+
+✏ Przykłady kodu
+
+
+
+### :clap: Przykład: pipeline CI który generuje klaster Kubernetes w locie ([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 Równoległe wykonywanie testu
+
+:white_check_mark: **Opis:** Po prawidłowym przeprowadzeniu, testowanie to twój przyjaciel 24/7, który zapewnia niemal natychmiastową informację zwrotną. W praktyce wykonanie 500 testów jednostkowych powiązanych z CPU na jednym wątku może potrwać zbyt długo. Na szczęście nowoczesne test runners i platformy CI (takie jak [Jest](https://github.com/facebook/jest), [AVA](https://github.com/avajs/ava) oraz [rozszerzenia Mocha](https://github.com/yandex/mocha-parallel-tests)) potrafią równolegle przeprowadzić test w wiele procesów i osiągnąć znaczną poprawę czasu reakcji. Niektórzy dostawcy CI również przeprowadzają testy równoległe w kontenerach (!), co jeszcze bardziej skraca pętlę sprzężenia zwrotnego. Niezależnie od tego, czy lokalnie w wielu procesach, czy w niektórych interfejsach CLI w chmurze na wielu komputerach - równolegle do popytu, zachowując autonomię testów, ponieważ każdy może działać na różnych procesach
+
+❌ **W przeciwnym razie:** Uzyskanie wyników testu w 1 godzinę po opublikowaniu nowego kodu, gdy już kodujesz kolejne funkcje, to świetny przepis na uczynienie testowania mniej istotnym
+
+
+
+✏ Przykłady kodu
+
+
+
+### :clap: Przykład robienia tego dobrze: Mocha parallel & Jest łatwo prześcigną tradycyjne Mocha dzięki testowaniu równoległości ([Źródło: JavaScript Test-Runners Benchmark](https://medium.com/dailyjs/javascript-test-runners-benchmark-3a78d4117b4))
+
+")
+
+
+
+
+
+## ⚪ ️5.5 Unikaj problemów prawnych dzięki sprawdzeniu licencji i unikaniu plagiatu
+
+:white_check_mark: **Opis:** Problemy z licencjonowaniem i plagiatem nie są teraz prawdopodobnie Twoim głównym problemem, ale dlaczego nie zaznaczyć tego pola również za 10 minut? Kilka pakietów npm jak [kontrola licencji](https://www.npmjs.com/package/license-checker) i [kontrola plagiatu](https://www.npmjs.com/package/plagiarism-checker) (komercyjny z bezpłatnym planem) można łatwo wypalić w pipeline CI i sprawdzić, czy nie ma smutków, takich jak zależności, z restrykcyjnymi licencjami lub kodem, który został skopiowany z Stackoverflow i najwyraźniej narusza niektóre prawa autorskie
+
+❌ **W przeciwnym razie:** Nieumyślnie programiści mogą używać pakietów z nieodpowiednimi licencjami lub kopiować wklejony kod komercyjny i napotykać problemy prawne
+
+
+
+✏ Przykłady kodu
+
+
+
+### :clap: Przykład robienia tego dobrze:
+
+```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 Nieustannie sprawdzaj wrażliwe zależności
+
+:white_check_mark: **Opis:** Nawet najbardziej renomowane zależności, takie jak Express, mają znane luki w zabezpieczeniach. Można to łatwo oswoić za pomocą narzędzi community, takich jak [npm audit](https://docs.npmjs.com/getting-started/running-a-security-audit), lub komercyjnych narzędzi takich jak [snyk](https://snyk.io/) (oferuje również darmową wersję community). Oba mogą być wywoływane z twojego CI na każdej kompilacji
+
+❌ **W przeciwnym razie:** Utrzymywanie kodu w czystości przed lukami bez dedykowanych narzędzi będzie wymagało ciągłego śledzenia publikacji online na temat nowych zagrożeń. Dość nudne
+
+
+
+✏ Przykłady kodu
+
+
+
+### :clap: Przykład: wynik NPM Audit
+
+
+
+
+
+
+
+## ⚪ ️5.7 Zautomatyzuj aktualizacje zależności
+
+:white_check_mark: **Opis:** Yarn i npm ostatnie wprowadzenie Package-lock.json wprowadziło poważne wyzwanie (droga do piekła jest wybrukowana dobrymi intencjami) - domyślnie teraz pakiety nie otrzymują już aktualizacji. Nawet zespół prowadzący wiele nowych wdrożeń z ‘npm install’ & ‘npm update’ nie otrzyma nowych aktualizacji. Prowadzi to w najlepszym razie do obniżonych wersji pakietów lub w najgorszym przypadku do podatnego kodu. Zespoły polegają teraz na dobrej woli programistów i pamięci, aby ręcznie aktualizować package.json lub korzystać ręcznie z narzędzi [taki jak ncu](https://www.npmjs.com/package/npm-check-updates). Bardziej niezawodnym sposobem może być zautomatyzowanie procesu uzyskiwania najbardziej niezawodnych wersji zależności, chociaż nie ma jeszcze srebrnych rozwiązań, istnieją dwie możliwe drogi automatyzacji:
+
+(1) CI może zawieść buildy, które mają przestarzałe zależności - przy użyciu narzędzi takich jak [‘npm outdated’](https://docs.npmjs.com/cli/outdated) lub ‘npm-check-updates (ncu)’. Takie postępowanie zmusi programistów do aktualizacji zależności.
+
+(2) Użyj komercyjnych narzędzi, które skanują kod i automatycznie wysyłają pull requesty ze zaktualizowanymi zależnościami. Pozostaje jedno interesujące pytanie: jaka powinna być zasada aktualizacji zależności - aktualizacja każdej poprawki generuje zbyt wiele narzutów, aktualizowanie zaraz po wydaniu wersji głównej może wskazywać na niestabilną wersję (wiele pakietów było podatnych na atak już w pierwszych dniach po wydaniu, [zobacz](https://nodesource.com/blog/a-high-level-post-mortem-of-the-eslint-scope-security-incident/) eslint-scope incident).
+
+Skuteczne zasady aktualizacji mogą pozwolić na pewien 'okres nabywania uprawnień' - pozwól, aby kod pozostawał w tyle za @latest przez pewien czas i wersjami, zanim uzna lokalną kopię za przestarzałą (np. wersja lokalna to 1.3.1, a wersja repozytorium to 1.3.8)
+
+
+❌ **W przeciwnym razie:** Produkcja będzie uruchamiać pakiety, które zostały wyraźnie oznaczone przez autora jako ryzykowne
+
+
+
+✏ Przykłady kodu
+
+
+
+### :clap: Przykład: [ncu](https://www.npmjs.com/package/npm-check-updates) może być używany ręcznie lub w pipeline CI, aby wykryć, w jakim stopniu kod opóźnia się w stosunku do najnowszych wersji
+
+
+
+
+
+
+
+## ⚪ ️ 5.8 Inne, niezwiązane z Node'm, porady CI
+
+:white_check_mark: **Opis:** Ten post skupia się na poradach dotyczących testowania, które są związane lub przynajmniej mogą być zilustrowane przykładem Node JS. Ten punkt zawiera jednak kilka dobrze znanych wskazówek niezwiązanych z 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
+
+
+❌ **W przeciwnym razie:** Będziesz tęsknić za latami mądrości
+
+
+
+## ⚪ ️ 5.9 Build matrix: Uruchom te same kroki CI, używając wielu wersji Node
+
+:white_check_mark: **Opis:** W kontroli jakości chodzi o przypadkowość, im więcej masz miejsca, tym więcej masz szczęścia we wczesnym wykrywaniu problemów. Podczas opracowywania pakietów wielokrotnego użytku lub uruchamiania produkcji dla wielu klientów z różnymi wersjami konfiguracji i wersji Node'a, CI musi uruchomić pipeline testów na wszystkich kombinacjach konfiguracji. Na przykład, zakładając, że używamy MySQL dla niektórych klientów i Postgres dla innych - niektórzy dostawcy CI obsługują funkcję o nazwie 'Matrix', która pozwala na uruchomienie zestawu testów dla wszystkich permutacji MySQL, Postgres i wielu wersji Node, takich jak 8, 9 i 10. Odbywa się to przy użyciu konfiguracji tylko bez dodatkowego wysiłku (zakładając, że masz testy lub inne kontrole jakości). Inne CI, które nie obsługują Matrix, mogą mieć rozszerzenia lub poprawki, aby to umożliwić
+
+
+❌ **W przeciwnym razie:** Czy po tak ciężkiej pracy związanej z pisaniem testów pozwolimy, aby błędy wkradły się tylko z powodu problemów z konfiguracją?
+
+
+
+✏ Przykłady kodu
+
+
+
+### :clap: Przykład: użycie definicji kompilacji Travis (dostawca CI) do uruchomienia tego samego testu w wielu wersjach Node
+
+language: node_js
node_js:
- "7"
- "6"
- "5"
- "4"
install:
- npm install
script:
- npm run test
+
+
+
+
+# Zespół
+
+## Yoni Goldberg
+
+
+
+
+
+**Rola:** Writer
+
+**Opis:** Jestem niezależnym konsultantem, który współpracuje z firmami Fortune 500 i garażowymi startupami przy dopracowywaniu aplikacji JS i Node.js. Bardziej niż jakikolwiek inny temat fascynuje mnie, i mam na celu, opanowanie sztuki testowania. Jestem także autorem [Node.js Best Practices](https://github.com/goldbergyoni/nodebestpractices)
+
+**📗 Kurs online:** Podobał Ci się ten przewodnik i chcesz maksymalnie wykorzystać swoje umiejętności testowania? Rozważ skorzystanie z mojego kompleksowego kursu [Testowanie Node.js i JavaScript od A do Z](https://www.testjavascript.com)
+
+
+
+**Obserwuj:**
+
+- [🐦 Twitter](https://twitter.com/goldbergyoni/)
+- [📞 Kontakt](https://testjavascript.com/contact-2/)
+- [✉️ Newsletter](https://testjavascript.com/newsletter//)
+
+
+
+
+
+## [Bruno Scheufler](https://github.com/BrunoScheufler)
+
+**Rola:** Recenzent i doradca techniczny
+
+Zadbał o poprawienie, ulepszenie, usunięcie i dopracowanie wszystkich tekstów
+
+**Opis:** full-stack web engineer, entuzjasta Node.js & GraphQL
+
+
+
+
+## [Ido Richter](https://github.com/idori)
+
+**Rola:** Koncepcja, projekt i świetna rada
+
+**Opis:** Wytrawny frontend developer, ekspert CSS i emojis freak
+
+## [Kyle Martin](https://github.com/js-kyle)
+
+**Rola:** Pomaga utrzymać ten projekt w ruchu i weryfikuje praktyki związane z bezpieczeństwem
+
+**Opis:** Uwielbia pracę nad projektami Node.js i bezpieczeństwem aplikacji internetowych.
+
+## Współtwórcy ✨
+
+Podziękowania dla tych wspaniałych ludzi, którzy przyczynili się do tego repozytorium!
+
+
+
+
+
+
+
+
+
+
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
new file mode 100644
index 00000000..9d0e4075
--- /dev/null
+++ b/readme-pt-br.md
@@ -0,0 +1,2060 @@
+
+
+
+
+# 👇 Por que este guia pode levar suas habilidades de teste para o próximo nível
+
+
+
+## 📗 45+ boas práticas: Super abrangente e exaustivo
+Este é um guia para a confiabilidade JavaScript & Node.js da A-Z. Ele resume e organiza para você dezenas das melhores publicações, livros, ferramentas e postagens de blogs que o mercado tem a oferecer
+
+
+## 🚢 Avançado: vai 10.000 milhas além do básico
+Entre em uma jornada que vai muito além do básico, para tópicos avançados como testes em produção, testes de mutação, testes baseados em propriedades e muitas outras ferramentas estratégicas e profissionais. Se você ler todas as palavras deste guia, é provável que suas habilidades de teste superem a média
+
+
+## 🌐 Full-stack: front, backend, CI(Integração Contínua), qualquer coisa
+Comece entendendo as práticas de teste onipresentes que são a base para qualquer camada de aplicativo. Em seguida, mergulhe na sua área de escolha: front-end/UI, back-end, CI(Integração Contínua) ou talvez todos eles?
+
+
+
+### Escrito por Yoni Goldberg
+* Um consultor JavaScript & Node.js
+* 👨🏫 [Minha oficina de testes](https://www.testjavascript.com) - aprenda sobre [meus workshops](https://www.testjavascript.com) na Europe & Estados Unidos
+* [Me siga no twitter ](https://twitter.com/goldbergyoni/)
+* Venha me ouvir falar em [LA](https://js.la/), [Verona](https://2019.nodejsday.it/), [Kharkiv](https://kharkivjs.org/), [free webinar](https://zoom.us/webinar/register/1015657064375/WN_Lzvnuv4oQJOYey2jXNqX6A). Eventos futuros TBD
+* [Newsletter informativo de qualidade sobre JavaScript](https://testjavascript.com/newsletter/) - insights e conteúdo apenas em assuntos estratégicos
+
+
+
+### 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 💜
+
+
+
+
+## `Índice`
+
+#### [`Seção 0: A Regra de ouro`](#seção-0️⃣-a-regra-de-ouro)
+
+Um único conselho que inspira todos os outros (1 tópico especial)
+
+#### [`Seção 1: A Anatomia do Teste`](#seção-1-a-anatomia-do-teste)
+
+A fundação - estruturando testes limpos (12 tópicos)
+
+#### [`Seção 2: Teste de Backend`](#seção-2️⃣-teste-de-backend)
+
+Escrevendo testes de back-end e microsserviços com eficiência (8 tópicos)
+
+#### [`Seção 3: Teste de Frontend`](#seção-3️⃣-teste-de-frontend)
+
+Escrevendo testes para interface do usuário da web, incluindo testes de componentes e E2E (11 tópicos)
+
+#### [`Seção 4: Medindo a Eficácia dos Testes`](#seção-4️⃣-medindo-a-eficácia-dos-testes)
+
+Observando o vigia - medindo a qualidade do teste (4 tópicos)
+
+#### [`Seção 5: Integração Contínua`](#section-5️⃣-ci-and-other-quality-measures)
+
+Diretrizes para CI no mundo JS (9 tópicos)
+
+
+
+
+
+# Seção 0️⃣: A Regra de Ouro
+
+
+
+## ⚪️ 0 A Regra de Ouro: Design para testes enxutos
+
+:white_check_mark: **Faça:**
+O código de teste não é como o código de produção - projete-o para ser simples, curto, sem abstrações, plano, agradável de se trabalhar, enxuto. Deve-se olhar para um teste e obter a intenção instantaneamente.
+
+Nossas mentes estão cheias com o código principal de produção, não temos 'espaço de sobra' para complexidade adicional. Se tentarmos espremer outro código desafiador em nosso cérebro fraco, a equipe ficará mais lenta, o que vai de encontro com a razão pela qual fazemos os testes. Na prática é aqui que muitas equipes abandonam os testes.
+
+Os testes são uma oportunidade para outra coisa - um assistente amigável e sorridente, que é agradável de trabalhar e oferece grande valor para um investimento tão pequeno. A ciência diz que temos dois sistemas cerebrais: o sistema 1, usado para atividades sem esforço, como dirigir um carro em uma estrada vazia, e o sistema 2, destinado a operações complexas e conscientes, como resolver uma equação matemática. Projete seu teste para o sistema 1, ao analisar o código de teste, ele deve parecer tão fácil quanto modificar um documento HTML e não como resolver um equação 2X (17 × 24).
+
+Isso pode ser alcançado através de técnicas, ferramentas e alvos de teste selecionados de forma econômica, que são econômicos e proporcionam um ótimo ROI. Teste apenas o necessário, esforce-se para mantê-lo ágil, às vezes vale a pena abandonar alguns testes e trocar a confiabilidade por agilidade e simplicidade.
+
+
+
+A maioria dos conselhos abaixo são derivados desse princípio.
+
+### Pronto para começar?
+
+
+
+
+# Seção 1: A Anatomia do Teste
+
+
+
+## ⚪ ️ 1.1 Inclua 3 partes em cada nome de teste
+
+:white_check_mark: **Faça:** Um relatório de teste deve informar se a revisão atual do aplicativo atende aos requisitos para as pessoas que não estão necessariamente familiarizadas com o código: o testador, o engenheiro DevOps que está implantando e você daqui a dois anos. Isso pode ser melhor alcançado se os testes falarem no nível de requisitos e incluirem 3 partes:
+
+(1) O que está sendo testado? Por exemplo, o método ProductsService.addNewProduct
+
+(2) Sob que circunstâncias e cenário? Por exemplo, nenhum preço é passado para o método
+
+(3) Qual é o resultado esperado? Por exemplo, o novo produto não é aprovado
+
+
+
+
+❌ **Caso contrário:** Uma implantação acabou de falhar, um teste chamado "Adicionar produto" falhou. Isso diz o que exatamente está com defeito?
+
+
+
+**👇 Nota:** Cada tópico possui exemplos de código e alguns tem ilustrações. Clique para expandir
+✏ Códigos de Exemplo
+
+
+
+### :clap: Exemplo Fazendo Certo: um nome de teste que constitui 3 partes
+
+
+
+```javascript
+//1. unidade em teste
+describe('Products Service', function() {
+ describe('Add new product', function() {
+ //2. cenário e 3. expectativa
+ 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: Exemplo Fazendo Certo: um nome de teste que constitui 3 partes
+
+
+
+
+
+
+## ⚪ ️ 1.2 Testes de estrutura pelo padrão em inglês AAA
+
+:white_check_mark: **Faça:** Estruture seus testes com 3 seções bem separadas: Ajeitar, Atuar e Afirmar (AAA). Seguir essa estrutura garante que o leitor não gaste CPU do cérebro na compreensão do plano de teste:
+
+1st A - Ajeitar: todo o código de configuração para levar o sistema ao cenário que o teste pretende simular. Isso pode incluir instanciar a unidade sob o construtor de teste, adicionar registros de banco de dados, mockar/stubbing de objetos e qualquer outro código de preparação
+
+2nd A - Atuar: Execute teste em unidade. Geralmente 1 linha de código
+
+3rd A - Afirmar: Garanta que o valor recebido satisfaça a expectativa. Geralmente 1 linha de código
+
+
+
+
+
+❌ **Caso contrário:** Você não gasta apenas longas horas diárias para entender o código principal, agora também o que deveria ter sido a parte simples do dia (teste) estica seu cérebro
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :clap: Exemplo Fazendo Certo: Um teste estruturado com o padrão AAA
+
+ 
+
+```javascript
+describe('Customer classifier', () => {
+ test('When customer spent more than 500$, should be classified as premium', () => {
+ //Ajeitar
+ const customerToClassify = {spent:505, joined: new Date(), id:1}
+ const DBStub = sinon.stub(dataAccess, "getCustomer")
+ .reply({id:1, classification: 'regular'});
+
+ //Atuar
+ const receivedClassification = customerClassifier.classifyCustomer(customerToClassify);
+
+ //Afirmar
+ expect(receivedClassification).toMatch('premium');
+ });
+});
+```
+
+
+
+### :thumbsdown: Exemplo Anti-padrão: Sem separação, um grande volume, mais difícil de interpretar
+
+```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 Descrever expectativas em um idioma do produto: use afirmações no estilo BDD
+
+:white_check_mark: **Faça:** Codificar seus testes em um estilo declarativo permite que o leitor entenda instantaneamente, sem gastar nem um único ciclo de CPU do cérebro. Quando você escreve um código imperativo, repleto de lógica condicional, o leitor entra em um estado mental de esforço. Nesse sentido, codifique a expectativa em uma linguagem humana, estilo declarativo de BDD usando expect ou should e não usando código personalizado. Se Chai e Jest não incluem a afirmação desejada e são altamente repetíveis, considere [estender o Jest matcher (Jest)](https://jestjs.io/docs/en/expect#expectextendmatchers) ou escrever um [plugin Chai personalizado](https://www.chaijs.com/guide/plugins/)
+
+
+
+❌ **Caso Contrário:** A equipe escreverá menos testes e decorará os irritantes com .skip()
+
+
+
+✏ Códigos de Exemplo
+
+ 
+
+ ### :thumbsdown: Exemplo Anti-padrão: O leitor deve percorrer códigos não tão curtos e imperativos apenas para chegar a história do teste
+
+```javascript
+test("When asking for an admin, ensure only ordered admins in results" , () => {
+ //supondo que adicionamos aqui dois administradores "admin1", "admin2" e "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: Exemplo Fazendo Certo: Percorrer o teste declarativo a seguir é fácil
+
+
+```javascript
+it("When asking for an admin, ensure only ordered admins in results" , () => {
+ //supondo que adicionamos aqui dois administradores
+ const allAdmins = getUsers({adminOnly:true});
+
+ expect(allAdmins).to.include.ordered.members(["admin1" , "admin2"])
+ .but.not.include.ordered.members(["user1"]);
+});
+
+```
+
+
+
+
+
+
+
+## ⚪ ️ 1.4 Atenha-se ao teste de caixa preta: teste apenas métodos públicos
+
+:white_check_mark: **Faça:** Testar os componentes internos gera uma enorme sobrecarga por quase nada. Se o seu código/API fornecer os resultados certos, você deve realmente investir suas próximas 3 horas em testes de COMO funcionou internamente e depois manter esses testes frágeis? Sempre que um comportamento público é verificado, a implementação privada também é implicitamente testada e seus testes serão interrompidos apenas se houver um determinado problema (por exemplo, saída incorreta). Essa abordagem também é chamada de teste comportamental. Por outro lado, se você testar os componentes internos (abordagem de caixa branca) — seu foco muda do planejamento do resultado do componente para detalhes minuciosos e seu teste pode ser interrompido devido a pequenas refatorações de código, embora os resultados sejam bons- isso aumenta drasticamente a carga de manutenção
+
+
+
+❌ **Caso Contrário:** Seu teste se comporta como [O Pastor Mentiroso e o Lobo](https://pt.wikipedia.org/wiki/O_Pastor_Mentiroso_e_o_Lobo): gera falsos positivos (por exemplo, um teste falha porque um nome de variável privada foi alterado). Sem surpresa, as pessoas logo começarão a ignorar as notificações de IC até que um dia um bug real seja ignorado…
+
+
+✏ Códigos de Exemplo
+
+
+
+### :thumbsdown: Exemplo Anti-padrão: Um caso de teste está testando os internos sem um bom motivo
+
+```javascript
+class ProductService{
+ //esse método é usado apenas internamente
+ //Alterar este nome fará com que os testes falhem
+ calculateVATAdd(priceWithoutVAT){
+ return {finalPrice: priceWithoutVAT * 1.2};
+ //Alterar o formato do resultado ou o nome da chave acima fará com que os testes falhem
+ }
+ //método público
+ 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 () => {
+ //Não é necessário permitir que os usuários calculem o VAT, apenas mostrem o preço final. No entanto, insistimos aqui falsamente para testar os internos da classe
+ expect(new ProductService().calculateVATAdd(0).finalPrice).to.equal(0);
+});
+
+```
+
+
+
+
+
+
+
+
+## ⚪ ️ ️1.5 Escolha os dublês de teste certos: evite mocks a favor de stubs e spies
+
+:white_check_mark: **Faça:** Os dublês de teste são um mal necessário, porque são acoplados às aplicações internas, no entanto, alguns fornecem um imenso valor ([Leia aqui um lembrete sobre dublês de teste: mocks vs stubs vs spies](https://martinfowler.com/articles/mocksArentStubs.html)).
+
+Antes de usar dublês de teste, faça uma pergunta muito simples: Eu o uso para testar funcionalidades que aparecem ou podem aparecer no documento de requisitos? Se não, isso cheira a teste de caixa branca.
+
+Por exemplo, se você quiser testar se seu aplicativo se comporta razoavelmente quando o serviço de pagamento estiver inativo, você pode desconsiderar (stub) o serviço de pagamento e acionar um retorno ‘No Response’ para garantir que a unidade em teste retorne o valor correto. Isso verifica o comportamento/resposta/resultado do aplicativo em certos cenários. Você também pode usar um spy para afirmar que um email foi enviado quando esse serviço está inoperante — isso é novamente uma verificação comportamental que provavelmente aparecerá em um documento de requisitos (“Envie um email se o pagamento não puder ser salvo”). Por outro lado, se você criar um mock do serviço de pagamento e garantir que ele foi chamado com os tipos de JavaScript certos— então seu teste será focado em itens internos que não tem nada a ver com a funcionalidade do aplicativo e provavelmente mudarão frequentemente
+
+
+
+❌ **Caso Contrário:** Qualquer refatoração de código exige a pesquisa de todas as simulações no código e a atualização em conformidade. Os testes se tornam um fardo e não um amigo útil
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :thumbsdown: Exemplo Anti-padrão: Mocks foca nos internos
+
+```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 () => {
+ //Suponha que já adicionamos um produto
+ const dataAccessMock = sinon.mock(DAL);
+ //hmmm RUIM: testar os internos é realmente nosso principal objetivo aqui, não apenas um efeito colateral
+ dataAccessMock.expects("deleteProduct").once().withArgs(DBConfig, theProductWeJustAdded, true, false);
+ new ProductService().deletePrice(theProductWeJustAdded);
+ dataAccessMock.verify();
+});
+```
+
+
+### :clap: Exemplo Fazendo Certo: spies concentram-se em testar os requisitos, mas como efeito colateral inevitavelmente tocam os internos
+
+```javascript
+it("When a valid product is about to be deleted, ensure an email is sent", async () => {
+ //Suponha que já adicionamos aqui um produto
+ const spy = sinon.spy(Emailer.prototype, "sendEmail");
+ new ProductService().deletePrice(theProductWeJustAdded);
+ //hmmm OK: lidamos com internos? Sim, mas como efeito colateral do teste dos requisitos (envio de um email)
+ expect(spy.calledOnce).to.be.true;
+});
+```
+
+
+
+
+
+
+
+## ⚪ ️1.6 Não use “foo”, use dados de entrada realistas
+
+:white_check_mark: **Faça:** Muitas vezes, os bugs de produção são revelados com informações muito específicas e surpreendentes— quanto mais realista for a entrada de teste, maiores serão as chances de detectar bugs mais cedo. Use bibliotecas dedicadas como [Faker](https://www.npmjs.com/package/faker) gerar dados pseudo-reais que se assemelham à variedade e forma dos dados de produção. Por exemplo, essas bibliotecas podem gerar números de telefone, nomes de usuários, cartões de crédito, nomes de empresas e até mesmo textos 'lorem ipsum' realistas. Você também pode criar alguns testes (além dos testes de unidade) que randomizam os dados dos fakers para esticar sua unidade sob teste ou até importar dados reais do seu ambiente de produção. Quer elevar para o próximo nível? Veja o próximo tópico (teste baseado em propriedades).
+
+
+
+❌ **Caso Contrário:** Todos os seus testes de desenvolvimento parecerão falsamente verdes quando você usar entradas sintéticas como “Foo”, mas a produção poderá ficar vermelha quando um hacker passar uma string desagradável como “@ 3e2ddsf. ## '1 fdsfds. fds432 AAAA ”
+
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :thumbsdown: Exemplo Anti-padrão: Uma suíte de testes aprovada devido a dados não realistas
+
+
+
+
+```javascript
+const addProduct = (name, price) =>{
+ const productNameRegexNoSpace = /^\S*$/;//nenhum espaço em branco permitido
+
+ if(!productNameRegexNoSpace.test(name))
+ return false;//esse caminho nunca foi alcançado devido a entradas não realistas
+
+ //alguma lógica aqui
+ return true;
+};
+
+test("Wrong: When adding new product with valid properties, get successful confirmation", async () => {
+ //A string "Foo" usada em todos os testes nunca dispara um resultado falso
+ const addProductResult = addProduct("Foo", 5);
+ expect(addProductResult).toBe(true);
+ //Positivo-falso: a operação foi bem-sucedida porque nunca tentamos com
+ //nome de produto longo incluindo espaços
+});
+
+```
+
+
+### :clap: Exemplo Fazendo Certo: Randomizando entrada realista
+```javascript
+it("Better: When adding new valid product, get successful confirmation", async () => {
+ const addProductResult = addProduct(faker.commerce.productName(), faker.random.number());
+ //Entrada aleatória gerada: {'Sleek Cotton Computer', 85481}
+ expect(addProductResult).to.be.true;
+ //O teste falhou, a entrada aleatória acionou um caminho que nunca planejamos.
+ //Descobrimos um bug cedo!
+});
+```
+
+
+
+
+
+
+
+
+## ⚪ ️ 1.7 Teste muitas combinações de entrada usando testes baseados em propriedades
+
+:white_check_mark: **Faça:** Normalmente, escolhemos algumas amostras de entrada para cada teste. Mesmo quando o formato de entrada se assemelha a dados do mundo real (veja o tópico ‘Não use foo’), cobrimos apenas algumas combinações de entrada (method(‘’, true, 1), method(“string” , false” , 0)), No entanto, em produção, uma API chamada com 5 parâmetros pode ser chamada com milhares de permutações diferentes, uma delas pode tornar nosso processo inativo ([consulte Teste do Fuzz](https://pt.wikipedia.org/wiki/Fuzzing)). E se você pudesse escrever um único teste que envie 1000 permutações de entradas diferentes automaticamente e capte para qual entrada nosso código falhou em retornar a resposta correta? O teste baseado em propriedades é uma técnica que faz exatamente isso: ao enviar todas as combinações de entradas possíveis para sua unidade em teste, aumenta a possibilidade de encontrar um bug. Por exemplo, dado um método — addNewProduct(id, name, isDiscount) — as bibliotecas de suporte chamarão esse método com muitas combinações de (number, string, boolean) como (1, “iPhone”, false), (2, “Galaxy”, true). Você pode executar testes baseados em propriedades usando seu test runner favorito (Mocha, Jest, etc) usando bibliotecas como [js-verify](https://github.com/jsverify/jsverify) ou [testcheck](https://github.com/leebyron/testcheck-js) (documentação muito melhor). Atualização: Nicolas Dubien sugere nos comentários abaixo [verificar check-fast](https://github.com/dubzzz/fast-check#readme) que parece oferecer alguns recursos adicionais e também ser mantido ativamente
+
+
+
+❌ **Caso Contrário:** Inconscientemente, você escolhe as entradas de teste que cobrem apenas os caminhos de código que funcionam bem. Infelizmente, isso diminui a eficiência dos testes como veículo para expor caminhos de bugs que funcionam bem.
+
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :clap: Exemplo Fazendo Certo: Testando muitas permutações de entrada com “fast-check”
+
+
+
+```javascript
+import fc from "fast-check";
+
+describe("Product service", () => {
+ describe("Adding new", () => {
+ //isso será executado 100 vezes com diferentes propriedades aleatórias
+ 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 Se necessário, use apenas snapshots curtas e em linha
+
+:white_check_mark: **Faça:** Quando houver necessidade de [testes de snapshot](https://jestjs.io/docs/en/snapshot-testing), use apenas snapshots curtas e focadas (ou seja, 3-7 linhas) incluídas como parte do teste ([Snapshot em Linha](https://jestjs.io/docs/en/snapshot-testing#inline-snapshots)) e não dentro de arquivos externos. Manter essa diretriz garantirá que seus testes continuem auto-explicativos e menos frágeis.
+
+Por outro lado, os tutoriais e ferramentas de "clássicos de snapshot" incentivam o armazenamento de arquivos grandes (por exemplo. marcação de renderização de componente, resultado JSON da API) em algum meio externo e garantir, sempre que o teste for executado, que seja comparado o resultado recebido com a versão salva. Isso, por exemplo, pode implicitamente associar nosso teste a 1000 linhas com 3000 valores de dados sobre os quais o autor do teste nunca leu e argumentou. Por que isso está errado? Ao fazer isso, existem 1000 razões para o seu teste falhar - basta alterar uma única linha para que a snapshot fique inválida e é provável que isso aconteça muito. Com que frequência? Para cada espaço, comentário ou pequena alteração de CSS/HTML. Não apenas isso, o nome do teste não daria uma pista sobre a falha, pois apenas verifica se 1000 linhas não foram alteradas; também incentiva o redator do teste a aceitar como a verdade desejada um longo documento que ele não pôde inspecionar e verificar. Todos estes são sintomas de teste obscuro e ansioso, que não são focados e têm como objetivo alcançar muito
+
+Vale ressaltar que existem poucos casos em que snapshots longos e externos são aceitáveis - ao afirmar no schema e não nos dados (extrair valores e focar em campos) ou quando o documento recebido raramente muda
+
+
+❌ **Caso Contrário:** Um teste de UI falha. O código parece correto, a tela renderiza pixels perfeitos, o que aconteceu? seu teste de snapshot acabou de encontrar uma diferença do documento de origem para o atual recebido - um único caractere de espaço foi adicionado ao markdown...
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :thumbsdown: Exemplo Anti-padrão: Acoplando nosso teste a 2000 linhas de código invisíveis
+
+
+
+```javascript
+it('TestJavaScript.com is renderd correctly', () => {
+
+//Ajeitar
+
+//Atuar
+const receivedPage = renderer
+.create( Test JavaScript < /DisplayPage>)
+.toJSON();
+
+//Afirmar
+expect(receivedPage).toMatchSnapshot();
+//Agora mantemos implicitamente um documento com 2000 linhas
+//cada quebra de linha ou comentário adicional - interromperá este teste
+
+});
+```
+
+
+### :clap: Exemplo Fazendo Certo: As expectativas são visíveis e focadas
+```javascript
+it('When visiting TestJavaScript.com home page, a menu is displayed', () => {
+//Ajeitar
+
+//Atuar
+const receivedPage tree = renderer
+.create( Test JavaScript < /DisplayPage>)
+.toJSON();
+
+//Afirmar
+
+const menu = receivedPage.content.menu;
+expect(menu).toMatchInlineSnapshot(`
+
+- Home
+- About
+- Contact
+
+`);
+});
+```
+
+
+
+
+
+
+## ⚪ ️1.9 Evite acessórios de teste e sementes globais, adicione dados por teste
+
+:white_check_mark: **Faça:** Seguindo a regra de ouro (tópico 0), cada teste deve adicionar e agir em seu próprio conjunto de linhas de banco de dados para evitar o acoplamento e raciocinar facilmente sobre o fluxo de teste. Na realidade, isso geralmente é violado por testadores que propagam o banco de dados com dados antes de executar os testes ([também conhecido como "acessórios de teste"](https://en.wikipedia.org/wiki/Test_fixture)) por uma questão de melhoria de desempenho. Embora o desempenho seja realmente uma preocupação válida— pode ser mitigado (consulte o tópico "Teste de componentes"), no entanto, a complexidade do teste é uma tarefa muito dolorosa que deve governar outras considerações na maioria das vezes. Na prática, faça com que cada caso de teste inclua explicitamente os registros do banco de dados necessários e atue somente nesses registros. Se o desempenho se tornar uma preocupação crítica — um compromisso equilibrado pode vir na forma de propagação do único conjunto de testes que não está alterando dados (por exemplo, consultas)
+
+
+
+❌ **Caso Contrário:** Poucos testes falham, uma implantação é abortada, nossa equipe gastará um tempo precioso agora, temos um bug? Vamos investigar, oh não - parece que dois testes estavam modificando os mesmos dados iniciais
+
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :thumbsdown: Exemplo Anti-padrão: testes não são independentes e dependem de algum gancho global para alimentar dados globais de banco de dados
+
+
+
+```javascript
+before(() => {
+ //adicionando dados de sites e administradores ao nosso banco de dados. Onde estão os dados? lado de fora. Em alguma estrutura json ou de migração externa
+ await DB.AddSeedDataFromJson('seed.json');
+});
+it("When updating site name, get successful confirmation", async () => {
+ //Eu sei que o nome do site "portal" existe - eu vi nos arquivos de sementes
+ 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 () => {
+ //Eu sei que o nome do site "portal" existe - eu vi nos arquivos de sementes
+ const siteToCheck = await SiteService.getSiteByName("Portal");
+ expect(siteToCheck.name).to.be.equal("Portal"); //Falha! O teste anterior altera o nome :[
+});
+
+```
+
+
+### :clap: Exemplo Fazendo Certo: Podemos permanecer dentro do teste, cada teste atua em seu próprio conjunto de dados
+
+```javascript
+it("When updating site name, get successful confirmation", async () => {
+ //teste está adicionando registros novos e atuando apenas nos registros
+ const siteUnderTest = await SiteService.addSite({
+ name: "siteForUpdateTest"
+ });
+
+ const updateNameResult = await SiteService.changeName(siteUnderTest, "newName");
+
+ expect(updateNameResult).to.be(true);
+});
+
+```
+
+
+
+
+
+
+## ⚪ ️ 1.10 Não pegue erros, espere-os
+:white_check_mark: **Faça:** Ao tentar afirmar que alguma entrada aciona um erro, pode parecer correto usar try-catch-finally e afirmar que a entramos na cláusula catch. O resultado é um caso de teste estranho e detalhado (exemplo abaixo) que oculta a intenção simples do teste e as expectativas do resultado
+
+Uma alternativa mais elegante é o uso da asserção Chai dedicada de uma linha: expect(method).to.throw (ou no Jest: expect(method).toThrow()). É absolutamente obrigatório também garantir que a exceção contenha uma propriedade que indique o tipo de erro; caso contrário, apenas um erro genérico que o aplicativo não poderá fazer muito, em vez de mostrar uma mensagem decepcionante ao usuário
+
+
+
+❌ **Caso contrário:** Será um desafio deduzir dos relatórios de teste (por exemplo, relatórios de IC) o que deu errado
+
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :thumbsdown: Exemplo Anti-padrão: Um longo caso de teste que tenta afirmar a existência de erro com 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;
+//se essa afirmação falhar, os resultados/relatórios dos testes mostrarão apenas
+//que algum valor é null, não haverá uma palavra sobre um erro de ausência
+});
+
+```
+
+
+### :clap: Exemplo Fazendo Certo: Uma expectativa legível por humanos que pode ser entendida facilmente, talvez até pelo controle de qualidade ou pelo gerente de produto
+
+```javascript
+it.only("When no product name, it throws error 400", async() => {
+ await expect(addNewProduct({})).to.eventually.throw(AppError).with.property('code', "InvalidInput");
+});
+
+```
+
+
+
+
+
+
+
+
+## ⚪ ️ 1.11 Marque seus testes
+
+:white_check_mark: **Faça:** Testes diferentes devem ser executados em diferentes cenários: testes rápidos de fumaça, sem IO, devem ser executados quando um desenvolvedor salva ou dá commit em um arquivo, testes completos de ponta a ponta geralmente são executados quando uma nova pull request é enviada, etc. Isso pode ser alcançado marcando testes com palavras-chave como #cold #api #sanity para que você possa selecionar com sua ferramenta de teste e chamar o subconjunto desejado. Por exemplo, é assim que você invocaria apenas o grupo de teste de sanidade com Mocha: mocha — grep ‘sanity’
+
+
+
+❌ **Caso Contrário:** A execução de todos os testes, incluindo testes que executam dezenas de consultas ao banco de dados, sempre que um desenvolvedor faz uma pequena alteração pode ser extremamente lenta e mantém os desenvolvedores longe de executar testes
+
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :clap: Exemplo Fazendo Certo: Marcando testes como ‘#cold-test’ permite que a ferramenta de teste execute apenas testes rápidos (Cold===testes rápidos que não fazem IO e podem ser executados com freqüência, mesmo quando o desenvolvedor está digitando)
+
+
+```javascript
+//esse teste é rápido (sem banco de dados) e estamos marcando de forma correspondente
+//agora o usuário/IC pode executá-lo com frequência
+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() {
+ //lógica de código aqui
+ });
+ });
+});
+
+
+```
+
+
+
+
+
+
+
+
+## ⚪ ️1.12 Outra boa higiene genérica para testes
+:white_check_mark: **Faça:** Esta postagem é focada em conselhos de teste relacionados ou pelo menos podem ser exemplificados com Node JS. Este tópico, no entanto, agrupa algumas dicas não relacionadas a Node que são bem conhecidas
+
+Aprenda e pratique [princípios TDD](https://www.sm-cloud.com/book-review-test-driven-development-by-example-a-tldr/) — eles são extremamente valiosos para muitos, mas não se intimidem se não se encaixarem no seu estilo, você não é o único. Considere escrever os testes antes do código em um [estilo vermelho-verde-refatorar](https://blog.cleancoder.com/uncle-bob/2014/12/17/TheCyclesOfTDD.html), certifique-se de que cada teste verifica exatamente uma coisa, quando você encontrar um erro—antes de corrigir, escreva um teste que detectará esse erro no futuro, deixe que cada teste falhe pelo menos uma vez antes de ficar verde, inicie um módulo escrevendo um código rápido e simplista que satisfaça o teste - refatore gradualmente e leve-o a um nível de produção, evitar qualquer dependência do ambiente (caminhos, SO, etc)
+
+
+
+❌ **Caso Contrário:** Você sentirá falta das pérolas de sabedoria que foram coletadas por décadas
+
+
+
+
+# Seção 2️⃣: Teste de Backend
+
+## ⚪ ️2.1 Enriqueça seu portfólio de testes: Olhe além dos testes de unidade e da pirâmide
+
+:white_check_mark: **Faça:** A [pirâmide de testes](https://martinfowler.com/bliki/TestPyramid.html), apesar de ter 10> anos de idade, é um modelo excelente e relevante que sugere três tipos de teste e influencia a estratégia de teste da maioria dos desenvolvedores. Ao mesmo tempo, mais de um punhado de novas e brilhantes técnicas de teste surgiram e estão escondidas nas sombras da pirâmide de testes. Dadas todas as mudanças dramáticas que vimos nos últimos 10 anos (Microsserviços, cloud, serverless), é possível que um modelo bastante antigo seja adequado a *todos* os tipos de aplicações? O mundo dos testes não deveria considerar acolher novas técnicas de teste?
+
+Não me interpretem mal, em 2019 a pirâmide de testes, TDD e testes de unidade ainda são técnicas poderosas e provavelmente são as mais compatíveis para muitas aplicações. Apenas como qualquer outro modelo, apesar de sua utilidade, [às vezes está errado](https://en.wikipedia.org/wiki/All_models_are_wrong). Por exemplo, considere um aplicativo IOT que ingere muitos eventos em um padronizador de mensagens como Kafka/RabbitMQ, que fluem para algum armazenamento de dados e, eventualmente, são consultados por algum UI. Deveríamos realmente gastar 50% do nosso orçamento em testes escrevendo testes de unidade para um aplicativo centrado na integração e quase sem lógica? À medida que a diversidade de tipos de aplicativos aumenta (bots, crypto, Alexa-skills) maiores são as chances de encontrar cenários em que a pirâmide de teste não é a melhor correspondência.
+
+É hora de enriquecer seu portfólio de testes e se familiarizar com mais tipos de modelos mentais de testes (os próximos tópicos sugerem poucas idéias), como a pirâmide de testes, mas também combinar tipos de teste com problemas do mundo real que você está enfrentando (‘Ei, nossa API está quebrada, vamos escrever testes de contrato orientados ao consumidor!’), diversifique seus testes como um investidor que constrói um portfólio com base na análise de risco — avaliar onde os problemas podem surgir e combinar algumas medidas de prevenção para mitigar esses riscos potenciais
+
+Uma palavra de cautela: o argumento TDD no mundo do software tem uma cara típica de dicotomia, alguns pregam para usá-lo em todo lugar, outros acham que ele é o diabo. Todo mundo que fala em absoluto está errado :]
+
+
+
+
+❌ **Caso Contrário:** Você perderá algumas ferramentas com um ROI incrível, algumas como Fuzz, lint e mutation podem fornecer valor em 10 minutos
+
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :clap: Exemplo Fazendo Certo: Cindy Sridharan sugere um rico portfólio de testes em seu incrível post "Testing Microservices - the sane way"
+
+
+☺️Example: [YouTube: “Além dos testes de unidade: 5 tipos de teste Node.JS brilhante (2018)” (Yoni Goldberg)](https://www.youtube.com/watch?v=-2zP494wdUY&feature=youtu.be)
+
+
+
+
+
+
+
+
+
+
+
+
+
+## ⚪ ️2.2 O teste de componentes pode ser o seu melhor caso
+
+:white_check_mark: **Faça:** Cada teste de unidade cobre uma pequena parte do aplicativo e é caro cobrir o todo, enquanto os testes de ponta-a-ponta cobrem muito terreno, mas são escamosos e mais lentos, por que não aplicar uma abordagem equilibrada e escrever testes maiores que os testes unitários, mas menores que os testes de ponta-a-ponta? Teste de componentes é a música desconhecida do mundo dos testes— eles fornecem o melhor dos dois mundos: desempenho razoável e possibilidade de aplicar padrões TDD + cobertura realista e ótima.
+
+Os testes de componentes concentram-se na 'unidade' do Microsservico, eles trabalham contra a API, não fazem mock de nada que pertença ao próprio Microsserviço (por exemplo. banco de dados real ou pelo menos a versão na memória desse banco de dados) mas fazem stub (desconsideram) qualquer coisa externa como chamadas para outros Microsserviços. Ao fazer isso, testamos o que implementamos, abordamos o aplicativo de fora para dentro e obtemos grande confiança em um período de tempo razoável.
+
+
+
+❌ **Caso Contrário:** Você pode passar longos dias escrevendo testes de unidade para descobrir que possui apenas 20% de cobertura do sistema
+
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :clap: Exemplo Fazendo Certo: O Supertest permite abordar a API Express em processo (rápido e cobre muitas camadas)
+
+
+
+ permite abordar a API Express em processo (rápido e cobre muitas camadas)")
+
+
+
+
+
+## ⚪ ️2.3 Verifique se os novos releases não quebram a API em uso
+
+:white_check_mark: **Faça:** Então, seu Microsserviço possui vários clientes e você executa várias versões do serviço por motivos de compatibilidade (mantendo todos felizes). Então você muda algum campo e ‘boom!’, algum cliente importante que depende desse campo fica irritado. Este é o Catch-22 do mundo da integração: É muito desafiador para o lado do servidor considerar todas as múltiplas expectativas dos clientes— Por outro lado, os clientes não podem realizar nenhum teste porque o servidor controla as datas de lançamento. [Contratos orientados ao consumidor e o framework PACT](https://docs.pact.io/) nasceram para formalizar esse processo com uma abordagem muito perturbadora — não é o servidor que define o plano de teste por si mesmo, mas o cliente define os testes do… servidor! PACT pode gravar a expectativa do cliente e colocar em um local compartilhado, “corretor”, para que o servidor possa puxar as expectativas e executar em cada build usando a biblioteca PACT para detectar contratos quebrados— uma expectativa do cliente que não é atendida. Ao fazer isso, todas as incompatibilidades da API do servidor-cliente são detectadas cedo durante a compilação/IC e podem poupar muita frustração
+
+
+
+❌ **Caso Contrário:** As alternativas são exaustivos testes manuais ou medo de implantação
+
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :clap: Exemplo Fazendo Certo:
+
+
+
+
+
+
+
+
+
+
+
+
+
+## ⚪ ️ 2.4 Teste seus Middlewares isoladamente
+
+:white_check_mark: **Faça:** Muitos evitam os testes de Middleware porque representam uma pequena parte do sistema e requerem um servidor Express ativo. Ambas as razões estão erradas — Os Middlewares são pequenos, mas afetam todas ou a maioria das solicitações e podem ser testados facilmente como funções puras que recebem objetos JS {req, res}. Para testar uma função de middleware, basta invocá-la e espionar ([usando o Sinon por exemplo](https://www.npmjs.com/package/sinon)) na interação com os objetos {req, res} para garantir que a função executou a ação correta. A biblioteca [node-mock-http](https://www.npmjs.com/package/node-mocks-http) vai ainda mais longe e fatora os objetos {req, res}, além de espionar seu comportamento. Por exemplo, ela pode afirmar se o status http que foi definido no objeto res corresponde à expectativa (veja o exemplo abaixo)
+
+
+
+❌ **Caso Contrário:** Um bug no middleware Express === um bug em todas ou na maioria das solicitações
+
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :clap: Exemplo Fazendo Certo: Testando o middleware isoladamente sem emitir chamadas de rede e acordar toda a máquina Express
+
+
+
+```javascript
+//o middleware que queremos testar
+const unitUnderTest = require('./middleware')
+const httpMocks = require('node-mocks-http');
+//Sintaxe Jest, equivalente a describe() & it() no 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 Meça e refatore usando ferramentas de análise estática
+:white_check_mark: **Faça:** O uso de ferramentas de análise estática ajuda a fornecer maneiras objetivas de melhorar a qualidade do código e manter seu código sustentável. Você pode adicionar ferramentas de análise estática à sua compilação de IC para abortar quando encontrar mal cheiros no código. Suas principais vantagens em relação a usar simplesmente um linter são a capacidade de inspecionar a qualidade no contexto de vários arquivos (por exemplo. detectar duplicações), executar análise avançada (por exemplo, complexidade do código) e seguir o histórico e o progresso dos problemas de código. Dois exemplos de ferramentas que você pode usar são: [Sonarqube](https://www.sonarqube.org/) (2,600+ [stars](https://github.com/SonarSource/sonarqube)) e [Code Climate](https://codeclimate.com/) (1,500+ [stars](https://github.com/codeclimate/codeclimate))
+
+Créditos:: [Keith Holliday](https://github.com/TheHollidayInn)
+
+
+
+
+❌ **Caso Contrário:** Com baixa qualidade de código, bugs e desempenho sempre serão um problema que nenhuma nova biblioteca brilhante ou recursos avançados podem corrigir
+
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :clap: Exemplo Fazendo Certo: CodeClimate, uma ferramenta comercial que pode identificar métodos complexos:
+
+
+
+
+
+
+
+
+
+
+
+
+## ⚪ ️ 2.6 Verifique sua preparação para o caos relacionado ao Node
+:white_check_mark: **Faça:** Estranhamente, a maioria dos testes de software trata apenas de lógica e dados, mas algumas das piores coisas que acontecem (e são realmente difíceis de mitigar) são questões de infra-estrutura. Por exemplo, você já testou o que acontece quando a memória do processo está sobrecarregada, ou quando o servidor/processo morre, ou o seu sistema de monitoramento percebe quando a API fica 50% mais lenta?. Para testar e mitigar esse tipo de coisas ruins — [Chaos engineering](https://principlesofchaos.org/) nasceu pela Netflix. O objetivo é fornecer conscientização, frameworks e ferramentas para testar a resiliência de nosso aplicativo para problemas caóticos. Por exemplo, uma de suas famosas ferramentas, [o chaos monkey](https://github.com/Netflix/chaosmonkey), mata servidores aleatoriamente para garantir que nosso serviço ainda possa atender usuários e não depender em um único servidor (existe também uma versão para Kubernetes, [kube-monkey](https://github.com/asobti/kube-monkey), que mata pods). Todas essas ferramentas funcionam no nível de hospedagem/plataforma, mas e se você quiser testar e gerar o caos puro do Node, por exemplo verificar como o processo do nó lida com erros não detectados, rejeições de promises não tratadas, Memória do v8 sobrecarregada com o máximo permitido de 1,7 GB ou se o seu UX permanece satisfatório quando o loop de eventos é bloqueado com frequência? para resolver isso que escrevi, [node-chaos](https://github.com/i0natan/node-chaos-monkey) (alpha) que fornece todos os tipos de atos caóticos relacionados ao Node
+
+
+
+❌ **Caso Contrário:** Não há escapatória aqui, a lei de Murphy afetará sua produção sem piedade
+
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :clap: Exemplo Fazendo Certo: : O Node-Chaos pode gerar todo tipo de pegadinha do Node.js, para que você possa testar a resiliência do seu aplicativo ao caos
+
+
+
+
+
+
+## ⚪ ️2.7 Evite acessórios de teste e sementes globais, adicione dados por teste
+
+:white_check_mark: **Faça:** Seguindo a regra de ouro (tópico 0), cada teste deve adicionar e agir em seu próprio conjunto de linhas de banco de dados para evitar o acoplamento e raciocinar facilmente sobre o fluxo de teste. Na realidade, isso geralmente é violado por testadores que propagam o banco de dados com dados antes de executar os testes (também conhecido como "acessórios de teste") por uma questão de melhoria de desempenho. Embora o desempenho seja realmente uma preocupação válida— pode ser mitigado (consulte o tópico "Teste de componentes"), no entanto, a complexidade do teste é uma tarefa muito dolorosa que deve governar outras considerações na maioria das vezes. Na prática, faça com que cada caso de teste inclua explicitamente os registros do banco de dados necessários e atue somente nesses registros. Se o desempenho se tornar uma preocupação crítica — um compromisso equilibrado pode vir na forma de propagação do único conjunto de testes que não está alterando dados (por exemplo, consultas)
+
+
+
+❌ **Caso Contrário:** Poucos testes falham, uma implantação é abortada, nossa equipe gastará um tempo precioso agora, temos um bug? Vamos investigar, oh não - parece que dois testes estavam modificando os mesmos dados iniciais
+
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :thumbsdown: exemplo Anti-padrão: testes não são independentes e dependem de algum gancho global para alimentar dados globais de banco de dados
+
+
+
+```javascript
+before(() => {
+ //adicionando dados de sites e administradores ao nosso banco de dados. Onde estão os dados? Do lado de fora. Em alguma estrutura json ou de migração externa
+ await DB.AddSeedDataFromJson('seed.json');
+});
+it("When updating site name, get successful confirmation", async () => {
+ //Eu sei que o nome do site "portal" existe - eu vi nos arquivos de sementes
+ 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 () => {
+ //Eu sei que o nome do site "portal" existe - eu vi nos arquivos de sementes
+ const siteToCheck = await SiteService.getSiteByName("Portal");
+ expect(siteToCheck.name).to.be.equal("Portal"); //Falha! O teste anterior altera o nome :[
+});
+
+```
+
+
+### :clap: Exemplo Fazendo Certo: Podemos permanecer dentro do teste, cada teste atua em seu próprio conjunto de dados
+
+```javascript
+it("When updating site name, get successful confirmation", async () => {
+ //teste está adicionando registros novos e atuando apenas nos registros
+ const siteUnderTest = await SiteService.addSite({
+ name: "siteForUpdateTest"
+ });
+ const updateNameResult = await SiteService.changeName(siteUnderTest, "newName");
+ expect(updateNameResult).to.be(true);
+});
+
+```
+
+
+
+
+
+# Seção 3️⃣: Teste de Frontend
+
+## ⚪ ️ 3.1 Separar UI da funcionalidade
+
+:white_check_mark: **Faça:** Ao focar no teste da lógica dos componentes, os detalhes da interface do usuário se tornam um ruído que deve ser extraído, para que seus testes possam se concentrar em dados puros. Na prática, extraia os dados desejados da marcação de uma maneira abstrata que não seja muito acoplada à implementação gráfica, afirme apenas dados puros (vs detalhes gráficos de HTML/CSS) e desative animações que diminuem a velocidade. Você pode cair na tentação de evitar renderizar e testar apenas a parte de trás da interface do usuário (por exemplo, serviços, ações, armazenamento), mas isso resultará em testes fictícios que não se assemelham à realidade e não revelam casos em que os dados corretos nem chegam na interface do usuário
+
+
+
+
+❌ **Caso contrário:** Os dados puramente calculados do seu teste podem estar prontos em 10 ms, mas o teste inteiro durará 500 ms (100 testes = 1 min) devido a alguma animação sofisticada e irrelevante
+
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :clap: Exemplo Fazendo Certo: Separando os detalhes da interface do usuário
+
+ 
+
+```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: Exemplo Anti-padrão: Afirmações misturam detalhes da UI e dados
+```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 Consultar elementos HTML com base em atributos que provavelmente não serão alterados
+
+:white_check_mark: **Faça*:** Consulte elementos HTML com base em atributos que provavelmente sobreviverão a alterações gráficas, diferentemente dos seletores CSS e sim como os rótulos de formulário. Se o elemento designado não tiver esses atributos, crie um atributo dedicado a teste como 'test-id-submit-button'. Seguir essa rota não apenas garante que seus testes funcionais/lógicos nunca sejam quebrados devido a alterações de aparência, mas também fica claro para toda a equipe que esse elemento e atributo são utilizados por testes e não devem ser removidos
+
+
+
+❌ **Caso contrário:** Você deseja testar a funcionalidade de login que abrange muitos componentes, lógica e serviços, tudo está configurado perfeitamente - stubs, spies, chamadas Ajax são isoladas. Tudo parece perfeito. Em seguida, o teste falha porque o designer alterou a classe CSS da div de 'thick-border' para 'thin-border'
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :clap: Exemplo Fazendo Certo: Consultando um elemento usando um atributo dedicado para teste
+
+
+
+```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: Exemplo Anti-padrão: Confiando em atributos 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 Sempre que possível, teste com um componente realista e totalmente renderizado
+
+:white_check_mark: **Faça:** Sempre que tiver um tamanho razoável, teste seu componente de fora como os usuários, renderize a interface do usuário, atue sobre ela e afirme que a interface do usuário renderizada se comporta conforme o esperado. Evite todo tipo de simulação, renderização parcial e superficial - essa abordagem pode resultar em erros não capturados devido à falta de detalhes e dificultar a manutenção, pois os testes interferem nos internos (veja o tópico 'Favorecer o teste de caixa preta'). Se um dos componentes filhos estiver desacelerando significativamente (por exemplo, animação) ou complicando a instalação - considere substituí-lo explicitamente por um falso
+
+Com tudo isso dito, uma palavra de cautela é necessária: essa técnica funciona para componentes pequenos/médios que contêm um tamanho razoável de componentes filhos. A renderização completa de um componente com muitos filhos dificultará o raciocínio sobre falhas de teste (análise de causa raiz) e poderá ficar muito lenta. Nesses casos, escreva apenas alguns testes contra esse componente pai pesado e mais testes contra seus filhos
+
+
+
+❌ **Caso contrário:** Ao entrar no interno de um componente, invocando seus métodos privados e verificando o estado interno - você teria que refatorar todos os testes ao refatorar a implementação dos componentes. Você realmente tem capacidade para esse nível de manutenção?
+
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :clap: Exemplo Fazendo Certo: Trabalhando realisticamente com um componente totalmente renderizado
+
+ 
+
+```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: Exemplo Anti-padrão: Simulando a realidade com renderização superficial
+```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 Não durma, use o suporte incorporado de frameworks para eventos assíncronos. Também tente acelerar as coisas
+
+:white_check_mark: **Faça:** Em muitos casos, o tempo de conclusão da unidade em teste é desconhecido (por exemplo, a animação suspende a aparência do elemento) - nesse caso, evite dormir (por exemplo, setTimeOut) e prefira métodos mais determinísticos que a maioria das plataformas fornece. Algumas bibliotecas permitem aguardar operações (por exemplo, [Cypress cy.request('url')](https://docs.cypress.io/guides/references/best-practices.html#Unnecessary-Waiting)), outras fornecem API para esperar como [@testing-library/dom method wait(expect(element))](https://testing-library.com/docs/guide-disappearance). Às vezes, uma maneira mais elegante é esboçar o recurso lento, como a API, por exemplo, e depois que o momento da resposta se torna determinístico, o componente pode ser explicitamente renderizado novamente. Quando, dependendo de algum componente externo que dorme, pode ser útil [apressar o relógio](https://jestjs.io/docs/en/timer-mocks). Dormir é um padrão a ser evitado, porque força o teste a ser lento ou arriscado (ao esperar por um período muito curto). Sempre que dormir e pesquisar for inevitável e não há suporte do framework de teste, algumas bibliotecas do NPM, como [wait-for-expect](https://www.npmjs.com/package/wait-for-expect) podem ajudar com uma solução semi-determinística
+
+
+❌ **Caso contrário:** Ao dormir por um longo tempo, os testes serão uma ordem de magnitude mais lenta. Ao tentar dormir por pequenos números, o teste falha quando a unidade em teste não responde em tempo hábil. Portanto, tudo se resume a uma troca entre descamação e mau desempenho
+
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :clap: Exemplo Fazendo Certo: API E2E que resolve somente quando as operações assíncronas são concluídas (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: Exemplo Fazendo Certo: Biblioteca de teste que aguarda elementos do 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: Exemplo Anti-padrão: código de suspensão personalizado
+```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 Veja como o conteúdo é servido na rede
+
+
+
+✅ **Faça:** Aplique um monitor ativo que garanta que o carregamento da página na rede real seja otimizado - isso inclui qualquer preocupação de UX, como carregamento lento da página ou pacote não minificado. O mercado de ferramentas de inspeção não é curto: ferramentas básicas como [pingdom](https://www.pingdom.com/), AWS CloudWatch, [gcp StackDriver](https://cloud.google.com/monitoring/uptime-checks/) podem ser facilmente configuradas para verificar se o servidor está ativo e responde sob um SLA razoável. Isso apenas arranha a superfície do que pode estar errado; portanto, é preferível optar por ferramentas especializadas em frontend(por exemplo, [lighthouse](https://developers.google.com/web/tools/lighthouse/), [pagespeed](https://developers.google.com/speed/pagespeed/insights/)) e realizar análises mais ricas. O foco deve estar nos sintomas, nas métricas que afetam diretamente o UX, como o tempo de carregamento da página, [meaningful paint](https://scotch.io/courses/10-web-performance-audit-tips-for-your-next-billion-users-in-2018/fmp-first-meaningful-paint), [ttempo até a página ficar interativa (TTI)](https://calibreapp.com/blog/time-to-interactive/). Além disso, também é possível observar causas técnicas, como garantir que o conteúdo seja compactado, tempo até o primeiro byte, otimizar imagens, garantir tamanho razoável de DOM, SSL e muitos outros. É aconselhável ter esses monitores avançados durante o desenvolvimento, como parte do CI e o mais importante - 24x7 nos servidores de produção/CDN
+
+
+
+❌ **Caso contrário:** Deve ser decepcionante perceber que, após um cuidado tão grande na criação de uma interface do usuário, testes 100% funcionais passando e empacotamento sofisticado - o UX é horrível e lento devido à configuração incorreta da CDN
+
+
+
+✏ Códigos de Exemplo
+
+### :clap: Exemplo Fazendo Certo: Relatório de inspeção de carregamento de página do Lighthouse
+
+
+
+
+
+
+
+
+
+## ⚪ ️ 3.6 Esboce recursos escamosos e lentos, como APIs de backend
+
+:white_check_mark: **Faça:** Ao codificar seus testes convencionais (não os testes E2E), evite envolver qualquer recurso que esteja além de sua responsabilidade e controle como a API de backend e use esboços (por exemplo, teste duplo). Na prática, em vez de chamadas de rede reais para APIs, use alguma biblioteca de teste duplo (como [Sinon](https://sinonjs.org/), [Test doubles](https://www.npmjs.com/package/testdouble), etc) para esboçar a resposta da API. O principal benefício é evitar falhas - APIs de teste ou preparo, por definição, não são altamente estáveis e, de tempos em tempos, serão reprovados em seus testes, embora SEU componente se comporte perfeitamente (o ambiente de produção não foi projetado para testes e geralmente limita as solicitações). Isso permitirá simular vários comportamentos da API que devem direcionar o comportamento do componente como quando nenhum dado foi encontrado ou o caso em que a API gera um erro. Por último, mas não menos importante, as chamadas de rede desacelerarão bastante os testes
+
+
+
+❌ **Caso contrário:** Um teste médio é executado em não mais do que alguns ms, uma chamada típica da API dura 100ms>, isso torna cada teste ~ 20x mais lento
+
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :clap: Exemplo Fazendo Certo: fazendo o esboço ou interceptando chamadas de 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 Tenha muito poucos testes de ponta a ponta que abrangem todo o sistema
+
+:white_check_mark: **Faça:** Embora o E2E (ponta a ponta) geralmente signifique testes apenas da interface do usuário com um navegador real (consulte o item 3.6), para outros, eles significam testes que abrangem todo o sistema, incluindo o backend real. O último tipo de teste é altamente valioso, pois cobre erros de integração entre frontend e backend que podem ocorrer devido a um entendimento incorreto do esquema de troca. Eles também são um método eficiente para descobrir problemas de integração de backend para backend (por exemplo, o microsserviço A envia a mensagem errada para o microsserviço B) e até mesmo para detectar falhas de implantação - não há estruturas de backend para testes E2E que sejam tão amigáveis e maduras quanto as estruturas de interface do usuário, como [Cypress](https://www.cypress.io/) e [Pupeteer](https://github.com/GoogleChrome/puppeteer). A desvantagem de tais testes é o alto custo de configuração de um ambiente com tantos componentes e principalmente sua fragilidade - com 50 microsserviços, mesmo se um falhar, todo o E2E falhou. Por esse motivo, devemos usar essa técnica com moderação e provavelmente ter de 1 a 10 desses e não mais. Dito isto, mesmo um pequeno número de testes E2E provavelmente capturará o tipo de problemas para os quais eles são direcionados - falhas de implantação e integração. É aconselhável executá-las em um ambiente de preparação semelhante à produção
+
+
+
+❌ **Caso contrário:** A interface do usuário pode investir muito em testar sua funcionalidade apenas para perceber muito tarde que a carga útil retornada pelo backend (o esquema de dados com o qual a interface do usuário precisa trabalhar) é muito diferente do esperado
+
+
+
+## ⚪ ️ 3.8 Acelere os testes do E2E reutilizando credenciais de login
+
+:white_check_mark: **Faça:** Nos testes E2E que envolvem um back-end real e dependem de um token de usuário válido para chamadas de API, não vale a pena isolar o teste para um nível em que um usuário é criado e conectado a cada solicitação. Em vez disso, efetue login apenas uma vez antes do início da execução dos testes (ou seja, antes de tudo), salve o token em algum armazenamento local e reutilize-o nas solicitações. Isso parece violar um dos principais princípios de teste - mantenha o teste autônomo sem acoplamento de recursos. Embora essa seja uma preocupação válida, nos testes E2E o desempenho é uma preocupação importante e a criação de 1-3 solicitações de API antes de iniciar cada teste individual pode levar a um tempo de execução horrível. Reutilizar credenciais não significa que os testes precisam agir nos mesmos registros do usuário - se depender de registros do usuário (por exemplo, histórico de pagamentos do usuário do teste), certifique-se de gerar esses registros como parte do teste e evitar compartilhar sua existência com outros testes. Lembre-se também de que o backend pode ser falsificado - se seus testes estiverem focados no frontend, pode ser melhor isolá-lo e esboçar a API de backend (consulte o item 3.6).
+
+
+
+❌ **Caso contrário:** Dado 200 casos de teste e assumindo login=100ms = 20 segundos apenas para efetuar login novamente e novamente
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :clap: Exemplo Fazendo Certo: Fazer login antes de todos e não antes de cada
+
+
+
+```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 Faça um teste de fumaça E2E que viaja pelo mapa do site
+
+:white_check_mark: **Faça:** Para monitoramento de produção e verificação de integridade do tempo de desenvolvimento, execute um único teste E2E que visite todas/a maioria das páginas do site e garanta que nenhuma quebre. Esse tipo de teste traz um ótimo retorno do investimento, pois é muito fácil de escrever e manter, mas pode detectar qualquer tipo de falha, incluindo problemas funcionais, de rede e de implantação. Outros estilos de verificação de fumaça e sanidade não são tão confiáveis e exaustivos - algumas equipes de operações apenas fazem ping na página inicial (produção) ou desenvolvedores que executam muitos testes de integração os quais não descobrem problemas de empacotamento e de navegador. Nem precisa dizer que o teste de fumaça não substitui os testes funcionais, apenas visa servir como um detector de fumaça rápido
+
+
+
+❌ **Caso contrário:** Tudo pode parecer perfeito, todos os testes são aprovados, a verificação de integridade da produção também é positiva, mas o componente Payment teve algum problema de embalagem e apenas a rota /Payment não está sendo processada
+
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :clap: Exemplo Fazendo Certo: Fumaça viajando por todas as páginas
+
+```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 Expor os testes como um documento colaborativo vivo
+
+:white_check_mark: **Faça:** Além de aumentar a confiabilidade do aplicativo, os testes trazem outra oportunidade atraente para a mesa - servem como documentação viva do aplicativo. Como os testes falam inerentemente em uma linguagem menos técnica e de produto/UX, usando as ferramentas certas, eles podem servir como um artefato de comunicação que alinha muito os colegas - desenvolvedores e seus clientes. Por exemplo, algumas estruturas permitem expressar o fluxo e as expectativas (ou seja, plano de testes) usando uma linguagem legível por humanos, para que qualquer parte interessada, incluindo gerentes de produto, possa ler, aprovar e colaborar nos testes que acabaram de se tornar o documento de requisitos dinâmicos. Essa técnica também está sendo chamada de 'teste de aceitação', pois permite ao cliente definir seus critérios de aceitação em linguagem simples. Isso é [BDD (teste orientado ao comportamento)](https://en.wikipedia.org/wiki/Behavior-driven_development) na sua forma mais pura. Um dos frameworks populares que permitem isso é [Cucumber que tem um sabor de JavaScript](https://github.com/cucumber/cucumber-js), veja o exemplo abaixo. Outra oportunidade semelhante, porém diferente, [StoryBook](https://storybook.js.org/), permite expor componentes da interface do usuário como um catálogo gráfico, onde é possível percorrer os vários estados de cada componente (por exemplo. renderizar uma grid sem filtros, renderizar essa grid com várias linhas ou sem nenhuma, etc.), ver como ele se parece e como acionar esse estado - isso também pode atrair as pessoas do produto, mas serve principalmente como documento ativo para desenvolvedores que consomem esses componentes.
+
+❌ **Otherwise:** Depois de investir muitos recursos em testes, é uma pena não alavancar esse investimento e obter grande valor
+
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :clap: Exemplo Fazendo Certo: Descrevendo testes em linguagem humana usando 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: Exemplo Fazendo Certo: Visualizando nossos componentes, seus vários estados e entradas usando o Storybook
+
+
+
+
+
+
+
+
+
+
+## ⚪ ️ 3.11 Detecte problemas visuais com ferramentas automatizadas
+
+
+:white_check_mark: **Faça:** Configure ferramentas automatizadas para capturar a tela da interface do usuário quando alterações forem apresentadas e detectar problemas visuais, como sobreposição ou quebra de conteúdo. Isso garante que não apenas os dados corretos sejam preparados, mas também que o usuário possa vê-los convenientemente. Essa técnica não é amplamente adotada, nossa mentalidade de teste se inclina para testes funcionais, mas é o visual que o usuário experimenta e, com tantos tipos de dispositivos, é muito fácil ignorar alguns erros desagradáveis da interface do usuário. Algumas ferramentas gratuitas podem fornecer o básico - gerar e salvar capturas de tela para a inspeção dos olhos humanos. Embora essa abordagem possa ser suficiente para aplicativos pequenos, ela é falha como qualquer outro teste manual que exige mão de obra humana sempre que algo muda. Por outro lado, é bastante desafiador detectar problemas de interface do usuário automaticamente devido à falta de definição clara - é aqui que o campo de 'Regressão Visual' entra em cena e resolve esse quebra-cabeça comparando a interface antiga com as alterações mais recentes e detectando diferenças. Algumas ferramentas OSS/gratuitas podem fornecer algumas dessas funcionalidades (por exemplo, [wraith](https://github.com/BBC-News/wraith), [PhantomCSS]([https://github.com/HuddleEng/PhantomCSS](https://github.com/HuddleEng/PhantomCSS)) mas pode cobrar um tempo significativo de configuração. A linha comercial de ferramentas (por exemplo [Applitools](https://applitools.com/), [Percy.io](https://percy.io/)) dá um passo adiante ao suavizar a instalação e incluir recursos avançados, como interface de gerenciamento, alerta, captura inteligente, eliminando o 'ruído visual' (por exemplo, anúncios, animações) e até mesmo a análise de causa raiz das alterações no DOM/css que levaram ao problema
+
+
+
+❌ **Caso contrário:** Quão boa é uma página de conteúdo que exibe ótimo conteúdo (100% nos testes aprovados), carrega instantaneamente, mas metade da área de conteúdo está oculta?
+
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :thumbsdown: Exemplo Anti-padrão: Uma regressão visual típica - conteúdo correto que é mal servido
+
+
+
+
+
+
+### :clap: Exemplo Fazendo Certo: Configurando o wraith para capturar e comparar instantâneos da 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: Exemplo Fazendo Certo: Usando Applitools para obter comparação de captura instantânea e outros recursos avançados
+
+ 
+
+```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');
+ });
+});
+```
+
+
+
+
+
+
+
+
+
+
+
+# Seção 4️⃣: Medindo a Eficácia dos Testes
+
+
+
+## ⚪ ️ 4.1 Obtenha cobertura suficiente para ter confiança, ~ 80% parece ser o número da sorte
+
+:white_check_mark: **Faça:** O objetivo do teste é obter confiança suficiente para avançar rapidamente, obviamente, quanto mais código for testado, mais confiante a equipe pode ter. Cobertura é uma medida de quantas linhas de código (e ramificações, instruções etc.) estão sendo alcançadas pelos testes. Então, quanto é suficiente? 10-30% é obviamente muito baixo para ter noção da correção da compilação, por outro lado, 100% é muito caro e pode mudar seu foco dos caminhos críticos para os cantos exóticos do código. A resposta longa é que depende de muitos fatores, como o tipo de aplicativo - se você está construindo a próxima geração do Airbus A380, 100% é uma obrigação, para um site de imagens de desenhos animados 50% pode ser demais. Embora a maioria dos entusiastas do teste afirme que o limite de cobertura correto é contextual, a maioria deles também menciona o número 80% como a regra de ouro ([Fowler: “na casa dos 80% ou 90%”](https://martinfowler.com/bliki/TestCoverage.html)) que presumivelmente, deve satisfazer a maioria das aplicações.
+
+Dicas de implementação: Convém configurar sua integração contínua (CI) para ter um limite mínimo de cobertura ([Jest link](https://jestjs.io/docs/en/configuration.html#collectcoverage-boolean)) e interrompa uma compilação que não atenda a esse padrão (também é possível configurar o limite por componente, veja o exemplo de código abaixo). Além disso, considere detectar uma diminuição na cobertura da construção (quando um código recém-confirmado tem menos cobertura) — isso fará com que os desenvolvedores aumentem ou, pelo menos, preservem a quantidade de código testado. Tudo isso dito, a cobertura é apenas uma medida, quantitativa, que não é suficiente para demonstrar a robustez dos seus testes. E também pode ser enganada, como ilustrado nos próximos tópicos
+
+
+
+
+❌ **Caso contrário:** Confiança e números andam de mãos dadas, sem realmente saber que você testou a maior parte do sistema - haverá também algum medo. e o medo vai atrasá-lo
+
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :clap: Exemplo: um relatório de cobertura típico
+
+
+
+
+### :clap: Exemplo Fazendo Certo: Configurando a cobertura por componente (usando o Jest)
+
+
+
+")
+
+
+
+
+
+
+
+## ⚪ ️ 4.2 Inspecionar relatórios de cobertura para detectar áreas não testadas e outras esquisitices
+
+:white_check_mark: **Faça:** Alguns problemas se escondem logo abaixo do radar e são realmente difíceis de encontrar usando ferramentas tradicionais. Esses não são realmente erros, mas um comportamento surpreendente do aplicativo que pode ter um impacto grave. Por exemplo, geralmente algumas áreas de código nunca ou raramente são invocadas — você pensou que a classe 'PricingCalculator' está sempre definindo o preço do produto, mas, na verdade, nunca é invocada, embora tenhamos 10000 produtos no banco de dados e muitas vendas... Os relatórios de cobertura de código ajudam a perceber se o aplicativo se comporta da maneira que você acredita. Além disso, ele também pode destacar quais tipos de código não foram testados—ser informado que 80% do código é testado não informa se as partes críticas estão cobertas. Gerar relatórios é fácil—basta executar seu aplicativo em produção ou durante o teste com rastreamento de cobertura e ver relatórios coloridos que destacam a frequência com que cada área de código é invocada. Se você dedicar um tempo para vislumbrar esses dados—poderá encontrar algumas pegadinhas
+
+
+
+❌ **Caso contrário:** Se você não sabe quais partes do seu código são deixadas sem teste, não sabe de onde os problemas podem surgir
+
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :thumbsdown: Exemplo Anti-padrão: O que há de errado com este relatório de cobertura?
+
+Com base em um cenário do mundo real, onde rastreamos o uso de nossos aplicativos no controle de qualidade e descobrimos padrões de login interessantes (Dica: a quantidade de falhas de login não é proporcional, algo está claramente errado. Por fim, verificou-se que algum bug do frontend continua atingindo a API de login do back-end)
+
+
+
+
+
+
+
+
+## ⚪ ️ 4.3 Meça a cobertura lógica usando teste de mutação
+
+:white_check_mark: **Faça:** A métrica de cobertura tradicional geralmente mente: Pode mostrar 100% de cobertura do código, mas nenhuma de suas funções, nem mesmo uma, retorna a resposta correta. Por quê? Ele simplesmente mede sobre quais linhas de código o teste visitou, mas não verifica se os testes realmente testaram alguma coisa— afirmou para a resposta certa. Como alguém que viaja a negócios e mostra seus carimbos de passaporte—isso não prova nenhum trabalho, apenas que ele visitou alguns aeroportos e hotéis.
+
+O teste baseado em mutação está aqui para ajudar, medindo a quantidade de código que foi realmente TESTADO e não apenas VISITADO. [Stryker](https://stryker-mutator.io/) é uma biblioteca JavaScript para teste de mutação e a implementação é realmente legal:
+
+(1) intencionalmente altera o código e "planta bugs". Por exemplo, o código newOrder.price===0 torna-se newOrder.price!=0. Esses "bugs" são chamados de mutações
+
+(2) executa os testes, se todos tiverem sucesso, então temos um problema— os testes não serviram ao seu propósito de descobrir bugs, as mutações são chamadas sobreviventes. Se os testes falharem, então ótimo, as mutações foram mortas.
+
+Saber que todas ou a maioria das mutações foram mortas dá uma confiança muito maior do que a cobertura tradicional e o tempo de instalação é semelhante
+
+
+
+❌ **Caso contrário:** Você ficará enganado ao acreditar que 85% de cobertura significa que seu teste detectará bugs em 85% do seu código
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :thumbsdown: Exemplo Anti-padrão: 100% de cobertura, 0% de teste
+
+
+```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({asignee: "John@mailer.com",price: 120});
+});//Triggers 100% code coverage, but it doesn't check anything
+
+```
+
+
+### :clap: Exemplo Fazendo Certo: Stryker reports, uma ferramenta para teste de mutação, detecta e conta a quantidade de código que não foi testado (mutações)
+
+")
+
+
+
+
+
+
+
+## ⚪ ️4.4 Impedindo problemas de código de teste com os linters de teste
+
+:white_check_mark: **Faça:** Um conjunto de plugins ESLint foi construído especificamente para inspecionar os padrões de código de testes e descobrir problemas. Por exemplo, [eslint-plugin-mocha](https://www.npmjs.com/package/eslint-plugin-mocha) avisará quando um teste for escrito em nível global (não é filho de uma declaração describe()) ou quando os testes são [pulados](https://mochajs.org/#inclusive-tests) o que pode levar a uma falsa crença de que todos os testes estão passando. Similarmente, [eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest) pode, por exemplo, avisar quando um teste não tem afirmações (não verificando nada)
+
+
+
+
+❌ **Caso contrário:** Ver 90% de cobertura de código e 100% de testes verdes fará com que seu rosto seja um grande sorriso apenas até você perceber que muitos testes não afirmam nada e que muitos conjuntos de testes foram ignorados. Tomara que você não tenha implantado nada com base nessa observação falsa
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :thumbsdown: Exemplo Anti-padrão: Um caso de teste cheio de erros, felizmente, todos são pegos por 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
+});
+```
+
+
+
+
+
+
+# Seção 5️⃣: IC e Outras Medidas de Qualidade
+
+
+
+## ⚪ ️ 5.1 Enriqueça seus linters e aborte construções que tenham problemas de lint
+
+:white_check_mark: **Faça:** Linters são um almoço grátis, com 5 minutos de configuração, você obtém gratuitamente um piloto automático que protege seu código e captura de problemas significativos enquanto digita. Já se foram os dias em que linting era apenas por beleza (sem ponto e vírgula!). Hoje em dia, Linters podem detectar problemas graves, como erros que não são lançados corretamente e perda de informações. Além do seu conjunto básico de regras (como [ESLint padrão](https://www.npmjs.com/package/eslint-plugin-standard) ou [estilo Airbnb](https://www.npmjs.com/package/eslint-config-airbnb)), considere incluir alguns Linters especializados como [eslint-plugin-chai-expect](https://www.npmjs.com/package/eslint-plugin-chai-expect) que pode descobrir testes sem asserções, [eslint-plugin-promise](https://www.npmjs.com/package/eslint-plugin-promise?activeTab=readme) pode descobrir promessas sem resolução (seu código nunca vai continuar), [eslint-plugin-security](https://www.npmjs.com/package/eslint-plugin-security?activeTab=readme) que pode descobrir expressões regulares inseguras que podem ser usadas para ataques do DOS e[eslint-plugin-you-dont-need-lodash-underscore](https://www.npmjs.com/package/eslint-plugin-you-dont-need-lodash-underscore) é capaz de alarmar quando o código usa métodos da biblioteca de utilitários que fazem parte dos métodos principais do V8, como Lodash._map(…)
+
+
+
+❌ **Caso Contrário:** Considere um dia chuvoso em que sua produção continua travando, mas os logs não exibem o rastreamento do stack de erros. O que aconteceu? Seu código lançou um objeto sem erro por engano e o rastreamento do stack foi perdido, uma boa razão para bater a cabeça contra uma parede de tijolos. Uma configuração de linter de 5 minutos pode detectar esse erro de DIGITAÇÃO e salvar seu dia
+
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :thumbsdown: Exemplo Anti-padrão: O objeto sem a propriedade erro é lançado por engano, nenhum rastreamento do stack será exibido para esse erro. Felizmente, o ESLint capta o próximo bug de produção
+
+
+
+
+
+
+
+
+
+# ⚪ ️ 5.2 Encurte o ciclo de feedback com o IC de desenvolvedor local
+
+:white_check_mark: **Faça:** Usando uma IC com inspeções de qualidade brilhantes, como testes, linting, verificação de vulnerabilidades, etc? Ajude os desenvolvedores a executar esse pipeline também localmente para solicitar feedback instantâneo e diminuir o [ciclo de feedback](https://www.gocd.org/2016/03/15/are-you-ready-for-continuous-delivery-part-2-feedback-loops/). Por quê? um processo de teste eficiente constitui muitos loops iterativos: (1) tentativas -> (2) feedback -> (3) refatoração. Quanto mais rápido o feedback, mais iterações de aprimoramento um desenvolvedor pode executar por módulo e aperfeiçoar os resultados. Por outro lado, quando o feedback chegar atrasado, menos iterações de melhoria poderão ser agrupadas em um único dia, a equipe já pode ter avançado para outro tópico/tarefa/módulo e pode não estar apta a refinar esse módulo.
+
+Na prática alguns fornecedores de IC (exemplo: [CircleCI local CLI](https://circleci.com/docs/2.0/local-cli/)) permitir a execução do pipeline localmente. Algumas ferramentas comerciais como [wallaby fornece informações valiosas e intuições de teste](https://wallabyjs.com/) como um protótipo de desenvolvedor (sem afiliação). Como alternativa, você pode apenas adicionar um npm script no package.json que executa todos os comandos de qualidade (por exemplo teste, lint, vulnerabilidades) — use ferramentas como [concurrently](https://www.npmjs.com/package/concurrently) para paralelismo e código de saída diferente de zero, se uma das ferramentas falhar. Agora o desenvolvedor deve apenas chamar um comando— por exemplo ‘npm run quality’ — para obter feedback instantâneo. Considere também abortar um commit se a verificação de qualidade falhar usando um githook ([husky pode ajudar](https://github.com/typicode/husky))
+
+
+
+❌ **Caso Contrário:** Quando os resultados da qualidade chegam no dia seguinte ao código, o teste não se torna uma parte fluente do desenvolvimento, e sim um artefato formal após o fato
+
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :clap: Exemplo Fazendo Certo: npm scripts que realizam inspeção de qualidade de código, todos são executados em paralelo sob demanda ou quando um desenvolvedor está tentando enviar um novo código
+```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 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.
+
+O enorme ecossistema Kubernetes ainda não formalizou uma ferramenta conveniente padrão para espelhamento local e de IC, embora muitas novas ferramentas sejam lançadas com frequência. Uma abordagem é executar ‘minimized-Kubernetes’ usando ferramentas como [Minikube](https://kubernetes.io/docs/setup/minikube/) e [MicroK8s](https://microk8s.io/) que se assemelham-se à coisa real só vêm com menos sobrecarga. Outra abordagem é testar em um ambiente remoto ‘real-Kubernetes’, alguns provedores de IC (por exemplo, [Codefresh](https://codefresh.io/)) possuem integração nativa com ambiente Kubernetes e facilitam a execução do pipeline do IC sobre o ambiente real, outros permitem scripts personalizados em um Kubernetes remoto.
+
+
+
+❌ **Caso Contrário:** O uso de tecnologias diferentes para demandas de produção e teste mantém dois modelos de implantação e mantém os desenvolvedores e a equipe de operações separados
+
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :clap: Exemplo: um pipeline de IC que gera clusters Kubernetes em tempo real ([Créditos: 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 Paralelizar a execução do teste
+:white_check_mark: **Faça:** Quando bem feito, o teste é seu amigo 24/7, fornecendo feedback quase instantâneo. Na prática, a execução de 500 testes de unidade limitados à CPU em um único thread pode levar muito tempo. Felizmente, executores de teste modernos e plataformas de IC (como [Jest](https://github.com/facebook/jest), [AVA](https://github.com/avajs/ava) e [extenções Mocha](https://github.com/yandex/mocha-parallel-tests)) podem paralelizar os testes em vários processos e obter uma melhoria significativa no tempo de feedback. Alguns fornecedores de IC também paralelam testes entre contêineres (!) o que reduz ainda mais o ciclo de feedback. Seja localmente em vários processos ou em alguma ILC na nuvem usando várias máquinas— paralelizando a demanda, mantendo os testes autônomos, pois cada um pode ser executado em diferentes processos
+
+
+❌ **Caso Contrário:** Obter resultados de testes 1 hora após o envio do novo código, enquanto você codifica os próximos recursos, é uma ótima receita para tornar os testes menos relevantes
+
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :clap: Exemplo Fazendo Certo: Mocha parallel & Jest superam facilmente o Mocha tradicional graças ao teste de paralelização ([Créditos: JavaScript Test-Runners Benchmark](https://medium.com/dailyjs/javascript-test-runners-benchmark-3a78d4117b4))
+")
+
+
+
+
+
+
+
+
+## ⚪ ️5.5 Fique longe de questões legais usando licença e verificação de plágio
+:white_check_mark: **Faça:** Problemas de licenciamento e plágio provavelmente não são sua principal preocupação no momento, mas por que não resolver isso também em 10 minutos? Um monte de pacotes npm como [license check](https://www.npmjs.com/package/license-checker) e [plagiarism check](https://www.npmjs.com/package/plagiarism-checker) (comercial com plano gratuito) podem ser facilmente incorporado ao seu pipeline de IC e inspecionar problemas, como dependências com licenças restritivas ou código que foi copiado e colado do Stackoverflow e aparentemente viola alguns direitos autorais
+
+❌ **Caso Contrário:** Involuntariamente, os desenvolvedores podem usar pacotes com licenças inadequadas ou copiar e colar código comercial e enfrentar problemas legais
+
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :clap: Exemplo Fazendo Certo:
+```javascript
+//instale license-checker no seu ambiente IC ou também localmente
+npm install -g license-checker
+
+//solicite que verifique todas as licenças e falhe com o código de saída diferente de 0 se encontrar uma licença não autorizada. O sistema de IC deve detectar essa falha e interromper a construção
+license-checker --summary --failOn BSD
+
+```
+
+
+
+
+
+
+
+
+
+
+
+
+## ⚪ ️5.6 Inspecionar constantemente as dependências vulneráveis
+:white_check_mark: **Faça:** Mesmo as dependências mais respeitáveis, como o Express, têm vulnerabilidades conhecidas. Isso pode ser facilmente domado usando ferramentas da comunidade, como [npm audit](https://docs.npmjs.com/getting-started/running-a-security-audit), ou ferramentas comerciais como [snyk](https://snyk.io/) (oferece também uma versão comunitária gratuita). Ambos podem ser chamados a partir do seu IC em cada build
+
+❌ **Caso Contrário:** Para manter seu código livre de vulnerabilidades sem ferramentas dedicadas, é necessário seguir constantemente as publicações on-line sobre novas ameaças. Bastante tedioso
+
+
+
+
+✏ Códigos de Exemplos
+
+
+
+### :clap: Exemplo: Resultado de NPM Audit
+
+
+
+
+
+
+
+
+
+## ⚪ ️5.7 Automatize atualizações de dependência
+:white_check_mark: **Faça:** Yarn e npm recentemente incluiram package-lock.json que introduziu um sério desafio (o caminho para o inferno é pavimentado com boas intenções) — por padrão agora, os pacotes não estão mais recebendo atualizações. Mesmo uma equipe executando muitas implantações novas com ‘npm install’ & ‘npm update’ não receberão novas atualizações. Isso leva a versões abaixo dos pacotes de dependência, na melhor das hipóteses, ou ao código vulnerável, na pior das hipóteses. As equipes agora contam com a boa vontade e a memória dos desenvolvedores para atualizar manualmente o package.json ou usar ferramentas [como ncu](https://www.npmjs.com/package/npm-check-updates) manualmente. Uma maneira mais confiável seria automatizar o processo de obtenção das versões de dependência mais confiáveis, embora não haja uma solução certeira ainda, existem duas vias de automação possíveis:
+
+(1) O IC pode falhar nas construções que possuem dependências obsoletas — usando ferramentas como [‘npm outdated’](https://docs.npmjs.com/cli/outdated) ou ‘npm-check-updates (ncu)’ . Fazer isso forçará os desenvolvedores a atualizar dependências.
+
+(2) Use ferramentas comerciais que varrem o código e enviam automaticamente pull requests com dependências atualizadas. Uma questão interessante que resta é qual deve ser a política de atualização de dependência— a atualização em cada patch gera muitos custos indiretos; a atualização quando uma versão nova é lançado pode apontar para uma versão instável (muitos pacotes são descobertos como vulneráveis nos primeiros dias após o lançamento, [veja o](https://nodesource.com/blog/a-high-level-post-mortem-of-the-eslint-scope-security-incident/) incidente do eslint-scope).
+
+Uma política de atualização eficiente pode permitir um ‘período de acomodação’ — deixe o código ficar atrás do @latest por algum tempo e versões antes de considerar a cópia local como obsoleta (por exemplo. versão local é 1.3.1 e a versão do repositório é 1.3.8)
+
+
+
+❌ **Caso Contrário:** Sua produção executará pacotes que foram explicitamente marcados pelo autor como arriscados
+
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :clap: Exemplo: [ncu](https://www.npmjs.com/package/npm-check-updates) pode ser usado manualmente ou em um pipeline de IC para detectar quanto o código está atrasado em relação às versões mais recentes
+
+
+
+
+
+
+
+
+## ⚪ ️ 5.8 Outras dicas de IC não relacionadas ao Node
+:white_check_mark: **Faça:** Esta postagem é focada em conselhos de teste relacionados ou pelo menos podem ser exemplificados com o Node JS. Este marcador, no entanto, agrupa algumas dicas não relacionadas ao nó que são bem conhecidas
+
+ - Use uma sintaxe declarativa. Essa é a única opção para a maioria dos fornecedores, mas as versões mais antigas do Jenkins permitem o uso de código ou interface do usuário.
- Opte por um fornecedor que tenha suporte nativo ao Docker
- Falhe cedo, execute seus testes mais rápidos primeiro. Crie uma etapa/meta de "Teste de fumaça" que agrupe várias inspeções rápidas (por exemplo linting, testes unitários) e fornecer feedback instantâneo para o responsável pelo código
- Facilite a varredura de todos os artefatos de construção, incluindo relatórios de teste, relatórios de cobertura, relatórios de mutação, logs, etc.
- Crie vários pipelines/trabalhos para cada evento, reutilize as etapas entre eles. Por exemplo, configure um trabalho para commits de branches de recursos e outro para PR na master. Permita que cada uma reutilize a lógica usando etapas compartilhadas (a maioria dos fornecedores fornece algum mecanismo para reutilização de código)
- Nunca incorpore segredos em uma declaração de trabalho, pegue-os em um armazenamento secreto ou na configuração do trabalho
- Explicitamente aumente a versão em uma compilação de versão ou pelo menos garanta que o desenvolvedor o fez
- Compileapenas uma vez e execute todas as inspeções no artefato de construção único (por exemplo, imagem do Docker)
- Teste em um ambiente efêmero que não varia de estado entre compilações. Armazenar em cache node_modules pode ser a única exceção
+
+
+
+❌ **Caso Contrário:** Você perderá anos de sabedoria
+
+
+
+## ⚪ ️ 5.9 Matriz de construção: execute as mesmas etapas de IC usando várias versões do Node
+:white_check_mark: **Faça:** A verificação da qualidade é sobre acaso, quanto mais você cobrir, mais sorte terá na detecção de problemas mais cedo. Ao desenvolver pacotes reutilizáveis ou executar uma produção de vários clientes com várias configurações e versões do Node, o IC deve executar o pipeline de testes em todas as permutações de configurações. Por exemplo, supondo que usamos o MySQL para alguns clientes e o Postgres para outros — alguns fornecedores de IC suportam um recurso chamado "Matriz" que permitem executar o processo de teste contra todas as permutações do MySQL, Postgres e várias versões do Node, como 8, 9 e 10. Isso é feito usando a configuração apenas sem nenhum esforço adicional (supondo que você tenha testes ou quaisquer outras verificações de qualidade). Outros ICs que não suportam Matrix podem ter extensões ou ajustes para permitir isso
+
+
+
+❌ **Caso Contrário:** Então, depois de fazer todo esse trabalho duro de escrever testes, vamos permitir que os bugs entrem apenas por causa de problemas de configuração?
+
+
+
+
+✏ Códigos de Exemplo
+
+
+
+### :clap: Exemplo: Usando a definição de construção do Travis (fornecedor de IC) para executar o mesmo teste em várias versões do Node
+language: node_js
node_js:
- "7"
- "6"
- "5"
- "4"
install:
- npm install
script:
- npm run test
+
+
+
+
+# Time
+
+
+
+## Yoni Goldberg
+
+
+
+
+
+**Função:** Escritor
+
+**Sobre:** Sou um consultor independente que trabalha com 500 empresas afortunadas e startups de garagem para aprimorar seus aplicativos JS & Node.js. Mais do que qualquer outro tópico, me fascina e tenho como objetivo dominar a arte de testar. Eu também sou o autor de [Melhores práticas do Node.js.](https://github.com/goldbergyoni/nodebestpractices)
+
+
+
+**Oficina:** 👨🏫 Deseja aprender todas essas práticas e técnicas em seus escritórios (Europa & EUA)? [Registre-se aqui para minha oficina de testes](https://testjavascript.com/)
+
+
+**Siga:**
+
+* [🐦 Twitter](https://twitter.com/goldbergyoni/)
+* [📞 Contato](https://testjavascript.com/contact-2/)
+* [✉️ Boletim de Notícias](https://testjavascript.com/newsletter//)
+
+
+
+
+
+## [Bruno Scheufler](https://github.com/BrunoScheufler)
+
+**Função:** Revisor e consultor técnico
+
+Teve o cuidado de revisar, melhorar, usar lint e polir todos os textos
+
+**Sobre:** full-stack web engineer, entusiasta de Node.js e GraphQL
+
+
+
+## [Ido Richter](https://github.com/idori)
+
+**Função:** Conceito, design e ótimos conselhos
+
+**Sobre:** Um desenvolvedor front-end esclarecido, especialista em CSS e emojis
+
+## [Kyle Martin](https://github.com/js-kyle)
+
+**Função:** Ajuda a manter esse projeto em execução e analisa práticas relacionadas à segurança
+
+**Sobre:** Adora trabalhar em projetos Node.js. e segurança de aplicativos da web.
+
+## Contribuidores ✨
+Agradecemos a essas pessoas maravilhosas que contribuíram para este repositório!
+
+
+
+
+
+
+
+
+
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
new file mode 100644
index 00000000..18b3ee5f
--- /dev/null
+++ b/readme-zh-CN.md
@@ -0,0 +1,2057 @@
+
+
+
+
+# 👇 为什么本指南可以助你将测试能力提升到下一层级
+
+
+
+## 📗 45+ 最佳实践:非常全面彻底
+这篇文章从 A 到 Z 给出了 JavaScript & Node.js 的稳定性指南。它为你整理总结了市面上大量的最佳博客文章、书籍以及工具。
+
+
+## 🚢 进阶:在基础上前进 10000 公里
+从基础领域跨上前往进阶话题的旅程,包括:在生产环境测试、编译测试、基于属性的测试以及很多策略 & 专业工具。如果你仔细阅读了本指南中的每个字,则你的测试能力将有可能大大超过平均水平。
+
+
+## 🌐 全栈:前端、后端、CI、任何岗位
+先了解通用的测试实践为其他应用层的打下基础。然后,在你自己的领域深入探索:前端/UI、后端、CI 甚至是他们所有的层面。
+
+
+
+### 作者 Yoni Goldberg
+- 一位 JavaScript & Node.js 顾问
+- 👨🏫 [我的测试网站](https://www.testjavascript.com/) - 在欧洲 & 美国了解 [我的测试网站](https://www.testjavascript.com/)
+- [在 Twitter 关注我](https://twitter.com/goldbergyoni/)
+- 来 [LA](https://js.la/), [Verona](https://2019.nodejsday.it/), [Kharkiv](https://kharkivjs.org/), [free webinar](https://zoom.us/webinar/register/1015657064375/WN_Lzvnuv4oQJOYey2jXNqX6A)听我的演讲。后续工作待定。
+- [我的 JavaScript 质量新闻](https://testjavascript.com/newsletter/) - 战略层面的视野和信息
+
+
+
+
+## `内容列表`
+
+#### [`第 0 章:黄金法则`](#第-0-章黄金法则-1)
+
+一条启发所有人的建议(特殊的 1 条)
+
+#### [`第一章:测试剖析`](#第一章-测试剖析)
+
+基础 - 搭建干净的测试(12 条)
+
+#### [`第二章:后端测试`](#第二章-后端测试)
+
+高效地编写后端和微服务的测试(8 条)
+
+#### [`第三章:前端测试`](#第三章-前端测试)
+
+为 UI(包括组件和 E2E 测试)编写测试(11 条)
+
+#### [`第四章:度量测试效果`](#第四章-度量测试效果)
+
+度量测试质量(4 条)
+
+#### [`第五章:持续集成(CI)`](#第五章持续集成ci-1)
+
+JS 领域的 CI 指南(9 条)
+
+
+
+
+# 第 0 章:黄金法则
+
+
+
+## ⚪️ 0 黄金法则:设计瘦测试
+
+:white_check_mark: **建议:** 测试代码与生产代码不同,要使它变得极其简单、短小、没有抽象、扁平化、使人愉悦、瘦。一段测试代码需要做到让人一眼就能看出其目的。
+
+我们的思维空间被主体生产代码充满,因此无法腾出额外的“大脑空间”存放复杂的东西。如果向可怜的大脑中塞进其他复杂代码,将会使得整个部分变慢,而这个部分正是用来解决我们需要测试的问题的。这也是大部分团队放弃测试的原因。
+
+另一方面,测试是一个友好的助手,一个你乐于与之合作、投资小回汇报大的助手。科学证明我们有两套大脑系统:系统 1 用于无需努力的活动如在一个空旷的路上开车;系统 2 用于复杂和繁琐的工作如算一道数学表达式。将你的测试为系统 1 设计,当你看一段测试代码时,需要像改 HTML 文档一样简单而不是像计算 2 × (17 × 24)。
+
+为了达到这个目的,我们可以通过选择性价比高、投入产出比(ROI)高的技术、工具以及测试对象。仅测试需要的内容,努力保持其灵活性,某些时候甚至值得去舍弃一些测试来换取灵活性和简洁性。
+
+
+
+下面的大部分建议衍生自这一原则。
+
+### 准备好开始了吗?
+
+
+
+
+# 第一章: 测试剖析
+
+
+
+## ⚪ ️ 1.1 每个测试用例的名称必须包含三个部分
+
+:white_check_mark: **建议:** 一个测试报告需要让不熟悉代码的人(测试、运维)明确知道新的变更是符合需求。因此测试名称需要从**需求层面**描述,并且包含三个部分:
+
+(1) 被测的是什么?(比如 ProductsService.addNewProduct 方法)
+
+(2) 在什么条件和场景下?(比如没有 向该方法传入 price 参数)
+
+(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: 正例: 一个包含三部分的用例名
+
+
+
+
+
+
+## ⚪ ️ 1.2 使用 AAA 模式构造测试内容
+
+:white_check_mark: **建议:** 将你的测试内容划分为三个部分:布置,执行,断言 —— Arrange, Act & Assert (AAA)。这样读者就无需动用脑细胞理解你的测试内容了:
+
+1st A - 准备(Arrange):一些用于提供上下文的代码。可能包含:构造数据、添加 DB 记录、mocking/stubbing 对象,以及其他的准备代码;
+
+2nd A - 执行(Act):执行测试单元。通常一行代码。
+
+3rd 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});
+
+ const 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 坚持黑盒测试:只测 public 方法
+
+: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
+ calculateVAT(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;
+ }
+}
+
+
+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 关注于测试需求本身,而作为副作用不得不接触内部
+
+```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)
+});
+```
+
+
+
+
+
+
+
+## ⚪ ️1.6 不要“foo”,使用真实数据
+
+:white_check_mark: **建议:** 生产环境中的 bug 通常是在一些特殊或者意外的输入下出现的——所以测试的输入数据越真实,越容易在早期抓住问题。使用现有的一些库(比如 [Faker](https://www.npmjs.com/package/faker))去造“假”真数据来模拟生产环境数据的多样性和形式。比如,这些库可以生成真实的电话号码、用户名、信用卡、公司名等等。你还可以创建一些测试(在单元测试之上,而不是替代)生产随机 fakers 数据来扩展你的测试单元,甚至从生产环境中导入真实的数据。想要进阶的话,请看下一条:基于属性的测试。
+
+
+
+❌ **否则:** 你所有的用例都在 “foo” 之类的输入值下表现正确,结果上线后收到诸如 “[@3e2ddsf ]() . ##’ 1 fdsfds . fds432 AAAA” 之类的输入后挂掉了。
+
+
+
+
+✏ 代码示例
+
+
+
+### :thumbsdown: 反例: 一个用例因使用非真实数据而通过
+
+
+
+
+```javascript
+const addProduct = (name, price) =>{
+ const productNameRegexNoSpace = /^\S*$/;//no white-space allowd
+
+ 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 基于属性的测试:测试输入的多种组合
+
+:white_check_mark: **建议:** 通常我们只会选择部分的数据样例去测试,即使是使用了上一节讲到的工具去模拟真实数据,我们也只覆盖到了一部分输入的组合(`method(‘’, true, 1), method(“string” , false” , 0)`)。然而在生产环境中,一个拥有 5 个参数的 API,可能会遇到上千种排列组合,而其中的某一种可能会把你的进程搞挂([见 Fuzz Testing](https://en.wikipedia.org/wiki/Fuzzing))。如何自动生成这上千种组合并在它们出问题后 catch 到?基于属性的测试适用于这种需求:向你的测试单元传入所有可能的输入组合,以增加发现 bug 的可能。例如,给定一个方法 —— `addNewProduct(id, name, isDiscount)`,支持属性测试的库将使用一批`(number, string, boolean)`组合调用此方法,比如`(1,“iPhone”,false)`,`(2,“Galaxy”,true)`。您可以使用您最喜欢的测试运行器(Mocha、Jest等),
通常,我们为每个测试选择一些输入样本。即使输入格式类似于现实世界的数据(见子弹“别foo”),我们只涉及几个输入组合(方法(“,真的,1),方法(“字符串”,假”,0)),然而,在生产中,一个API调用与成千上万的5个参数可以调用不同的排列,其中一个可能使我们的流程(见模糊测试)。如果您可以编写一个测试,自动发送1000个不同输入的排列组合,并捕获我们的代码未能返回正确响应的输入,那该怎么办?基于属性的测试就是这样一种技术:通过发送所有可能的输入组合到你的测试单元中,它增加了发现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) 它提供了更多的能力,似乎更易维护。
+
+
+
+❌ **否则:** 你无意中选择的输入数据只覆盖了没问题的代码路径。不幸的是,它没有真正发现了 bug。
+
+
+
+
+✏ 代码示例
+
+
+
+### :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 snapshot:如果需要,仅使用短的行内快照
+
+:white_check_mark: **建议:** 如果你需要 [快照测试](https://jestjs.io/docs/en/snapshot-testing),仅使用端快照(比如 3-7 行),并且把它们作为测试的一部分([内联快照](https://jestjs.io/docs/en/snapshot-testing#inline-snapshots))而不是存放到外部文件中。遵循这条指导原则将确保您的测试保持自解释并且不那么脆弱。
+
+另一方面,“经典”快照教程和工具鼓励我们在一些外部介质上存储大文件(如组件的渲染结果,API 的 JSON 结果),并确保每次运行测试时将新结果与保存的版本进行比较。打个比方,这么做有可能隐式地将我们的测试与包含 3000 个数据值的 1000 行内容关联起来,而这些数据值是测试编写者从来没有读过和考虑过的。这么做将会使得你的用例有 1000 个失败的理由 —— 常常改一行代码就会导致快照失效。这个频率有多高?对于每个空格,注释或少量的 CSS/HTML 更改。不仅如此,失败结果不会给出关于失败的任何提示,因为它只是检查 1000 行内容有没有改动,而且测试编写人员不得不将这一大堆他无法自己验证的长文档作为期望的 true。所有这些都是测试目标不明确、测试目标过多的症状。
+
+这将会使得我们的测试带上一大堆我们以后可能不会再看的数据。这样做有什么问题?你的测试将有无数种理由失败,因为你放入了太多自己不需要关心的结果数据进去,而你又无法抽出足够的精力从结果的 diff 中判断当前表现是否符合期望。
+
+仅在很少的场景下,长外部快照是可以接受的——当测试断言 schema 而不是数据时(提取值并关注其中的字段),或者当快照的内容很少被更改时。
+
+
+
+❌ **否则:** 一个 UI 测试挂掉了。代码看起来 ok,屏幕上正确渲染了每个像素,发生了什么?- 你的测试发现跟之前的快照相比,新的快照 markdown 中多了一个空格……
+
+
+
+✏ 代码示例
+
+
+
+### :thumbsdown: 反例: 为我们的用例耦合看不到的 2000 行代码
+
+
+
+```javascript
+it('TestJavaScript.com is renderd correctly', () => {
+
+//Arrange
+
+//Act
+const receivedPage = renderer
+.create( Test JavaScript < /DisplayPage>)
+.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
+receivedPage tree = renderer
+.create( Test JavaScript < /DisplayPage>)
+.toJSON();
+
+//Assert
+
+const menu = receivedPage.content.menu;
+expect(menu).toMatchInlineSnapshot(`
+
+- Home
+- About
+- Contact
+
+`);
+});
+```
+
+
+
+
+
+
+## ⚪ ️1.9 不要写全局的 fixtures 和 seeds,而是放在每个测试中
+
+:white_check_mark: **建议:** 参照黄金法则,每条测试需要在它自己的 DB 行中运行避免互相污染。现实中,这条规则经常被打破:为了性能提升而在执行测试前全局初始化数据库([也被称为‘test fixture’](https://en.wikipedia.org/wiki/Test_fixture))。尽管性能很重要,但是它可以通过后面讲的「分组件测试」缓和。为了减轻复杂度,我们可以在每个测试中只初始化自己需要的数据。除非性能问题真的非常显著,那么可以做一定的妥协——仅在全局放不会改变的数据(比如 query)。
+
+
+
+❌ **否则:** 一部分测试挂了,我们的团队花费大量宝贵时间后发现,是由于两个测试同时改变了同一个 seed 数据导致的。
+
+
+
+
+✏ 代码示例
+
+
+
+### :thumbsdown: 反例: 用例之间不独立,而是依赖同一个全局钩子来生成全局 DB 数据
+
+
+
+```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 :[
+});
+
+```
+
+
+### :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 测试看起来似乎没问题。但结果会比较奇葩(会隐藏测试的意图和期望结果),并且把 tc 复杂化(比如下面的例子)。
+
+一个更优雅的替代方法是使用 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({name:'nest'});}
+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: 正例: 一个人类可读的期望,它很容易被理解,甚至可被 QA 或技术 PM 理解
+
+```javascript
+it.only("When no product name, it throws error 400", async() => {
+ expect(addNewProduct)).to.eventually.throw(AppError).with.property('code', "InvalidInput");
+});
+
+```
+
+
+
+
+
+
+
+
+## ⚪ ️ 1.11 为你的测试用例打标签
+
+:white_check_mark: **建议:** 不同的测试需要在不同的场景中执行:快速冒烟、IO 测试、开发者保存或者提交文件后的测试、当一个新的 PR 提交后需要全量执行的端到端测试 等等。你可以用一些 #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: **建议:** 本文主要讨论与 Node JS 相关的测试建议,或者至少可以用 Node JS 作为例子。然而,本小节整理了一些众所周知的与 Node 无关的技巧。
+
+学习并实践 [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 时,在修复前新增一个测试在未来检测到它,让每一个测试在变绿之前至少失败一次,快速编写一个简单的代码模块以满足这个测试,然后逐渐将其重构至生产水平,避免任何依赖环境(路径、操作系统等)。
+
+❌ **否则:** 你会错过数十年来智慧的结晶。
+
+
+
+
+# 第二章: 后端测试
+
+## ⚪ ️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 这样的消息总线中,然后这些事件流入一些数据仓库并被分析 UI 查询。我们真的需要花费 50% 的测试预算去为这个几乎没有逻辑的集成中心化的应用写单测吗?随着应用类型(机器人、密码、Alexa-skills)的多样性增长,测试金字塔可能将不再是某些场景的最佳选择了。
+
+是时候丰富你的测试组合并了解更多的测试类型了(下一节会给你一些小建议),这些类似于测试金字塔的思维模型与你所面临的现实问题更匹配('嘿,我们的API 挂了,试试消费者驱动的合同测试!'),让您的测试多样化,比如建立基于风险分析的检查模型 —— 评估可能出现问题的位置,并提供一些预防措施以减轻这些潜在风险。
+
+需要注意的是:软件世界中的 TDD 模型面临两个极端的态度,一些人鼓吹到处使用它,另一些人则认为它是魔鬼。 每个说绝对的人都是错的 :]
+
+
+
+
+❌ **否则:** 你将错过一些超高投入产出比的工具,比如 Fuzz、lint、mutation 这些工具只需 10 分钟配置就能贡献价值。
+
+
+
+
+✏ 代码示例
+
+
+
+### :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)
+
+
+
+
+
+
+
+
+
+
+
+
+
+## ⚪ ️2.2 组件化测试可能是最有效的利器
+
+:white_check_mark: **建议:** 应用的每个单元测试仅能覆盖应用的一小部分,覆盖全部会非常麻烦,而端到端测试可以很轻松地覆盖大量区域,但是比较脆弱而且很慢。何不找一个平衡点:写一些比单测大,但是比端到端测试小的测试。组件测试是测试世界的一颗遗珠——它找到了两个模式的最佳平衡点:不错的性能和使用 TDD 模式的可能性 + 真实且强大的覆盖率。
+
+组件测试关注于微服务“单元”,他们反对 API,不 mock 任何属于微服务本身的东西(比如:真实的 DB,甚至是该 DB 的内存版本)但是 stub 所有外部的东西比如调用其他微服务。这么做,我们测试我们部署的部分,由外而内地覆盖应用,节省大量时间。
+
+
+
+
+❌ **否则:** 你可能花了好几天写单测,却发现仅得到了 20% 的系统覆盖率。
+
+
+
+
+✏ 代码示例
+
+
+
+### :clap: 正例: 使用 Supertest 测试 Express API (快速、覆盖很多层)
+
+
+ allows approaching Express API in-process (fast and cover many layers)")
+
+
+
+
+
+## ⚪ ️2.3 保证新的 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 将会在 构建/CI 阶段被 catch 到,从而减少你大量的烦恼。
+
+
+
+
+❌ **否则:** 所有的变更将带来繁琐的手动测试,导致开发者惧怕发布。
+
+
+
+
+✏ 代码示例
+
+
+
+### :clap: 正例:
+
+
+
+
+
+
+
+
+
+
+
+
+
+## ⚪ ️ 2.4 单独测试你的中间件
+
+:white_check_mark: **建议:** 许多人拒绝测试中间件,是因为它们仅占据系统的一小部分而且依赖真实的 Express server。这两个原因都不正确——中间件虽然小,但是影响全部或者大部分请求,而且可以被简单地作为纯函数测试(参数为 {req,res} JS 对象)。测试中间件函数,你仅需调用它,并且 spy ([比如使用 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 中间件上的一个 bug === 所有或者大部分请求的 bug
+
+
+
+
+✏ 代码示例
+
+
+
+### :clap:正例: 隔离地测试中间件,不发出网络调用或唤醒整个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/) (2,600+ stars) and [Code Climate](https://codeclimate.com/) (1,500+ stars)
+
+贡献:: [Keith Holliday](https://github.com/TheHollidayInn)
+
+
+
+
+❌ **否则:** 由于代码质量差,再新的库和 feature 也无法拯救你的 bug 和性能。
+
+
+
+
+✏ 代码示例
+
+
+
+### :clap: 正例: CodeClimate —— 一个用于发现复杂方法的商业工具
+
+
+
+
+
+
+
+
+
+
+
+
+## ⚪ ️ 2.6 你是否准备好迎接 Node 相关的噪声
+
+:white_check_mark: **建议:** 怪异的是,大部分软件测试仅关注逻辑和数据,但是最糟糕(而且很难减轻)的往往是基础设施问题。例如,你测试过当你的进程存储过载、服务器/进程挂掉时的表现吗?或者你的监控系统会检测到 API 减慢 50% 了吗?为了测试及减轻类似问题,Netflix 设立了 [噪声工程](https://principlesofchaos.org/)。它的目的是为我们的系统在故障问题下的健壮性提供意识、框架及工具。比如,著名的工具之一 [噪声猴子](https://github.com/Netflix/chaosmonkey),随机地杀掉服务以保证我们的服务仍服务于用户,而不是仅依赖一个单独的服务器(Kubernetes 也有一个版本 [kube-monkey](https://github.com/asobti/kube-monkey) 用于杀掉 pods)。这些工具都是作用于服务器/平台层面,但如果你想测试及生产纯粹的 Node 噪声比如检查你的 Node 进程如何处理未知错误、未知的 promise rejection、v8 内存超过 1.7GB 的限制以及当事件循环经常卡住后你的 UX 是否仍正常运行?为了解决上面提到的这些问题, [node-chaos](https://github.com/i0natan/node-chaos-monkey)(alpha)提供了各种 Node 相关的噪声。
+
+
+
+❌ **否则:** 墨菲定律一定会无情地砸中你的产品,跑不掉的。
+
+
+
+
+✏ 代码示例
+
+
+
+### :clap: 正例: Node-chaos 可以生成所有类型的 Node.js 问题,因此您可以测试您的应用程序对混乱的适应能力
+
+
+
+
+
+
+## ⚪ ️2.7 不要写全局的 fixtures 和 seeds,而是放在每个测试中
+
+:white_check_mark: **建议:** 参照黄金法则,每条测试需要在它自己的 DB 行中运行避免互相污染。现实中,这条规则经常被打破:为了性能提升而在执行测试前全局初始化数据库([也被称为‘test fixture’](https://en.wikipedia.org/wiki/Test_fixture))。尽管性能很重要,但是它可以通过后面讲的「分组件测试」缓和。为了减轻复杂度,我们可以在每个测试中只初始化自己需要的数据。除非性能问题真的非常显著,那么可以做一定的妥协——仅在全局放不会改变的数据(比如 query)。
+
+
+
+❌ **否则:** 一部分测试挂了,我们的团队花费大量宝贵时间后发现,是由于两个测试同时改变了同一个 seed 数据导致的。
+
+
+
+
+✏ 代码示例
+
+
+
+### :thumbsdown: 反例: 用例之间不独立,而是依赖同一个全局钩子来生成全局 DB 数据
+
+
+
+```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 :[
+});
+
+```
+
+
+### :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);
+});
+
+```
+
+
+
+
+
+# 第三章: 前端测试
+
+## ⚪ ️ 3.1 将 UI 与功能分离
+
+:white_check_mark: **建议:** 当专注于测试组件逻辑时,UI 细节就变成了应该剔除的噪音,这样您的测试就可以集中在纯数据上。实际上,通过抽象从代码中提取所需的数据将降低与图形实现的耦合,仅对纯数据 (vs 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 CSS 类从 'thick-border' 更改为 'thin-border'。
+
+
+
+✏ 代码示例
+
+
+
+### :clap: 正例: 使用专用的 attrbiute 查询元素进行测试
+
+
+
+```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 attribute
+```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、部分和 shallow render——这么做可能会由于缺乏细节导致未捕获的 bug,并且由于测试与内部的混在一起将增加维护成本(参见小结“多用黑盒测试”)。如果其中一个子组件明显拖慢测试(如动画)或使很难配置,可以考虑主动用伪组件替换它。
+
+综上所述,需要注意的是: 这种技术适用于封装一定数量子组件的中小型组件。如果一个组件包含太多的子组件,那么将很难对失败测试进行定位(分析根本原因),并且可能会变得过于缓慢。在这种情况下,只需针对胖父组件编写少量测试,而针对其子组件编写更多测试。
+
+
+
+❌ **否则:** 之前通过调用组件的私有方法来测试组件的内部状态。后续重构组件时你必须重构所有测试。你真的有能力进行这种程度的维护吗?
+
+
+
+
+✏ 代码示例
+
+
+
+### :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 render 测试伪组件
+```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,使用框架内置的对 async 事件的支持。并且尝试提效。
+
+:white_check_mark: **建议:** 在许多情况下,被测试单元的完成时间是未知的 (例如,animation 挂起了元素表现 )——在这种情况下,不要 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 仅在异步完成后 resolve (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/)。最重要的是,你还可以关注技术原因,比如确保内容被压缩、第一个字节的时间、优化图像、确保合理的 DOM 大小、SSL 和许多其他方面。建议在开发期间使用这些丰富的监视器,作为 CI 的一部分,最重要的是在生产服务器/CDN上 24x7 使用它们。
+
+
+
+❌ **否则:** 在精心设计了一个UI、通过了100%的功能测试并进行了复杂的打包之后,用户体验却因为 CDN 的错误配置变得糟糕而缓慢。
+
+
+
+✏ 代码示例
+
+### :clap: 正例: Lighthouse 页面加载检查报告
+
+
+
+
+
+
+
+
+
+## ⚪ ️ 3.6 stub 古怪或缓慢的资源如后端 API
+
+:white_check_mark: **建议:** 当编写你的主流测试 (不是 E2E 测试) 时,避免接触任何超出你职责和控制范围的资源,如后端 API,而是使用 stub(即测试替身)。使用一些测试替身库 (如[Sinon](https://sinonjs.org/)、[test double](https://www.npmjs.com/package/testdouble) 等) 来 stub API 响应,而不是真正的对API的网络调用。最大的好处是防止出现故障——测试或预发环境下 api 的定义不是很稳定,尽管组件的表现正确(生产环境不适合测试,它通常会限制请求),但有时会请求失败。通过 stub 允许模拟各种 API 行为,比如当没有找到数据或 API 抛出错误时测试组件行为。最后但并非最不重要的原因是,网络调用将大大降低测试速度。
+
+
+
+❌ **否则:** 测试的平均时长不在是几毫秒,一个经典的 API 调用花费 100ms+,这使得每个用例变慢 ~20x。
+
+
+
+
+✏ 代码示例
+
+
+
+### :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 写几个跨越整个系统的端到端测试
+
+:white_check_mark: **建议:** 虽然 E2E (端到端) 通常表示在真实浏览器中进行 UI 测试(见 3.6),但某些情况下,它们表示覆盖整个系统的测试,包括真正的后端。后一种测试非常有价值,因为它们涵盖了前端和后端之间的集成 bug,这些 bug 可能是由于沟通 schema 时产生误会导致的。它们也是一种有效的方法来发现 backend-to-backend 集成问题 (例如微服务 A 将错误的信息发送给微服务 B) 甚至检测部署失败,目前后端没有像 [Cypress](https://www.cypress.io/) 和 [Puppeteer](https://github.com/GoogleChrome/puppeteer) 友好的 UI 框架一样友好且成熟的 E2E 框架。这种测试的缺点是,配置一个包含如此多组件的环境的成本很高,而且大多数组件都很脆弱——假设有 50 个微服务,即使其中一个失败,整个 E2E 也会失败。出于这个原因,我们应该少用这种技术,大概1-10个就够了。也就是说,即使是少量的 E2E 测试也很有可能捕获它们所针对的问题——部署和集成故障。建议在与生产环境相似的预发运行它们。
+
+
+
+❌ **否则:** UI 可能在测试它的功能上投入了大量的精力,但最后才意识到后端返回的有效负载 (UI 必须使用的数据模式) 与预期有很大的不同。
+
+
+
+## ⚪ ️ 3.8 通过复用登录凭证提速 E2E 测试
+
+:white_check_mark: **建议:** 在涉及真实的后端并依赖有效的用户 token 进行 API 调用的 E2E 测试中,我们没有必要将测试按照「创建用户并在每个请求中登录」的级别隔离。相反,在测试执行开始之前只登录一次 (即 before-all hook),将 token 保存在一些本地存储中,并在请求之间复用它。这似乎违反了核心测试原则之一——保持测试的自治,不要耦合资源。虽然这是一个合理的担忧,但在 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: **建议:** 为了监控生产环境以及开发时的完整性检查,运行一个 E2E 测试,该测试访问所有或大部分站点页面并确保没有被中断。这种测试投资回报率极高,因为它非常容易编写和维护,但可以检测任何类型的故障,包括功能、网络和部署问题。其他类型的冒烟和完备性检查并没有那么可靠和详尽——一些 ops 团队只是 ping 主页 (生产),或者开发人员运行一些集成测试无法发现打包和浏览器问题。毫无疑问,烟雾测试不会取代功能测试,而只是作为一个快速的烟雾探测器。
+
+
+
+❌ **否则:** 一切似乎都很完美,所有的测试都通过了,生产环境健康检查也是 OK 的,但是支付组件有一些打包问题,只有 `/Payment` 路径没有渲染。
+
+
+
+
+✏ 代码示例
+
+
+
+### :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.contains('https://mysite.com/Login');
+ cy.contains('Login');
+ cy.contains('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),它具有 JavaScript 风格,参见下面的示例。另一个相似但不同的场景是 [StoryBook](https://storybook.js.org/),它可以将 UI 组件公开为一个图形化的目录,用户可以浏览每个组件的各种状态(如一个栅格组件的 w/o filter,使其渲染多行或者 0 行,等等),查看它的展示形式,以及如何触发状态——这也可以提供给产品人原,但主要是作为实时文档提供给消费这些组件的开发人员。
+
+❌ **否则:** 你在测试上耗费了大量的资源,如果不利用这项投资来获取更大的价值,是很可惜的。
+
+
+
+
+✏ 代码示例
+
+
+
+### :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 bug。目前部分免费工具可以提供一些基础功能——生成和保存屏幕截图以供肉眼检查。虽然这种方法对于小应用来说可能已经足够了,但是它的缺陷与任何其他手动测试一样
:任何变更后都需要耗费人力来处理。另一方面,由于缺乏清晰的定义,自动检测 UI 问题非常具有挑战性——这就是“视觉回归”领域解决这个难题的切入点:对比旧 UI 与最新的更改并检测差异。一些开源/免费的工具可以提供这个能力 (例如: [wraith](https://github.com/BBC-News/wraith)、PhantomCSS) 但可能安装耗时比较久。一些商业工具 (如 [Applitools](https://applitools.com/)、[Percy.io](https://percy.io/)) 则更进一步,它们简化了安装过程,并封装了高级特性,如管理 UI、告警、通过去除“视觉噪音”(如广告、动画) 进行智能捕获,甚至可以分析引发问题的 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.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 中设置覆盖率门槛,并阻止不满足要求的构建(也可以为每一个组件设置门槛,见下面的例子)。另外,我们可以监测构建的覆盖率下降(当新提交的代码的覆盖率较低时)——这将推动开发者提升或者至少保持被测试的代码数。说了这么多,覆盖率仅仅是一个可量化的度量值,它并不能确切地证明你的测试的健壮性,你也可能被它骗到(见下一节内容)。
+
+
+
+
+❌ **否则:** 信心和数字是相辅相成的,如果无法确保你的测试已经覆盖了了大部分的系统,那你将会害怕,害怕会让你慢下来。
+
+
+
+
+✏ 代码示例
+
+
+
+### :clap: 正例: 一个经典的覆盖率报告
+
+
+
+
+### :clap: 正例: 为每个组件设置覆盖率 (使用 Jest)
+
+
+
+")
+
+
+
+
+
+
+
+## ⚪ ️ 4.2 检查覆盖率报告,以发现未覆盖的区域和其他奇怪的地方
+
+:white_check_mark: **建议:** 有些问题隐藏在雷达之下,而使用传统工具很难发现它们。它们通常不是真正的 bug,大多数情况下是应用的怪异表现,而这种表现可能造成严重影响。例如,一些代码区域几乎不会或很少被调用——你以为“PricingCalculator”类只会设置产品价格,结果他几乎不会被调用,即使我们的数据库中有 10000 件商品以及很多交易……代码覆盖率报告可以帮助你发现应用是否按照你的期望执行。初次之外,它高亮了那些类型的代码没有被测试到——80% 的代码被测试并不能说明你的关键部分被覆盖到。生成报告很简单——只需在构造或测试覆盖率时跑你的应用,然后看看花花绿绿的报告来告诉你每一片代码区域被多频繁地调到。如果你花一点时间看看这些数据——你可能会发现一些问题。
+
+
+
+
+❌ **否则:** 如果你不知道你的代码中有哪些部分没有被测试到,则你没法准确定位问题的来源。
+
+
+
+
+✏ 代码示例
+
+
+
+### :thumbsdown: 反例: 这份覆盖率报告有什么问题?基于一个真实的场景,我们跟踪了 QA 中的应用程序使用情况,并发现了一些有趣的登录模式(提示:登录失败的数量是不成比例的,有些地方显然有问题。最终表现为一些前端的 bug 不断触发后端登录API)
+
+
+
+
+
+
+
+
+## ⚪ ️ 4.3 使用「变异测试」度量逻辑覆盖率
+
+:white_check_mark: **建议:** 传统覆盖率通常是骗人的:它可能显示了 100% 的代码覆盖率,但是你的所有的函数都没有返回正确的结果。怎么回事?它只是简单地度量你的测试代码访问过哪些代码行,而不会检查 tc 是否真正地测试了什么——断言了正确的返回。
基于变更的测试适用于这个需求。它度量了真正被**测试过**的代码而不是仅仅被**访问过**的。[Stryker](https://stryker-mutator.io/) 是一个用于变异测试的 JavaScript 库,而它的实现很巧妙:
+
+(1) 它有意地改变代码并「植入 bug」。例如代码 newOrder.price===0 会被改成 newOrder.price!=0,这个 “bug”即成为变异。
+
+(2) 它跑一遍用例,如果所有都成功了则说明有问题——这些用例没有真正实现他们发现 bug 的目的,这些变异即所谓的“存活”了。如果用例失败了,那么很棒,变异被杀掉了。
+
+相对于传统覆盖率,得知所有或者大部分变异被杀掉会给予你更高的信心,而两者花费的时间差不多。
+
+
+
+❌ **否则:** 你会误以为 85% 的覆盖率代表你的测试会发现你代码中的 85% 的 bug.
+
+
+
+✏ 代码示例
+
+
+
+### :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({asignee: "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
+});
+```
+
+
+
+
+
+
+# 第五章:持续集成(CI)以及其他质量度量手段
+
+
+
+## ⚪ ️ 5.1 丰富你的 linter 并丢弃有 lint 问题的构建
+
+:white_check_mark: **建议:** 只需五分钟配置,即可免费获取自动保护代码的工具来捕获代码中的显著问题。Lint 不再只是样式工具,现在的 linter 可以捕获很多严重的问题比如 error 没有被正确抛出以及信息丢失。在基础 rule(如 [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 而日志没有显示错误堆栈信息。到底发生了什么?你的代码错误地抛了一个非 error 的对象,而堆栈 trace 丢失了,真让人头秃……只需要五分钟配置一个 linter 来发现这个书写错误即可节省你大量的时间。
+
+
+
+✏ 代码示例
+
+
+
+### :thumbsdown: 反例: 出错的对象被错误地抛出,没有显示这个错误的堆栈信息。好在 ESLint 捕获到了后面的生产错误
+
+
+
+
+
+
+
+
+
+# ⚪ ️ 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 load CLI](https://circleci.com/docs/2.0/local-cli/)) 支持在本地执行 CI。一些商业工具如 [wallaby](https://wallabyjs.com/) 为开发原型提供了非常有用的测试能力。或者你可以仅仅在 package.json 中添加 npm 脚本来跑一些质量命令——使用工具如 [concurrently](https://www.npmjs.com/package/concurrently) 来并行执行,并在任何工具失败后抛出非 0 exit code。则开发者只需执行一个命令(如 `npm run quality` )来快速获取反馈。可以用 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 在真实的生产环境镜像中执行端到端测试
+
+:white_check_mark: **建议:** 端到端测试是每个 CI 的主要挑战——实时创建一个生产环境镜像并带上所有相关的云服务是很费时费力的。你需要找到最佳的折中:[Docker-compose](https://serverless.com/) 通过一个文本文件将独立的 docker 环境放置到相同的容器中,但是背后的技术(如网络、构建模型)与真实世界有所差别。你可以将其与[‘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 管道变得很容易,其他的则允许针对远程 Kubernetes 进行自定义脚本。
+
+
+
+
+❌ **否则:** 生产和测试环境使用不同的技术,需要维护两个部署模型,并将开发人员和 ops 团队分隔开来。
+
+
+
+
+✏ 代码示例
+
+
+
+### :clap: 正例: 动态生成 Kubernetes 集群的 CI 管道 (贡献: 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: **建议:** 只要操作合理,测试是你 7x24 小时的朋友,为你提供非常及时的反馈。实际上,在单个线程上执行 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 上 - 并行化需要保证测试用例的独立性,因为每个用例可能在不同的进程上运行。
+
+
+❌ **否则:** 在推送新代码 1 小时后获得测试结果,而你已经在写下一个 feature 了。
+
+
+
+
+✏ 代码示例
+
+
+
+### :clap: 正例: Mocha parallel & Jest 轻松地加速了传统的 Mocha,感谢并行测试([贡献:JavaScript测试运行基准](https://medium.com/dailyjs/javascript-test-runners-benchmark-3a78d4117b4))
+")
+
+
+
+
+
+
+
+
+## ⚪ ️5.5 使用许可证和抄袭检查避免法务问题
+:white_check_mark: **建议:** 许可和抄袭问题可能不是您现在主要关注的问题,但为什么不在10分钟内加上这个能力呢? 许多 npm 包,如 [license check](https://www.npmjs.com/package/license-checker) 和 [plagiarism check](https://www.npmjs.com/package/plagiarism-checker)(商业的,但是有免费选项)可以很容易地加入您的 CI 管道,并检查一些坑:依赖限制性许可证或从Stackoverflow复制粘贴的代码,或者很明显地侵犯了某些版权。
+
+❌ **否则:** 无意中,开发人员可能会使用包含不适当许可证的软件包或复制粘贴商业代码并遇到法务问题。
+
+
+
+
+✏ 代码示例
+
+
+
+### :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 调用他俩。
+
+❌ **否则:** 在没有专用工具的帮助下保持代码远离漏洞,将需要不断关注有关新威胁的发布信息。 这相当乏味。
+
+
+
+
+✏ 代码示例
+
+
+
+### :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)使用商业工具,他们可以扫描代码并自动发送更新依赖的 PR。剩下的一个有趣的问题是依赖更新策略—— 每个补丁的更新都会产生太多的开销,而大版本发布时更新可能会指向一个不稳定的版本(许多软件包在发布后的几天内被爆出漏洞,请[参阅](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: 正例: 可以手动或在 CI 管道中使用 [ncu](https://www.npmjs.com/package/npm-checkupdates) 来检测代码在最新版本之后的滞后程度
+
+
+
+
+
+
+
+
+
+## ⚪ ️ 5.8 其他的,与 Node 无关的,CI 小建议
+
+:white_check_mark: **建议:** 本文的重点是多少与 Node JS 有点关系的测试建议。但是,本节整理了一些众所周知的与 Node 无关的技巧:
+
+1. 使用声明性语法。这是大多数工具的唯一选择,但旧版本的 Jenkins 允许使用代码或 UI。
+1. 选择具有本地 Docker 支持的工具。
+1. 尽快失败,先运行最快的测试。设立一个“冒烟测试” 阶段/里程碑,对多个快速检查工具(如 linting,单元测试)进行分组,为代码提交者提供快速反馈。
+1. 设法方便地浏览构建的所有产出,包括测试报告,覆盖率报告,变异报告,日志等。
+1. 为每个事件创建多个管道/作业,提取他们的相同工作。例如,为功能分支的提交配置一个作业,为 master PR配置另一个。(大多数工具提供了一些代码重用的机制)
+1. 永远不要在工作声明中加入机密信息,从机密库或工作的配置中获取它。
+1. 在发布构建中明确目标版本号
+1. 仅构建一次并对整个构建执行所有检查(例如Docker镜像)
+1. 在一个临时的环境中进行测试,这个环境不会在不同构建之间产生状态漂移。缓存 node_modules 可能是惟一的例外。
+
+
+
+
+❌ **否则:** 你会错过多年来智慧的结晶
+
+
+
+## ⚪ ️ 5.9 构建模型(Matrix):使用多个 Node 版本执行同一个 CI 流程
+
+:white_check_mark: **建议:** 质量检查是用于发现意外,你覆盖的部分越多,你就越可能尽早地发现问题。 在开发包或运行具有各种配置和 Node 版本的多客户生产环境时,CI 必须在所有配置的组合上运行测试管道。 例如,假设我们的某些客户使用 MySQL,另一批客户使用 Postgres。一些 CI 工具支持一种称为“Matrix”的功能,该功能可以针对 MySQL、Postgres 和多个 Node 版本(如8、9、10)的所有组合执行测试。 只要配置即可完成而无需任何额外工作。 其他不支持 Matrix 的 CI 可能可以通过扩展或一定调整来实现这个功能。
+
+
+
+❌ **否则:** 在辛辛苦苦写完所有用例编写之后,怎么可以因为配置问题而让漏洞溜进来?
+
+
+
+
+✏ 代码示例
+
+
+
+### :clap: 正例: 使用 Travis (CI 提供商) 构建配置,在多个 Node 版本上运行相同的测试
+
+```yaml
+language: node_js
+node_js:
+ - "7"
+ - "6"
+ - "5"
+ - "4"
+install:
+ - npm install
+script:
+ - npm run test
+```
+
+
+
+
+
+# Team
+
+
+
+## Yoni Goldberg
+
+
+
+
+
+**Role:** 作者
+
+**About:** 我是一名独立顾问,与 500 强企业和创业公司合作,完善他们的 JS 和 Node.js 应用。与其他任何话题相比,我更感兴趣的是掌握测试的艺术。我也是[Node.js 最佳实践](https://github.com/goldbergyoni/nodebestpractices)的作者。
+
+
+
+**Workshop:** 👨🏫 是否想在您自己的办公室中(欧洲和美国)学习所有这些实践和技术? [在此处注册我的测试工作室](https://testjavascript.com/)
+
+
+**关注:**
+
+* [🐦 Twitter](https://twitter.com/goldbergyoni/)
+* [📞 Contact](https://testjavascript.com/contact-2/)
+* [✉️ Newsletter](https://testjavascript.com/newsletter//)
+
+
+
+
+
+
+## [Bruno Scheufler](https://github.com/BrunoScheufler)
+
+**角色:** 技术评审人和顾问
+
+致力于修改、完善、备注及优化所有文字。
+
+**关于我:** 全栈 Web 工程师,Node.js 和 GraphQL 爱好者
+
+
+
+## [Ido Richter](https://github.com/idori)
+
+**Role:** 概念,设计以及提供好的建议
+
+**About:** 优秀的前端开发者,CSS 专家,emoji 怪
+
+## [Kyle Martin](https://github.com/js-kyle)
+
+**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
new file mode 100644
index 00000000..f2f90843
--- /dev/null
+++ b/readme.kr.md
@@ -0,0 +1,1930 @@
+
+
+
+
+# 👇 이 가이드가 당신의 테스트 기술을 한 단계 끌어 올리는 이유
+
+
+
+## 📗 철저하고 매우 포괄적인 45가지 이상의 모범 사례
+JavaScript 및 Node.js에 대한 A부터 Z까지의 믿음직한 가이드입니다. 수십 가지 최고의 블로그 게시물, 서적 및 도구를 요약하고 정리합니다.
+
+## 🚢 기초를 뛰어넘어 고급으로
+운영중인 제품의 테스트, 돌연변이 테스트, 속성 기반 테스트 및 기타 여러 전략적 & 전문 도구와 같은 고급 주제로 넘어가는 여정을 경험하십시오.
+이 가이드의 모든 단어를 읽으면 당신의 테스트 기술이 평균보다 높아질 수 있습니다.
+
+## 🌐 Full-stack: 프론트, 백엔드, CI, 무엇이든
+모든 응용프로그램 계층의 기초가 되는 유비쿼터스 테스트 방법을 이해하는 것으로부터 시작하십시오. 그런 다음 프론트엔드/UI, 백엔드, CI 혹은 이 모든것을 공부하세요.
+
+
+
+### Yoni Goldberg 작성
+
+* JavaScript & Node.js 컨설턴트
+* 👨🏫 [나의 테스팅 워크샵](https://www.testjavascript.com) - 유럽과 미국에서의 [제 워크샵](https://www.testjavascript.com/)에 대해서 알아보십시오.
+* [트위터 팔로우 하기](https://twitter.com/goldbergyoni/)
+* [LA](https://js.la/), [베로나](https://2019.nodejsday.it/), [하르키우](https://kharkivjs.org/), [무료 웨비나](https://zoom.us/webinar/register/1015657064375/WN_Lzvnuv4oQJOYey2jXNqX6A)를 들으러 오십시오. 향후 이벤트는 곧 결정될 것입니다.
+* [저의 JavaScript 뉴스 레터](https://testjavascript.com/newsletter/) - 인사이트와 오직 전략적인 문제에 대한 내용
+
+
+
+## `목차`
+
+#### [`섹션 0: 황금률`](#섹션-0️⃣-황금률)
+
+모든 모든 사람들에게 영감을 주는 하나의 조언(하나의 특수한 항목)
+
+#### [`섹션 1: 테스트 해부`](#섹션-1-테스트-해부-1)
+
+기초 - 깔끔한 테스트 구성하기(12개)
+
+#### [`섹션 2: 백엔드`](#섹션-2️⃣-백엔드-테스트)
+
+백엔드 및 마이크로서비스 테스트 효율적으로 작성하기(8개)
+
+#### [`섹션 3: 프론트엔드`](#섹션-3️⃣-프론트엔드-테스트)
+
+컴포넌트 및 E2E 테스트를 포함한 웹 UI에 대한 테스트 작성하기(11개)
+
+#### [`섹션 4: 테스트 효과 측정`](#섹션-4️⃣-테스트-효과-측정)
+
+감시자를 감시하기 - 테스트 품질 측정(4개)
+
+
+#### [`섹션 5: 지속적인 통합`](#섹션-5️⃣-지속적인-통합)
+
+자바스크립트 세계에서 CI에 대한 지침(9개)
+
+
+
+# 섹션 0️⃣: 황금률
+
+
+
+## ⚪ ️ 0 황금률: 린 테스트를 위한 설계
+
+:white_check_mark: **이렇게 해라:** 테스트 코드는 제품 코드와 다릅니다. 단순하고, 짧고, 추상화가 없고, 무난하고, 작업하기에 편리하고, 린하게 디자인 하십시오. 테스트를 보고 즉시 의미를 알아챌 수 있어야 합니다.
+
+우리 머리속은 제품 코드로 가득하고 부가적인 복잡한 것들을 생각할 여유가 없습니다. 또 다른 어려운 코드를 억지로 생각해내려고 한다면, 팀의 속도를 늦추게 되어 우리가 테스트를 하는 이유가 무색해 질 것입니다. 실제로 많은 팀들이 이런 이유를 테스트를 포기합니다.
+
+테스트는 친절하고 웃는 동료와 함께 일하는 것이 즐거울 수 있는 기회이고, 적은 투자로 큰 가치를 제공하는 것입니다. 과학은 우리에게 두 개의 뇌 시스템이 있다고 말합니다. 빈 도로에서 자동차를 운전하는 등의 간편한 활동에 사용되는 시스템 1, 그리고 수학 방정식을 푸는 것과 같이 복잡하고 의식적인 연산을 위한 시스템 2. 테스트 코드를 볼 때 수학 문제를 푸는 것 같은게 아닌, HTML 문서를 수정하는 것만 큼 쉬워야하는 시스템 1에 맞게 테스트를 설계하십시오.
+
+선택적인 체리픽 기술, 툴 그리고 비용-효율적이고 뛰어난 ROI를 제공하는 테스트 대상 선정으로 이러한 목적을 달성할 수 있습니다. 필요한 만큼의 테스트, 융통성 있게 유지하려는 노력, 때로는 애자일함과 단순성을 위해 일부 테스트와 신뢰성을 포기하는 것도 가치가 있습니다.
+
+
+
+아래 대부분의 조언은 이 원칙의 파생입니다.
+
+### 시작할 준비 되셨나요?
+
+
+
+# 섹션 1: 테스트 해부
+
+
+
+## ⚪ ️ 1.1 각 테스트 이름은 세 부분으로 구성된다.
+
+:white_check_mark: **이렇게 해라:** 테스트는 현재 애플리케이션의 개정판이 요구 사항을 충족하는지 여부를 다음과 같은 사람들에게 알려야합니다: 배포를 할 테스터, DevOps 엔지니어, 2년 후의 미래에 코드가 익숙하지 않은 사람. 테스트가 요구 사항 수준에서 작성되어 있고 세 부분으로 구성되어 있다면, 목적을 이룰 수 있습니다:
+
+(1) 무엇을 테스트하고 있는가? 예) 제품서비스.새제품추가 메서드
+
+(2) 어떤 상황과 시나리오에서? 예) 메서드에 가격이 전달되지 않는다.
+
+(3) 예상되는 결과는 무엇인가? 예) 신제품은 승인되지 않는다.
+
+
+
+❌ **그렇지 않으면:** 배포에 실패하였고 "제품 추가" 라는 테스트에 실패하였다. 이것이 정확히 어떤 오작동 인지를 알려주나요?
+
+
+
+**👇 주의:** 각 글에는 예제 코드가 있으며 때로는 이미지도 있습니다. 클릭하여 확장
+
+✏ 예제 코드
+
+
+
+### :clap: 올바른 예: 세 부분으로 구성된 테스트 이름
+
+
+
+```javascript
+//1. 단위 테스트
+describe('제품 서비스', function() {
+ describe('새 제품 추가', function() {
+ //2. 시나리오 3. 예상
+ it('가격을 지정하지 않으면 제품 상태는 승인 대기중이다.', ()=> {
+ const newProduct = new ProductService().add(...);
+ expect(newProduct.status).to.equal('승인 대기');
+ });
+ });
+});
+```
+
+
+
+### :clap: 올바른 예: 세 부분으로 구성된 테스트 이름
+
+
+
+
+
+
+
+## ⚪ ️ 1.2 AAA 패턴에 의한 테스트 구조
+
+:white_check_mark: **이렇게 해라:** 3개의 잘 잘 구분된 섹션 AAA(Arrange, Act, Assert)으로 테스트를 구성하십시오. 이 구조를 따르면 테스트를 쉽게 읽을 수 있습니다:
+
+첫번째 A - Arrange(준비): 테스트가 목표로 하는 시나리오에 필요한 시스템을 제공하기 위한 모든 설정 코드. 여기에는 테스트 생성자의 단위 인스턴스화, DB 데이터 추가, 객체에 대한 mock/stub 및 기타 준비 코드가 포함될 수 있습니다.
+
+두번째 A - Act(행동): 단위 테스트를 실행. 일반적으로 코드 한줄
+
+세번째 A - Assert(주장, 예상): 받은 예상값이 충족하는지 확인하십시오. 일반적으로 코드 한줄
+
+
+
+❌ **그렇지 않으면:** 테스트는 오늘 일의 아주 단순한 부분에 불과하지만, 메인 코드를 이해하는데 많은 시간을 낭비 할 것입니다.
+
+
+
+✏ 예제 코드
+
+
+
+### :clap: 올바른 예: AAA 패턴으로 구성된 테스트
+
+ 
+
+```javascript
+describe('고객 분류기', () => {
+ test('고객이 500달러 이상을 소비한 경우 프리미엄으로 분류해야 합니다.', () => {
+ //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('프리미엄으로 분류해야 합니다.', () => {
+ 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 스타일의 Assertion을 사용
+:white_check_mark: **이렇게 해라:** 테스트를 선언적 스타일로 작성하면 읽는 사람이 즉시 파악할 수 있습니다. 조건부 논리로 채워진 명령형 코드로 작성하면 테스트를 읽기가 쉽지 않습니다. 그런 의미에서 임의의 사용자 정의 코드를 사용하지 말고, 선언적 BDD 스타일의 expect 또는 should를 사용하여 인간과 같은 언어로 테스트를 작성하십시오. Chai & Jest에 원하는 Assertion이 포함되어 있지 않고 반복성이 높은 경우 [extending Jest matcher (Jest)](https://jestjs.io/docs/en/expect#expectextendmatchers) 혹은 [custom Chai plugin](https://www.chaijs.com/guide/plugins/) 작성을 고려하십시오.
+
+
+
+❌ **그렇지 않으면:** 팀은 테스트를 덜 작성하고 성가신 것들을 .skip() 으로 장식합니다.
+
+
+
+✏ 예제 코드
+
+ 
+
+### :thumbsdown: 올바르지 않은 예: 읽는 사람은 테스트 스토리를 이해하기 위해 짧지않은 명령형 코드를 훑어봐야 합니다.
+
+```javascript
+test("관리자 요청이 들어오면 정렬된 관리자 목록만 결과에 포함된다." , () => {
+ // 여기에 두 명의 관리자 "admin1", "admin2" 및 "user1" 을 추가했다고 가정합니다.
+ const allAdmins = getUsers({adminOnly:true});
+ const admin1Found, adming2Found = false;
+ allAdmins.forEach(aSingleUser => {
+ if(aSingleUser === "user1"){
+ assert.notEqual(aSingleUser, "user1", "관리자가 아닌 사용자를 찾았다.");
+ }
+ if(aSingleUser==="admin1"){
+ admin1Found = true;
+ }
+ if(aSingleUser==="admin2"){
+ admin2Found = true;
+ }
+ });
+ if(!admin1Found || !admin2Found ){
+ throw new Error("모든 관리자가 반환되지 않았다.");
+ }
+});
+```
+
+
+
+### :clap: 올바른 예: 다음과 같은 선언적 테스트는 이해하기 쉽습니다.
+
+```javascript
+it("관리자 요청이 들어오면 정렬된 관리자 목록만 결과에 포함된다." , () => {
+ // 여기에 두 명의 관리자를 추가했다고 가정합니다.
+ const allAdmins = getUsers({adminOnly:true});
+ expect(allAdmins).to.include.ordered.members(["admin1" , "admin2"])
+ .but.not.include.ordered.members(["user1"]);
+});
+```
+
+
+
+
+
+## ⚪ ️ 1.4 블랙박스 테스트에 충실: public method만 테스트
+
+:white_check_mark: **이렇게 해라:** 내부테스트는 거의 아무것도 하지 않는 엄청난 오버헤드를 발생시킵니다. 만약 당신의 코드 혹은 API가 올바른 결과를 반환한다면, 내부적으로 어떻게 동작했는지의 테스트에 3시간을 투자해야 합니까? 깨지기 쉬운 테스트를 유지해야 합니까? public method가 잘 동작할 때마다 private method 또한 암시적으로 테스트가 되고, 특정 문제(예. 잘못된 출력)가 있는 경우에만 테스트가 깨집니다. 이 접근법은 행동 테스트라고도 합니다. 다른 한편으로 당신은 내부 테스트를 해야합니까?(화이트박스 접근) - 컴포넌트를 설계하는 것에서 핵심 세부 사항으로 초점이 이동하거나 작은 코드의 리펙토링으로 인해 테스트가 중단 될 수 있지만, 결과는 훌륭합니다. - 이는 유지보수 부담을 크게 증가시킵니다.
+
+
+
+❌ **그렇지 않으면:** 당신의 테스트는 다음과 같이 동작합니다. [양치기 소년](https://en.wikipedia.org/wiki/The_Boy_Who_Cried_Wolf): 늑대가 나타났다!(예. private 변수가 변경되어 테스트에 실패하였습니다). 당연히 사람들은, 언젠가 진짜 버그가 무시될 때 까지 CI 알람을 무시하기 시작할 것입니다...
+
+
+
+✏ 예제 코드
+
+
+
+### :thumbsdown: 올바르지 않은 예: 테스트 케이스는 이유없이 내부를 테스트합니다.
+
+
+
+```javascript
+class ProductService{
+ // 이 method 는 내부에서만 사용됩니다.
+ // 이 이름을 변경하면 테스트가 실패합니다.
+ calculateVAT(priceWithoutVAT){
+ return {finalPrice: priceWithoutVAT * 1.2};
+ // 결과 형식이나 키 이름을 변경하면 테스트가 실패합니다.
+ }
+ // public method
+ getPrice(productId){
+ const desiredProduct= DB.getProduct(productId);
+ finalPrice = this.calculateVATAdd(desiredProduct.price).finalPrice;
+ }
+}
+
+it("화이트박스 테스트: 내부 method가 VAT 0을 받으면 0을 반환합니다.", async () => {
+ // 사용자가 VAT를 계산할 수 있게 하는 요구사항은 없으며, 최종 가격만 표시합니다.
+ // 그럼에도 불구하고 여기에서 내부 테스트 수행
+ expect(new ProductService().calculateVATAdd(0).finalPrice).to.equal(0);
+});
+```
+
+
+
+
+
+## ⚪ ️ 1.5 올바른 테스트 더블 선택: Stub과 Spy를 위한 Mock을 피하십시오.
+
+:white_check_mark: **이렇게 해라:** 테스트 더블은 어플리케이션 내부에 연결되어 있기때문에 필요악이지만 일부는 엄청난 가치를 제공합니다([테스트 더블에 대한 알림: mocks vs stubs vs spies](https://martinfowler.com/articles/mocksArentStubs.html)).
+
+테스트 더블을 사용하기 전에 간단한 질문: 요구사항 문서에 있거나 있을 수 있는 기능을 테스트하는 데 테스트 더블을 사용합니까? 만약 아니라면 화이트박스 테스트 낌새가 보입니다.
+
+예를 들어, 결제 서비스가 중단되었을 때 앱이 적절하게 작동하는 것을 테스트하려는 경우, 테스트중인 단위가 올바른 값을 반환하도록, 결제 서비스를 stub하고 '응답 없음' 반환을 트리거 할 수 있습니다.
+이것은 특정 시나리오에서 애플리케이션의 동작/응답/결과를 확인합니다. 그리고 spy를 사용하여 해당 서비스가 중단되었을 때 메일이 보내지는지를 assert 할 수 있습니다. 이것은 다시 요구사항 문서에 있을 수 있는 행동에 대한 점검입니다(결제가 저장되지 않으면 메일은 보낸다). 반대로, 결제 서비스를 mock 하고 올바른 JavaScript 타입으로 호출 되었는지를 확인한다면 - 당신의 테스트는 애플리케이션 기능에 전혀 영향을 받지 않고 자주 변경될 수 있는 내부 구현에 초점을 둔 경우입니다.
+
+
+❌ **그렇지 않으면:** 코드를 리펙토링 할 때, 모든 mock을 찾아서 수정해야 합니다. 테스트가 도움이 아닌 부담이 됩니다.
+
+
+
+✏ 예제 코드
+
+
+
+### :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:올바른 예: spy는 요구사항을 테스트하는데 초점을 두고있지만, 내부를 건드리는 side-effect를 피할 순 없습니다.
+
+```javascript
+it("유효한 제품을 삭제하려고 할 때, 메일을 보낸다", async () => {
+ // 이미 제품을 추가했다고 가정
+ const spy = sinon.spy(Emailer.prototype, "sendEmail");
+ new ProductService().deletePrice(theProductWeJustAdded);
+ // 좋음: 우리는 내부를 다루는가? 그렇다, 그러나 요구사항(이메일을 보낸다)에 대한 테스트의 side-effect이다.
+});
+```
+
+
+
+
+
+## ⚪ ️ 1.6 의미없는 인풋 데이터를 사용하지 말고, 실제와 같은 인풋 데이터를 사용해라
+
+:white_check_mark: **이렇게 해라:** 흔히 제품의 버그들은 매우 특수한 인풋데이터를 통해 나타납니다 - 테스트 인풋이 혈실적일 수록 버그를 조기에 발견할 가능성이 높아집니다. 실제 데이터와 다양성 및 형태가 유사한 데이터를 생성해 주는 [Faker](https://www.npmjs.com/package/faker) 같은 전용 라이브러리들을 사용하십시오. 이런 라이브러리들은 실제같은 전화번호, 사용자 이름, 신용카드, 회사명 그리고 심지어 'lorem ipsum'같은 문자등을 생성할 수도 있습니다. 당신은 가상의 데이터를 사용하여 테스트(단위 테스트 위에서)를 무작위화 하거나 심지어 실제 환경으로부터의 실제 데이터를 임포트 할수도 있습니다. 다음 단계를 얻기를 원하십니까? 그렇다면 아래로 가십시오 (property-based testing).
+
+
+
+❌ **그렇지 않다면:** "Foo"와 같은 인풋을 사용하면 당신의 모든 테스트가 모두 통과한것 처럼 표시되지만, 실제 환경에서는 해커가 “@3e2ddsf . ##’ 1 fdsfds . fds432 AAAA” 같은 인풋을 전달해 실패 할수도 있습니다.
+
+
+
+✏ 예제 코드
+
+
+
+### :thumbsdown: 올바르지 않은 예: 현실적이지 않은 데이터 때문에 통과하는 테스트
+
+
+
+
+```javascript
+const addProduct = (name, price) =>{
+ const productNameRegexNoSpace = /^\S*$/;// 공백은 허용되지 않음
+
+ if(!productNameRegexNoSpace.test(name))
+ return false;//도달하지 않는 곳
+
+ //some logic here
+ return true;
+};
+
+test("잘못된 예제: 유효한 속성과 함께 제품을 추가한다면, 성공을 얻는다.", async () => {
+ //모든 테스트에서 false 가 리턴되지 않는 "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 프로퍼티 기반(Property-based) 테스트를 통해 다양한 인풋 값 조합으로 테스트를 하십시오.
+
+:white_check_mark: **이렇게 해라:** 우리는 일반적으로 적은 수의 인풋 샘플 데이터를 가지고 테스트를 합니다. 심지어 인풋 데이터 형식이 실제 데이터와 비슷할 때에도 다음과 같이 제한된 인풋 조합으로만 테스트를 커버합니다.(method(‘’, true, 1), method(“string” , false” , 0)) 하지만, 운영시에는 5개의 파라미터를 가지는 API는 수 천 개의 다른 조합의 파라미터로 호출 될 수 있고, 이 중 하나가 우리의 시스템을 다운시킬 수도 있습니다. 그렇다면 만약 1000 가지 조합의 인풋값을 자동으로 생성하고 올바른 응답을 반환하지 못하는 인풋값을 찾아내는 단위 테스트를 작성할 수 있다면 어떨까요?
+프로퍼티 기반 테스트는 단위 테스트에 모든 가능한 인풋 조합을 사용하여 생각하지 못 한 버그를 찾을 확률을 높여줍니다. 예를들어, 다음의 메소드가 주어졌을 때 — addNewProduct(id, name, isDiscount) — 프로퍼티 기반 테스트 라이브러리들은 다양한 파라미터 (number, string, boolean) 조합으로 - (1, “iPhone”, false), (2, “Galaxy”, true) - 이 메소드를 호출합니다. [js-verify](https://github.com/jsverify/jsverify) 나 [testcheck](https://github.com/leebyron/testcheck-js) (much better documentation) 같은 라이브러리를 지원하는 테스트 러너들 (Mocha, Jest, etc) 중 당신이 가장 선호하는 방법을 통해 프로퍼티 기반 테스트를 할 수 있습니다.
+업데이트 : 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", () => {
+ //서로 다른 무작위 값으로 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)가 필요한 경우 외부 파일이 아닌 테스트의 일부 ([인라인 스냅샷](https://jestjs.io/docs/en/snapshot-testing#inline-snapshots))에 포함 된 짧고 집중된 스냅샷(3~7 라인)만 사용하십시오. 이 지침을 따르면 따로 설명이 필요없고 잘 깨지지 않는 테스트가 됩니다.
+
+반면에, '고전적인 스냅샷' 튜토리얼 및 도구는 외부에 큰 파일(예: 구성 요소 랜더링 마크업, API JSON 결과)를 저장하고, 테스트를 실행할 때 마다 수신된 결과를 저장된 버전과 비교하기를 권장합니다. 예를 들어, 이것은 1,000 라인(우리가 절대 읽지 않고 추론하지 않을 3,000개의 데이터 값을 가진)의 코드를 우리 테스트에 암시적으로 연결할 수 있습니다. 왜 이것이 잘못 되었을까요? 이렇게하면 테스트에 실패할 1,000 가지 이유가 생깁니다. 한줄만 변경되어도 스냅샷이 유효하지 않게 되고, 이런일이 일어날 가능성이 높습니다. 얼마나 자주? 모든 공백, 주석에서 혹은 사소한 CSS/HTML 변경에 대해서. 뿐만 아니라 테스트 이름은 1,000 라인이 변경되지 않았는지를 나타내기 때분에, 실패에 대한 단서를 제공하지 않습니다. 또한 테스트 작성자가 긴 문서(검사하고 확인할 수 없는)를 받아들이게끔 합니다. 이 모든 것은 초점이 맞지않고 너무 많은 것을 달성하려는 모호하고 간절한 테스트 증상입니다.
+
+긴 외부 스냅샷이 허용되는 경우가 거의 없다는 점은 주목할 가치가 있습니다 - 데이터가 아닌 스키마를 assert 할 때(값 추출 및 필드에 집중) 또는 수신된 문서가 거의 변경되지 않는 경우
+
+
+
+❌ **그렇지 않다면:** UI 테스트가 실패합니다. 코드가 문제없어 보이고 화면이 완벽한 픽셀을 렌더링합니다. 어떻게 되었습니까? 스냅샷 테스트에서 원본 문서와 현재 수신된 문서와의 차이점을 발견했습니다. 빈칸 하나가 마크 다운에 추가되었습니다...
+
+
+
+✏ 예제 코드
+
+
+
+### :thumbsdown: 올바르지 않은 예: 보이지 않는 2,000 라인의 코드를 우리 테스트에 연결
+
+
+
+```javascript
+it("TestJavaScript.com 이 올바르게 랜더링 된다.", () => {
+ //Arrange
+
+ //Act
+ const receivedPage = renderer
+ .create( Test JavaScript )
+ .toJSON();
+
+ //Assert
+ expect(receivedPage).toMatchSnapshot();
+ // 이제 2,000 라인의 문서를 암묵적으로 유지합니다.
+ // 모든 줄바꿈 또는 주석이 테스트를 망가뜨립니다.
+});
+```
+
+
+
+### :clap: 올바른 예: expectation이 잘 보이고 집중된다.
+
+```javascript
+it("TestJavaScript.com 홈페이지를 방문하면, 메뉴가 보인다.", () => {
+ //Arrange
+
+ //Act
+ const receivedPage = renderer
+ .create( Test JavaScript )
+ .toJSON();
+
+ //Assert
+
+ const menu = receivedPage.content.menu;
+ expect(menu).toMatchInlineSnapshot(`
+
+- Home
+- About
+- Contact
+
+`);
+});
+```
+
+
+
+
+
+## ⚪ ️ 1.9 테스트 데이터를 글로벌로 하지말고 테스트별로 따로 추가하라.
+
+:white_check_mark: **이렇게 해라:** 황금률에 따르면(섹션 0), 각 테스트는 커플링을 방지하고 테스트 흐름을 쉽게 추론하기 위해 자체 DB 데이터를 추가하고 실행해야 합니다. 실제로 성능 향상(테스트를 실행하기 전에 DB 데이터를 준비(['테스트 픽스쳐'라고도 합니다](https://en.wikipedia.org/wiki/Test_fixture)))을 위해 이를 위반하는 테스터들이 많습니다. 성능은 실제로 유효한 문제이지만 완화될 수 있습니다(2.2 컴포넌트 테스트 참고). 그러나 테스트 복잡성은 대부분의 다른 고려사항들을 통제해야 하는 고통을 수반합니다. 각 테스트에 필요한 DB 레코드를 명시적으로 추가하고, 해당 데이터에 대해서만 테스트를 수행하십시오. 성능이 중요한 문제가 되는 경우 - 데이터를 변경하지 않는 테스트 모음(예: 쿼리)에 대해서 데이터를 준비하는 형태로 타협할 수 있습니다.
+
+
+
+❌ **그렇지 않으면:** 테스트 실패, 배포 중단으로 팀원들이 귀중한 시간을 소비할 것입니다. 버그가 있습니까? 조사해보니 '없습니다' - 두 테스트에서 동일한 테스트 데이터를 변겨안 것으로 보입니다.
+
+
+
+✏ 예제 코드
+
+
+
+### :thumbsdown: 올바르지 않은 예: 테스트는 독립적이지 않으며 글로벌 훅에 의한 DB 데이터에 의존
+
+
+
+```javascript
+before(() => {
+ // 사이트 및 관리자 데이터를 DB에 추가. 데이터는 어디에 있습니까? 외부에. 외부 JSON 또는 마이그레이션 프레임워크에
+ await DB.AddSeedDataFromJson('seed.json');
+});
+it("사이트 이름을 업데이트 할 때, 성공을 확인한다.", async () => {
+ // 사이트 이름 "portal"이 존재한다는 것을 알고있습니다. 시드파일에서 봤습니다.
+ const siteToUpdate = await SiteService.getSiteByName("Portal");
+ const updateNameResult = await SiteService.changeName(siteToUpdate, "newName");
+ expect(updateNameResult).to.be(true);
+});
+it("사이트 이름을 쿼리할 때, 올바른 사이트 이름을 얻는다.", async () => {
+ // 사이트 이름 "portal"이 존재한다는 것을 알고있습니다. 시드파일에서 봤습니다.
+ 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);
+});
+```
+
+
+
+
+
+## ⚪ ️ 1.10 오류를 catch 하지말고 expect 하십시오.
+
+:white_check_mark: **이렇게 해라:** 오류를 발생시키는 입력값을 assert 할 때, try-catch-finally를 사용하고 catch 블럭에서 assert 하는게 맞아 보일수도 있습니다. 아래 예는 테스트 의도와 결과 expectation을 숨기는 어색하고 장황한 테스트 사례입니다.
+
+보다 우아한 대안은 한줄짜리 Chai assertion을 사용하는 것 입니다: expect(method).to.throw (혹은 Jest: expect(method).toThrow()). 오류 유형을 알려주는 속성이 예외에 포함되어야 합니다. 그렇지 않고 일반적인 오류를 발생시키면 어플리케이션은 사용자에게 실망스러운 메시지를 표시하는 것 밖에 할 수 없습니다.
+
+
+
+❌ **그렇지 않으면:** 무엇이 잘못되었는지 테스트 보고서(예: CI 보고서)에서 추론하기 어려울 것입니다.
+
+
+
+✏ 예제 코드
+
+
+
+### :thumbsdown: 올바르지 않은 예: try-catch로 오류가 존재한다고 assert 하는 긴 테스트 사례
+
+
+
+```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;
+ // 이 asserting이 실패하면, 테스트 결과에서 누락된 입력값에 대한 단어는 알 수 없고
+ // 입력값이 null 이라는 것만 알 수 있습니다.
+});
+```
+
+
+
+### :clap: 올바른 예: QA나 PM이라도 쉽게 이해할 수 있고 읽기 쉬운 expectation
+
+```javascript
+it("제품명이 없으면, 400 오류를 던진다.", async () => {
+ await expect(addNewProduct({}))
+ .to.eventually.throw(AppError)
+ .with.property("code", "InvalidInput");
+});
+```
+
+
+
+
+
+## ⚪ ️ 1.11 테스트에 태깅하십시오.
+
+:white_check_mark: **이렇게 해라:** 다른 테스트는 꼭 다른 시나리오에서 실행해야 합니다: 개발자가 파일을 저장하거나 커밋을 할 때 빠르고, IO가 많이 없는 테스트를 실행해야 합니다. 전체 end-to-end 테스트는 일반적으로 새로운 Pull Request가 제출되었을 때 실행됩니다. 등.. 이러한 경우에 #cold #api #sanity와 같은 키워드로 테스트에 태깅하면 테스트를 효율적으로 grep 할 수 있고, 원하는 하위세트를 호출할 수 있습니다. 예) Mocha를 이용해서 sanity 테스트 그룹만 실행하는 방법입니다: mocha - grep 'sanity'
+
+
+
+❌ **그렇지 앟으면:** 개발자가 작은 변경을 할 때마다 수십 개의 DB 쿼리를 수행하는 테스트를 포함한 모든 테스트를 실행한다면, 속도가 매우 느려져 개발자가 테스트를 수행하지 않게 만들 것입니다.
+
+
+
+✏ 예제 코드
+
+
+
+### :clap: 올바른 예: 테스트를 '#cold-test'로 태깅하면 테스트를 수행하는 사람이 빠른 테스트만 실행할 수 있습니다(IO를 수행하지 않고 개발자가 코딩하는 중에도 자주 실행할 수 있는 테스트 cold === quick).
+
+
+
+```javascript
+// 이 테스트는 빠르고(DB 없음) 현재 사용자/CI가 자주 실행할 수 있는 태그를 지정하고 있습니다.
+describe('주문 서비스', function() {
+ describe('새 주문 추가 #cold-test #sanity', function() {
+ test('시나리오 - 통화가 제공되지 않음. 예외 - 기본 통화 사용 #sanity', function() {
+ // code logic here
+ });
+ });
+});
+```
+
+
+
+
+
+## ⚪ ️ 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 등)에 대한 종속성을 피하십시오.
+
+
+
+❌ **그렇지 않으면:** 수십 년 동안 수집 된 아주 소중한 조언을 놓치게 될 것입니다.
+
+
+
+# 섹션 2️⃣: 백엔드 테스트
+
+## ⚪ ️ 2.1 당신의 테스트 포트폴리오를 풍부하게 하십시오: 단위 테스트와 피라미드를 넘어서세요.
+
+:white_check_mark: **이렇게 해라:** 10년이 넘은 모델인 [테스트 피라미드](https://martinfowler.com/bliki/TestPyramid.html)는 세 가지 테스트 유형을 제시하고 대다수 개발자의 테스트 전략에 영향을 주는 훌륭한 모델입니다. 동시에, 몇 가지 반짝이는 새로운 테스트 기술들이 등장하였지만 모두 테스트 피라미드의 그림자 뒤로 사라졌습니다. 우리가 최근 10년간 보아 온 극적인 기술의 변화들(Microservices, cloud, serverless)을 고려할 때, 아주 오래된 모델 하나가 *모든* 어플리케이션 유형에 적합하다는 것이 가능한가요? 테스트 세계는 새로운 기술을 받아들이는 것을 고려하지 않나요?
+
+오해는 하지 마세요. 2019 테스트 피라미드에서 TDD와 단위 테스트는 여전히 강력한 기술이고 아마도 많은 어플리케이션에 가장 어울리는 기술입니다. 다른 모델과 마찬가지로, 테스트 미라미드는 유용하지만 [그것이 항상 맞는 것은 아닙니다](https://en.wikipedia.org/wiki/All_models_are_wrong). 예를 들어, 어떤 IOT 어플리케이션을 생각해 봅시다. 이 어플리케이션은 다수의 이벤트를 Kafka/RabbitMQ 같은 메세지 버스로 보내고 다시 데이터 웨어하우스로 흘려보냅니다. 그리고 이 데이터들은 어떤 분석 UI에서 조회됩니다. 우리는 정말 우리의 테스트 예산의 50%를 통합 중심적(intergration-centric)이고 로직이 거의 없는 어플리케이션의 단위 테스트를 작성하는데 할애해야 할까요? 어플리케이션 유형들이 다양해질 수록(bots, crypto, Alexa-skills) 테스트 피라미드가 적합하지 않은 시나리오들을 발견할 가능성이 커집니다.
+
+지금이 당신의 테스트 포트폴리오를 넓히고 더 많은 테스트 유형들에 익숙해질 시간입니다. (다음 항목에서 몇 가지 아이디어들을 제안합니다.) 테스트 피라미드 같은 모델들도 염두에 둘 뿐만 아니라 당신이 직면하고 있는 현실 세계의 문제들에 적합한 테스트 유형들을 찾으세요. ("우리 API 깨졌어. Consumer-driven contract 테스트 작성하자!" 처럼요.) 위험성 분석을 기반으로 포르폴리오를 구축하는 투자자처럼 당신의 테스트를 다양화하세요 - 문제가 발생할 수 있는 부분을 가늠하고 잠재적 위험성을 줄일 수 있는 예방 방법을 찾으세요.
+
+주의 사항 : 소프트웨어 세계에서의 TDD 논쟁은 전형적인 잘못된 이분법입니다. 어떤 사람들은 TDD를 모든 곳에 적용하라고 주장하지만, 다른 일부는 TDD를 악마라고 생각합니다. 절대적으로 한쪽만 주장하는 사람들은 모두 틀렸습니다 :]
+
+
+
+❌ **그렇지 않으면:** 당신은 굉장한 ROI를 주는 몇 가지 툴들을 놓칠 것입니다. Fuzz, lint, mutation 테스트들은 단 10분만에 당신에게 가치를 제공할 수 있습니다.
+
+
+
+✏ 코드 예제
+
+
+
+### :clap: 올바른 예: Cindy Sridharan은 그녀의 훌륭한 글 ‘Testing Microservices — the sane way’에서 풍부한 테스트 포트폴리오를 제안합니다. 
+
+예제: [YouTube: “Beyond Unit Tests: 5 Shiny Node.JS Test Types (2018)” (Yoni Goldberg)](https://www.youtube.com/watch?v=-2zP494wdUY&feature=youtu.be)
+
+
+
+
+
+
+
+
+
+## ⚪ ️2.2 컴포넌트 테스트가 최선의 방법일 수 있다.
+
+:white_check_mark: **이렇게 해라:** 각각의 단위 테스트는 어플리케이션의 매우 작은 부분만을 커버하고 전체를 모두 커버하기에는 비용이 많이 듭니다. 반면에, end-to-end 테스트는 간단하게 많은 부분을 커버할 수 있지만 깊이가 얕고 더 느립니다. 그렇다면 균형 잡힌 접근법을 적용하여 단위 테스트보다는 크지만 end-to-end 테스트보다는 작은 테스트를 작성하는 것은 어떨까요? 컴포넌트 테스트는 테스트 세계에서 잘 알려지지 않은 방법입니다. - 컴포넌트 테스트는 다음의 두 가지 이점을 모두 제공합니다: 합리적인 성능과 TDD 패턴을 적용할 수 있는 가능성 + 현실적이면서 훌륭한 커버리지
+
+컴포넌트 테스트는 마이크로 서비스 '단위'에 중점을 두고 API에 대하여 동작합니다. 마이크로서비스 그 자체에 속한 것들 (예를들면, 실제 DB 또는 해당 DB의 인-메모리 버전)은 모킹(Mock)하지 않고, 다른 마이크로서비스 호출과 같은 외부적인 것은 스텁(Stub)합니다. 그렇게 함으로써 우리는 우리가 배포하는 것을 테스트하고 어플리케이션의 바깥쪽에서 안쪽으로 접근하며, 적당한 시간 안에서 큰 자신감을 얻을 수 있습니다.
+
+
+
+❌ **그렇지 않으면:** 시스템 커버리지가 20%에 불과하다는 것을 깨닫기까지 단위 테스트를 작성하는 데 오랜 시간이 걸릴 수 있습니다.
+
+
+
+✏ 코드 예제
+
+
+
+### :clap: 올바른 예: Supertest를 통해 프로세스 내 Express API에 접근할 수 있습니다. (빠르고 다양한 계층을 커버함)
+
+
+
+ allows approaching Express API in-process (fast and cover many layers)")
+
+
+
+
+
+## ⚪ ️2.3 신규 릴리즈가 API 사용을 깨지게 하지 마십시오.
+
+:white_check_mark: **이렇게 해라:** 당신의 마이크로서비스는 다수의 클라이언트를 가지고 있고 호환성의 이유로 여러 버전의 서비스를 운영합니다 (모든 사람을 만족시키기 위해서). 그런 상황에서 당신이 일부 필드를 변경하면 이 필드를 믿고 사용하던 일부 중요한 클라이언트는 화가 날 것입니다. 이것은 통합(integration) 세계에서 해결하기 어려운 진퇴양난에 놓인 문제입니다: 서버 사이드가 여러 클라이언트들의 모든 기댓값을 고려하는 것은 매우 어려운 일입니다. - 반면에, 서버가 릴리즈 날짜를 결정하기 때문에 클라이언트는 어떠한 테스트도 수행할 수 없습니다.
+[소비자 주도 계약 테스트(Consumer-driven contracts)와 PACT 프레임워크](https://docs.pact.io/)는 매우 파괴적인 방법으로 이러한 프로세스를 표준화하기 위해 나타났습니다. - 서버가 서버의 테스트 계획을 결정하지 않고, 클라이언트가 서버의 테스트를 결정합니다! PACT는 클라이언트의 기댓값을 기록하여 "브로커"라는 공유된 위치에 올려둘 수 있습니다. 그러면 서버는 그 기댓값을 당겨 받을 수 있고 빌드할 때마다 PACT 라이브러리를 사용하여 깨진 계약(contract - 충족되지 않은 클라이언트의 기댓값)을 감지할 수 있습니다. 이렇게 함으로써, 모든 서버-클라이언트 API 간 일치하지 않은 것들을 빌드/CI 환경에서 조기에 잡을 수 있고 당신의 큰 절망감을 줄여줄 수 있을 것입니다.
+
+
+
+
+❌ **그렇지 않으면:** 대안은 수동 배포나 배포에 대한 두려움을 안고 가는 것 뿐입니다.
+
+
+
+✏ 코드 예제
+
+
+
+### :clap: 올바른 예:
+
+
+
+
+
+
+
+
+
+
+
+
+
+## ⚪ ️ 2.4 당신의 미들웨어를 독립적으로 테스트 하십시오.
+
+:white_check_mark: **Do:** 많은 사람들은 미들웨어(Middleware) 테스트를 피합니다. 왜냐하면 미들웨어 테스트는 시스템의 작은 부분일 뿐이고 라이브 Express 서버가 필요하기 때문입니다. 하지만 두 가지 이유 모두 틀렸습니다. - 미들웨어는 작지만 모든 요청 또는 대부분의 요청에 영향을 미치고, {req,res} JS 객체를 가지는 순수한 함수로 쉽게 테스트할 수 있기 때문입니다. 미들웨어 함수를 테스트하기 위해서는 단지 함수를 불러오고 함수가 올바르게 동작하는 것을 확인하기 위해 {req, res} 객체에 대한 인터렉션을 스파이(spy)([예를들어 Sinon을 사용](https://www.npmjs.com/package/sinon))하면 됩니다. 라이브러리 [node-mock-http](https://www.npmjs.com/package/node-mocks-http)는 더 나아가서 행위에 대한 스파이와 함께 {req, res} 객체도 테스트할 수 있습니다. 예를 들어, response 객체의 http 상태가 기대했던 값과 일치하는지 여부를 확인(assert)할 수 있습니다. (아래 예제를 보세요)
+
+
+
+❌ **Otherwise:** Express 미들웨어에서의 버그 === 모든 요청 또는 대부분의 요청에서의 버그
+
+
+
+
+✏ 코드 예제
+
+
+
+### :clap:올바른 예: 네트워크 호출 없이 전체 Express 시스템도 깨우지 않으면서 미들웨어를 독립적으로 테스트
+
+
+
+```javascript
+//테스트하고 싶은 미들웨어
+const unitUnderTest = require('./middleware')
+const httpMocks = require('node-mocks-http');
+//Jest 문법으로 Mocha의 describe() & it()과 동일
+test('헤더에 인증정보가 없는 요청은, 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 빌드에 추가하여 코드 냄새(code smell)가 발견되면 중단되도록 할 수 있습니다. 정적 분석 도구가 일반적인 린트(lint) 도구보다 더 좋은 점은 여러 파일들의 컨텍스트 안에서 품질을 검사하고(예: 중복 탐지), 고급 분석(예: 코드 복잡성)을 할 수 있으며 코드 이슈에 대한 히스토리와 프로세스를 추적할 수 있다는 것입니다. 사용할 수 있는 정적 분석 도구 두 가지는 [Sonarqube](https://www.sonarqube.org/) (2,600+ [stars](https://github.com/SonarSource/sonarqube))와 [Code Climate](https://codeclimate.com/) (1,500+ [stars](https://github.com/codeclimate/codeclimate))입니다.
+
+Credit:: [Keith Holliday](https://github.com/TheHollidayInn)
+
+
+
+
+❌ **그렇지 않으면:** 코드 품질이 좋지 않으면 버그와 성능은 빛나는 새 라이브러리나 최신 기능으로 해결할 수 없는 문제가 될 것입니다.
+
+
+
+
+✏ 코드 예제
+
+
+
+### :clap: 올바른 예: 복잡도가 높은 함수를 찾아내는 상용 도구인 CodeClimate:
+
+
+
+
+
+
+
+
+
+
+
+
+## ⚪ ️ 2.6 노드 혼돈(chaos)대한 준비상태를 확인하십시오.
+:white_check_mark: **이렇게 해라:** 이상하게도 대부분의 소프트웨어 테스트는 오직 로직과 데이터를 대상으로 합니다. 하지만 최악의 상황(정말 해결하기 어렵기도 한 상황) 중 일부는 인프라 이슈입니다. 예를 들어, 프로세스 메모리가 과부하 되거나 서버/프로세스가 죽는 상황, 또는 API 속도가 50% 아래로 떨어질 때 모니터링 시스템이 인식하는 상황에 대해서 테스트한 적이 있나요? 이러한 문제 상황들을 테스트하고 줄이기 위해서 - [카오스 엔지니어링(Chaos engineering)](https://principlesofchaos.org/)이 넷플릭스에 의해 탄생했습니다. 카오스 엔지니어링은 혼돈(chaos) 상황에 대한 어플리케이션의 복원력을 테스트하기 위해서 상황에 대한 인식, 프레임워크, 툴들을 제공하는 것을 목표로 합니다. 예를 들어, 유명한 툴 중에 하나인 [카오스 몽키(chaos monkey)](https://github.com/Netflix/chaosmonkey)는 서버를 무작위로 종료시키고 이러한 상황에도 사용자는 서비스를 계속 사용할 수 있어 시스템이 단일 서버에 의존하지 않고 있다는 것을 테스트합니다. (쿠버네티스 버전인 [kube-monkey](https://github.com/asobti/kube-monkey)는 팟(Pod)을 종료시킴) 이러한 툴들은 모두 호스팅/플랫폼 레벨에서 동작합니다. 하지만 당신이 순수 노드 혼돈을 테스트하고 발생시키고 싶으면 어떻게 해야 할까요? 예를 들면, 노드 프로세스가 어떻게 잡히지 않은 오류, 처리되지 않은 프로미스 거부(promise rejection), 최대로 허용된 1.7GB에 대한 v8 메모리 과부하를 처리하는지. 혹은 이벤트 루프가 자주 차단될 때 UX가 만족스럽게 유지되는지 여부 같은 것들이요. 이 문제를 해결하기 위해 제가 모든 종류의 노드 관련된 카오스 행위를 제공하는 [node-chaos](https://github.com/i0natan/node-chaos-monkey) (alpha)를 만들었습니다.
+
+
+
+
+❌ **그렇지 않으면:** 탈출구는 없습니다. 머피의 법칙은 자비없이 당신의 시스템에 타격을 줄 것입니다.
+
+
+
+✏ 코드 예제
+
+
+
+### :clap: 올바른 예: Node-chaos는 모든 종류의 Node.js 행위들을 발생시켜서 당신의 어플리케이션이 얼마나 혼돈 상태에 대한 복원력이 있는지 테스트 할 수 있습니다.
+
+
+
+
+
+
+## ⚪ ️2.7 글로벌한 초기 테스트 데이터 집합을 만들지 말고 각 테스트 마다 데이터를 추가하십시오.
+:white_check_mark: **이렇게 해라:** 황금률(섹션 0)에 따르면 각 테스트는 커플링을 방지하고 테스트 흐름에 대해서 쉽게 추론하기 위해 자신의 DB 데이터들을 추가하고 해당 데이터로 테스트되어야 합니다. 하지만 현실 세계에선 성능 향상을 위해 테스트를 실행하기 전에 초기 데이터를 DB에 추가하는(‘test fixture’라고 알려져 있음) 테스터들에 의해서 이 규칙은 종종 깨지곤 합니다. 성능은 실제로 중요한 문제입니다. - 이 문제는 완화될 수 있습니다 ('컴포넌트 테스트' 섹션을 보세요). 하지만 테스트 복잡성은 대부분의 다른 고려사항들을 지배해 버리는 더욱 고통스런 문제입니다. 실질적으로 각 테스트 케이스에 필요한 DB 레코드만 명시적으로 추가하고 해당 레코드를 가지고만 테스트하세요. 만약 성능이 중요한 문제라면 - 데이터를 변경하지 않는 테스트들에 대해서만 초기 데이터를 채우는 형태로 타협할 수 있습니다. (예: 쿼리)
+
+
+
+❌ **그렇지 않으면:** 테스트가 실패하고 배포는 중단되어 팀원들은 지금 소중한 시간을 할애해야 합니다. 버그가 있습니까? 찾아봅시다, 오 이런 - 두 개의 테스트가 동일한 테스트 데이터(seed data)를 변경한 것으로 보입니다.
+
+
+
+✏ 코드 예제
+
+
+
+### :thumbsdown: 올바르지 않은 예: 테스트는 독립적이지 않고 테스트마다 글로벌 DB 데이터를 사용하도록 훅이 걸려있습니다.
+
+
+
+```javascript
+before(async () => {
+ // DB에 사이트와 어드민 데이터를 추가합니다. 데이터는 어디에 있나요? 외부에 있습니다. 외부 json 파일이나 마이그레이션 프레임워크에 있습니다.
+ await DB.AddSeedDataFromJson('seed.json');
+});
+it("사이트 이름을 변경하면, 성공 결과값을 받아온다", async () => {
+ //"portal"이라는 이름의 사이트가 있다는 것을 알고 있습니다. - 씨드 파일에서 봤습니다.
+ const siteToUpdate = await SiteService.getSiteByName("Portal");
+ const updateNameResult = await SiteService.changeName(siteToUpdate, "newName");
+ expect(updateNameResult).to.be(true);
+});
+it("사이트 이름으로 조회 했을 때, 해당 사이트를 가져온다", async () => {
+ //"portal"이라는 이름의 사이트가 있다는 것을 알고 있습니다. - 씨드 파일에서 봤습니다.
+ 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);
+});
+
+```
+
+
+
+
+
+# 섹션 3️⃣: 프론트엔드 테스트
+
+## ⚪ ️ 3.1 기능으로부터 화면을 분리하십시오
+
+:white_check_mark: **이렇게 해라:** 컴포넌트 로직을 테스트할때, 화면의 세부사항들은 제외되어야할 노이즈가 됩니다. 그것을 제외함으로써 당신의 테스트들은 순수한 데이터에 집중할 수 있습니다. 실제로, 그래픽 구현에 너무 결합되지 않는 추상적인 방법을 통해 요구되어지는 데이터를 마크업으로부터 추출하십시오. 그리고 느리게 만드는 애니메이션들을 제외한 오직 순수한 데이터를 검증하십시오(vs HTML/CSS 화면 세부사항). 당신은 렌더링하는 것을 피하고 오직 화면의 뒷부분(서비스, 액션, 스토어등과 같은)만을 테스트 하려고 할 수도 있습니다. 하지만, 이것은 실제와 같지도 않으며 심지어 화면에 올바른 데이터가 도달하지 않은 경우를 나타내지도 않는 가짜 테스트에서의 결과가 될 것 입니다.
+
+
+
+
+❌ **그렇지 않으면:** 당신의 테스트의 순수하게 계산된 데이터는 10ms 내에 준비될수도 있지만, 전체 테스트는 화려하고 불필요한 애니메이션 때문에 500ms(100 테스트 = 1분) 동안 지속될 것 입니다.
+
+
+
+
+✏ 코드 예제
+
+
+
+### :clap: 올바른 예: 화면의 세부사항을 빼내는 것
+
+ 
+
+```javascript
+test('오직 VIP를 보기위해 사용자 목록을 표시 했을 때, 오직 VIP 멤버들만 보여져야 한다', () => {
+ // 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); // 화면에 아닌 데이터를 비교
+});
+
+```
+
+
+
+### :thumbsdown: 잘못된 예: 화면 세부사항들과 데이터를 섞어서 검증
+```javascript
+test('오직 VIP를 보기위해 사용자 목록을 표시 했을 때, 오직 VIP 멤버들만 보여져야 한다', () => {
+ // 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: **이렇게 해라:** CSS 검색자들과 다르게 양식 레이블들과 같이 그래픽 변경에도 살아남을 요소들을 기반으로 HTML 엘리먼트들을 찾으십시오. 만약 설계된 엘리먼트가 이와 같은 요소들을 가지고 있지 않다면, 'test-id-submit-button' 과 같이 테스트에 한정된 요소를 만드십시오. 이 방법은 당신의 기능/로직 테스트들이 룩앤필때문에 절대 망가지지 않을 것을 보장할 뿐만 아니라, 이 엘리먼트와 요소가 테스트에 의해 사용되어지고 제거되어서는 안된다는것을 팀 전체에게 명확하게 합니다.
+
+
+
+❌ **그렇지 않으면:** 당신은 로그인 기능을 테스트하기를 원합니다. 이 기능은 많은 컴포넌트들, 로직 그리고 서비스들에 걸쳐져 있고 모든 것은 완벽하게 준비되어 있습니다 - 스텁, 스파이, Ajax 호출은 격리되어져 있습니다. 모든것은 완벽한 것 처럼 보입니다. 그렇지만, 이 테스트는 디자이너에 의해 div 클래스 이름이 'thick-border' 에서 'thin-border'로 바뀌었기 때문에 실패합니다.
+
+
+
+✏ 코드 예제
+
+
+
+### :clap: 올바른 예: 테스트를 위해 한정된 요소를 사용해서 엘리먼트를 찾으십시오
+
+
+
+```jsx
+// the markup code (part of React component)
+
+
+ {value}
+ {/* data-test-id 속성 참고 */}
+
+
+```
+
+```javascript
+// react-testing-library를 사용한 예제
+test("metric에 데이터가 전달되지 않으면, 0을 기본값으로 보여준다", () => {
+ // Arrange
+ const metricValue = undefined;
+
+ // Act
+ const { getByTestId } = render();
+
+ expect(getByTestId("errorsLabel").text()).toBe("0");
+});
+```
+
+
+
+### :thumbsdown: 잘못된 예: CSS 요소들에 의존
+```jsx
+// the markup code (part of React component)
+{value}
+// 만약 디자이너가 클래스를 변경한다면?
+```
+
+```javascript
+// this exammple is using enzyme
+test("데이터가 전달되지 않으면, 0을 보여준다", () => {
+ // ...
+
+ expect(wrapper.find("[className='d-flex-column']").text()).toBe("0");
+});
+```
+
+
+
+
+
+
+
+
+
+## ⚪ ️ 3.3 가능한한, 실제와 같고 완전히 렌더링된 컴포넌트를 테스트하십시오
+
+:white_check_mark: **이렇게 해라:** 적당한 크기가 때마다 사용자가 하는 것처럼 외부로부터 컴포넌트를 테스트하고, 화면를 완전히 렌더링하고, 그에 따라 조치를 취하고 렌더링 된 화면이 예상대로 작동하는지 확인하십시오. 모든 종류의 목킹, 부분 및 얕은 렌더링을 피하십시오. 이 접근은 세부정보의 부족으로 인해 걸리지 않는 버그가 발생할 수 있으며, 내부요소들과 함께 지저분해진 테스트들과 같이 유지보수를 하기 어렵게 만들 수도 있습니다. (see bullet 'Favour blackbox testing'). 만약 자식 컴포넌트들중 하나가 심각하게 느려지게 하거나(예: 애니메이션)) 설정을 복잡하게 하는 경우에는, 해당요소를 가상으로 처리하는 것이 좋습니다.
+
+주의할 점: 이 기술은 자식 컴포넌트들의 크기가 적당하게 묶여있는, 소형 혹은 중형 컴포넌트들에게 적합합니다. 너무 많은 자식들과 함께 렌더링된 컴포넌트는, 테스트가 실패한 원인(근본원인 분석)을 추론하기도 어렵고 매우 느려질 수도 있습니다. 이러한 경우들에서는, 부모에 대해서는 몇가지 테스트만을 작성하고, 자식들에 대해서 더 많은 테스트를 작성하십시오.
+
+
+
+❌ **그렇지 않으면:** 내부 메소드를 호출하여 컴포넌트의 내부에 영향을 주고 그리고 내부의 상태를 확인한다면 - 당신이 컴포넌트의 구현을 리팩토링할때, 모든 테스트도 함께 변경해야 합니다. 당신은 유지보수를 위한 그런 여유가 있습니까?
+
+
+
+✏ 코드 예제
+
+
+
+### :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("실제적인 접근: 필터들을 클릭하면, 필터들이 화면에 표시된다", () => {
+ // Arrange
+ const wrapper = mount();
+
+ // Act
+ wrapper.find("button").simulate("click");
+
+ // Assert
+ expect(wrapper.text().includes("Choose Filter"));
+ // 사용자가 요소에 접근하는 방법: 텍스트를 이용
+});
+
+```
+
+### :thumbsdown: 잘못된 예: 얕은 렌더링과 함께 실제를 목킹
+```javascript
+test("얕은/목킹 접근: 필터들을 클릭하면, 필터들이 화면에 표시된다", () => {
+ // Arrange
+ const wrapper = shallow();
+
+ // Act
+ wrapper
+ .find("filtersPanel")
+ .instance()
+ .showFilters();
+ // 내부를 탭하고, 화면을 무시한채 메소드를 호출. 화이트박스 접근
+
+ // Assert
+ expect(wrapper.find("Filter").props()).toEqual({ title: "Choose Filter" });
+ // name을 변경하거나, 관련된 다른 것들을 전달하지 않는다면 어떻게 될까?
+});
+```
+
+
+
+
+
+
+## ⚪ ️ 3.4 슬립을 사용하지 마십시오. 프레임워크에서 비동기 이벤트들을 위해 지원하는 내장 기능을 사용하십시오. 그리고 속도를 높이려 노력하십시오
+
+:white_check_mark: **이렇게 해라:** 대부분의 경우에 테스트 완료시간은 알 수 없습니다. (예: 애니메이션은 요소의 출현을 지연시킴) - 이런 경우에는 슬립(예: setTimeout)을 피하고, 대부분의 플랫폼들이 제공하는 더 결정적인 메소드들을 사용하십시오. 몇몇 라이브러리들은 awaiting 기능을 허용합니다. (예: [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)가 유용할 수 있습니다. 슬립은 당신의 테스트를 느리고 위험하게 만들기 때문에 피해야할 패턴입니다(너무 짧은 시간 기다려야할 경우). 만약 슬립과 폴링이 필연적이고 테스트 프레임워크의 지원이 없다면, [wait-for-expect](https://www.npmjs.com/package/wait-for-expect)와 같은 라이브러리들이 준결정 솔루션으로서 도움을 줄 수도 있습니다.
+
+
+❌ **그렇지 않으면:** 오랜 시간동안 슬립하는 경우, 테스트는 더 느려질 것 입니다. 슬립할때, 테스트중인 유닛이 제 시간에 반응하지 않으면 테스트는 실패할 것 입니다. 그래서 그것은 테스트가 실패하는 약점과 나쁜 성능간의 트레이드 오프를 가지게 됩니다.
+
+
+
+
+✏ 코드 예제
+
+
+
+### :clap: 올바른 예: 비동기 실행이 완료될때 처리되는 E2E API (Cypress)
+
+ 
+
+```javascript
+// using Cypress
+cy.get('#show-products').click()// navigate
+cy.wait('@products')// wait for route to appear
+// 라우트가 준비되면 실행 됩니다.
+
+```
+
+### :clap: 올바른 예: DOM 요소를 기다리는 테스트 라이브러리
+
+```javascript
+// @testing-library/dom
+test("영화 제목이 나타난다", async () => {
+ // 요소는 초기에 존재 하지 않음...
+
+ // 출현을 대기
+ 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);
+
+ // 출현을 기다린 후 요소를 리턴
+ 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/) 를 사용하면 쉽게 서버의 상태와 response 내용을 모니터링 하도록 설정 할 수 있고, 화면에서의 오류는 최소화 하면서 테스트를 진행 할 수 있습니다. 이들은 프론트엔드에 최적화 된 다른 도구([lighthouse](https://developers.google.com/speed/pagespeed/insights/), [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), [화면이 인터렉티브 하게 되는 시간](https://calibreapp.com/blog/time-to-interactive/)과 같이 화면에 직접적으로 영향을 주는 항목이나 상태에 중점을 둬야 합니다. 무엇보다 contents가 압축 되었는지, 첫 byte 응답까지의 시간, image 최적화, 적절한 DOM 의 크기 파악, SSL 등의 기술적 이슈에 중점을 둬야 합니다. 이런 항목에 대한 모니터링은 개발시점이나, CI, 24x7 운영 중 수행할 것을 권장 합니다.
+
+
+
+❌ **그렇지 않으면:** 완벽한 UI 를 개발해 두고, 100% 기능 테스트까지 완료 했지만, 최악의 UX를 제공하게 되거나 CDN의 설정 오류 등의 이유 때문에 너무 느린 서비스를 제공하게 될 수 있습니다.
+
+
+
+✏ 코드 예제
+
+### :clap: 올바른 예: Lighthouse 페이지 로딩 검사 보고서
+
+
+
+
+
+
+
+
+
+## ⚪ ️ 3.6 백엔드 API와 같이 자주 멈출 수 있거나 느린 리소스는 stub 하십시오.
+
+:white_check_mark: **이렇게 해라:** 주요 기능에 대한 테스트 코드(E2E테스트 아님) 작성 시, backend API 처럼 그 기능의 주 역할에서 벗어나는 항목은 제외할 수 있도록 stub(예: test double) 하십시오. 실제 네트워크를 호출하지 말고, test double 라이브러리([Sinon](https://sinonjs.org/), [Test doubles](https://www.npmjs.com/package/testdouble))를 사용하십시오. 이 경우 가장 큰 장점은 예상치 못한 테스트 실패를 예방 할 수 있습니다. 코드단에서 API 정의에 따라 test를 적용하는 작업은 안정적이지 않고 당신의 component는 문제가 없지만 수시로 test가 실패 할 수 있습니다.(운영 상태의 env 설정은 testing에서는 사용하지 마세요. API 요청에 병목이 발생 할 수 있습니다.) 이런식으로 해서 API 응답 데이터가 없는 경우, 에러가 응답되는 경우 등의 다양한 API 상태에 따른 테스트를 진행 할 수 있습니다. 다시한번 강조하지만 실제 네트워크 호출은 test를 매우 느리게 만들것입니다.
+
+
+
+❌ **그렇지 않으면:** 평균 테스트 실행 시간이 몇 ms 인 경우, API 호출로 개당 최소 100ms 시간이 걸리게 되고 테스트는 약 20배 느려지게 됩니다.
+
+
+
+
+✏ 코드 예제
+
+
+
+### :clap: 올바른 예: Stubbing 하거나 API 응답값 변조
+ 
+
+```jsx
+// 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("products가 없는 경우, 적절한 메시지 표시한다", () => {
+ // 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)는 일반적으로 실제 브라우저를 사용한 UI를 위한 테스트를 의미하지만(3.6 참고), 다른 의미로 실제 백엔드를 포함하여 전체 시스템을 확장하는 테스트를 의미합니다. 후자의 테스트 유형은 교환 스키마에 대한 잘못된 이해로 인해 발생할 수 있는 프론트엔드와 백엔드간의 통합 버그를 커버하기 때문에 상당히 유용합니다. 또한 백엔드간 통합 문제(예 : 마이크로서비스 A가 마이크로서비스 B에 잘못된 메시지를 보낸다)를 발견하고 배포 실패를 감지하는 효과적인 방법입니다. [Cypress](https://www.cypress.io)와 [Pupeteer](https://github.com/GoogleChrome/puppeteer)같은 UI 프레임워크만큼 친숙하고 성숙한 E2E 테스트 백엔드 프레임워크는 없습니다. 이러한 테스트의 단점은 구성 요소가 많은 환경을 구성하는 데 드는 높은 비용과 주로 불안정성 입니다 - 50개의 마이크로서비스가 제공되는데, 하나가 실패하더라도 전체 E2E가 실패. 따라서 이 기법을 적절히 사용해야 하며, 그 중 1~10개 정도만 사용해야 합니다. 즉, 소수의 E2E 테스트 일지라도 배포 및 통합 오류와 같은 유형의 문제를 잡을 수 있습니다. 프로덕션과 같은 스테이징 환경에서 실행하는 것이 좋습니다.
+
+
+
+❌ **그렇지 않으면:** UI는 백엔드에서 리턴하는 페이로드가 예상과 매우 다르다는 것을 알아차리기 위하여 기능 테스트에 많은 투자를 할 수 있습니다.
+
+
+
+## ⚪ ️ 3.8 로그인 자격 증명을 재사용하여 E2E 테스트 속도 향상
+
+:white_check_mark: **이렇게 해라:** 실제 백엔드를 포함하고 API 호출이 가능한 사용자 토큰을 사용하는 E2E 테스트에서, 모든 요청에서 사용자가 생성되고 로그인이 되는 수준으로 테스트를 격리하는 것은 아닙니다. 대신 테스트가 시작되기 전에 한 번만 로그인하고(즉, before-all), 토큰을 로컬 저장소에 저장해서 여러 요청에 재사용하십시오. 이것은 핵심 테스트 원칙 중 하나를 위반하는 것 같습니다 - 리소스 커플링 없이 테스트를 자율적으로 유지하십시오. 이것은 우려할 만 하지만 E2E 테스트에서 성능은 핵심 관심사이며, 개별 테스트를 시작하기 전에 매번 1~3개의 API 요청을 보내게 되면 실행 시간이 끔직해질 수 있습니다. 자격 증명을 재사용한다고해서 테스트가 동일한 사용자 레코드에 대해 수행되어야 한다는 의미는 아닙니다. 사용자 레코드(예: 테스트 유저 결제 내역)에 의존하는 경우 해당 레코드를 테스트의 일부로 생성하고 다른 테스트와의 공유를 피하십시오. 또한 백엔드가 위조될 수 있음을 기억하십시오 - 테스트가 프론트엔드에 중점을 둔 경우, 테스트를 분리하고 백엔드 API를 스텁하는 것이 좋습니다.(3.6 참고)
+
+
+
+❌ **그렇지 않으면:** 200개의 테스트 케이스가 주어졌고 로그인에 100ms가 소요된다고 가정하면 매번 로그인에만 20초가 소요된다.
+
+
+
+✏ 코드 예제
+
+
+
+### :clap: 올바른 예: before-each가 아닌 before-all에 로그인
+
+
+
+```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 테스트를 실행하십시오. 이런 유형의 테스트는 간단히 작성하고 유지보수 할 수 있지만 그에 따른 효과는 거대합니다. 기능적이거나 네트워크, 개발 관련 이슈를 발견할 수 있습니다. 다른 종류의 테스트들은 E2E 만큼 신뢰할 수는 없고 완벽하지 않습니다.(일부 운영팀에서는 단순히 운영 서버로 ping을 하고있고, 개발자들이 단순 기능 테스트만을 실행하여 패키징이나 브라우져 관련 이슈는 확인 할 수 없습니다.) smoke 테스트는 기능 테스트를 대체하기 보다는 smoke 발견을 위한 도구로 볼수 있습니다.
+
+
+❌ **그렇지 않으면:** 모든 테스트도 패스하고 서비스 핼스체크도 정상이라 모든것이 완벽해 보일수 있지만, Payment component가 패키징 이슈가 있어서 페이지 이동 시 화면 랜더링이 안될 수 있습니다.
+
+
+
+✏ 코드 예제
+
+
+
+### :clap: 올바른 예: 모든 페이지의 smoke 탐색하기
+
+```javascript
+it('모든 페이지를 smoke 테스트 할 때, 페이지들이 정상적으로 로드되어야 한다', () => {
+ // Cypress를 이용한 예제 입니다
+ // 다른 E2E 도구로도 쉽게 구현이 가능합니다
+ 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 다같이 협업가능한 문서로 테스트 내용을 내보내기 하십시오
+
+:white_check_mark: **이렇게 해라:** App의 신뢰도를 높일 수 있고, 테스트를 통해 또 다른 개선의 기회가 될 수 있습니다. 테스트 내용은 덜 기술적이면서 제품/UX와 관련된 표현으로 되어 있어서, 다양한 협업자들(개발자, 고객 등)간에 의사소통 수단으로 사용될 수 있습니다. 예를 들어, 어떤 프레임워크들은 비즈니스 흐름과 예상되는 결과들을 누구나(스테이크홀더, PM) 이해할 수 있는 언어로 표현하여 같이 확인하고 협업 할 수 있게 도움을 주는 필수적인 문서가 될 수 있습니다. 고객은 자신의 인수조건을 같이 정의해 나가면서 테스트를 작성하고 이것은 결국 ‘인수 테스트’ 가 됩니다. 이게 바로 [BDD (behavior-driven testing)](https://en.wikipedia.org/wiki/Behavior-driven_development) 입니다. 가장 유명한 프레임워크로는 [Cucumber(자바향)](https://github.com/cucumber/cucumber-js)가 있습니다. 아래의 예제를 참고하세요. 이와 비슷하면도 좀 다른 프레임워크로는 UI 각 컴포넌트의 다양한 상태별 실제 화면을 확인하고 어떤 경우 그런 상태가 렌더링 되는지 확인 할 수 있는 [StoryBook](https://storybook.js.org/) 이 있습니다.(예. Grid를 데이터가 있는 경우와 없는 경우, 필터된 경우로 렌더링해 볼 수 있습니다.) 이런 것들은 제품 관계자에게 매력적일 수도 있지만 보통 개발자들에게 개발 문서 처럼 사용됩니다.
+
+❌ **그렇지 않으면:** 테스트 작성을 위해 많은 공수를 들였지만 결과적으로 위와 같은 장점을 놓치지 됩니다.
+
+
+
+✏ 코드 예제
+
+
+
+### :clap: 올바른 예: cucumber-js 의 humnan 언어를 사용한 테스트 코드
+
+
+```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을 이용한 components 상태, 입력 값별 visualizing
+
+
+
+
+
+
+
+
+
+## ⚪ ️ 3.11 자동화된 툴을 사용하여 시각적 문제(Visual Issues)를 감지해라.
+
+
+:white_check_mark: **이렇게 해라:** 컨테츠가 겹치거나 깨지는 등의 시각적 문제들이 감지될 때, UI 스크린 샷을 캡처하기 위해 자동화 도구를 셋업하세요. 이를 통해, 올바른 데이터가 준비 될 뿐만 아니라 사용자가 편리하게 변경을 확인할 수 있습니다. 이러한 기술은 현재 널리 채택되지는 않았습니다. 우리의 테스트 사고 방식은 여전히 기능 테스트에 의존하지만 사용자가 실제로 경험하는 것은 시각적 요소이며 다양한 디바이스 유형때문에 일부 UI 버그들은 간과되기 쉽습니다. 일부 무료 툴들은 육안 검사를 위한 스크린 샷을 생성하거나 저장하는 기능과 같은 기본적인 기능들을 제공합니다. 이 방법은 작은 크기의 App에는 충분하지만, 변경이 발생할 때마다 사람의 손길이 필요한 다른 수동 테스트를 수행하기에는 제약이 있습니다. 반면에, 명확한 정의가 없기 때문에 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의 근본 원인을 분석하는 고급 기능들을 제공합니다.
+
+
+
+❌ **Otherwise:** How good is a content page that display great content (100% tests passed), loads instantly but half of the content area is hidden?
+
+
+
+
+✏ 코드 예제
+
+
+
+### :thumbsdown: Anti Pattern Example: A typical visual regression - right content that is served badly
+
+
+
+
+
+
+### :clap: Doing It Right Example: Configuring wraith to capture and compare UI snapshots
+
+
+
+```
+# 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: Doing It Right Example: Using Applitools to get snapshot comaprison and other advanced features
+
+ 
+
+```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: **이렇게 해라:** 테스트의 목적은 빠른 변경에 대한 충분한 자신감을 갖기 위한 것입니다. 분명히 더 많은 코드가 테스트 될수록 팀은 더 자신감을 가질 수 있습니다. 커버리지는 얼마나 많은 라인(브랜치, 구문(statements) 등)이 테스트에 의해 커버되었는지에 대한 지표입니다. 그렇다면 어느 정도가 충분할까요? 10–30%는 빌드 정확성에 대해 판단하기에는 분명히 너무 낮습니다. 반면에 100%는 비용이 많이 들고 정작 당신의 관심을 중요한 부분이 아닌 테스트 코드로 옮겨버릴지도 모릅니다. 이것에 대한 답은 수치는 어플리케이션 유형과 같은 다양한 요소들에 따라 달라진다는 것입니다. - 만약 당신이 Airbus A380의 차세대 버전을 만들면 100%로 맞춰야 하지만 웹툰 사이트라면 50%면 충분합니다. 비록 테스트에 열성인 대부분의 사람들은 적절한 커버리지 임계값이 상황에 따라 달라져야 한다고 하지만, 그들 중 대부분은 대다수의 어플리케이션을 만족하기 위해서 경험상으로 80%([마틴 파울러: “in the upper 80s or 90s”](https://martinfowler.com/bliki/TestCoverage.html))가 적절하다고 얘기합니다.
+
+구현 팁: 당신의 CI 환경에서 커버리지 임계치를 설정하여 그 기준에 미치지 못하면 빌드를 멈추도록 하고 싶을 것입니다. (컴포넌트 당 임계치를 설정하는 것도 가능합니다. 아래 예제 코드를 보세요). 이 위에, 빌드 커버리지 감소에 대한 감지도 고려해 보세요. (새로 커밋 된 코드가 커버리지에 못 미칠 때) - 이렇게 함으로써 개발자들이 커버리지를 올리거나 적어도 유지하도록 압박할 수 있습니다. 말한대로 커버리지는 오직 하나의 양적 지표일 뿐 테스트의 견고성을 나타내기에는 충분하지 않습니다. 그리고 다음 항목에 나와있는 것처럼 당신을 속일 수 있습니다.
+
+
+
+❌ **그렇지 않으면:** Confidence and numbers go hand in hand, without really knowing that you tested most of the system — there will also be some fear. and fear will slow you down
+
+
+
+✏ 코드 예제
+
+
+
+### :clap: 예제: 일반적인 커버리지 보고서
+
+
+
+
+
+### :clap: 올바른 예: 컴포넌트 당 커버리지를 설정하십시오. (Jest를 사용하여)
+
+
+
+
+
+
+
+
+
+## ⚪ ️ 4.2 커버리지 리포트를 확인하여 테스트 되지 않은 부분과 기타 이상한 점들을 감지하십시오.
+
+:white_check_mark: **이렇게 해라:** 일부 문제들은 레이더망 아래로 숨어버려 기존의 툴들을 사용하여 찾기 매우 어렵습니다. 이것들은 실제로 버그는 아니지만 심각한 영향을 줄 수 있는 생각지 못 한 어플리케이션 동작들입니다. 예를 들어, 일부 코드 영역은 절대 또는 거의 호출되지 않습니다. - ‘PricingCalculator’라는 상품 가격을 설정하는 클래스가 있다고 생각해 보세요. DB에 100000개의 상품이 있고 판매도 많지만 이 클래스는 실제로 절대 호출되지 않는 것으로 밝혀졌습니다... 코드 커버리지 리포트를 통해 어플리케이션이 당신이 원하는 대로 동작하는지 확인할 수 있습니다. 그 외에도 리포트는 어떤 코드들이 테스트되지 않았는지를 강조해서 보여줄 수도 있습니다. - 코드의 80%가 테스트 되었다는 알림이 중요한 부분이 커버되었는지에 대한 여부를 나타내진 않습니다. 리포트를 만드는 것은 쉽습니다. - 운영 또는 테스트를 할 때 커버리지 트래킹을 하면서 어플리케이션을 실행하세요. 그러고 나서 각 코드 영역이 얼마나 자주 호출됐는지를 나타내는 형형색색의 리포트를 보세요. 잠깐 시간을 내서 이 데이터들을 보면 몇 가지 문제점들을 발견하게 될 수도 있습니다.
+
+
+❌ **그렇지 않으면:** 어떤 코드가 테스트되지 않았는지 알 수 없으면 문제의 원인도 알 수 없습니다.
+
+
+
+✏ 코드 예제
+
+
+
+### :thumbsdown: 올바르지 않은 예: 이 커버리지 리포트에는 어떤 문제가 있나요? 현실 세계 시나리오로 QA에서 어플리케이션 사용을 추적했고 흥미로운 로그인 패턴을 찾았습니다. (힌트: 로그인 실패 횟수가 비례하지 않습니다. 분명히 무언가 잘못되었습니다.) 마침내 일부 프론트엔드 버그가 백엔드 로그인 API를 계속 호출하고 있다는 것이 밝혀졌습니다.
+
+
+
+
+
+
+
+## ⚪ ️ 4.3 mutation 테스트를 사용하여 논리적인 범위를 측정
+
+:white_check_mark: **이렇게 해라:** 전통적인 커버리지 측정은 거짓말쟁이: 100%의 코드 커버리지를 표시할 수 있지만, 함수 중 올바른 응답을 반환하는 기능은 없습니다. 심지어 하나도. 어찌하여? 테스트가 방문한 코드 라인을 단순하게 측정하지만, 테스트에서 실제로 테스트(올바른 응답을 assertion) 한 것이 있는지 확인하지는 않습니다. 출장을 위해 여행하고 여권 스템프를 보여주는 사람처럼 - 이것은 단지 공항과 호텔을 방문했을 뿐, 일을 했는지 어떤 것도 증명하지 못한다.
+
+mutation 기반의 테스트는 단순한 '방문'이 아닌 실제로 테스트 '된' 코드의 양을 측정하는데 도움이 됩니다. [Stryker](https://stryker-mutator.io)는 mutation 테스트를 위한 JavaScript 라이브러리이며 구현이 정말 깔끔합니다:
+
+(1) 의도적으로 코드를 변경하고 "버그를 심습니다". 예를 들면, newOrder.price === 0 는 newOrder.price != 0이 됩니다. 이 "버그"를 mutation이라고 합니다.
+
+(2) 모든 테스트가 성공하면 우리는 문제가 있다 - 테스트는 버그를 발견하는 목적을 달성하지 못했고, mutation은 살아남았다. 테스트가 실패하면 엄청난 mutation이 죽었다.
+
+모든 혹은 대부분의 mutation이 죽었다는 것을 알면 전통적인 커버리지보다 훨씬 더 높은 신뢰를 얻을 수 있으며 구성 시간은 비슷합니다.
+
+
+
+❌ **그렇지 않으면:** 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: 올바른 예: mutation 테스트 도구인 Stryker 보고서는 테스트 되지 않은 코드의 양을 감지하고 계산합니다.
+
+
+
+
+
+
+
+## ⚪ ️ 4.4 테스트 린터로 테스트 코드 문제 방지
+
+:white_check_mark: **이렇게 해라:** ESLint 플러그인 세트는 테스트 코드 패턴을 검사하고 문제를 발견하기 위해 특별히 제작되었습니다. 예를 들어 [eslint-plugin-mocha](https://www.npmjs.com/package/eslint-plugin-mocha)는 테스트가 글로벌 수준에서 작성될 때(describe() 문 아래에 있지 않음) 또는 테스트를 건너 뛰고 모든 테스트가 통과되었다는 잘못된 믿음을 가질 때 경고합니다. 유사하게, [eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest)는 예를 들어 테스트에 아무런 assertion이 없을 때 경고합니다.(아무것도 확인하지 않음)
+
+
+
+❌ **그렇지 않으면:** 90%의 코드 커버리지와 100%의 녹색 테스트를 보며 미소짓는 것은 많은 테스트가 아무것도 assertion하지 않고 많은 테스트 스위트가 건너 뛰어진다는 것을 알 때 까지만입니다. 이 잘못된 결과를 바탕으로 어떤 것도 배포하지 않았기를 바랍니다.
+
+
+
+✏ 예제 코드
+
+
+
+### :thumbsdown: 올바르지 않은 예: 오류로 가득 찬 테스트 케이스, 운 좋게도 린터가 잡았습니다.
+
+```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️⃣: 지속적인 통합
+
+
+
+## ⚪ ️ 5.1 린터를 풍성하게 구성하고 린트 문제가 있는 빌드를 중단하십시오
+
+:white_check_mark: **이렇게 해라:** 린터는 공짜 점심이며, 5분의 설정만으로 코드를 지켜주고 입력과 동시에 중요한 문제를 포착하는 자동 조종 장치를 거저 얻을 수 있습니다. 린터가 장식(세미콜론)에 지나지 않던 시대는 지나갔습니다. 요즘의 린터는 올바르게 throw되지 않고 정보가 손실되는 오류와 같은 심각한 문제를 포착 할 수 있습니다. 기본 규칙 세트 ([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) 는 resolve되지 않는 Promise를 발견해줍니다 (이런 코드는 계속해서 실행되는것이 불가능합니다), [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)는 코드가 Lodash._map(...)과 같은 V8 코어 메소드의 일부인 유틸리티 라이브러리 메소드를 사용할 때 경고해줍니다.
+
+
+
+❌ **그렇지 않으면:** 프로덕션이 계속 깨지는데 로그에 에러의 stack trace가 표시되지 않는 우울한 날을 상상해봅시다. 어떻게 된 걸까요? 실수로 코드가 에러가 아닌 객체를 던지고 있어서 stack trace가 손실되었다면, 벽에 머리를 들이박기 딱 좋을것입니다. 5분의 린터 설정으로 이런 오타를 감지하고 하루를 지켜낼 수 있습니다.
+
+
+
+✏ 코드 예제
+
+
+
+### :thumbsdown: 올바르지 않은 예: 잘못된 Error 객체가 실수로 throw되어 이 오류에 대한 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 공급 업체 (예: [CircleCI load CLI](https://circleci.com/docs/2.0/local-cli/)) 는 파이프라인의 로컬 실행을 허용합니다. [wallaby](https://wallabyjs.com/)와 같은 몇몇 상용 도구는 개발자 프로토타입으로 높은 가치의 테스트 통찰력을 제공합니다(협찬 아님). 또는 모든 품질 관련 명령어(예 : test, lint, vulnerabilities)를 실행하는 npm 스크립트를 package.json에 추가 할 수 있습니다. 병렬화를 위해 [concurrently](https://www.npmjs.com/package/concurrently)와 같은 도구를 사용하고 명령어 중 하나가 실패할 경우에는 0이 아닌 종료 코드를 사용하십시오. 이제 개발자는 하나의 명령을 호출해야 합니다. ‘npm run quality’— 즉각적인 피드백을 받습니다. githook을 사용하여 품질 검사에 실패한 경우 커밋을 중단하는 것도 고려하십시오 ([husky가 도움될 수 있음](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 서비스 기능과 함께 동작하게 하기 위해 이를 ['AWS Local'](https://github.com/localstack/localstack)과 결합할 수 있습니다. [Serverless](https://serverless.com)와 Serverless같은 여러 프레임워크를 사용하는 경우 [AWS SAM](https://docs.aws.amazon.com/lambda/latest/dg/serverless_app.html)을 이용해 로컬에서 FaaS 코드를 호출할 수 있습니다.
+
+거대한 쿠버네티스 생태계는 많은 새로운 도구가 자주 나오지만, 아직 로컬 및 CI 미러링을 위한 편리한 도구의 표준을 공식화하지 않았습니다. 한가지 방법은 실제와 비슷하지만 오버헤드가 적은 [Minikube](https://kubernetes.io/docs/setup/minikube) 및 [MicroK8s](https://microk8s.io)과 같은 도구를 사용하여 '최소화된 쿠버네티스'를 실행하는 것입니다. 또 다른 방법은 원격 '실제 쿠버네티스'를 테스트하는 것입니다. 일부 CI 벤더(예: [Codefresh](https://codefresh.io)은 쿠버네티스 환경과 통합되어 있어서 실제 상황에서 CI 파이프라인을 쉽게 실행할 수 있으며, 또 원격 쿠버네티스에 대한 사용자 지정 스크립팅을 허용합니다.
+
+
+
+❌ **그렇지 않으면:** 프로덕션 및 테스트에 서로 다른 기술을 사용하면 두 가지 배포 모델의 관리가 필요하고 개발자와 운영 팀이 분리됩니다.
+
+
+
+✏ 예제 코드
+
+
+
+### :clap: 올바른 예: 쿠버네티스 클러스터를 즉시 생성하는 CI 파이프라인 ([동적 환경 쿠버네티스](https://blog.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개의 CPU 제한 단위 테스트를 실행하는데는 시간이 오래 걸릴 수 있습니다. 운좋게도 최신 테스트 러너와 CI 플랫폼(예: [Jest](https://github.com/facebook/jest), [AVA](https://github.com/avajs/ava), [Mocha extension](https://github.com/yandex/mocha-parallel-tests))은 테스트를 여러 프로세스로 병렬화하여 피드백 시간을 크게 개선할 수 있습니다. 일부 CI 벤더는 테스트를 컨테이너 간 병렬화하여(!) 피드백 시간을 더욱 단축시킵니다. 각각 다른 프로세스에서 테스트를 실행(로컬에서 여러 프로세스로 혹은 여러 머신을 사용하는 일부 클라우드 CLI를 통해)하여 테스트를 자동화 하십시오.
+
+❌ **그렇지 않으면:** 새 코드를 푸쉬하고 이미 다음 기능을 코딩 할 때, (한 시간 후에) 테스트 결과를 얻는 것은 테스트의 관련성을 떨어뜨리기 위한 훌륭한 방법입니다.
+
+
+
+✏ 예제 코드
+
+
+
+### :clap: 올바른 예: Mocha Parallel과 Jest 는 테스트 병렬화 덕분에 덕분에 기존의 Mocha를 쉽게 능가합니다. ([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: 올바른 예:
+
+```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의 모든 빌드에서 사용할 수 있습니다.
+
+
+
+❌ **그렇지 않으면:** 전용 도구 없이 코드를 취약점으로부터 안전하게 유지하려면 새로운 위협에 대한 최신 소식을 지속적으로 쫓아야 해서 매우 지루합니다.
+
+
+
+✏ 예제 코드
+
+
+
+### :clap: 올바른 예: NPM Audit 결과
+
+
+
+
+
+
+
+## ⚪ ️ 5.7 종속성 업데이트 자동화
+
+:white_check_mark: **이렇게 해라:** yarn과 npm이 최근 소개한 package-lock.json는 중대한 도전입니다(지옥으로 가는 길은 좋은 의도로 포장되어 있습니다). 기본적으로 이제 패키지는 더이상 업데이트되지 않습니다. 'npm install'과 'npm update'을 통해 새로운 배포를 많이 수행하는 팀도 새로운 업데이트를 받지 않습니다. 이로 인해 하위 종속 패키지 버전이 최상이거나, 최악의 경우에는 취약한 코드가 됩니다. 팀은 이제 패키지를 수동으로 업데이트하기 위해 개발자의 의지와 기억에 의존하거나 [ncu](https://www.npmjs.com/package/npm-check-updates)같은 도구를 수동으로 사용하게 됩니다. 보다 안정적인 방법은 가장 안정적인 종속성 버전을 얻는 프로세스를 자동화하는 것입니다. 묘책은 없지만 가능한 두가지 자동화 방법이 있습니다:
+
+(1) CI는 ['npm outdated'](https://docs.npmjs.com/cli/outdated) 또는 'npm-check-updates(ncu)'같은 툴을 사용하여 오래된 종속성을 가진 빌드를 실패하게 할 수 있습니다. 그렇게하면 개발자가 종속성 업데이트를 해야합니다.
+
+(2) 코드를 스캔하고 업데이트된 종속성으로 PR을 자동으로 보내주는 상용 도구를 사용하십시오. 남아있는 흥미로운 질문 하나는 종속성 업데이트 정책입니다. 모든 패치에서 업데이트를 하면 너무 많은 오버헤드가 발생합니다. 메이저 버전이 공개될 때 바로 업데이트 하면 불안정한 버전을 가리킬 수 있습니다.(많은 패키지가 출시된 직후 첫 날에 취약한 것으로 밝혀 짐 [esline-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 기타, 노드와 관련없는 CI 팁
+
+:white_check_mark: **이렇게 해라:** 이 글은 Node.js와 관련이 있거나 최소한 Node.js로 예를 들 수 있는 테스트 조언에 중점을 두고 있습니다. 그러나 이번에는 Node.js와 관련없지만 잘 알려진 팁 몇개를 그룹화 하였습니다.
+
+1. 선언적 구문을 사용하십시오. 대부분의 벤더에서는 선택할 수 없지만, 이전 버전의 Jenkins에서 코드 또는 UI를 사용할 수 있습니다.
+2. 고유 Docker를 지원하는 벤더를 선택하십시오.
+3. 일찍 실패하고 가장 빠른 테스트를 먼저 실행하십시오. 다양한 빠른 Inspection(예: 린트, 단위테스트)를 그룹화 하고 코드 커미터에 대한 신속한 피드백을 제공할 수 있는 [스모크 테스트](https://terms.naver.com/entry.nhn?docId=864587&cid=42346&categoryId=42346) 단계/마일스톤을 만드십시오.
+4. 테스트 보고서, 커버리지, 변화, 로그 등의 모든 결과물을 훑어보기 쉽게 하십시오.
+5. 각 이벤트에 대해 여러 파이프라인/작업을 작성하고, 그 사이 단계를 재사용 하십시오. 예를 들면, feature 브랜치 커밋이나 마스터 PR에 대한 작업 구성. 각 재사용 로직이 공유 단계를 사용하게 하십시오.(대부분의 벤더는 코드 재사용을 위한 메커니즘을 제공합니다)
+6. 작업 선언에 어떤한것도 숨겨놓지 마십시오.
+7. 릴리스 빌드에서 명시적으로 버전을 충돌 시켜보거나 최소한 개발자가 그렇게했는지 확인하십시오.
+8. 한번만 빌드하고 단일 빌드 결과물(예: Docker 이미지)에 대해 모든 검사를 수행하십시오.
+9. 빌드간에 상태가 변하지 않는 임시 환경에서 테스트하십시오. node_modules 캐싱은 유일한 예외 일 수 있습니다.
+
+
+
+❌ **그렇지 않으면:** 수년간의 노하우를 놓치는 것과 같습니다.
+
+
+
+## ⚪ ️ 5.9 빌드 매트릭스: 여러 노드 버전을 사용해서 동일한 CI 단계를 실행 하십시오.
+
+:white_check_mark: **이렇게 해라:** 품질 검사는 [세런디피티](https://ko.wikipedia.org/wiki/%EC%84%B8%EB%9F%B0%EB%94%94%ED%94%BC%ED%8B%B0)에 관한 것으로, 문제를 조기에 발견하는데 도움이 되는 더 많은 기회를 제공합니다. 재사용 가능한 패키지를 개발하거나 다양한 구성 및 노드 버전으로 여러 고객의 제품을 실행하는 경우, CI는 모든 구성의 순열에 대해 테스트 파이프 라인을 실행해야합니다. 예를 들어, 일부 고객은 MySQL을 사용하고 다른 고객은 PostgreSQL을 사용한다고 가정합시다. 일부 CI 벤더는 '매트릭스'라는 기능을 제공하여 MySQL, PostgreSQL 및 8, 9, 10과 같은 여러 노드 버전의 모든 순열에 대해 테스트를 실행할 수 있습니다. 이 경우에는 어떠한 추가 노력없이 구성(설정)만을 사용하여 가능합니다(테스트 또는 기타 품질 검사가 있다고 가정). 매트릭스를 지원하지 않는 다른 CI는 확장이나 조정이 필요할 수 있습니다.
+
+
+
+❌ **그렇지 않으면:** So after doing all that hard work of writing testing are we going to let bugs sneak in only because of configuration issues?
+
+
+
+
+✏ 예제 코드
+
+
+
+### :clap: 올바른 예: Travis(CI 벤더) 빌드 정의를 사용하여 여러 노드 버전에 대한 동일한 테스트를 실행하십시오.
+
+language: node_js
node_js:
- "7"
- "6"
- "5"
- "4"
install:
- npm install
script:
- npm run test
+
+
+
+
+# 팀
+
+## Yoni Goldberg
+
+
+
+
+
+
+
+**Role:** 저자
+
+**About:** 저는 포춘 500대 기업 및 스타트업과 함께 JS 및 Node.js 어플리케이션을 개발하는 독립 컨설턴트입니다. 다른 어떤 주제보다 더 흥미를 끄는 테스트 기술을 습득하는 것을 목표로 합니다. 또한 [Node.js Best Practices](https://github.com/goldbergyoni/nodebestpractices)의 저자이기도 합니다.
+
+
+
+**Workshop:** 👨🏫 이러한 모든 프랙티스와 기술을 배우고 싶습니까?(유럽 & 미국) [테스트 워크샵에 등록하십시오](https://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:** 기술 검토 및 고문
+
+모든 텍스트를 수정, 개선, lint 및 다듬었습니다.
+
+**About:** 풀 스택 웹 엔지니어, Node.js 및 GraphQL의 열렬한 지지자
+
+
+
+
+
+## [Ido Richter](https://github.com/idori)
+
+**Role:** 컨셉, 디자인 및 훌륭한 조언
+
+**About:** 정통한 프론트 엔드 개발자, CSS 전문가 및 이모티콘에 관심이 많은 사람
diff --git a/readme.md b/readme.md
index 85a65646..211643f3 100644
--- a/readme.md
+++ b/readme.md
@@ -1,93 +1,102 @@
+
-# 👇 Why this guide can take your testing skills to the next level
+# 👇 Why this guide can take your testing skills to the next level
-## 📗 45+ 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
+## 📗 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
## 🚢 Advanced: Goes 10,000 miles beyond the basics
-Hop into a journey that travells 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
-* A JavaScript & Node.js consultant
-* [My testing workshop](https://www.testjavascript.com) - Learn all these testing practices and techniques live
-* [Follow me on Twitter ](https://twitter.com/goldbergyoni/)
-* Come hear me speak at [LA](https://js.la/), [Verona](https://2019.nodejsday.it/), [Kharkiv](https://kharkivjs.org/), [free webinar](https://zoom.us/webinar/register/1015657064375/WN_Lzvnuv4oQJOYey2jXNqX6A). Future events TBD
-* [My JavaScript Quality newsletter](https://testjavascript.com/newsletter/) - insights and content only on strategic matters
+### Written By Yoni Goldberg - A JavaScript & Node.js consultant
+### 👨🏫 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/)
-
-
-# `Table of contents`
+
-* ### `Section 0: The Golden Rule`
+### Translations - read in your own language
-A single advice that inspires all the others (1 special bullet)
+- 🇨🇳[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)
+- 🇯🇵[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)
-* ### `Section 1: The Test Anatomy`
+- Want to translate to your own language? please open an issue 💜
-The foundation - strucuturing clean tests (12 bullets)
+
+## `Table of Contents`
-* ### `Section 2: Backend`
+#### [`Section 0: The Golden Rule`](#section-0️⃣-the-golden-rule)
-Writing backend and Microservices tests efficiently (8 bullets)
+A single advice that inspires all the others (1 special bullet)
+#### [`Section 1: The Test Anatomy`](#section-1-the-test-anatomy-1)
-* ### `Section 3: Frontend, UI, E2E`
+The foundation - structuring clean tests (12 bullets)
-Writing tests for web UI including component and E2E tests (11 bullets)
+#### [`Section 2: Backend`](#section-2️⃣-backend-testing)
+Writing backend and Microservices tests efficiently (13 bullets)
-* ### `Section 4: Measuring Tests Effectivenss`
+#### [`Section 3: Frontend`](#section-3️⃣-frontend-testing)
-Watching the watchman - measuring test quality (4 bullets)
+Writing tests for web UI including component and E2E tests (11 bullets)
+#### [`Section 4: Measuring Tests Effectiveness`](#section-4️⃣-measuring-test-effectiveness)
-* ### `Section 5: Continous Integration`
+Watching the watchman - measuring test quality (4 bullets)
-Guideliness for CI in the JS world (9 bullets)
+#### [`Section 5: Continuous Integration`](#section-5️⃣-ci-and-other-quality-measures)
+Guidelines for CI in the JS world (9 bullets)
-
-# Section 0️⃣ : The Golden Rule
+# Section 0️⃣: The Golden Rule
-## ⚪️ 0. The Golden Rule: Design for lean testing
+## ⚪️ 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.
+:white_check_mark: **Do:**
+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.
-
-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 we have two brain systems: system 1 which 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).
+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.
-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.
+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, and strive to keep it nimble, sometimes it's even worth dropping some tests and trading reliability for agility and simplicity.

-
+
Most of the advice below are derivatives of this principle.
### Ready to start?
-
-# Section 1. The Test Anatomy
+# Section 1: The Test Anatomy
@@ -103,20 +112,20 @@ Most of the advice below are derivatives of this principle.
-
❌ **Otherwise:** A deployment just failed, a test named “Add product” failed. Does this tell you what exactly is malfunctioning?
**👇 Note:** Each bullet has code examples and sometime also an image illustration. Click to expand
+
+
✏ Code Examples
### :clap: Doing It Right Example: A test name that constitutes 3 parts
-
+
```javascript
//1. unit under test
@@ -131,30 +140,35 @@ describe('Products Service', function() {
});
```
+
### :clap: Doing It Right Example: A test name that constitutes 3 parts
+

+
+© Credits & read-more
+ 1. Roy Osherove - Naming standards for unit tests
+
+
## ⚪ ️ 1.2 Structure tests by the AAA pattern
-: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:
+: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
3rd A - Assert: Ensure that the received value satisfies the expectation. Usually 1 line of code
-
-
-❌ **Otherwise:** Not only you spend long daily hours on understanding the main code, now also what should have been the simple 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
@@ -162,176 +176,154 @@ describe('Products Service', function() {
-### :clap: Doing It Right Example: A test strcutured with the AAA pattern
+### :clap: Doing It Right Example: A test structured with the AAA pattern
+
+ 
- 
-
```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');
- });
+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: Anti Pattern Example: No separation, one bulk, harder to interpret
+### :thumbsdown: Anti-Pattern Example: No separation, one bulk, harder to interpret
```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');
- });
+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 Describe expectations in a product language: use BDD-style assertions
-:white_check_mark: **Do:** Coding your tests in a declarative-style allows the reader to get the grab instantly without spending even a single brain-CPU cycle. When you write an imperative code that is packed with conditional logic the reader is thrown away to an effortful mental mood. In that sense, code the expectation in a human-like language, declarative BDD style using expect or should and not using custom code. If Chai & Jest don’t include the desired assertion and it’s highly repeatable, consider [extending Jest matcher (Jest)](https://jestjs.io/docs/en/expect#expectextendmatchers) or writing a [custom Chai plugin](https://www.chaijs.com/guide/plugins/)
+:white_check_mark: **Do:** Coding your tests in a declarative-style allows the reader to get the grab instantly without spending even a single brain-CPU cycle. When you write imperative code that is packed with conditional logic, the reader is forced to exert more brain-CPU cycles. In that case, code the expectation in a human-like language, declarative BDD style using `expect` or `should` and not using custom code. If Chai & Jest doesn't include the desired assertion and it’s highly repeatable, consider [extending Jest matcher (Jest)](https://jestjs.io/docs/en/expect#expectextendmatchers) or writing a [custom Chai plugin](https://www.chaijs.com/guide/plugins/)
-
-❌ **Otherwise:** The team will write less test and decorate the annoying ones with .skip()
+❌ **Otherwise:** The team will write less tests and decorate the annoying ones with .skip()
✏ Code Examples
- 
-
- ### :thumbsdown: Anti Pattern Example: The reader must skim through not so short, and imperative code just to get the test story
+ 
+
+### :thumbsdown: Anti-Pattern Example: The reader must skim through not so short, and imperative code just to get the test story
```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});
-
- const 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;
- }
- });
+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;
- if(!admin1Found || !admin2Found ){
- throw new Error("Not all admins were returned");
+ 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: Doing It Right Example: Skimming through the following declarative test is a breeze
-
```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});
+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"]);
+ expect(allAdmins)
+ .to.include.ordered.members(["admin1", "admin2"])
+ .but.not.include.ordered.members(["user1"]);
});
-
```
-
+## ⚪ ️ 1.4 Stick to black-box testing: Test only public methods
-## ⚪ ️ 1.4 Stick to black-box testing: Test only public methods
-
-:white_check_mark: **Do:** Testing the internals brings huge overhead for almost nothing. If your code/API deliver the right results, should you really invest your next 3 hours in testing HOW it worked internally and then maintain these fragile tests? Whenever a public behavior is checked, the private implementation is also implicitly tested and your tests will break only if there is a certain problem (e.g. wrong output). This approach is also referred to as behavioral testing. On the other side, should you test the internals (white box approach) — your focus shifts from planning the component outcome to nitty-gritty details and your test might break because of minor code refactors although the results are fine- this dramatically increases the maintenance burden
+:white_check_mark: **Do:** Testing the internals brings huge overhead for almost nothing. If your code/API delivers the right results, should you really invest your next 3 hours in testing HOW it worked internally and then maintain these fragile tests? Whenever a public behavior is checked, the private implementation is also implicitly tested and your tests will break only if there is a certain problem (e.g. wrong output). This approach is also referred to as `behavioral testing`. On the other side, should you test the internals (white box approach) — your focus shifts from planning the component outcome to nitty-gritty details and your test might break because of minor code refactors although the results are fine - this dramatically increases the maintenance burden
-
-❌ **Otherwise:** Your test behaves like the [child who cries wolf](https://en.wikipedia.org/wiki/The_Boy_Who_Cried_Wolf): shoot out loud false-positive cries (e.g., A test fails because a private variable name was changed). Unsurprisingly, people will soon start to ignore the CI notifications until someday a real bug will get ignored…
+❌ **Otherwise:** Your tests behave like the [boy who cried wolf](https://en.wikipedia.org/wiki/The_Boy_Who_Cried_Wolf): shouting false-positive cries (e.g., A test fails because a private variable name was changed). Unsurprisingly, people will soon start to ignore the CI notifications until someday, a real bug gets ignored…
✏ Code Examples
-### :thumbsdown: Anti Pattern Example: A test case is testing the internals for no good reason
-
+### :thumbsdown: Anti-Pattern Example: A test case is testing the internals for no good reason
+
+
+
```javascript
-class ProductService{
+class ProductService {
//this method is only used internally
//Change this name will make the tests fail
- calculateVAT(priceWithoutVAT){
- return {finalPrice: priceWithoutVAT * 1.2};
+ 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;
+ getPrice(productId) {
+ const desiredProduct = DB.getProduct(productId);
+ const 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);
+ //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 Choose the right test doubles: Avoid mocks in favor of stubs and spies
-:white_check_mark: **Do:** Test doubles are a necessary evil because they are coupled to the application internals, yet some provide an immense value ([Read here a reminder about test doubles: mocks vs stubs vs spies](https://martinfowler.com/articles/mocksArentStubs.html)). However, the various techniques were not born equal: some of them, spies and stubs, are focused on testing the requirements but as an inevitable side-effect they also slightly touch the internals. Mocks, on the contrary side, are focused on testing the internals — this brings huge overhead as explained in the bullet “Stick to black box testing”.
-
-However, the various techniques were not born equal: some of them, spies and stubs, are focused on testing the requirements but as an inevitable side-effect they also slightly touch the internals. Mocks, on the contrary side, are focused on testing the internals — this brings huge overhead as explained in the bullet “Stick to black box testing”.
+: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 smell of white-box testing.
+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 what 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 got nothing with the application functionality and are likely to change frequently
+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
-
❌ **Otherwise:** Any refactoring of code mandates searching for all the mocks in the code and updating accordingly. Tests become a burden rather than a helpful friend
@@ -341,45 +333,53 @@ For example, if you want to test what your app behaves reasonably when the payme
### :thumbsdown: Anti-pattern example: Mocks focus on the internals
-
+
+
+
```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);
- mock.verify();
+ //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:Doing It Right Example: spies are focused on testing the requirements but as a side-effect are unavoidably touching to the internals
```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)
+ //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;
});
```
+
+## 📗 Want to learn all these practices with live video?
+
+### Visit my online course [Testing Node.js & JavaScript From A To Z](https://www.testjavascript.com)
-## ⚪ ️1.6 Don’t “foo”, use realistic input dataing
+## ⚪ ️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 instead) 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 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 seem 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”
-
+❌ **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”
@@ -389,104 +389,92 @@ it("When a valid product is about to be deleted, ensure an email is sent", async
### :thumbsdown: Anti-Pattern Example: A test suite that passes due to non-realistic data
-
-
-
+
+
```javascript
-const addProduct = (name, price) =>{
- const productNameRegexNoSpace = /^\S*$/;//no white-space allowd
+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
+ if (!productNameRegexNoSpace.test(name)) return false; //this path never reached due to dull input
- //some logic here
- return true;
+ //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
+ //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:Doing It Right Example: Randomizing realistic input
+
```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!
+ 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 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
-
✏ Code Examples
-### :clap: Doing It Right Example: Testing many input permutations with “mocha-testcheck”
+### :clap: Doing It Right Example: Testing many input permutations with “fast-check”
-
+
```javascript
-require('mocha-testcheck').install();
-const {expect} = require('chai');
-const faker = require('faker');
+import fc from "fast-check";
-describe('Product service', () => {
- describe('Adding new', () => {
+describe("Product service", () => {
+ describe("Adding new", () => {
//this will run 100 times with different random properties
- check.it('Add new product with random yet valid properties, always successful',
- gen.int, gen.string, (id, name) => {
- expect(addNewProduct(id, name).status).to.equal('approved');
- });
- })
+ 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 If needed, use only short & inline snapshots
: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...
@@ -496,42 +484,41 @@ It’s worth noting that there are few cases where long & external snapshots are
### :thumbsdown: Anti-Pattern Example: Coupling our test to unseen 2000 lines of code
-
-
-```javascript
-it('TestJavaScript.com is renderd correctly', () => {
-
-//Arrange
-
-//Act
-const receivedPage = renderer
-.create( Test JavaScript < /DisplayPage>)
-.toJSON();
-
-//Assert
-expect(receivedPage).toMatchSnapshot();
-//We now implicitly maintain a 2000 lines long document
-//every additional line break or comment - will break this test
+
+```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: Doing It Right Example: Expectations are visible and focused
+
```javascript
-it('When visiting TestJavaScript.com home page, a menu is displayed', () => {
-//Arrange
+it("When visiting TestJavaScript.com home page, a menu is displayed", () => {
+ //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
@@ -543,17 +530,14 @@ expect(menu).toMatchInlineSnapshot(`
-
-## ⚪ ️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
@@ -561,59 +545,55 @@ expect(menu).toMatchInlineSnapshot(`
-### :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(() => {
- //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");
- expect(updateNameResult).to.be(true);
-});
-```
+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});
-
+ // 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
-:white_check_mark: **Do:** When trying to assert that some input triggers an error, it might look right to use try-catch-finally and asserts that the catch clause was entered. The result is an awkward and verbose test case (example below) that hides the simple test intent and the result expectations
+
+:white_check_mark: **Do:** When trying to assert that some input triggers an error, it might look right to use try-catch-finally and asserts that the catch clause was entered. The result is an awkward and verbose test case (example below) that hides the simple test intent and the result expectations
A more elegant alternative is the using the one-line dedicated Chai assertion: expect(method).to.throw (or in Jest: expect(method).toThrow()). It’s absolutely mandatory to also ensure the exception contains a property that tells the error type, otherwise given just a generic error the application won’t be able to do much rather than show a disappointing message to the user
-
-❌ **Otherwise:**It will be challenging to infer from the test reports (e.g. CI reports) what went wrong
-
+❌ **Otherwise:** It will be challenging to infer from the test reports (e.g. CI reports) what went wrong
@@ -623,51 +603,46 @@ A more elegant alternative is the using the one-line dedicated Chai assertion: e
### :thumbsdown: Anti-pattern Example: A long test case that tries to assert the existence of error with try-catch
-
-
+
+
```javascript
-/it("When no product name, it throws error 400", async() => {
-let errorWeExceptFor = null;
-try {
- const result = await addNewProduct({name:'nest'});}
-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
+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: Doing It Right Example: A human-readable expectation that could be understood easily, maybe even by QA or technical PM
```javascript
-it.only("When no product name, it throws error 400", async() => {
- expect(addNewProduct)).to.eventually.throw(AppError).with.property('code', "InvalidInput");
+it("When no product name, it throws error 400", async () => {
+ await expect(addNewProduct({}))
+ .to.eventually.throw(AppError)
+ .with.property("code", "InvalidInput");
});
-
```
-
-
-
## ⚪ ️ 1.11 Tag your tests
-:white_check_mark: **Do:** Different tests must run on different scenarios: quick smoke, IO-less, tests should run when a developer saves or commits a file, full end-to-end tests usually run when a new pull request is submitted, etc. This can be achieved by tagging tests with keywords like #cold #api #sanity so you can grep with your testing harness and invoke the desired subset. For example, this is how you would invoke only the sanity test group with Mocha: mocha — grep ‘sanity’
+:white_check_mark: **Do:** Different tests must run on different scenarios: quick smoke, IO-less, tests should run when a developer saves or commits a file, full end-to-end tests usually run when a new pull request is submitted, etc. This can be achieved by tagging tests with keywords like #cold #api #sanity so you can grep with your testing harness and invoke the desired subset. For example, this is how you would invoke only the sanity test group with Mocha: mocha — grep ‘sanity’
-
❌ **Otherwise:** Running all the tests, including tests that perform dozens of DB queries, any time a developer makes a small change can be extremely slow and keeps developers away from running tests
-
✏ Code Examples
@@ -676,66 +651,115 @@ it.only("When no product name, it throws error 400", async() => {
### :clap: Doing It Right Example: Tagging tests as ‘#cold-test’ allows the test runner to execute only fast tests (Cold===quick tests that are doing no IO and can be executed frequently even as the developer is typing)
-
+
+
```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. Excpectation - Use the default currency #sanity', function() {
+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 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 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 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
+
+
+✏ Code Examples
+
+
+
+### :clap: Doing It Right Example: Structuring suite with the name of unit under test and scenarios will lead to the convenient report that is shown below
+
+
+
+```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: Anti-pattern Example: A flat list of tests will make it harder for the reader to identify the user stories and correlate failing tests
+
-
+```javascript
+test("Then the response status should decline", () => {});
-## ⚪ ️1.12 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
+test("Then it should send email", () => {});
+
+test("Then there should not be a new transfer record", () => {});
+```
+
+
-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 satsifies the test - then refactor gradually and take it to a prdoction grade level, avoid any dependency on the environment (paths, OS, etc)
+
+
+
+
+## ⚪ ️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 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, 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
-
-# Section 2️⃣ : Backend Testing
+# Section 2️⃣: Backend Testing
## ⚪ ️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 :]
-
❌ **Otherwise:** You’re going to miss some tools with amazing ROI, some like Fuzz, lint, and mutation can provide value in 10 minutes
-
✏ Code Examples
-### :clap: Doing It Right Example: Cindy Sridharan suggests a rich testing portfolio in her amazing post ‘Testing Microservices — the sane way’
+### :clap: Doing It Right Example: Cindy Sridharan suggests a rich testing portfolio in her amazing 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)
@@ -744,24 +768,21 @@ 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, 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.
-
+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)
-❌ **Otherwise:** You may spend long days on writing unit tests to find out that you got only 20% system coverage
+
+❌ **Otherwise:** You may spend long days on writing unit tests to find out that you got only 20% system coverage
@@ -771,8 +792,7 @@ Component tests focus on the Microservice ‘unit’, they work against the API,
### :clap: Doing It Right Example: Supertest allows approaching Express API in-process (fast and cover many layers)
-
+
 allows approaching Express API in-process (fast and cover many layers)")
@@ -780,15 +800,13 @@ Component tests focus on the Microservice ‘unit’, they work against the API,
-## ⚪ ️2.3 Ensure new releases don’t break the API using
+## ⚪ ️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
-
✏ Code Examples
@@ -797,28 +815,21 @@ Component tests focus on the Microservice ‘unit’, they work against the API,
### :clap: Doing It Right Example:
-
-
-
+
+
-
-
-
## ⚪ ️ 2.4 Test your middlewares in isolation
:white_check_mark: **Do:** Many avoid Middleware testing because they represent a small portion of the system and require a live Express server. Both reasons are wrong — Middlewares are small but affect all or most of the requests and can be tested easily as pure functions that get {req,res} JS objects. To test a middleware function one should just invoke it and spy ([using Sinon for example](https://www.npmjs.com/package/sinon)) on the interaction with the {req,res} objects to ensure the function performed the right action. The library [node-mock-http](https://www.npmjs.com/package/node-mocks-http) takes it even further and factors the {req,res} objects along with spying on their behavior. For example, it can assert whether the http status that was set on the res object matches the expectation (See example below)
-
❌ **Otherwise:** A bug in Express middleware === a bug in all or most requests
-
✏ Code Examples
@@ -827,46 +838,84 @@ Component tests focus on the Microservice ‘unit’, they work against the API,
### :clap:Doing It Right Example: Testing middleware in isolation without issuing network calls and waking-up the entire Express machine
-
+
```javascript
//the middleware we want to test
-const unitUnderTest = require('./middleware')
-const httpMocks = require('node-mocks-http');
+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', () => {
+test("A request without authentication header, should return http status 403", () => {
const request = httpMocks.createRequest({
- method: 'GET',
- url: '/user/42',
+ method: "GET",
+ url: "/user/42",
headers: {
- authentication: ''
+ authentication: ""
}
});
const response = httpMocks.createResponse();
unitUnderTest(request, response);
expect(response.statusCode).toBe(403);
});
-
```
+
+
+## ⚪ ️2.5 Measure and refactor using static analysis tools
+
+:white_check_mark: **Do:** Using static analysis tools helps by giving objective ways to improve code quality and keep your code maintainable. You can add static analysis tools to your CI build to abort when it finds code smells. Its main selling points over plain linting are the ability to inspect quality in the context of multiple files (e.g. detect duplications), perform advanced analysis (e.g. code complexity) and follow the history and progress of code issues. Two examples of tools you can use are [SonarQube](https://www.sonarqube.org/) (4,900+ [stars](https://github.com/SonarSource/sonarqube)) and [Code Climate](https://codeclimate.com/) (2,000+ [stars](https://github.com/codeclimate/codeclimate))
+
+Credit: [Keith Holliday](https://github.com/TheHollidayInn)
+
+
+
+❌ **Otherwise:** With poor code quality, bugs and performance will always be an issue that no shiny new library or state of the art features can fix
+
+
+
+✏ Code Examples
+
+
+### :clap: Doing It Right Example: CodeClimate, a commercial tool that can identify complex methods:
+
+
+
+
+
-## ⚪ ️2.5 Measure and refactor using static analysis tools
-:white_check_mark: **Do:** Using static analysis tools helps by giving objective ways to improve code quality and keep your code maintainable. You can add static analysis tools to your CI build to abort when it finds code smells. Its main selling points over plain linting are the ability to inspect quality in the context of multiple files (e.g. detect duplications), perform advanced analysis (e.g. code complexity) and follow the history and progress of code issues. Two examples of tools you can use are [Sonarqube](https://www.sonarqube.org/) (2,600+ [stars](https://github.com/SonarSource/sonarqube)) and [Code Climate](https://codeclimate.com/) (1,500+ [stars](https://github.com/codeclimate/codeclimate))
+## ⚪ ️ 2.6 Check your readiness for Node-related chaos
-Credit:: [Keith Holliday](https://github.com/TheHollidayInn)
+:white_check_mark: **Do:** Weirdly, most software testings are about logic & data only, but some of the worst things that happen (and are really hard to mitigate) are infrastructural issues. For example, did you ever test what happens when your process memory is overloaded, or when the server/process dies, or does your monitoring system realizes when the API becomes 50% slower?. To test and mitigate these type of bad things — [Chaos engineering](https://principlesofchaos.org/) was born by Netflix. It aims to provide awareness, frameworks and tools for testing our app resiliency for chaotic issues. For example, one of its famous tools, [the chaos monkey](https://github.com/Netflix/chaosmonkey), randomly kills servers to ensure that our service can still serve users and not relying on a single server (there is also a Kubernetes version, [kube-monkey](https://github.com/asobti/kube-monkey), that kills pods). All these tools work on the hosting/platform level, but what if you wish to test and generate pure Node chaos like check how your Node process copes with uncaught errors, unhandled promise rejection, v8 memory overloaded with the max allowed of 1.7GB or whether your UX remains satisfactory when the event loop gets blocked often? to address this I’ve written, [node-chaos](https://github.com/i0natan/node-chaos-monkey) (alpha) which provides all sort of Node-related chaotic acts
+
+
+❌ **Otherwise:** No escape here, Murphy’s law will hit your production without mercy
+✏ Code Examples
-❌ **Otherwise:** With poor code quality, bugs and performance will always be an issue that no shiny new library or state of the art features can fix
+
+
+### :clap: Doing It Right Example: : Node-chaos can generate all sort of Node.js pranks so you can test how resilience is your app to chaos
+
+
+
+
+
+
+
+## ⚪ ️2.7 Avoid global test fixtures and seeds, add data per-test
+: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’) 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)
+
+
+❌ **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
@@ -874,27 +923,116 @@ Credit:: {
+ //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: Doing It Right Example: We can stay within the test, each test acts on its own set of data
-
-
-
+```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);
+});
+```
+
+
+## ⚪ ️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.
-
-## ⚪ ️ 2.6 Check your readiness for Node-related chaos
-:white_check_mark: **Do:** Weirdly, most software testings are about logic & data only, but some of the worst things that happen (and are really hard to mitigate ) are infrastructural issues. For example, did you ever test what happens when your process memory is overloaded, or when the server/process dies, or does your monitoring system realizes when the API becomes 50% slower?. To test and mitigate these type of bad things — [Chaos engineering](https://principlesofchaos.org/) was born by Netflix. It aims to provide awareness, frameworks and tools for testing our app resiliency for chaotic issues. For example, one of its famous tools, [the chaos monkey](https://github.com/Netflix/chaosmonkey), randomly kills servers to ensure that our service can still serve users and not relying on a single server (there is also a Kubernetes version, [kube-monkey](https://github.com/asobti/kube-monkey), that kills pods). All these tools work on the hosting/platform level, but what if you wish to test and generate pure Node chaos like check how your Node process copes with uncaught errors, unhandled promise rejection, v8 memory overloaded with the max allowed of 1.7GB or whether your UX stays satisfactory when the event loop gets blocked often? to address this I’ve written, [node-chaos](https://github.com/i0natan/node-chaos-monkey) (alpha) which provides all sort of Node-related chaotic acts
-
-❌ **Otherwise:** No escape here, Murphy’s law will hit your production without mercy
-
+❌ **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
@@ -902,21 +1040,34 @@ Credit:: {
+ // ...
+ //Assert
+ expect(receivedAPIResponse).toMatchObject({
+ status: 200,
+ data: {
+ id: expect.any(Number), // Any number satisfies this test
+ mode: 'approved',
+ },
+ });
+});
+```
-## ⚪ ️2.7 Avoid global test fixtures and seeds, add data per-test
+## ⚪ ️2.11 Check integrations corner cases and chaos
-: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’) 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:** 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:** 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:** All your tests pass, it's only the production who will crash or won't report errors correctly when 3rd parties send exceptional responses
@@ -924,61 +1075,64 @@ Credit:: {
- //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 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);
});
-
```
+
+
+
-### :clap: Doing It Right Example: We can stay within the test, each test acts on its own set of data
-```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);
-});
+## ⚪ ️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
-## ⚪ ️ 3.1. Separate UI from functionality
+## ⚪ ️ 3.1 Separate UI from functionality
:white_check_mark: **Do:** When focusing on testing component logic, UI details become a noise that should be extracted, so your tests can focus on pure data. Practically, extract the desired data from the markup in an abstract way that is not too coupled to the graphic implementation, assert only on pure data (vs HTML/CSS graphic details) and disable animations that slow down. You might get tempted to avoid rendering and test only the back part of the UI (e.g. services, actions, store) but this will result in fictional tests that don't resemble the reality and won't reveal cases where the right data doesn't even arrive in the UI
-
❌ **Otherwise:** The pure calculated data of your test might be ready in 10ms, but then the whole test will last 500ms (100 tests = 1 min) due to some fancy and irrelevant animation
-
✏ Code Examples
@@ -987,57 +1141,44 @@ it("When updating site name, get successful confirmation", async () => {
### :clap: Doing It Right Example: Separating out the UI details
- 
+ 
```javascript
-test('When users-list is flagged to show only VIP, should display only VIP members', () => {
+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 }
- ];
+ const allUsers = [{ id: 1, name: "Yoni Goldberg", vip: false }, { id: 2, name: "John Doe", vip: true }];
// Act
- const { getAllByTestId } = render();
+ 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);
+ 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: Anti Pattern Example: Assertion mix UI details and data
+### :thumbsdown: Anti-Pattern Example: Assertion mix UI details and data
+
```javascript
-test('When flagging to show only VIP, should display only VIP members', () => {
+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 }
- ];
+ const allUsers = [{ id: 1, name: "Yoni Goldberg", vip: false }, { id: 2, name: "John Doe", vip: true }];
// Act
- const { getAllByTestId } = render();
+ const { getAllByTestId } = render();
// Assert - Mix UI & data in assertion
- expect(getAllByTestId('user')).toEqual('[- John Doe
]');
+ expect(getAllByTestId("user")).toEqual('[- John Doe
]');
});
-
```
-
-
-
-
## ⚪ ️ 3.2 Query HTML elements based on attributes that are unlikely to change
:white_check_mark: **Do:** Query HTML elements based on attributes that are likely to survive graphic changes unlike CSS selectors and like form labels. If the designated element doesn't have such attributes, create a dedicated test attribute like 'test-id-submit-button'. Going this route not only ensures that your functional/logic tests never break because of look & feel changes but also it becomes clear to the entire team that this element and attribute are utilized by tests and shouldn't get removed
@@ -1052,62 +1193,59 @@ test('When flagging to show only VIP, should display only VIP members', () => {
-### :clap: Doing It Right Example: Querying an element using a dedicated attrbiute for testing
+### :clap: Doing It Right Example: Querying an element using a dedicated attribute for testing
+
+
-
-
-```html
+```jsx
// the markup code (part of React component)
- {value}
+ {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");
- });
+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: Anti-Pattern Example: Relying on CSS attributes
-```html
+
+```jsx
-{value}
+{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");
- });
-```
+test("Whenever no data is passed, error metric shows zero", () => {
+ // ...
+ expect(wrapper.find("[className='d-flex-column']").text()).toBe("0");
+});
+```
-
-
-
## ⚪ ️ 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
@@ -1115,80 +1253,75 @@ With all that said, a word of caution is in order: this technique works for smal
❌ **Otherwise:** When poking into a component's internal by invoking its private methods, and checking the inner state - you would have to refactor all tests when refactoring the components implementation. Do you really have a capacity for this level of maintenance?
-
✏ Code Examples
-### :clap: Doing It Right Example: Working realstically with a fully rendered component
+### :clap: Doing It Right Example: Working realistically with a fully rendered component
+
+ 
- 
-
```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('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
-})
+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: Anti-Pattern Example: Mocking the reality with 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?
-})
+```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 Don't sleep, use frameworks built-in support for async events. Also try to speed things up
-:white_check_mark: **Do:** In many cases, the unit under test completion time is just unknown (e.g. animation suspends element appearance) - in that case, avoid sleeping (e.g. setTimeOut) and prefer more deterministic methods that most platforms provide. Some libraries allows awaiting on operations (e.g. [Cypress cy.request('url')](https://docs.cypress.io/guides/references/best-practices.html#Unnecessary-Waiting)), other provide API for waiting like [@testing-library/dom method wait(expect(element))](https://testing-library.com/docs/guide-disappearance). Sometimes a more elegant way is to stub the slow resource, like API for example, and then once the response moment becomes deterministic the component can be explicitly re-rendered. When depending upon some external component that sleeps, it might turn useful to [hurry-up the clock](https://jestjs.io/docs/en/timer-mocks). Sleeping is a pattern to avoid because it forces your test to be slow or risky (when waiting for a too short period). Whenever sleeping and polling is inevitable and there's no support from the testing framework, some npm libraries like [wait-for-expect](https://www.npmjs.com/package/wait-for-expect) can help with a semi-deterministic solution
+:white_check_mark: **Do:** In many cases, the unit under test completion time is just unknown (e.g. animation suspends element appearance) - in that case, avoid sleeping (e.g. setTimeOut) and prefer more deterministic methods that most platforms provide. Some libraries allows awaiting on operations (e.g. [Cypress cy.request('url')](https://docs.cypress.io/guides/references/best-practices.html#Unnecessary-Waiting)), other provide API for waiting like [@testing-library/dom method wait(expect(element))](https://testing-library.com/docs/guide-disappearance). Sometimes a more elegant way is to stub the slow resource, like API for example, and then once the response moment becomes deterministic the component can be explicitly re-rendered. When depending upon some external component that sleeps, it might turn useful to [hurry-up the clock](https://jestjs.io/docs/en/timer-mocks). Sleeping is a pattern to avoid because it forces your test to be slow or risky (when waiting for a too short period). Whenever sleeping and polling is inevitable and there's no support from the testing framework, some npm libraries like [wait-for-expect](https://www.npmjs.com/package/wait-for-expect) can help with a semi-deterministic solution
❌ **Otherwise:** When sleeping for a long time, tests will be an order of magnitude slower. When trying to sleep for small numbers, test will fail when the unit under test didn't respond in a timely fashion. So it boils down to a trade-off between flakiness and bad performance
-
✏ Code Examples
@@ -1197,67 +1330,60 @@ test('Shallow/mocked approach: When clicked to show filters, filters are display
### :clap: Doing It Right Example: E2E API that resolves only when the async operations is done (Cypress)
- 
+
+
```javascript
// using Cypress
-cy.get('#show-products').click()// navigate
-cy.wait('@products')// wait for route to appear
+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: Doing It Right Example: Testing library that waits for DOM elements
```javascript
// @testing-library/dom
-test('movie title appears', async () => {
- // element is initially not present...
+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'))
-})
+ // 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: Anti-Pattern Example: custom sleep code
-```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'))
-})
+```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. Watch how the content is served over the network
+## ⚪ ️ 3.5 Watch how the content is served over the network
-
+
✅ **Do:** Apply some active monitor that ensures the page load under real network is optimized - this includes any UX concern like slow page load or un-minified bundle. The inspection tools market is no short: basic tools like [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
@@ -1273,21 +1399,18 @@ test('movie title appears', async () => {

-
-
-## ⚪ ️ 3.6 Stub flakky and slow resources like backend APIs
+## ⚪ ️ 3.6 Stub flaky and slow resources like backend APIs
-:white_check_mark: **Do:** When coding your mainstream tests (not E2E tests), avoid involving any resource that is beyond your responsibility and control like backend API and use stubs instead (i.e. test double). Practically, instead of real network calls to APIs, use some test double library (like [Sinon]https://sinonjs.org/, [Test doubles](https://www.npmjs.com/package/testdouble), etc) for stubbing the API response. The main benefit is preventing flakiness - testing or staging APIs by definition are not highly stable and from time to time will fail your tests although YOUR component behaves just fine (production env was not meant for testing and it usually throttles requests). Doing this will allow simulating various API behavior that should drive your component behavior as when no data was found or the case when API throws an error. Last but not least, network calls will greatly slow down the tests
+:white_check_mark: **Do:** When coding your mainstream tests (not E2E tests), avoid involving any resource that is beyond your responsibility and control like backend API and use stubs instead (i.e. test double). Practically, instead of real network calls to APIs, use some test double library (like [Sinon](https://sinonjs.org/), [Test doubles](https://www.npmjs.com/package/testdouble), etc) for stubbing the API response. The main benefit is preventing flakiness - testing or staging APIs by definition are not highly stable and from time to time will fail your tests although YOUR component behaves just fine (production env was not meant for testing and it usually throttles requests). Doing this will allow simulating various API behavior that should drive your component behavior as when no data was found or the case when API throws an error. Last but not least, network calls will greatly slow down the tests
❌ **Otherwise:** The average test runs no longer than few ms, a typical API call last 100ms>, this makes each test ~20x slower
-
✏ Code Examples
@@ -1295,42 +1418,39 @@ test('movie title appears', async () => {
### :clap: Doing It Right Example: Stubbing or intercepting API calls
- 
-
-```javascript
+ 
+
+```javascript
// 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('When no products exist, show the appropriate message', () => {
- // Arrange
- nock("api")
- .get(`/products`)
- .reply(404);
+test("When no products exist, show the appropriate message", () => {
+ // 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();
});
-
```
@@ -1339,17 +1459,17 @@ 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 [Pupeteer](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
-❌ **Otherwise:** UI might invest much in testing its functionality only to realizes very late that the backend returned payload (the data schema the UI has to work with) is very differnt than expected
+❌ **Otherwise:** UI might invest much in testing its functionality only to realizes very late that the backend returned payload (the data schema the UI has to work with) is very different than expected
## ⚪ ️ 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 individial 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)).
@@ -1363,8 +1483,7 @@ test('When no products exist, show the appropriate message', () => {
### :clap: Doing It Right Example: Logging-in before-all and not before-each
-
+
```javascript
let authenticationToken;
@@ -1382,24 +1501,20 @@ 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))
- },
+ })
})
})
-
```
-
-
-
-## ⚪ ️ 3.9 Have one E2E smoke test that just travells across the site map
+## ⚪ ️ 3.9 Have one E2E smoke test that just travels across the site map
:white_check_mark: **Do:** For production monitoring and development-time sanity check, run a single E2E test that visits all/most of the site pages and ensures no one breaks. This type of test brings a great return on investment as it's very easy to write and maintain, but it can detect any kind of failure including functional, network and deployment issues. Other styles of smoke and sanity checking are not as reliable and exhaustive - some ops teams just ping the home page (production) or developers who run many integration tests which don't discover packaging and browser issues. Goes without saying that the smoke test doesn't replace functional tests rather just aim to serve as a quick smoke detector
@@ -1407,7 +1522,6 @@ beforeEach(setUser => () {
❌ **Otherwise:** Everything might seem perfect, all tests pass, production health-check is also positive but the Payment component had some packaging issue and only the /Payment route is not rendering
-
✏ Code Examples
@@ -1415,24 +1529,24 @@ beforeEach(setUser => () {
### :clap: Doing It Right Example: Smoke travelling across all 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');
- })
+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 Expose the tests as a live collaborative document
@@ -1441,69 +1555,65 @@ it('When doing smoke testing over all page, should load them all successfully',
❌ **Otherwise:** After investing top resources on testing, it's just a pity not to leverage this investment and win great value
-
✏ Code Examples
-### :clap: Doing It Right Example: Describing tests in human-language using cocumber-js
+### :clap: Doing It Right Example: Describing tests in human-language using cucumber-js
-
-```javascript
-// this is how one can describe tests using cocumber: 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
-
+
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 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
+

+
-
-
+
## ⚪ ️ 3.11 Detect visual issues with automated tools
-
-:white_check_mark: **Do:** Setup automated tools to capture UI screenshots when changes are presented and detect visual issues like content overlapping or breaking. This ensures that not only the right data is prepared but also the user can conveniently see it. This technique is not widely adopted, our testing mindset leans toward functional tests but it's the visuals what the user experience and with so many device types it's very easy to overlook some nasty UI bug. Some free tools can provide the basics - generate and save screenshots for the inspection of human eyes. While this approach might be sufficient for small apps, it's flawed as any other manual testing that demands human labor anytime something changes. On the other hand, it's quite challenging to detect UI issues automatically due to the lack of clear definition - this is where the field of 'Visual Regression' chime in and solve this puzzle by comparing old UI with the latest changes and detect differences. Some OSS/free tools can provide some of this functionality (e.g. [wraith](https://github.com/BBC-News/wraith), [PhantomCSS]([https://github.com/HuddleEng/PhantomCSS](https://github.com/HuddleEng/PhantomCSS)) but might charge signficant setup time. The commercial line of tools (e.g. [Applitools](https://applitools.com/), [Perci.io](https://percy.io/)) takes is a step further by smoothing the installation and packing advanced features like management UI, alerting, smart capturing by elemeinating 'visual noise' (e.g. ads, animations) and even root cause analysis of the DOM/css changes that led to the issue
+:white_check_mark: **Do:** Setup automated tools to capture UI screenshots when changes are presented and detect visual issues like content overlapping or breaking. This ensures that not only the right data is prepared but also the user can conveniently see it. This technique is not widely adopted, our testing mindset leans toward functional tests but it's the visuals what the user experience and with so many device types it's very easy to overlook some nasty UI bug. Some free tools can provide the basics - generate and save screenshots for the inspection of human eyes. While this approach might be sufficient for small apps, it's flawed as any other manual testing that demands human labor anytime something changes. On the other hand, it's quite challenging to detect UI issues automatically due to the lack of clear definition - this is where the field of 'Visual Regression' chime in and solve this puzzle by comparing old UI with the latest changes and detect differences. Some OSS/free tools can provide some of this functionality (e.g. [wraith](https://github.com/BBC-News/wraith), [PhantomCSS](<[https://github.com/HuddleEng/PhantomCSS](https://github.com/HuddleEng/PhantomCSS)>) but might charge significant setup time. The commercial line of tools (e.g. [Applitools](https://applitools.com/), [Percy.io](https://percy.io/)) takes is a step further by smoothing the installation and packing advanced features like management UI, alerting, smart capturing by eliminating 'visual noise' (e.g. ads, animations) and even root cause analysis of the DOM/CSS changes that led to the issue
❌ **Otherwise:** How good is a content page that display great content (100% tests passed), loads instantly but half of the content area is hidden?
-
✏ Code Examples
-### :thumbsdown: Anti Pattern Example: A typical visual regression - right content that is served badly
+### :thumbsdown: Anti-Pattern Example: A typical visual regression - right content that is served badly

-
### :clap: Doing It Right Example: Configuring wraith to capture and compare UI snapshots
-
+
```
# Add as many domains as necessary. Key will act as a label
@@ -1520,7 +1630,6 @@ screen_widths:
- 1024
- 1280
-
# Type page URL paths below, here are a couple of examples
paths:
about:
@@ -1531,64 +1640,33 @@ paths:
path: /subscribe
```
-### :clap: Doing It Right Example: Using Applitools to get snapshot comaprison and other advanced features
+### :clap: Doing It Right Example: Using Applitools to get snapshot comparison and other advanced features
- 
+ 
```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");
+ });
});
```
-
-
-
-
-
-
# Section 4️⃣: Measuring Test Effectiveness
@@ -1601,9 +1679,7 @@ Implementation tips: You may want to configure your continuous integration (CI)
-
-❌ **Otherwise:** Confidence and numbers go hand in hand, without really knowing that you tested most of the system — there will also be some fear. and fear will slow you down
-
+❌ **Otherwise:** Confidence and numbers go hand in hand, without really knowing that you tested most of the system — there will also be some fear and fear will slow you down
@@ -1612,21 +1688,19 @@ Implementation tips: You may want to configure your continuous integration (CI)
### :clap: Example: A typical coverage report
+

### :clap: Doing It Right Example: Setting up coverage per component (using Jest)
-
+
-
+")
-
-
## ⚪ ️ 4.2 Inspect coverage reports to detect untested areas and other oddities
@@ -1634,28 +1708,27 @@ Implementation tips: You may want to configure your continuous integration (CI)
:white_check_mark: **Do:** Some issues sneak just under the radar and are really hard to find using traditional tools. These are not really bugs but more of surprising application behavior that might have a severe impact. For example, often some code areas are never or rarely being invoked — you thought that the ‘PricingCalculator’ class is always setting the product price but it turns out it is actually never invoked although we have 10000 products in DB and many sales… Code coverage reports help you realize whether the application behaves the way you believe it does. Other than that, it can also highlight which types of code is not tested — being informed that 80% of the code is tested doesn’t tell whether the critical parts are covered. Generating reports is easy — just run your app in production or during testing with coverage tracking and then see colorful reports that highlight how frequent each code area is invoked. If you take your time to glimpse into this data — you might find some gotchas
-
❌ **Otherwise:** If you don’t know which parts of your code are left un-tested, you don’t know where the issues might come from
-
✏ Code Examples
-### :thumbsdown: Anti-Pattern Example: What’s wrong with this coverage report? based on a real-world scenario where we tracked our application usage in QA and find out interesting login patterns (Hint: the amount of login failures is non-proportional, something is clearly wrong. Finally it turned out that some frontend bug keeps hitting the backend login API)
+### :thumbsdown: Anti-Pattern Example: What’s wrong with this coverage report?
-
+Based on a real-world scenario where we tracked our application usage in QA and find out interesting login patterns (Hint: the amount of login failures is non-proportional, something is clearly wrong. Finally it turned out that some frontend bug keeps hitting the backend login API)
-
+
+
## ⚪ ️ 4.3 Measure logical coverage using mutation testing
-:white_check_mark: **Do:** The Traditional Coverage metric often lies: It may show you 100% code coverage, but none of your functions, even not one, return the right response. How come? it simply measures over which lines of code the test visited, but it doesn’t check if the tests actually tested anything — asserted for the right response. Like someone who’s traveling for business and showing his passport stamps — this doesn’t prove any work done, only that he visited few airports and hotels.
+:white_check_mark: **Do:** The Traditional Coverage metric often lies: It may show you 100% code coverage, but none of your functions, even not one, return the right response. How come? it simply measures over which lines of code the test visited, but it doesn’t check if the tests actually tested anything — asserted for the right response. Like someone who’s traveling for business and showing his passport stamps — this doesn’t prove any work done, only that he visited few airports and hotels.
Mutation-based testing is here to help by measuring the amount of code that was actually TESTED not just VISITED. [Stryker](https://stryker-mutator.io/) is a JavaScript library for mutation testing and the implementation is really neat:
@@ -1666,7 +1739,6 @@ Mutation-based testing is here to help by measuring the amount of code that was
Knowing that all or most of the mutations were killed gives much higher confidence than traditional coverage and the setup time is similar
-
❌ **Otherwise:** You’ll be fooled to believe that 85% coverage means your test will detect bugs in 85% of your code
@@ -1675,52 +1747,48 @@ Knowing that all or most of the mutations were killed gives much higher confiden
-### :thumbsdown: Anti Pattern Example: 100% coverage, 0% testing
+### :thumbsdown: Anti-Pattern Example: 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}`);
+ 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});
-});//Triggers 100% code coverage, but it doesn't check anything
-
+ addNewOrder({ assignee: "John@mailer.com", price: 120 });
+}); //Triggers 100% code coverage, but it doesn't check anything
```
+
### :clap: Doing It Right Example: Stryker reports, a tool for mutation testing, detects and counts the amount of code that is not tested (Mutations)
-
+")
-
-
## ⚪ ️4.4 Preventing test code issues with Test linters
-:white_check_mark: **Do:** A set of ESLint plugins were built specifically for inspecting the tests code patterns and discover issues. For example, [eslint-plugin-mocha](https://www.npmjs.com/package/eslint-plugin-mocha) will warn when a test is written at the global level (not a son of a describe() statement) or when tests are [skipped](https://mochajs.org/#inclusive-tests) which might lead to a false belief that all tests are passing. Similarly, [eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest) can, for example, warn when a test has no assertions at all (not checking anything)
+:white_check_mark: **Do:** A set of ESLint plugins were built specifically for inspecting the tests code patterns and discover issues. For example, [eslint-plugin-mocha](https://www.npmjs.com/package/eslint-plugin-mocha) will warn when a test is written at the global level (not a son of a describe() statement) or when tests are [skipped](https://mochajs.org/#inclusive-tests) which might lead to a false belief that all tests are passing. Similarly, [eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest) can, for example, warn when a test has no assertions at all (not checking anything)
-
❌ **Otherwise:** Seeing 90% code coverage and 100% green tests will make your face wear a big smile only until you realize that many tests aren’t asserting for anything and many test suites were just skipped. Hopefully, you didn’t deploy anything based on this false observation
-
✏ Code Examples
-### :thumbsdown: Anti Pattern Example: A test case full of errors, luckily all are caught by Linters
+### :thumbsdown: Anti-Pattern Example: A test case full of errors, luckily all are caught by Linters
```javascript
describe("Too short description", () => {
@@ -1732,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
});
```
@@ -1740,19 +1808,16 @@ it("Test name", () => {*//error:no-identical-title. Assign unique titles to test
-
-# Section 5️⃣ CI and Other Quality Measures
+# Section 5️⃣: CI and Other Quality Measures
## ⚪ ️ 5.1 Enrich your linters and abort builds that have linting issues
-:white_check_mark: **Do:** Linters are a free lunch, with 5 min setup you get for free an auto-pilot guarding your code and catching significant issue as you type. Gone are the days where linting was about cosmetics (no semi-colons!). Nowadays, Linters can catch severe issues like errors that are not thrown correctly and losing information. On top of your basic set of rules (like [ESLint standard](https://www.npmjs.com/package/eslint-plugin-standard) or [Airbnb style](https://www.npmjs.com/package/eslint-config-airbnb)), consider including some specializing Linters like [eslint-plugin-chai-expect](https://www.npmjs.com/package/eslint-plugin-chai-expect) that can discover tests without assertions, [eslint-plugin-promise](https://www.npmjs.com/package/eslint-plugin-promise?activeTab=readme) can discover promises with no resolve (your code will never continue), [eslint-plugin-security](https://www.npmjs.com/package/eslint-plugin-promise?activeTab=readme) which can discover eager regex expressions that might get used for DOS attacks, and [eslint-plugin-you-dont-need-lodash-underscore](https://www.npmjs.com/package/eslint-plugin-you-dont-need-lodash-underscore) is capable of alarming when the code uses utility library methods that are part of the V8 core methods like Lodash._map(…)
+:white_check_mark: **Do:** Linters are a free lunch, with 5 min setup you get for free an auto-pilot guarding your code and catching significant issue as you type. Gone are the days where linting was about cosmetics (no semi-colons!). Nowadays, Linters can catch severe issues like errors that are not thrown correctly and losing information. On top of your basic set of rules (like [ESLint standard](https://www.npmjs.com/package/eslint-plugin-standard) or [Airbnb style](https://www.npmjs.com/package/eslint-config-airbnb)), consider including some specializing Linters like [eslint-plugin-chai-expect](https://www.npmjs.com/package/eslint-plugin-chai-expect) that can discover tests without assertions, [eslint-plugin-promise](https://www.npmjs.com/package/eslint-plugin-promise?activeTab=readme) can discover promises with no resolve (your code will never continue), [eslint-plugin-security](https://www.npmjs.com/package/eslint-plugin-security?activeTab=readme) which can discover eager regex expressions that might get used for DOS attacks, and [eslint-plugin-you-dont-need-lodash-underscore](https://www.npmjs.com/package/eslint-plugin-you-dont-need-lodash-underscore) is capable of alarming when the code uses utility library methods that are part of the V8 core methods like Lodash.\_map(…)
-
-❌ **Otherwise:** Consider a rainy day where your production keeps crashing but the logs don’t display the error stack trace. What happened? Your code mistakenly threw a non-error object and the stack trace was lost, a good reason for banging your head against a brick wall. A 5min linter setup could detect this TYPO and save your day
-
+❌ **Otherwise:** Consider a rainy day where your production keeps crashing but the logs don’t display the error stack trace. What happened? Your code mistakenly threw a non-error object and the stack trace was lost, a good reason for banging your head against a brick wall. A 5 min linter setup could detect this TYPO and save your day
@@ -1760,42 +1825,39 @@ it("Test name", () => {*//error:no-identical-title. Assign unique titles to test
-### :thumbsdown: Anti Pattern Example: The wrong Error object is thrown mistakenly, no stack-trace will appear for this error. Luckily, ESLint catches the next production bug
+### :thumbsdown: Anti-Pattern Example: The wrong Error object is thrown mistakenly, no stack-trace will appear for this error. Luckily, ESLint catches the next production bug
+

-
-
-
-# ⚪ ️ 5.2 Shorten the feedback loop with local developer-CI
+## ⚪ ️ 5.2 Shorten the feedback loop with local developer-CI
-:white_check_mark: **Do:** Using a CI with shiny quality inspections like testing, linting, vulnerabilities check, etc? Help developers run this pipeline also locally to solicit instant feedback and shorten the [feedback loop](https://www.gocd.org/2016/03/15/are-you-ready-for-continuous-delivery-part-2-feedback-loops/). Why? an efficient testing process constitutes many and iterative loops: (1) try-outs -> (2) feedback -> (3) refactor. The faster the feedback is, the more improvement iterations a developer can perform per-module and perfect the results. On the flip, when the feedback is late to come fewer improvement iterations could be packed into a single day, the team might already move forward to another topic/task/module and might not be up for refining that module.
+:white_check_mark: **Do:** Using a CI with shiny quality inspections like testing, linting, vulnerabilities check, etc? Help developers run this pipeline also locally to solicit instant feedback and shorten the [feedback loop](https://www.gocd.org/2016/03/15/are-you-ready-for-continuous-delivery-part-2-feedback-loops/). Why? an efficient testing process constitutes many and iterative loops: (1) try-outs -> (2) feedback -> (3) refactor. The faster the feedback is, the more improvement iterations a developer can perform per-module and perfect the results. On the flip, when the feedback is late to come fewer improvement iterations could be packed into a single day, the team might already move forward to another topic/task/module and might not be up for refining that module.
-Practically, some CI vendors (Example: [CircleCI load CLI](https://circleci.com/docs/2.0/local-cli/)) allow running the pipeline locally. Some commercial tools like [wallaby provide highly-valuable & testing insights](https://wallabyjs.com/) as a developer prototype (no affiliation). Alternatively, you may just add npm script to package.json that runs all the quality commands (e.g. test, lint, vulnerabilities) — use tools like [concurrently](https://www.npmjs.com/package/concurrently) for parallelization and non-zero exit code if one of the tools failed. Now the developer should just invoke one command — e.g. ‘npm run quality’ — to get instant feedback. Consider also aborting a commit if the quality check failed using a githook ([husky can help](https://github.com/typicode/husky))
+Practically, some CI vendors (Example: [CircleCI local CLI](https://circleci.com/docs/2.0/local-cli/)) allow running the pipeline locally. Some commercial tools like [wallaby provide highly-valuable & testing insights](https://wallabyjs.com/) as a developer prototype (no affiliation). Alternatively, you may just add npm script to package.json that runs all the quality commands (e.g. test, lint, vulnerabilities) — use tools like [concurrently](https://www.npmjs.com/package/concurrently) for parallelization and non-zero exit code if one of the tools failed. Now the developer should just invoke one command — e.g. ‘npm run quality’ — to get instant feedback. Consider also aborting a commit if the quality check failed using a githook ([husky can help](https://github.com/typicode/husky))
-
❌ **Otherwise:** When the quality results arrive the day after the code, testing doesn’t become a fluent part of development rather an after the fact formal artifact
-
✏ Code Examples
-### :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": {
+### :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
+
+```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": {
@@ -1803,53 +1865,43 @@ Practically, some CI vendors (Example: [CircleCI load CLI](https://circleci.com/
"precommit": "npm run inspect:all",
"prepush": "npm run inspect:all"
}
+ }
}
-
```
-
-
-
-# ⚪ ️5.3 Perform e2e testing over a true production-mirror
+## ⚪ ️5.3 Perform e2e testing over a true production-mirror
-:white_check_mark: **Do:** End to end (e2e) testing are the main challenge of every CI pipeline — creating an identical ephemeral production mirror on the fly with all the related cloud services can be tedious and expensive. Finding the best compromise is your game: [Docker-compose](https://serverless.com/) allows crafting isolated dockerized environment with identical containers using a single plain text file but the backing technology (e.g. networking, deployment model) is different from real-world productions. You may combine it with [‘AWS Local’](https://github.com/localstack/localstack) to work with a stub of the real AWS services. If you went [serverless](https://serverless.com/) multiple frameworks like serverless and [AWS SAM](https://docs.aws.amazon.com/lambda/latest/dg/serverless_app.html) allows the local invocation of Faas code.
+:white_check_mark: **Do:** End to end (e2e) testing are the main challenge of every CI pipeline — creating an identical ephemeral production mirror on the fly with all the related cloud services can be tedious and expensive. Finding the best compromise is your game: [Docker-compose](https://serverless.com/) allows crafting isolated dockerized environment with identical containers using a single plain text file but the backing technology (e.g. networking, deployment model) is different from real-world productions. You may combine it with [‘AWS Local’](https://github.com/localstack/localstack) to work with a stub of the real AWS services. If you went [serverless](https://serverless.com/) multiple frameworks like serverless and [AWS SAM](https://docs.aws.amazon.com/lambda/latest/dg/serverless_app.html) allows the local invocation of FaaS code.
-The huge Kubernetes eco-system is yet to formalize a standard convenient tool for local and CI-mirroring though many new tools are launched frequently. One approach is running a ‘minimized-Kubernetes’ using tools like [Minikube](https://kubernetes.io/docs/setup/minikube/) and [MicroK8s](https://microk8s.io/) which resemble the real thing only come with less overhead. Another approach is testing over a remote ‘real-Kubernetes’, some CI providers (e.g. [Codefresh](https://codefresh.io/)) has native integration with Kubernetes environment and make it easy to run the CI pipeline over the real thing, others allow custom scripting against a remote Kubernetes.
+The huge Kubernetes ecosystem is yet to formalize a standard convenient tool for local and CI-mirroring though many new tools are launched frequently. One approach is running a ‘minimized-Kubernetes’ using tools like [Minikube](https://kubernetes.io/docs/setup/minikube/) and [MicroK8s](https://microk8s.io/) which resemble the real thing only come with less overhead. Another approach is testing over a remote ‘real-Kubernetes’, some CI providers (e.g. [Codefresh](https://codefresh.io/)) has native integration with Kubernetes environment and make it easy to run the CI pipeline over the real thing, others allow custom scripting against a remote Kubernetes.
-
❌ **Otherwise:** Using different technologies for production and testing demands maintaining two deployment models and keeps the developers and the ops team separated
-
✏ Code Examples
-### :clap: Example: a CI pipeline that generates Kubernetes cluster on the fly ([Credit: Dynamic-environments Kubernetes](https://container-solutions.com/dynamic-environments-kubernetes/))
+### :clap: Example: a CI pipeline that generates Kubernetes cluster on the fly ([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 Parallelize test execution
-:white_check_mark: **Do:** When done right, testing is your 24/7 friend providing almost instant feedback. In practice, executing 500 CPU-bounded unit test on a single thread can take too long. Luckily, modern test runners and CI platforms (like [Jest](https://github.com/facebook/jest), [AVA](https://github.com/avajs/ava) and [Mocha extensions](https://github.com/yandex/mocha-parallel-tests)) can parallelize the test into multiple processes and achieve significant improvement in feedback time. Some CI vendors do also parallelize tests across containers (!) which shortens the feedback loop even further. Whether locally over multiple processes, or over some cloud CLI using multiple machines — parallelizing demand keeping the tests autonomous as each might run on different processes
+:white_check_mark: **Do:** When done right, testing is your 24/7 friend providing almost instant feedback. In practice, executing 500 CPU-bounded unit test on a single thread can take too long. Luckily, modern test runners and CI platforms (like [Jest](https://github.com/facebook/jest), [AVA](https://github.com/avajs/ava) and [Mocha extensions](https://github.com/yandex/mocha-parallel-tests)) can parallelize the test into multiple processes and achieve significant improvement in feedback time. Some CI vendors do also parallelize tests across containers (!) which shortens the feedback loop even further. Whether locally over multiple processes, or over some cloud CLI using multiple machines — parallelizing demand keeping the tests autonomous as each might run on different processes
❌ **Otherwise:** Getting test results 1 hour long after pushing new code, as you already code the next features, is a great recipe for making testing less relevant
-
✏ Code Examples
@@ -1857,20 +1909,18 @@ The huge Kubernetes eco-system is yet to formalize a standard convenient tool fo
### :clap: Doing It Right Example: Mocha parallel & Jest easily outrun the traditional Mocha thanks to testing parallelization ([Credit: JavaScript Test-Runners Benchmark](https://medium.com/dailyjs/javascript-test-runners-benchmark-3a78d4117b4))
+
")
-
-
-
## ⚪ ️5.5 Stay away from legal issues using license and plagiarism check
-:white_check_mark: **Do:** Licensing and plagiarism issues are probably not your main concern right now, but why not tick this box as well in 10 minutes? A bunch of npm packages like [license check](https://www.npmjs.com/package/license-checker) and [plagiarism check](https://www.npmjs.com/package/plagiarism-checker) (commercial with free plan) can be easily baked into your CI pipeline and inspect for sorrows like dependencies with restrictive licenses or code that was copy-pasted from Stackoverflow and apparently violates some copyrights
-❌ **Otherwise:** Unintentionally, developers might use packages with inappropriate licenses or copy paste commercial code and run into legal issues
+:white_check_mark: **Do:** Licensing and plagiarism issues are probably not your main concern right now, but why not tick this box as well in 10 minutes? A bunch of npm packages like [license check](https://www.npmjs.com/package/license-checker) and [plagiarism check](https://www.npmjs.com/package/plagiarism-checker) (commercial with free plan) can be easily baked into your CI pipeline and inspect for sorrows like dependencies with restrictive licenses or code that was copy-pasted from Stack Overflow and apparently violates some copyrights
+❌ **Otherwise:** Unintentionally, developers might use packages with inappropriate licenses or copy paste commercial code and run into legal issues
@@ -1879,33 +1929,28 @@ The huge Kubernetes eco-system is yet to formalize a standard convenient tool fo
### :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
-
```

-
-
-
## ⚪ ️5.6 Constantly inspect for vulnerable dependencies
-:white_check_mark: **Do:** Licensing and plagiarism issues are probably not your main concern right now, but why not tick this box as well in 10 minutes? A bunch of npm packages like license check and plagiarism check (commercial with free plan) can be easily baked into your CI pipeline and inspect for sorrows like dependencies with restrictive licenses or code that was copy-pasted from Stackoverflow and apparently violates some copyrights
-
+:white_check_mark: **Do:** Even the most reputable dependencies such as Express have known vulnerabilities. This can get easily tamed using community tools such as [npm audit](https://docs.npmjs.com/getting-started/running-a-security-audit), or commercial tools like [snyk](https://snyk.io/) (offer also a free community version). Both can be invoked from your CI on every build
-❌ **Otherwise:** Even the most reputable dependencies such as Express have known vulnerabilities. This can get easily tamed using community tools such as [npm audit](https://docs.npmjs.com/getting-started/running-a-security-audit), or commercial tools like [snyk](https://snyk.io/) (offer also a free community version). Both can be invoked from your CI on every build
-
+❌ **Otherwise:** Keeping your code clean from vulnerabilities without dedicated tools will require to constantly follow online publications about new threats. Quite tedious
@@ -1914,64 +1959,66 @@ license-checker --summary --failOn BSD
### :clap: Example: NPM Audit result
+

+
+## ⚪ ️5.7 Automate dependency updates
+:white_check_mark: **Do:** Yarn and npm latest introduction of package-lock.json introduced a serious challenge (the road to hell is paved with good intentions) — by default now, packages are no longer getting updates. Even a team running many fresh deployments with ‘npm install’ & ‘npm update’ won’t get any new updates. This leads to subpar dependent packages versions at best or to vulnerable code at worst. Teams now rely on developers goodwill and memory to manually update the package.json or use tools [like ncu](https://www.npmjs.com/package/npm-check-updates) manually. A more reliable way could be to automate the process of getting the most reliable dependency versions, though there are no silver bullet solutions yet there are two possible automation roads:
-
+(1) CI can fail builds that have obsolete dependencies — using tools like [‘npm outdated’](https://docs.npmjs.com/cli/outdated) or ‘npm-check-updates (ncu)’ . Doing so will enforce developers to update dependencies.
-## ⚪ ️5.7 Automate dependency updates
-:white_check_mark: **Do:** Yarn and npm latest introduction of package-lock.json introduced a serious challenge (the road to hell is paved with good intentions) — by default now, packages are no longer getting updates. Even a team running many fresh deployments with ‘npm install’ & ‘npm update’ won’t get any new updates. This leads to subpar dependent packages versions at best or to vulnerable code at worst. Teams now rely on developers goodwill and memory to manually update the package.json or use tools [like ncu](https://www.npmjs.com/package/npm-check-updates) manually. A more reliable way could be to automate the process of getting the most reliable dependency versions, though there are no silver bullet solutions yet there are two possible automation roads: (1) CI can fail builds that have obsolete dependencies — using tools like [‘npm outdated’](https://docs.npmjs.com/cli/outdated) or ‘npm-check-updates (ncu)’ . Doing so will enforce developers to update dependencies. (2) Use commercial tools that scan the code and automatically send pull requests with updated dependencies. One interesting question remaining is what should be the dependency update policy — updating on every patch generates too many overhead, updating right when a major is released might point to an unstable version (many packages found vulnerable on the very first days after being released, [see the](https://nodesource.com/blog/a-high-level-post-mortem-of-the-eslint-scope-security-incident/) eslint-scope incident). An efficient update policy may allow some ‘vesting period’ — let the code lag behind the @latest for some time and versions before considering the local copy as obsolete (e.g. local version is 1.3.1 and repository version is 1.3.8)
-
+(2) Use commercial tools that scan the code and automatically send pull requests with updated dependencies. One interesting question remaining is what should be the dependency update policy — updating on every patch generates too many overhead, updating right when a major is released might point to an unstable version (many packages found vulnerable on the very first days after being released, [see the](https://nodesource.com/blog/a-high-level-post-mortem-of-the-eslint-scope-security-incident/) eslint-scope incident).
+An efficient update policy may allow some ‘vesting period’ — let the code lag behind the @latest for some time and versions before considering the local copy as obsolete (e.g. local version is 1.3.1 and repository version is 1.3.8)
+
❌ **Otherwise:** Your production will run packages that have been explicitly tagged by their author as risky
-
✏ Code Examples
-### :clap: Example: [ncu](https://www.npmjs.com/package/npm-check-updates) can be used manually or within a CI pipeline to detect to which extent the code lag behind the latest versions
-
+### :clap: Example: [ncu](https://www.npmjs.com/package/npm-check-updates) can be used manually or within a CI pipeline to detect to which extent the code lag behind the latest versions
+
-
## ⚪ ️ 5.8 Other, non-Node related, CI tips
-: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
- - 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
-
+: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
+ - 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
+
❌ **Otherwise:** You‘ll miss years of wisdom
## ⚪ ️ 5.9 Build matrix: Run the same CI steps using multiple Node versions
-:white_check_mark: **Do:** Quality checking is about serendipity, the more ground you cover the luckier you get in detecting issues early. When developing reusable packages or running a multi-customer production with various configuration and Node versions, the CI must run the pipeline of tests over all the permutations of configurations. For example, assuming we use mySQL for some customers and Postgres for others — some CI vendors support a feature called ‘Matrix’ which allow running the suit of testing against all permutations of mySQL, Postgres and multiple Node version like 8, 9 and 10. This is done using configuration only without any additional effort (assuming you have testing or any other quality checks). Other CIs who doesn’t support Matrix might have extensions or tweaks to allow that
-
+:white_check_mark: **Do:** Quality checking is about serendipity, the more ground you cover the luckier you get in detecting issues early. When developing reusable packages or running a multi-customer production with various configuration and Node versions, the CI must run the pipeline of tests over all the permutations of configurations. For example, assuming we use MySQL for some customers and Postgres for others — some CI vendors support a feature called ‘Matrix’ which allow running the suit of testing against all permutations of MySQL, Postgres and multiple Node version like 8, 9 and 10. This is done using configuration only without any additional effort (assuming you have testing or any other quality checks). Other CIs who doesn’t support Matrix might have extensions or tweaks to allow that
+
❌ **Otherwise:** So after doing all that hard work of writing testing are we going to let bugs sneak in only because of configuration issues?
-
✏ Code Examples
-### :clap: Example: Using Travis (CI vendor) build definition to run the same test over multiple Node versions
+### :clap: Example: Using Travis (CI vendor) build definition to run the same test over multiple Node versions
+
language: node_js
node_js:
- "7"
- "6"
- "5"
- "4"
install:
- npm install
script:
- npm run test
@@ -1979,8 +2026,6 @@ license-checker --summary --failOn BSD
# Team
-
-
## Yoni Goldberg
@@ -1989,29 +2034,30 @@ license-checker --summary --failOn BSD
**Role:** Writer
-**About:** I'm an independent consultant who works with 500 fortune corporates 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)
+**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)
-**Workshop:** 👨🏫 Want to learn all these practices and techniques at your offices (Europe & USA)? [register here for my testing workshop](https://testjavascript.com/)
**Follow:**
-* [🐦 Twitter](https://twitter.com/goldbergyoni/)
-* [📞 Contact](https://testjavascript.com/contact-2/)
+- [🐦 Twitter](https://twitter.com/goldbergyoni/)
+- [📞 Contact](https://testjavascript.com/contact-2/)
+- [✉️ Newsletter](https://testjavascript.com/newsletter//)
+
-
-## [Bruno Scheufler](https://github.com/BrunoScheufler)
+## [Bruno Scheufler](https://github.com/BrunoScheufler)
**Role:** Tech reviewer and advisor
-Took care to revise, improve, lint and polish all the texts
+Took care to revise, improve, lint and polish all the texts
**About:** full-stack web engineer, Node.js & GraphQL enthusiast
+
@@ -2021,16 +2067,93 @@ Took care to revise, improve, lint and polish all the texts
**About:** A savvy frontend developer, CSS expert and emojis freak
-
-
-
-
-
\ No newline at end of file
+## [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.
+
+## Contributors ✨
+
+Thanks goes to these wonderful people who have contributed to this repository!
+
+
+
+
+
+
+
+
+
+