Источник: Nuances of Programming
Фреймворк модульного тестирования
Суть модульного тестирования заключается в проверке небольших изолированных фрагментов кода. Если тест использует внешний ресурс, например сеть или базу данных, он уже не является модульным.
Фреймворки модульного тестирования описывают тесты в человекочитаемом формате, чтобы те, кто не связан с технической сферой, смогли в них разобраться. Однако даже если вы являетесь представителем технической сферы, тесты в формате BDD могут значительно облегчить поиск проблемы.
Например, чтобы протестировать следующую функцию:
Нужно написать тест jasmine spec:
① Функция describe(string, function) определяет тестовый набор — набор индивидуальных спецификаций теста.
② Функция it(string, function) определяет отдельную спецификацию теста, которая содержит одно или несколько ожиданий теста.
③ Выражение expect(actual) — это фактическое значение в тесте. В сочетании с Matcher он описывает ожидаемый фрагмент поведения в приложении.
④ Выражение matcher(expected) — это Matcher. Он выполняет логическое сравнение ожидаемого значения с фактическим, переданным функции expect. Если они имеют ложное значение, спецификация не выполняется.
Setup и teardown
В некоторых случаях при тестировании функции необходимо выполнить настройку. Например, создать несколько тестовых объектов. Кроме того, после завершения теста может потребоваться очистка. Например, удаление файлов с жесткого диска.
Эти действия называются setup и teardown (для очистки). В Jasmine есть несколько функций для упрощения этого процесса:
beforeAll вызывается один раз перед запуском всех спецификаций в тестовом наборе.
afterAll вызывается один раз после завершения всех спецификаций в тестовом наборе.
beforeEach вызывается перед каждой спецификацией теста, если функция запущена.
afterEach вызывается после выполнения каждой спецификации теста.
Использование в Node
В проекте Node файлы модульного теста определяются в папке test в одной директории с папкой src:
Тест содержит файлы спецификации, которые являются модульными тестами для файлов в папке src. В package.json test находится в разделе script.
Если в командной строке запущен npm run test, тестовый фреймворк jest запустит все файлы спецификации в папке test и отобразит результат в командной строке.
Переходим к созданию собственного тестового фреймворка, который будет работать на Node.
В нашем тестовом фреймворке будет CLI-часть, с помощью которой он будет запускаться из командной строки. Вторая часть — исходный код тестового фреймворка, который будет находиться в папке lib.
Начнем с создания проекта Node:
Устанавливаем зависимость chalk, с помощью которой мы будем раскрашивать результаты тестов: npm i chalk.
Создаем папку lib, в которой будут находиться файлы:
mkdir lib
Создаем папку bin, поскольку фреймворк будет использоваться в качестве инструмента CLI Node:
mkdir bin
Начнем с создания файла CLI.
Создаем файл kwuo в папке bin и добавляем следующее:
#!/usr/bin/env nodeprocess.title = 'kwuo'require('../lib/cli/cli')
Устанавливаем шебанг с указанием на /usr/bin/env, чтобы запускать этот файл без команды node. Заголовок процесса устанавливаем на «kwuo» и запрашиваем файл «lib/cli/cli». Эти действия вызывают файл cli.js, который запускает процесс тестирования.
Переходим к установке и заполнению lib/cli/cli.js.
mkdir lib/cli
touch lib/cli/cli.js
Этот файл находит папку test, получает все файлы из нее и запускает их.
Прежде чем реализовывать «lib/cli/cli.js», нужно установить глобальные переменные. Функции describe, it, expect, afterEach, beforeEach, afterAll и beforeAll используются в тестовых файлах:
В начале работы мы добавили библиотеку chalk, чтобы обозначить неудачные тесты красным цветом, а пройденные — зеленым. Сокращаем console.log до log.
Затем устанавливаем массивы beforeEachs, afterEachs, afterAlls и beforeAlls. beforeEachs содержит функции, которые вызываются в начале функции it, к которой он прикреплен. afterEachs вызывается в конце it. beforeEachs и afterEachs вызываются в начале и конце describe.
Устанавливаем Totaltests, которая будет содержать количество запущенных тестов. passTests содержит количество пройденных тестов, а failTests — количество неудачных тестов.
stats собирает статистику каждой функции describe, а curDesc обозначает текущую функцию describe, запущенную для сбора данных тестирования. currIt содержит запущенную в данный момент функцию it, выполняющую сбор тестовых данных.
Устанавливаем функции beforeEach, afterEach, beforeAll и afterAll, которые передают аргумент функции в соответствующие массивы: afterAll — в массив afterAlls, beforeEach — в beforeEachs и т. д.
Также у нас есть функция expect. Она выполняет тестирование:
Функция expect принимает аргумент для тестирования и возвращает объект, который содержит функции matcher. В данном случае она возвращает объект с функциями toBe и toEqual с аргументом, который они используют для сопоставления с аргументом значения, предоставляемым функцией expect. toBe использует === для сопоставления аргумента значения с ожидаемым аргументом. toEqual использует == для проверки фактического значения с ожидаемым. Функции увеличивают переменные passedTests и failedTests в зависимости от результатов теста, а также записывают статистику в переменную currIt. Мы используем только две функции matcher, однако их гораздо больше:
- toThrow
- toBeNull
- toBeFalsy
- и т. д.
Вы также можете реализовать их.
Переходим к функции it. Аргумент desc содержит имя описания теста, а fn — функцию. Сначала он обрабатывает beforeEachs, устанавливает статистику и вызывает функцию fn и afterEachs.
Функция describe выполняет те же действия, что и it, но вызывает beforeAlls и afterAlls в начале и в конце.
Функция showTestsResults анализирует массив stats и печатает пройденные и неудачные тесты на терминале.
Таким образом, мы реализовали и установили все функции в объект global, чтобы тестовые файлы могли вызывать их без ошибок.
Вернемся к «lib/cli/cli.js»:
// lib/cli/cli.js
const path = require('path')
const fs = require('fs')
const { showTestsResults } = require('./../')
Сначала он импортирует функцию showTestsResult из «lib/index.js», которая отобразит результат запуска тестовых файлов в терминале. Кроме того, импорт этого файла установит глобальные переменные.
Продолжим:
Функция run является главной и запускает весь процесс. Она выполняет поиск папки test с помощью searchTestFolder, получает тестовые файлы в массиве — getTestFiles, а затем просматривает массив тестовых файлов и запускает их с помощью runTestFiles.
searchTestFolder использует метод fs#existSync, чтобы проверить, существует ли папка «test/» в проекте.
Функция getTestFiles читает содержимое папки «test» с помощью метода fs#readdirSync и возвращает его.
runTestFiles принимает файлы в массиве, просматривает их с помощью метода forEach и использует метод require для запуска каждого файла.
Структура папки kwuo выглядит следующим образом:
Тестирование фреймворка
Попробуем протестировать наш фреймворк с реальным проектом Node.
Создаем проект:
mkdir examples
mkdir examples/math
cd examples/math
npm init -y
Создаем папку src и добавляем add.js и sub.js:
mkdir src
touch src/add.js src/sub.js
Содержимое add.js и sub.js:
Создаем папку test и тестовые файлы:
mkdir test
touch test/add.spec.js test/sub.spec.js
Файлы спецификации будут проверять функции add и sub в add.js и sub.js:
Теперь воспользуемся «test» в разделе «scripts» в package.json для запуска тестового фреймворка:
Запускаем npm run test в командной строке:
$ npm run test
Результат теста будет следующим:
В статистике указано количество пройденных тестов в общей сложности и список тестовых наборов с маркировкой пройден/неудачный. «add Hello + World» возвращает «HelloWorld», однако ожидалось «Hello». После исправления и повторного запуска, все тесты будут пройдены.
Как видите, созданный тестовый фреймворк работает, как Jest и Jasmine.
Код на Github
Здесь можно найти полную версию кода.
Фреймворк можно использовать из NPM:
cd IN_YOUR_NODE_PROJECT
npm install kwuo -D
Измените «test» в package.json на следующее:
Читайте также:
Читайте нас в телеграмме и vk
Перевод статьи Chidume Nnamdi 🔥💻🎵🎮: Build Your Own JavaScript Testing Framework