Най-добри практики за тестване на Node.js и JavaScript (2019)

Важна забележка: Тази статия се превърна в хранилище на GitHub с допълнителни 15 най-добри практики и дискусии в общността

Кратко въведение

Като независим консултант по Node.js се ангажирам и преглеждам 10+ проекта всяка година и мотивирано молят клиентите ми да се съсредоточат върху тестване. Преди няколко месеца започнах да документирам тук прозренията и повтарящите се грешки, които наблюдавам на полето и изведнъж тя се натрупа в 30 най-добри практики за тестване.

Идеите по-долу обхващат теми като избора на правилните типове тестове, кодирането им правилно, измерването на тяхната ефективност и хостинга им в CI / CD тръбопровод по правилния начин. Някои примери са илюстрирани с помощта на Jest, други с Mocha - тази публикация е по-малко за инструментариума и повече за правилния подход и техники.

Казвам се Йони Голдбърг, независим консултант на Node.JS и съавтор на най-добрите практики на Node.js. Работя с клиенти в САЩ, Европа и Израел за полиране на техните приложения Node.js. Сред моите услуги са също планиране на тестове, преглед на тестове и настройка на CI / CD. Следвайте ме в Twitter ️

Прегледан и подобрен от Bruno Scheufler

️ 0. Златното правило: Тестването трябва да остане мъртво - просто и ясно като ден

Запознат ли сте с онзи усмихнат приятел, член на семейството или може би филмов герой, който винаги е на разположение, за да пощади добрите си работещи ръце, 24/7 помагайки, когато имате нужда от него, настроен с положителна енергия, но все пак да иска толкова малко за себе си? Ето как трябва да бъде проектиран код за тестване - лесно, ценно и забавно. Това може да се постигне чрез избирателно техники, инструменти и изпитателни цели, които са рентабилни и осигуряват голяма възвръщаемост на инвестициите. Тествайте само толкова, колкото е необходимо, стремете се да го поддържате пъргав, понякога дори си струва да откажете някои тестове и да търгувате надеждност за гъвкавост и простота.

Тестването не трябва да се третира като традиционен код на приложението - типичният екип така или иначе е предизвикан да поддържа основното си приложение (функциите, които кодираме и продаваме), той не може да толерира допълнителен сложен „проект“. Ако тестването нарасне, за да бъде допълнителен източник на болка - то ще изостави или значително ще забави развитието.

В този смисъл кодът за тестване трябва да остане мъртъв просто, с минимални зависимости, абстракции и нива на индирекция. Човек трябва да погледне тест и да получи намерението незабавно. Повечето от съветите по-долу са производни на този принцип

*** Раздел : Тестовата анатомия ***

Includ ️ 1. Включете 3 части във всяко име на теста

Направете: Тестовият доклад трябва да покаже дали текущата версия на приложението удовлетворява изискванията за хората, които не са задължително запознати с кода: тестер, инженерът на DevOps, който се внедрява, и бъдещето, което сте след две години. Това може да се постигне най-добре, ако тестовете говорят на ниво изисквания и включват 3 части:

(1) Какво се тества? Например методът ProductsService.addNewProduct

(2) При какви обстоятелства и сценарий? Например, към метода не се предава цена

(3) Какъв е очакваният резултат? Например новият продукт не е одобрен

В противен случай: Разгръщане току-що се провали, тестът с име „Добавяне на продукт“ не бе успешен. Това казва ли ви какво точно не работи?

Правилно правилно Пример: Тестово име, което представлява 3 части

Правилно правилно Пример: Тестовият доклад наподобява документа за изискванията

Включете 3 части във всеки тестов случай, говори езика на продукта

️ 2. Опишете очакванията на езика на продукта: използвайте твърдения в стил BDD

Направете: Кодирането на вашите тестове в декларативен стил позволява на читателя да вземе моментално без да харчи дори нито един цикъл мозък-процесор. Когато напишете императивен код, който е пълен с условна логика, четецът се изхвърля в усилено психическо настроение. В този смисъл кодирайте очакването в човешки език, декларативен BDD стил, използвайки очаквайте или трябва и не използвайки персонализиран код. Ако Chai & Jest не включват желаното твърдение и е много повторяемо, помислете за разширяване на Jest matcher (Jest) или написване на персонализиран Chai плъгин

В противен случай: Екипът ще напише по-малко тест и ще украси досадните с .skip ()

Example Пример за анти-шаблон: Читателят трябва да прегледа не толкова краткия и наложителен код, само за да получи тестовата история

Правилно правилно Пример: Промяната чрез следния декларативен тест е полъх

️ 3. Линт с плъгини, предназначени за тестване

Направете: Набор от ESLint приставки е създаден специално за проверка на моделите на тестовите кодове и откриване на проблеми. Например, eslint-plugin-mocha ще предупреди, когато тестът е написан на глобално ниво (не е син на оператор description ()) или когато тестовете са пропуснати, което може да доведе до невярно вярване, че всички тестове преминават. По същия начин, eslint-plugin-vice може например да предупреди, когато тестът изобщо няма твърдения (не проверява нищо)

В противен случай: Вижте 90% покритие на кода и 100% зелени тестове ще накарате лицето ви да носи голяма усмивка само докато не разберете, че много тестове не отстояват нищо и много тестови комплекти просто бяха пропуснати. Надяваме се, че не сте разгърнали нещо, базирано на това невярно наблюдение

Example Пример за анти-образец: Тестов случай, пълен с грешки, за щастие всички са хванати от Линтери

️ 4. Придържайте се към тестване с черни кутии: Тествайте само публични методи

