Новичкам приходится тратить время, чтобы понять, почему код не работает как положено, из-за этого проект не продвигается. Это бывает, когда юнит-тесты пишутся неправильно.
Сейчас рассмотрим юнит-тестирование с JavaScript-фреймворком Jest.
- Установки и настройки
- Матчеры: toBe, toEqual, toMatch и прочее
- Тестирование
- Несколько тестов
- Пропускаем ненужные
Тестирование в жизни
Понять важность Тестирования как процесса, а не только в ИТ-сфере, поможет пример из жизни. Представим, что есть бывший в употреблении автомобиль, с виду приличный. Его владелец утверждает, что автомобиль “как новый, на 10 из 10”. Конечно, надо проверить автомобиль перед покупкой, и глупо будет если этого не сделать. Так же и с софтом.
Понимание, как и зачем тестируется софт, полезно и разработчику, особенно если речь идет о юнит-тестах. Это улучшает его эффективность, дает понимание когда и почему софт не работает, или неудобен для клиента, помогает писать быстрый, хорошо структурированный код.
Юнит-тестирование
Юнит-тестирование (также называемое “модульным”) — это, как следует из второго названия, подвид тестирования, который сосредотачивается на тестировании отдельных модулей кода.
Jest
Этот фреймворк разработан Facebook, что уже должно о чем-то говорить.
Как утверждают создатели фреймворка из Jest Core Team, “он позволяет быстро писать легкие, удобные и функциональные тесты с отличным API”
Установки и настройки
1. Устанавливаем npm
Он может быть уже установлен, и чтобы проверить это, вводим в терминале npm —version. Если не установлен, идем сюда и читаем документацию, как установить npm.
2. Создаем package.json
Это можно сделать двумя способами. Или инициализация после команды npm init создаст package.json, но для этого надо сначала кое-что настроить, то есть ввести своё описание, присвоить название, и возможно ввести еще что-то дополнительное. Второй путь: ввод команды npm init -y, чтобы проскочить этот этап конфигурации, и тогда получим файл дефолтной конфигурации.
3. Устанавливаем Jest
Вводим в терминале npm install —save-dev jest. При этом автоматически пропишется нужная зависимость.
Далее идем к файлу package.json, находим в нем, в начале, раздел “scripts” и меняем в нем значение “test” на “jest”.
Все готово к тестированию.
Матчеры
Перед тем как погрузиться в процесс, нужно понять, что такое матчеры (matchers).
Как утверждается в документации, Jest применяет “матчеры” для проверки тестовых значений в различных ситуациях.
Перейдем к практике.
toBe
Матчер toBe означает полную эквивалентность. Другими словами “===”.
test('10 plus 10 is 20', () => { expect(10 + 10).toBe(20) })
Такой тест пройдет успешно.
test('numbers is the same as sameNumbers', () => { const numbers = [1, 2, 3] const sameNumbers = [1, 2, 3] expect(numbers).toBe(sameNumbers) })
А вот этот тест выше — упадет, даже если значения те же — потому что массивы имеют разные адреса в памяти. (Если этот нюанс вызывает затруднения, надо почитать что-то базовое об управлении памятью в JavaScript).
Как видим, тестируя массивы (или объекты), применять toBe — не лучшая опция. Для этого существует матчер удобнее — toEqual.
toEqual
Этот матчер почти всегда подойдет. Он означает то что называется “глубокая эквивалентность” (так называемая “deep equality”), то есть он попарно сопоставляет каждое значение в объектах или массивах. Поэтому если в предыдущем тесте применить toEqual вместо toBe, то тест пройдет.
toMatch
Сравнивая строки с регулярными выражениями, применяем toMatch.
test('there is an "e" in hello', () => { expect('hello').toMatch(/e/) })
toContain
Если хотим проверить, входит ли элемент в последовательность, применяем матчер toContain.
const numbers = [1, 2, 3] expect(numbers).toContain(2)
Тестирование на противоположность матчеру
Если нужно протестировать на противоположность, просто пишем .not перед матчером: если это матчер toBe, то пишем: .not.toBe(…) .
Посмотреть весь список матчеров и опций можно здесь — в документации по Jest.
Тестирование
Процесс тестирования достаточно прост.
- Допустим, у нас есть функция, принимающая массив из строк, и выбирающая из них самую длинную:
const longestStr = (arr) => { let longestString = arr[0] for (let i = 0; i < arr.length; i++) { if (longestString.length < arr[i].length) { longestString = arr[i] } } return longestString } module.exports = longestStr
Обращаем внимание на module.exports = longestStr в конце. Это для экспорта результата функции; так мы можем потом импортировать его в файл теста.
- Создаем отдельный JavaScript-файл, который при работе с функциями рекомендуется назвать так же как функция которую собираемся протестировать, и расширение ему желательно иметь или .spec.js, или .test.js. Оба расширения подходят.
- В файле из пункта 2, импортируем нашу функцию:
COPY const longestStr = require('./longestString')
- Теперь пишем свой тест. В нем пишем test(), получающий 2 параметра: первый это строка, описывающая что код будет делать.
И второй параметр — это функция в которую мы будем писать две вещи: 1) функцию называемую expect, в которую будем передавать функцию с проверяемыми аргументами. 2) наш матчер. Вот так:
const longestStr = require('./longestString')
- Запускаем тест, вводом команды npm test в терминале.
Тогда увидим что-то такое:

Если тест пройдет, увидим такое:

Если тест не пройдет, то увидим результат выполнения. (В данном примере мы специально изменили кое-что в в файле longestString.js, чтобы тест выдал fail):

Несколько тестов
Если надо запустить только один файл с тестами, надо указать его название при запуске npm test (и не забыть добавить расширение):
- Например у нас есть еще файл sum.js
- Если запустить npm test, оба тестовых файла запустятся одновременно, и если один из них упадет, можно запутаться. Поэтому указываем, какой файл запустить: npm test sum.spec.js
Пропускаем ненужные тесты
Итак, если у нас есть несколько тестов, в одном файле .spec.js или test.js, то при запуске npm test fileName все тесты запустятся одновременно. Если надо запустить лишь один отдельный тест, и чтобы не надо было удалять или закомментировать другие, используем метод .skip:
test.skip('adds 1 + 2 to equal 3', () => { expect(sum(1, 2)).toEqual(3) }) test.skip('adds 10+10 to equal 20', () => { expect(sum(10, 10)).toEqual(20) }) test('2 + 2 is not equal to 6', () => { expect(sum(2, 2)).not.toEqual(6) })
Запустится только один тест, остальные будут пропущены, однако попадут в лог:

На этом пока все, удачи в юнит-тестировании! Посмотри соответствующий раздел на сайте — там бывает полезное.