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

Преди няколко дни започнах да работя върху малък проект на Nodejs, за който реших да не използвам никаква библиотека освен тези, вградени в ядрото на Nodejs. Започнах и всичко беше перфектно, но се стигна до момента, че трябваше да пиша контролни. Както казах по-рано, няма библиотеки на трети страни, няма мока, чай или други библиотеки за тестване. Това означава, че трябваше да напиша собствен тестов инструмент и да използвам библиотеката за твърдения на възела, за да направя моето тестване.

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

Изисквания

Всичко, което се изисква за този проект, е Node — която и да е от LTS версиите, инсталирани на вашата машина, не забравяйте, че казах без библиотеки, така че няма да имаме нужда от npm, без package.json, освен ако може би не искате да го хоствате на Heroku или да използвате който и да е хостинг или CI/CD платформи.

Nodejs сървър

За да започнем, ще трябва да създадем сървър на възел, но това не е изискване, ако добавяте тестовия инструмент към вече съществуващо приложение. Продължавайки напред, ако нямате инсталиран възел, отидете на nodejs.org и изтеглете някоя от LTS версиите. Ще препоръчам LTS версия 10, тъй като някои от актуализациите в по-новата версия може да не се поддържат.

Създайте име на файл index.js или произволно име, което искате

В горния код използвах Http модула в node, за да създам сървър. Направих това, като създадох обект, наречен сървър, така че всички други функции или логика да могат да му бъдат предадени като методи. След това създадох метод, наречен httpServer, който използва Http модула за създаване на сървър. Накрая създаденият сървър слуша на порт. Портът първо проверява средата, в която се изпълнява, ако има порт, или по подразбиране е 3000. Изпълнете командата по-долу в терминала, за да стартирате сървъра.

$ node index.js

Горната команда ще отпечатате server is running on 3000 във вашата конзола. Страхотно, създадохте сървър на Nodejs, но това не е основният ни фокус тук. Целта е да се изгради тестов инструмент във възел, без да се използва библиотека на трета страна.

Тестовият бегач

Нека създадем файла за тестване, той може да бъде във вашата основна директория. Тъй като това не е голям проект, няма да се тревожим толкова много за файловата структура, но най-добрата практика е, че тестовите файлове могат да отидат в отделна папка или да бъдат част от всяка папка на модула. Продължавайки напред, именувайте файла test.js или index.test.js

Това е, което се случва в кода по-горе, първо създадох обект на приложение const _app = {}. FYI: Това е стилът на кодиране, който адаптирах, като създадох контейнерни обекти за всеки модул. Това улеснява експортирането и също така знае къде отива всяка логика. Можете да подходите към това по начина, по който работи за вас. След обекта на приложението създадох първия метод, който е основната логика за извършване на тестването, нарекох го runTest. Той преминава през наличния тест, като използва for цикъл, за да премине през обекта _app.tests, който ще създам по-късно. Той ще проведе всички налични тестове.