Направете: Тестването на вътрешните работи носи огромни разходи за почти нищо. Ако вашият код / ​​API даде правилни резултати, трябва ли наистина да инвестирате следващите си 3 часа в тестване КАК е работил вътрешно и след това да поддържате тези крехки тестове? Всеки път, когато се проверява публичното поведение, частната имплементация също се тества неявно и вашите тестове ще се счупят само ако има определен проблем (например грешен изход). Този подход се нарича също поведенчески тест. От друга страна, трябва ли да тествате вътрешните работи (подходът на бялото поле) - фокусът ви се измества от планирането на резултата от компонента към дебелозърнестите детайли и тестът ви може да се счупи поради незначителни кодови рефактори, въпреки че резултатите са добре - това драстично увеличава поддръжката тежест

В противен случай: Вашият тест се държи като детето, което плаче вълк: изстрелвайте силни фалшиво-позитивни викове (напр. Тестът се проваля, защото е променено име на частна променлива). Не е изненадващо, че хората скоро ще започнат да игнорират CI известията, докато някой ден истинска грешка няма да бъде игнорирана ...

Example Пример за анти-образец: Тестов случай тества вътрешните работи без основателна причина

️️5. Изберете правилните двойни тестове: Избягвайте подигравки в полза на мъничета и шпиони

Направете: Тестовите двойки са необходимо зло, тъй като са съчетани с вътрешността на приложението, но някои предоставят огромна стойност (Прочетете тук напомняне за тестови двойници: макети срещу мъничета срещу шпиони). Различните техники обаче не са се родили равни: някои от тях, шпиони и мъничета, са фокусирани върху тестване на изискванията, но като неизбежен страничен ефект те също леко докосват вътрешността. Отсреща, подигравките са фокусирани върху тестване на вътрешните работи - това носи огромни разходи, както е обяснено в куршума „Тестване на черни кутии“.

Преди да използвате тестови двойници, задайте много прост въпрос: Използвам ли го за тестване на функционалността, която се появява или може да се появи в документа за изискванията? Ако не, това е мирис на тестване в бяла кутия.

Например, ако искате да тествате какво приложението ви се държи разумно, когато платежната услуга не работи, можете да спрете платежната услуга и да задействате някакво връщане „Без отговор“, за да сте сигурни, че изпитваната единица връща правилната стойност. Това проверява нашето поведение / отговор / резултат на приложение при определени сценарии. Можете също така да използвате шпионин, за да твърдите, че имейл е изпратен, когато тази услуга не работи - това отново е поведенческа проверка, която вероятно ще се появи в документ за изискванията („Изпратете имейл, ако плащането не може да бъде запазено“). От друга страна, ако се подигравате с платежната услуга и гарантирате, че тя е била извикана с правилните типове JavaScript - тогава вашият тест е фокусиран върху вътрешни неща, които нямат нищо с функционалността на приложението и е вероятно да се променят често

В противен случай: Всяко рефакториране на кода изисква да се търсят всички макове в кода и да се актуализира съответно. Тестовете стават тежест, а не полезен приятел

Example Анти-модел пример: Смее се фокусира върху вътрешността

Правилно правилно Пример: шпионите са фокусирани върху тестването на изискванията, но като страничен ефект неизбежно се докосват до вътрешните работи

️ 6. Не "foo", използвайте реалистични входни данни

Да: Често производствените грешки се разкриват под някакъв много специфичен и изненадващ принос - колкото по-реалистичен е тестовият вход, толкова по-големи са шансовете да се хванат бъгове рано. Използвайте специализирани библиотеки като Faker, за да генерирате псевдореални данни, наподобяващи разнообразието и формата на производствените данни. Например такива библиотеки ще генерират произволни, но реалистични телефонни номера, потребителски имена, кредитна карта, имена на компании и дори текст „lorem ipsum“. Помислете дори да импортирате реални данни от вашата производствена среда и да ги използвате в тестовете си. Искате ли да го пренесете на следващото ниво? вижте следващия куршум (тестване въз основа на собственост)

В противен случай всички тестове за развитие ще изглеждат зелено, когато използвате синтетични входове като "Foo", но тогава производството може да стане червено, когато хакер премине в гаден низ като "@ 3e2ddsf. ## '1 fdsfds. fds432 AAAA ”

Example Анти-шаблон Пример: Тестов пакет, който преминава поради нереалистични данни

Example Прави правилно Пример: Случайно реалистично въвеждане

7. Тествайте много комбинации от входни данни, като използвате тестване на базата на свойства

Да: Обикновено ние избираме няколко входни проби за всеки тест. Дори когато входният формат прилича на данни от реалния свят (вж. Куршум „Не foo“), ние покриваме само няколко входни комбинации (метод („“, вярно, 1), метод („низ“, невярно “, 0) ), Въпреки това, при производството API, който се извиква с 5 параметъра, може да бъде извикан с хиляди различни пермутации, една от тях може да намали процеса ни (вижте Fuzz Testing). Какво ще стане, ако можете да напишете един тест, който изпраща 1000 пермутации от различни входове автоматично и улавя, за което въвеждането на нашия код не успява да върне правилния отговор? Тестване на базата на свойства е техника, която прави точно това: изпращайки всички възможни комбинации от входни данни към вашия тестван модул, той увеличава честотата на намиране на грешка. Например, като се има предвид метод - addNewProduct (id, име, isDiscount) - поддържащите библиотеки ще извикат този метод с много комбинации от (число, низ, логически) като (1, „iPhone“, false), (2, „Galaxy ", вярно). Можете да стартирате тестване на базата на собственост, като използвате любимия си тестов бегач (Mocha, Jest и т.н.), като използвате библиотеки като js-verify или тест-проверка (много по-добра документация). Актуализация: Николай Дубиен предлага в коментарите по-долу да проверите бърза проверка, която изглежда предлага някои допълнителни функции, а също така и да се поддържа активно

В противен случай: несъзнателно избирате тестовите входове, които покриват само кодови пътища, които работят добре. За съжаление, това намалява ефективността на тестването като средство за излагане на грешки

Правилно правилен пример: Тестване на много входни пермутации с „mocha-testcheck“

️ 8. Останете в рамките на теста: Минимизирайте външните помощници и абстракциите

Направете: Към настоящия момент вероятно е очевидно, че се застъпвам за мъртви прости тестове: Екипът не може да си позволи друг софтуерен проект, който изисква умствени усилия за разбиране на кода. Майкъл Линк обяснява това в страхотния си пост:

Добрият производствен код е добре установен; добрият тестов код е очевиден ... Когато пишете тест, помислете за следващия разработчик, който ще види почивката на теста. Те не искат да прочетат целия ви тестов пакет и със сигурност не искат да прочетат цялото дърво за наследяване на тест помощни програми.

Позволете на читателя да получи цялата история, без да напуска теста, да сведе до минимум утилитите, куките или какъвто и да е външен ефект върху тестов случай. Твърде много повторения и поставяне на копие? Добре, тест може да остане с един външен помощник и да остане очевиден. Но когато нарасне до три и четири помощници и куки, това означава, че сложна структура бавно се формира

В противен случай: изведнъж се озовахте с 4 помощника на тестов пакет, 2 от тях наследени от основен утилит, много настройки и закачалки? поздравление, току-що спечели друг предизвикателен проект за поддържане, може да напишете тестове скоро срещу вашия тестов пакет

Example Пример за анти-образец: Фантастична и непряка структура на теста. Разбирате ли тестовия случай без да преминавате към външни зависимости?

Правилно правилно Пример: Тест, който може да разберете, без да прескачате различни файлове

️ 9. Избягвайте световните тестови приспособления и семена, добавяйте данни на тест

Направете: В съответствие със златното правило (бук. 0), всеки тест трябва да добави и да действа по свой набор от редове на DB, за да се предотврати свързването и лесно да се разсъждава за тестовия поток. В действителност това често се нарушава от тестери, които посяват БД с данни преди да стартират тестовете (известен също като „тестова фиксация“) с цел подобряване на производителността. Въпреки че производителността наистина е важен проблем - може да бъде смекчена (вижте куршума „Тестване на компоненти”), но сложността на теста е много болезнена мъка, която би трябвало да управлява други съображения през повечето време. Практически направете всеки тестов случай изрично да добавя нужните DB записи и действа само върху тези записи. Ако ефективността стане критична загриженост - може да се постигне балансиран компромис под формата на засяване на единствения набор от тестове, които не мутират данни (например заявки)

В противен случай: Малко тестове се провалят, внедряването е прекъснато, нашият екип сега ще прекара ценно време, имаме ли грешка? нека да проучим, о, не - изглежда, че два теста мутираха едни и същи данни за семената

Example Пример за анти-образец: тестовете не са независими и разчитат на някаква глобална кука за захранване на глобални данни на DB

Example Да го направим правилен пример: Можем да останем в рамките на теста, всеки тест действа на свой собствен набор от данни

️ 10. Не хващайте грешки, очаквайте ги

Направете: Когато се опитвате да твърдите, че някакъв вход задейства грешка, може да изглежда правилно да се използва try-catch-накрая и да се твърди, че клаузата за улавяне е въведена. Резултатът е неудобен и подробен тестов случай (пример по-долу), който крие простото намерение за тест и очакванията за резултата

По-елегантна алтернатива е използването на едноредовото посветено твърдение на Chai: очаквайте (метод) .to.throw (или в Jest: очаквайте (метод) .toThrow ()). Абсолютно задължително е също така да се гарантира, че изключението съдържа свойство, което казва типа на грешката, в противен случай, давайки само обща грешка, приложението няма да може да направи много, вместо да покаже разочароващо съобщение на потребителя

В противен случай: ще бъде предизвикателство да се направи изводът от тестовите доклади (напр. Отчетите за CI) какво се обърка

Example Пример за анти-шаблон: Дълъг тестов случай, който се опитва да потвърди наличието на грешка с опит за улавяне

Да го направим правилен пример: Човекочитаемо очакване, което би могло да се разбере лесно, може би дори от QA или технически лидер

️10. Маркирайте вашите тестове

Направете: Различни тестове трябва да се изпълняват при различни сценарии: бърз дим, без IO, тестовете трябва да се стартират, когато разработчикът записва или ангажира файл, пълните тестове от край до край обикновено се изпълняват при подаване на нова заявка за изтегляне и др. Това може да се постигне чрез маркиране на тестове с ключови думи като #cold #api #sanity, така че можете да се поздравите с тестовата си сбруя и да извикате желания подмножество. Например, по този начин бихте извикали само групата за тест за здравина с Mocha: mocha - grep 'sanity'

В противен случай: Изпълняването на всички тестове, включително тестовете, които изпълняват десетки DB заявки, всеки път, когато даден предприемач направи малка промяна, може да бъде изключително бавен и да предпазва разработчиците от работа с тестове

Правилно правилно Пример: Маркирането на тестовете като „# студен тест“ позволява на тестовия бегач да изпълнява само бързи тестове (Cold === бързи тестове, които не правят IO и могат да се изпълняват често, дори когато разработчикът пише)

11. Други общи хигиени за добри изпитвания

Направете: Тази публикация е фокусирана върху съвети за тестване, които са свързани или поне могат да бъдат обяснени с Node JS. Този куршум обаче групира няколко съвета, които не са свързани с Node, които са добре известни

Научете и практикувайте принципите на TDD - те са изключително ценни за мнозина, но не се сплашвайте, ако не отговарят на вашия стил, не сте единственият. Помислете да напишете тестовете преди кода в стил червено-зелен рефактор, уверете се, че всеки тест проверява точно едно нещо, когато откриете грешка - преди да коригирате напишете тест, който ще открие тази грешка в бъдеще, нека всеки тест се провали поне веднъж преди да стане зелено, избягвайте всякаква зависимост от околната среда (пътеки, ОС и т.н.)

В противен случай: ще ви липсват перли на мъдростта, които са били събирани десетилетия

*** Раздел : Видове тестове ***

️ 12. Обогатете своето портфолио за тестване: Погледнете отвъд единичните тестове и пирамидата

Направете: Тестващата пирамида, макар и на 10 години, е страхотен и подходящ модел, който предлага три типа тестове и влияе върху стратегията за тестване на повечето разработчици. В същото време се появиха повече от шепа нови лъскави техники за тестване и се крият в сенките на тестващата пирамида. Предвид всички драматични промени, които видяхме през последните 10 години (Микросервизи, облак, без сървър), възможно ли е дори един доста стар модел да отговаря на * всички * видове приложения? не трябва ли светът на тестване да обмисля да посрещне нови техники за тестване?

Не ме разбирайте погрешно, през 2019 г. тестовата пирамида, тестовете на TDD и модулите все още са мощна техника и вероятно са най-доброто съвпадение за много приложения. Само като всеки друг модел, въпреки полезността си, той понякога трябва да греши. Например, помислете за приложение IOT, което поглъща много събития в шина на съобщения като Kafka / RabbitMQ, които след това се вливат в някакво хранилище на данни и в крайна сметка се питат от някои потребителски интерфейс за анализи. Трябва ли наистина да изразходваме 50% от нашия бюджет за тестване за писане на тестове на единици за приложение, което е ориентирано към интеграция и няма почти никаква логика? С увеличаването на разнообразието от типове приложения (ботове, крипто, Alexa-умения) по-големи са шансовете да се намерят сценарии, при които тестовата пирамида не е най-доброто съвпадение.

Време е да обогатите тестовото си портфолио и да се запознаете с повече типове тестове (следващите куршуми предполагат малко идеи), умни модели като тестовата пирамида, но също така съответстват на типовете тестове с проблемите в реалния свят, пред които сте изправени („Ей, нашия API е разбито, нека да напишем тестове за договори, насочени към потребителите! “), разнообразете вашите тестове като инвеститор, който изгражда портфолио въз основа на анализ на риска - преценете къде могат да възникнат проблеми и съпоставете някои мерки за превенция, за да смекчите тези потенциални рискове

Дума на внимание: аргументът TDD в софтуерния свят има типично фалшиво-дихотомия, някои проповядват да го използват навсякъде, други смятат, че е дяволът. Всеки, който говори в абсолюти, греши:]