for(let key in _app.tests){
  if(_app.test.hasOwnProperty(key)){
     const subTests = _app.tests[key];
// rest of the code...

В първата част на цикъла бегачът ще премине през _app.tests, който съдържа различните типове тестове. За по-лесно разбиране, нека обясня логиката. The _app е основният контейнер, който съдържа всички други методи, след това има подконтейнер, наречен tests, който ще съдържа типа тестове. В моя случай това е единично тестване и API тестване. След това отделните тестове ще се провеждат във всеки контейнер на обект от тип тестване. По този начин първи цикъл за получаване на типа тестове и последващ цикъл за получаване на единичния тест. Кодът по-долу илюстрира това, което обяснявам:

_app.tests = {
 'unit': {
         // individual unit tests goes here
       },
 'api': {
         // individual api tests goes here
       }
}

Или много по-проста версия ще бъде:

_app.tests.unit = {
  // individual unit tests goes here
}

_app.tests.api = {
  // individual api tests goes here
}

И двата кодови блока са еднакви, но просто го правят по-лесен за разбиране. Това ще е необходимо по-късно, беше за по-ясни неща. Така че първият for цикъл в този случай получава индивидуалния тип тест, който е единица и API. След това следва втори цикъл за получаване на действителния тестов код в обекта за тестване на единица или обекта за тестване на API съответно.

for(let testName in subTests){
        if(subTests.hasOwnProperty(testName)){
// rest of the code

След това проверява дали има тест, самоизвикваща се функция ще извика отделните тестови функции в блок try-catch. Опитът за улавяне е да предотврати грешката при твърдението да наруши цикъла, тъй като хвърля грешка, ако тестът е неуспешен. По този начин цикълът ще продължи в изключението и ще произведе съответно резултати в _app.showTestResult. Ако броят на тестовете стане равен на ограничението, това е броят на тестовете в обект от тип тест. showTestResult и countTestще бъдат създадени скоро.

(() => {
             const currentTest = testName;
             const testValue = subTests[testName];
             // call the test
             try{  
              testValue(() => {
                 console.log('\x1b[32m%s\x1b[0m', currentTest);
                 count ++;
                 success ++;
                 if(count == limit){
                   _app.showTestResult(success, limit, errors)
                 }
               });
             }catch (e) {
               errors.push({
                'name': testName,
                'error': e
               })
               console.log('\x1b[31m%s\x1b[0m', currentTest);
               count ++;
               if(count == limit){
                   _app.showTestResult(success, limit, errors)
               } 
             }
        })()
// rest on the code...

Обяснявайки по-нататък, в функцията за самоизвикване името на теста се присвоява на текущата тестова функция. console.log(‘\x1b[32m%s\x1b[0m’, currentTest) е функционалност на метода console.log, която прави възможно оценката на javascript в конзолата и стилизирането му. console.log(‘\x1b[32m%s\x1b[0m’, currentTest) ще оцвети резултата от втория аргумент на console.log в зелено, а console.log(‘\x1b[31m%s\x1b[0m’, currentTest) ще го оцвети в червено.

На този етап ще създам тези функции, които предстои да бъдат създадени. Ще започна с testCount.

Точно като метода runner, той задава начален брояч на 0, прави for цикъл два пъти, за да получи индивидуалния тест, увеличава брояча и след това го връщаме в края. Сега ще добавя метода showTestResult.

Той показа резултатите от нашите тестове, подобно на начина, по който повечето библиотеки за тестване показват резултатите от тестовете.

Той отпечатва резултатите от теста в конзолата и също така отпечатва подробностите за неуспешен тест.

Това ще бъде всичко за тестовия бегач. Не е необходимо да го експортираме, а да импортираме индивидуалния тестов код в този файл. След това го предавате като стойност на обектите в _app.test object, в моя случай или API, или модул.

_app.test.unit = require('./unit.test');

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

const returnOne = () => {
  return 1;
}

Ще импортирам библиотеката с твърдения във възел, за да я използвам при тестване на функцията returnOne.

const assert = require('assert');

_app.test.unit['returnOne function should return 1'] = done => {
   const val = returnOne();
   assert.strictEqual(val, 1);
   done();
}

Името на теста беше предадено като ключ към единичния обект и тестовата функция му беше присвоена като стойност, която приема done като аргумент. В рамките на функцията извикваме функцията returnOne и я присвояваме на const val. Което проверихме дали върнатата стойност е стриктно равна на 1. Библиотеката assert в Nodejs има много налични методи за assertion за извършване на тестване.

За да видите резултата, стартирайте $ node index.test според начина, по който наименувах моя файл. Ще бъде $ node <whatever you name your file>. Можете да добавите или пропуснете разширението js. Nodejs така или иначе ще стартира файла.

Резултатът от теста ще се покаже в конзолата като във функцията showTestResult. В нашия случай returnOne function should return 1 ще бъде в зелено, защото ще издържи теста.

Заключение

Не гледайте на тази статия като засрамване на библиотеки или молба да не ги използвате, но едно предимство като разработчик на софтуер, което можете да имате, е познаването на основната технология или логиката зад библиотеките и рамките, но не само да ги използвате. Помага за лесно отстраняване на грешки и разрешаване на проблеми, когато ги срещнете. Чувствайте се свободни да разгледате внимателно и коментирате проблемите и грешките, които може да срещнете, преминавайки през този урок. Проверете случай на употреба в това repo на Github