В противен случай: Вие ще пропуснете някои инструменти с невероятна възвръщаемост на инвестициите, някои като Fuzz, косъм и мутация могат да осигурят стойност за 10 минути

Правилно правилно Пример: Синди Шридхаран предлага богато портфолио за тестване в невероятния си пост „Тестване на микросервизи - най-добрият начин“

Пример: YouTube: „Извън тестовете на единица: 5 блестящи Node.JS тестови типове (2018)“ (Йони Голдбърг)

5 лъскави техники за тестване от Йони Голдбърг

️ 13. Тестването на компоненти може да бъде най-добрата ви връзка

Направете: Всеки единичен тест обхваща малка част от приложението и е скъпо да покриете цялото, докато тестовете от край до край лесно покриват много земя, но са люспести и по-бавни, защо да не прилагате балансиран подход и да пишете тестове, които са по-големи от единичните тестове, но по-малки от тестовете за край до край? Тестването на компоненти е неразкритата песен на тестващия свят - те предоставят най-доброто от двата свята: разумна производителност и възможност за прилагане на TDD модели + реалистично и страхотно покритие.

Тестовете за компоненти се фокусират върху „единицата“ на Microservice, те работят срещу API, не се подиграват с нищо, което принадлежи на самата Microservice (напр. Реална DB или поне версията на паметта на тази DB), но заглушават всичко, което е външно като обаждания към други микросервизи. Правейки това, тестваме какво разгръщаме, приближаваме приложението отвън навътре и печелим голяма увереност за разумен период от време.

В противен случай: Може да прекарате дълги дни в писане на тестове на единици, за да разберете, че имате само 20% покритие на системата

Правилно правилен пример: Supertest позволява да се приближи до Express API по време на процеса (бързо и покрива много слоеве)

️ 14. Уверете се, че новите версии не нарушават API с помощта на потребителски договори

Направете: Така че Вашата Микросервиз има множество клиенти и вие изпълнявате множество версии на услугата поради съображения за съвместимост (поддържайки всички доволни). След това сменяте някое поле и „бум!“, Някой важен клиент, който разчита на това поле, се ядосва. Това е Catch-22 от света на интеграцията: За сървъра е много предизвикателно да вземе предвид всички множество клиентски очаквания - От друга страна, клиентите не могат да извършват никакви тестове, тъй като сървърът контролира датите на пускане. Потребителските договори и рамката PACT са родени за формализиране на този процес с много разрушителен подход - не сървърът определя тестовия план на себе си, а клиентът определя тестовете на ... сървъра! PACT може да запише очакванията на клиента и да постави на споделено място „брокер“, така че сървърът да може да издърпа очакванията и да стартира във всяка компилация, използвайки библиотеката PACT, за да открие нарушени договори - очакване на клиента, което не е изпълнено. По този начин, всички несъответствия в API на сървър-клиент се улавят рано по време на изграждане / CI и може да ви спести много разочарование

В противен случай: Алтернативите са изтощително ръчно тестване или страх при разполагане

Правилно правилен пример:

️ 15. Тествайте вашите средни продукти изолирано

Направете: Мнозина избягват тестване на Middleware, тъй като те представляват малка част от системата и изискват жив Express сървър. И двете причини са грешни - Middlewares са малки, но засягат всички или повечето от заявките и могат да бъдат тествани лесно като чисти функции, които получават {req, res} JS обекти. За да тествате функция за междинен софтуер, просто трябва да я извикате и да шпионирате (използвайки например Sinon) за взаимодействието с обектите {req, res}, за да гарантирате, че функцията извърши правилното действие. Библиотеката node-mock-http го отнема още повече и факторира {req, res} обектите, заедно с шпионирането на тяхното поведение. Например, той може да твърди дали състоянието на http, което е зададено на res обекта, съответства на очакването (Вижте пример по-долу)

В противен случай: грешка в Express среден софтуер === грешка във всички или повечето заявки

Правилно правилно Пример: Тестване на междинен софтуер изолирано без издаване на мрежови повиквания и събуждане на цялата машина на Express

️ 16. Измерване и рефактор с помощта на инструменти за статичен анализ

Направете: Използването на инструменти за статичен анализ помага чрез даване на обективни начини за подобряване на качеството на кода и поддържане на кода ви поддържан. Можете да добавите инструменти за статичен анализ във вашата CI изграждане, за да се откажете, когато открие мирис на код. Неговите основни продажби в сравнение с обикновеното свързване са възможността да се проверява качеството в контекста на множество файлове (например откриване на дублирания), извършване на разширен анализ (например сложност на кода) и проследяване на историята и напредъка на проблемите с кода. Два примера от инструменти, които можете да използвате, са Sonarqube (2600+ звезди) и Code Climate (1500+ звезди)

Кредит: Кийт Холидей

В противен случай: При лошо качество на кода, грешките и производителността винаги ще бъдат проблем, който никоя лъскава нова библиотека или съвременни функции не могат да поправят

Правилно правилно Пример: CodeClimat, търговски инструмент, който може да идентифицира сложни методи:

️ 17. Проверете готовността си за хаос, свързан с Възлите

Направете: Странно, повечето софтуерни тестове са само за логика и данни, но някои от най-лошите неща, които се случват (и наистина са трудни за смекчаване), са инфраструктурни проблеми. Например, тествали ли сте някога какво се случва, когато паметта на вашия процес е претоварена или когато сървърът / процесът умре, или вашата система за наблюдение осъзнава, когато API става 50% по-бавен ?. За да тествате и смекчите този тип лоши неща - Chaos Engineering е роден от Netflix. Тя има за цел да осигури информираност, рамки и инструменти за тестване на нашата устойчивост на приложение за хаотични проблеми. Например, един от известните му инструменти, маймуната хаос, убива на случаен принцип сървъри, за да гарантира, че нашата услуга все още може да обслужва потребителите и да не разчита на един сървър (има и версия на Kubernetes, kube-маймуна, която убива шушулките). Всички тези инструменти работят на хостинг / платформа ниво, но какво ще стане, ако искате да тествате и генерирате чист хаос на Node, като проверете как вашият Node процес се справя с неуловими грешки, неразрешено отхвърляне на обещания, v8 памет, претоварена с максимално разрешено от 1,7 GB или дали вашето UX остава задоволително, когато цикълът на събитията се блокира често? за справяне с това Написах, възел-хаос (алфа), който предоставя всякакъв вид хаотични действия, свързани с Възлите

В противен случай: Тук няма бягство, законът на Мърфи ще се удари във вашата продукция без милост

Да го направим правилно Пример: Node-хаос може да генерира всякакви шеги на Node.js, така че можете да тествате колко устойчивост е приложението ви към хаос

*** Раздел : Измерване на ефективността на изпитването ***

️ 18. Получавайте достатъчно покритие, за да сте уверени, ~ 80% изглежда е късметлията

Направете: Целта на теста е да получите достатъчно увереност за бързо движение, очевидно колкото повече код се тества, толкова по-уверен може да бъде екипът. Покритието е мярка за това колко кодови линии (и клонове, извлечения и т.н.) са достигнати от тестовете. И така, колко е достатъчно? 10–30% очевидно е твърде ниско, за да придобиете някакъв смисъл за коректността на изграждането, от другата страна 100% е много скъпо и може да измести фокуса ви от критичните пътеки към екзотичните кътчета на кода. Дългият отговор е, че това зависи от много фактори като вида на приложението - ако създавате следващото поколение Airbus A380 от 100% е задължително, за уебсайт с картинки за карикатура 50% може да е твърде много. Въпреки че повечето ентусиасти на тестовете твърдят, че десният праг на покритие е контекстуален, повечето от тях също посочват числото 80% като палец на правило (Фаулър: „в горните 80-те или 90-те години“), което по презумпция трябва да задоволи повечето приложения ,

Съвети за внедряване: Може да искате да конфигурирате непрекъснатата си интеграция (CI), за да имате праг на покритие (Jest връзка) и да спрете изграждането, което не отговаря на този стандарт (възможно е също да конфигурирате праг за компонент, вижте примера на код по-долу) , На всичкото отгоре, помислете за откриване на намаляване на покритието на сглобяване (когато новопостъпилият код има по-малко покритие) - това ще тласне разработчиците да повишат или поне да запазят количеството на тествания код. Всичко казано, покритието е само една мярка, количествено основаваща се, която не е достатъчна, за да каже стабилността на вашето тестване. И също може да се заблуди, както е илюстрирано в следващите куршуми

В противен случай: Доверието и числата вървят ръка за ръка, без всъщност да знаете, че сте тествали по-голямата част от системата - също ще има страх. и страхът ще ви забави

☺ Пример: Типичен отчет за покритието

Доклад за покритие в Истанбул

Правилно правилно Пример: Настройка на покритие за компонент (използвайки Jest)

Контекстуално покритие

️ 19. Проверете докладите за покритие, за да откриете непроверени площи и други странности

Направете: Някои проблеми се промъкват точно под радара и наистина е трудно да се намери с помощта на традиционни инструменти. Това всъщност не са грешки, но повече от изненадващо поведение на приложението, което може да има сериозно влияние. Например, често някои кодови области никога не се използват или рядко се използват - вие сте мислили, че класът "PricingCalculator" винаги определя цената на продукта, но се оказва, че всъщност никога не се използва, въпреки че имаме 10000 продукта в DB и много продажби ... Код на покритие отчетите ви помагат да разберете дали приложението се държи така, както вярвате. Освен това, той може също така да подчертае кои видове код не се тества - информирането, че 80% от кода е тестван, не показва дали критичните части са обхванати. Генерирането на отчети е лесно - просто стартирайте приложението си в производство или по време на тестване с проследяване на покритието и след това вижте цветни отчети, които подчертават колко често се извиква всяка кодова зона. Ако отделите време, за да разгледате тези данни - може да намерите някои gotchas

В противен случай: Ако не знаете кои части от кода ви остават непроверени, не знаете откъде могат да възникнат проблемите

Example Пример за анти-шаблон: Какво не е наред с този отчет за покритие? въз основа на сценарий в реалния свят, при който проследихме използването на приложението си в QA и открихме интересни модели за вход (Съвет: количеството грешки при влизане е непропорционално, нещо очевидно не е наред. Най-накрая се оказа, че някаква грешка във фронталенд продължава да удря приложен API за вход)

️ 20. Измервайте логическото покритие, като използвате тест за мутация

Направете: Метрикът на традиционното покритие често лъже: Може да ви покаже 100% покритие на кода, но нито една от вашите функции, дори и една, не връща правилния отговор. Как така? той просто измерва кои редове от кода посещава тестът, но не проверява дали тестовете действително са тествали нещо - потвърдено за правилния отговор. Като някой, който пътува за бизнес и показва паспортните си печати - това не доказва никаква свършена работа, само че е посетил няколко летища и хотели.

Мутационното тестване е тук, за да помогне чрез измерване на количеството код, който всъщност е ИЗПИТВАНО, а не само ПОСЕТЕН. Stryker е JavaScript библиотека за тестване на мутации и реализацията е наистина чиста:

(1) тя умишлено променя кода и „растения бъгове“. Например кодът newOrder.price === 0 става newOrder.price! = 0. Тези „бъгове“ се наричат ​​мутации

(2) тя провежда тестовете, ако всички успеят, тогава имаме проблем - тестовете не са служили на целта им за откриване на бъгове, мутациите са така наречени оцелели. Ако тестовете се провалиха, значи страхотни, мутациите бяха убити.

Знанието, че всички или повечето мутации са били убити, дава много по-голяма увереност в сравнение с традиционното покритие и времето за настройка е подобно

В противен случай: ще се заблудите, че 85% покритие означава, че вашият тест ще открие грешки в 85% от вашия код

Example Пример за анти-модел: 100% покритие, 0% тестване

Правилно правилен пример: отчетите Stryker, инструмент за тестване на мутации, откриват и отчитат количеството код, който не е тестван (мутации)

Доклад на Stryker - Знаейки, че всички или повечето мутации са убити, дава много по-голяма увереност в сравнение с традиционното покритие и времето за настройка е подобно

*** Раздел : ОКИ и други мерки за качество ***

️ 21. Обогатете вашите лайнери и прекъснете конструкциите, които имат проблеми с връзването

Направете: Лайнерите са безплатен обяд, с настройка за 5 минути получавате безплатно автопилот, който пази вашия код и улавя значителни проблеми, докато пишете. Изминаха дните, в които облицовката беше за козметика (без полуколони!). В днешно време Linters може да хване тежки проблеми като грешки, които не са хвърлени правилно и загуба на информация. Освен основния си набор от правила (като стандарт на ESLint или стил Airbnb), помислете за включване на някои специализирани линтери като eslint-plugin-chai-очакване, които могат да откриват тестове без твърдения, eslint-plugin-obećanje може да открива обещания без решение (вашето код никога няма да продължи), eslint-плъгин-сигурността, която може да открие нетърпеливи изрази, които могат да се използват за DOS атаки, и eslint-plugin-you-do-need-lodash-underscore е в състояние да алармира, когато кодът използва методи на библиотеката на полезността които са част от основните методи на V8 като Lodash._map (…)

В противен случай: помислете за дъждовен ден, в който вашата продукция продължава да се срива, но дневниците не показват следата на стека на грешки. Какво стана? Кодът ви погрешно хвърли обект без грешка и следата от стека беше изгубена, добра причина да ударите главата си в тухлена стена. Настройка на 5-минутния лайнер може да открие това TYPO и да ви спести деня

Example Пример за анти шаблон: Грешният обект Грешка е хвърлен по погрешка, няма да се появи следа за стек за тази грешка. За щастие ESLint хваща следващата производствена грешка

Грешният обект Error е хвърлен по погрешка, няма да се появи следа за стека за тази грешка. За щастие ESLint хваща следващата производствена грешка

️ 22. Съкратете контура за обратна връзка с местния разработчик-CI

Прави ли: Използване на потребителски интерфейс с лъскави проверки на качеството като тестване, обвързване, проверка на уязвимости и т.н.? Помогнете на разработчиците да изпълняват този тръбопровод и локално, за да изискат незабавна обратна връзка и да съкратят контура за обратна връзка. Защо? един ефикасен процес на тестване представлява много итеративни вериги: (1) изпробване -> (2) обратна връзка -> (3) рефактор. Колкото по-бърза е обратната връзка, толкова повече итерации за подобрения може да се извърши на един модул и усъвършенства резултатите. От друга страна, когато обратната връзка е закъсняла да дойде по-малко итерации на подобрения, могат да бъдат опаковани в един ден, екипът може вече да премине към друга тема / задача / модул и може да не е готов за усъвършенстване на този модул.

На практика някои доставчици на CI (Пример: CLI зареждане CLI) позволяват локализирането на тръбопровода. Някои търговски инструменти като wallaby предоставят високо ценни и тестващи прозрения като прототип на програмист (без принадлежност). Като алтернатива можете просто да добавите npm скрипт към package.json, който изпълнява всички команди за качество (напр. Тест, подсказка, уязвимости) - използвайте инструменти като едновременно за паралелизация и ненулев код за излизане, ако някой от инструментите се е провалил. Сега разработчикът трябва само да извика една команда - напр. „Npm run quality“ - за да получите незабавна обратна връзка. Помислете също за прекъсване на ангажимент, ако проверката на качеството не е била успешна с помощта на джакузи (хъски може да помогне)

В противен случай: Когато качествените резултати пристигнат в деня след кода, тестването не става текуща част от развитието, а след официалния артефакт

Правилно правилно Пример: npm скриптове, които извършват проверка на качеството на кода, всички се изпълняват паралелно при поискване или когато разработчик се опитва да натисне нов код

️ 23. Извършете тестване на e2e върху истинско огледало за производство

Testing Извършване: Тестване от край до край (e2e) са основното предизвикателство за всеки тръбопровод на CI - създаването на идентично ефемерно огледално производство в движение с всички свързани облачни услуги може да бъде досадно и скъпо. Намирането на най-добрия компромис е вашата игра: Docker-compose позволява да се изработи изолирана докеризирана среда с идентични контейнери, използвайки един обикновен текстов файл, но технологията за създаване на резервни копия (например мрежа, модел на внедряване) е различна от реалните продукции. Можете да го комбинирате с „AWS Local“, за да работите със стърнище от истинските AWS услуги. Ако отидохте без сървър множество рамки като без сървър и AWS SAM позволява локално извикване на Faas код.

Огромната екосистема Kubernetes тепърва ще формализира стандартен удобен инструмент за локално и CI-огледало, въпреки че много нови инструменти се стартират често. Един от подходите е използването на „сведени до минимум Kubernetes“, като се използват инструменти като Minikube и MicroK8s, които наподобяват истинските неща, но и с по-малко разходи. Друг подход е тестване на отдалечен „реални Kubernetes“, някои доставчици на CI (например Codefresh) имат естествена интеграция с Kubernetes среда и улесняват пускането на тръбопровода CI върху реалното нещо, други позволяват персонализирани скриптове срещу отдалечени Kubernetes.

В противен случай: Използването на различни технологии за производство и тестване изисква поддържане на два модела на внедряване и поддържа разработчиците и ops екипа разделени

Пример: CI тръбопровод, който генерира Kubernetes клъстер в движение (Credit: Dynamic-Environment Kubernetes)

разполагане:
етап: разгръщане
изображение: register.gitlab.com/gitlab-examples/kubernetes-deploy
скрипт:
- ./configureCluster.sh $ KUBE_CA_PEM_FILE $ KUBE_URL $ KUBE_TOKEN
- kubectl създаване на ns $ NAMESPACE
- kubectl създаване на тайна -n $ NAMESPACE docker-register gitlab-register --docker-server = "$ CI_REGISTRY" --docker-username = "$ CI_REGISTRY_USER" --docker-password = "$ CI_REGISTRY_PASSWORD" --docker-email = "$ GITLAB_USER_EMAIL"
- mkdir .генериран
- ехо „$ CI_BUILD_REF_NAME- $ CI_BUILD_REF“
- sed -e "s / TAG / $ CI_BUILD_REF_NAME- $ CI_BUILD_REF / g" шаблони / сделки.yaml | тройник ".генериран / сделки.yaml"
- kubectl прилага - namespace $ NAMESPACE -f .generated / сделки.yaml
- kubectl прилага - namespace $ NAMESPACE -f шаблони / my-sock-shop.yaml
околен свят:
име: тест-за-ци

️ 24. Паралелизирайте изпълнението на теста

Направете: Когато направите правилно, тестването е вашият приятел 24 часа в денонощието и предоставя почти незабавна обратна връзка. На практика изпълнението на 500 CPU-ограничен тест на единица може да отнеме твърде много време. За щастие, съвременните тестови бегачи и CI платформи (като Jest, AVA и Mocha разширения) могат да паралелизират теста в множество процеси и да постигнат значително подобрение във времето за обратна връзка. Някои доставчици на CI също паралелизират тестовете в контейнерите (!), Което още повече скъсява контура за обратна връзка. Независимо дали локално над множество процеси или над някои облачни CLI, използващи множество машини - паралелизиране на търсенето, запазвайки тестовете автономни, тъй като всеки може да работи на различни процеси

В противен случай: Получаването на резултати от теста 1 час след натискане на нов код, тъй като вече кодирате следващите функции, е чудесна рецепта за правене на тестовете по-малко уместни

Правилно правилно Пример: Mocha паралел и Jest лесно изпреварват традиционната Mocha благодарение на тестовата паралелизация (Кредит: JavaScript Test-Runners Benchmark)

️ 25. Стойте далеч от правни въпроси, като използвате проверка за лиценз и плагиатство

Да: Проблемите с лицензирането и плагиатството вероятно не са основната ви грижа в момента, но защо да не поставите отметка и след 10 минути? Купа npm пакети като проверка на лиценз и проверка на плагиатство (комерсиална с безплатен план) могат лесно да бъдат изпечени във вашия CI тръбопровод и да проверят за скърби като зависимости с ограничителни лицензи или код, който бе копиран от Stackoverflow и очевидно нарушава някои авторски права

В противен случай: неволно, разработчиците могат да използват пакети с неподходящи лицензи или да копират и поставят търговски код и да се натъкнат на правни проблеми

Правилно правилен пример:

// инсталирайте проверка на лиценз във вашата CI среда или също и локално
npm install -g лиценз за проверка
// помолете го да сканира всички лицензи и да се провали с код за излизане, различен от 0, ако намери неразрешен лиценз. CI системата трябва да улови този провал и да спре изграждането
лиценз за проверка - обобщение - failOn BSD

️26. Постоянно проверявайте за уязвими зависимости

Да: Дори и най-уважаваните зависимости като Express имат известни уязвимости. Това може лесно да се опитоми с помощта на инструменти на общността като npm одит или търговски инструменти като snyk (предлагайте също безплатна версия на общността). И двете могат да бъдат извиквани от вашия потребителски потребител при всяка компилация

В противен случай: Поддържането на вашия код чист от уязвимости без специални инструменти ще изисква постоянно да следите онлайн публикации за нови заплахи. Доста досаден

Пример: Резултати от одит на NPM

️ 27. Автоматизирайте актуализациите на зависимостите

Направете: Най-новото въвеждане на пакет-lock.json представи сериозно предизвикателство (пътят към ада е павиран с добри намерения) - по подразбиране сега пакетите вече не получават актуализации. Дори екип, изпълняващ много нови внедрения с „npm install“ и „npm update“, няма да получи нови актуализации. Това води до версии на пакетите, зависими от подпарите, или в най-лошия случай до уязвим код. Сега екипите разчитат на добра воля и памет на разработчиците, за да актуализират ръчно пакета.json или да използват ръчно инструменти като ncu. По-надежден начин би могъл да бъде автоматизирането на процеса на получаване на най-надеждните версии на зависимостта, въпреки че няма решения за сребърни куршуми, но има два възможни пътя за автоматизация: (1) CI може да провали конструкции, които имат остарели зависимости - използвайки инструменти като 'npm остарели 'или' npm-check-updates (ncu) '. Това ще наложи разработчиците да актуализират зависимостите. (2) Използвайте търговски инструменти, които сканират кода и автоматично изпращат заявки за изтегляне с актуализирани зависимости. Останал един интересен въпрос е каква трябва да бъде политиката за актуализиране на зависимостта - актуализирането на всеки пластир генерира твърде много режийни разходи, актуализирането на правото при пускането на мажор може да сочи към нестабилна версия (много пакети, които се окажат уязвими още в първите дни след освобождаването, вижте инцидентът с обхвата на eslint). Ефективната политика за актуализиране може да позволи някакъв „период на придобиване“ - нека кодът изостава за @latest за известно време и версии, преди да считате локалното копие за остаряло (напр. Локалната версия е 1.3.1 и версията на хранилището е 1.3.8)

В противен случай: Вашето производство ще изпълнява пакети, които са изрично маркирани от автора им като рискови

Пример: ncu може да се използва ръчно или в CI тръбопровод, за да открие до каква степен кодът изостава от най-новите версии

️ 28. Други, свързани с Node, CI съвети

Направете: Тази публикация е фокусирана върху съвети за тестване, които са свързани или поне могат да бъдат обяснени с Node JS. Този куршум обаче групира няколко съвета, които не са свързани с Node, които са добре известни

  1. Използвайте декларативен синтаксис. Това е единствената опция за повечето доставчици, но по-старите версии на Jenkins позволяват използване на код или потребителски интерфейс
  2. Изберете доставчик, който има вградена поддръжка на Docker
  3. Проваляйте се рано, първо стартирайте най-бързите си тестове. Създайте стъпка / крайъгълен камък на „тестване на дим“, която групира множество бързи проверки (напр. Свързване, тестове на единици) и предоставя бърза обратна връзка на кодовия изпълнител
  4. Улеснявайте прегледа на всички артефакти, включително доклади от тестове, доклади за покрития, доклади за мутации, дневници и т.н.
  5. Създайте множество тръбопроводи / задания за всяко събитие, използвайте повторно стъпки между тях. Например, конфигурирайте задание за клонове за функции и различен за главен PR. Нека всяка логика за повторно използване с помощта на споделени стъпки (повечето доставчици предоставят някакъв механизъм за повторно използване на код
  6. Никога не вграждайте тайни в декларация за работа, вземете ги от секретен магазин или от конфигурацията на работата
  7. Изрично bump версия в версия на сглобяване или поне се уверете, че програмистът е направил това
  8. Създайте само веднъж и извършете всички проверки на единичния артефакт за изграждане (например изображение на Докер)
  9. Тествайте в ефимерна среда, която не се движи в състояние между компилации. Кеширането на node_modules може да бъде единственото изключение

В противен случай: ще ви липсват години на мъдрост

29. Изграждане на матрица: Изпълнете същите CI стъпки, използвайки множество версии на Node

Направете: Проверката на качеството е свързана със серендитивността, колкото повече основание покривате по-късметлия, който получавате при откриването на проблеми рано. Когато разработва пакети за многократна употреба или работи с множество клиенти с различни конфигурации и версии на Node, CI трябва да изпълни тръбопровода от тестове за всички престановки на конфигурации. Например, ако приемем, че използваме mySQL за някои клиенти и Postgres за други - някои доставчици на CI поддържат функция, наречена „Матрица“, която позволява стартирането на костюма за тестване срещу всички престановки на mySQL, Postgres и множество версии на Node като 8, 9 и 10. Това се прави с помощта на конфигурация само без допълнителни усилия (ако приемем, че имате тестване или други проверки на качеството). Други потребителски интерфейси, които не поддържат матрица, може да имат разширения или ощипвания, за да позволят това

В противен случай: Значи след като свършим цялата тази усилена работа по писане на тестове, ще оставим ли бъгове да се промъкнат само поради проблеми с конфигурацията?

☺ Пример: Използване на Travis (доставчик на CI) изграждане на дефиниция за изпълнение на един и същ тест в множество версии на Node

език: node_js
node_js:
  - "7"
  - "6"
  - "5"
  - "4"
Инсталирай:
  - npm инсталиране
скрипт:
  - тест за изпълнение на npm

Благодаря ти. Други статии, които може да ви харесат

  • Контролен списък: Най-добри практики за производство на Node.js (август 2018 г.)
  • 19 начина да станете по-добър разработчик на Node.js през 2019 г.
  • Най-добри практики за сигурност на Node.js (септември 2018 г.)
  • YouTube: 5 модерни и лъскави техники за тестване
  • Най-добри практики на Node.js - 79 най-добри практики за стабилно приложение на Node

Искате ли повече? следвайте ме в Twitter

Имате ли свой съвет за тестване? PR тук и със сигурност ще актуализирам тази статия