Модульное тестирование Node.js + Mongoose с использованием Jest

Модульное тестирование - это не ответ на все вопросы, но оно, черт возьми, действительно предотвращает несколько неловких и ошеломляющих ситуаций. Иногда, независимо от того, насколько осторожны вы при программировании, некоторые ошибки остаются незамеченными, и самый простой способ отловить их раньше других - это модульное тестирование.

В этом руководстве я покажу вам, как проводить модульное тестирование серверной части вашего стека MERN с помощью Jest. В частности, Node.js и Mongoose.

Если у вас есть опыт работы с Node.js, вы можете перейти к шагу 2, где подробно описано, как протестировать свои функции.

Шаг 1. Настройте серверную часть Express

Этот шаг должен быть довольно простым для большинства людей. Все, что вам действительно нужно сделать, это настроить npm, установить несколько пакетов и убедиться, что вы можете успешно запустить свой сервер. Я использую nodemon, но не стесняйтесь использовать и npm.

npm init -y
npm i express mongoose nodemon

Вот как должен выглядеть ваш проект к этому моменту:

Поскольку мы занимаемся только тестированием приложения, нам нужен только очень простой API, и, поскольку всем нравится еда, я подумал, почему бы не сделать еще Restaurant API!

Схема ресторана

В этом нет ничего нового, но перечисление стоимости может сбить с толку некоторых людей.

Перечисление просто настраивается для добавления дополнительного шага проверки к каждому объекту ресторана в отношении их свойства стоимости. Это означает, что «Стоимость ресторана должна быть строкой и может принимать только одно из значений« $ »,« $$ »,« $$$ ». Если стоимость не указана, укажите в ресторане стоимость «$$» ». Довольно просто, правда?

Один из наших тестов будет охватывать случай, когда строка, переданная для стоимости, не является одной из строк в перечислении. На этом этапе база данных не сможет сохранить новый объект, и мы увидим, как справиться с такой ошибкой.

Создание функции ресторана

Наша первая задача - создать ресторан с указанием названия, местоположения и бюджета.

Единственная причина, по которой он не может создать ресторан, кроме ошибки базы данных, заключается в том, что другой ресторан уже имеет данное имя (см. Строки 10–11) или если стоимость ресторана не является одной из строк в упомянутом перечислении. ранее (см. model / Restaurant.js).

В случае успеха он вернет идентификатор вновь созданного ресторана.

Создать метод публикации ресторана

Этот шаг не является обязательным для тестирования, но на всякий случай, если вы не привыкли использовать вспомогательные функции, я хотел бы указать, как вы можете включить их в свой код.

Как вы можете видеть на изображении выше, createRestaurant(name, loc, cost), который мы создали ранее, вызывается в почтовом запросе, и все это заключено в оператор try catch, так что если возникает ошибка (например, одно и то же имя используется дважды), мы обрабатываем ее. .

Шаг 2. Установите Jest и mongodb-memory-server

Как упоминалось в заголовке статьи, мы будем использовать Jest для запуска наших тестов.

Jest предоставляет методы для создания, организации и выполнения ваших тестов. Это один из самых популярных фреймворков для тестирования, поскольку он ориентирован на простоту, поэтому вы можете сосредоточиться на логике, лежащей в основе тестов.

«Jest - это восхитительный фреймворк для тестирования JavaScript с упором на простоту».

Второй пакет, который мы должны установить, - это mongodb-memory-server. Если вы уже знакомы с тем, как работает mongo, вы знаете, что создаете кластер, в котором хранятся все данные вашего приложения. Большинство приложений будут писать / запрашивать / обновлять базу данных довольно регулярно, и поэтому важно убедиться, что ваше взаимодействие с базой данных дает желаемый результат.

Сервер mongodb-memory создает «кластер», который существует только в основной памяти вашего устройства и физически не сохраняется на диске.

Следовательно, после завершения работы вашего приложения экземпляр базы данных больше не будет существовать. Этот метод также будет быстрее, чем если бы вы имитировали взаимодействие с базой данных.

Теперь, когда у нас есть теория, давайте установим оба пакета:

npm i mongodb-memory-server jest

Шаг 3. Обработка сервера в памяти

Чтобы убедиться, что мы не повторяем код и сохраняем хороший стиль, полезно создать и затем экспортировать функции для обработки базы данных в памяти, а затем вызывать эти экспортируемые функции, поскольку они требуются на протяжении всей остальной части проекта.

В пакете mongodb-memory-server, который мы установили ранее, есть очень полезная страница на github, которая объясняет, как подключиться и создать экземпляр db:



Если вы не хотите читать документацию, вам будет достаточно следующего кода, чтобы начать модульное тестирование.

Особая благодарность Пауле Сантамария за ее статью, в которой она предоставляет этот модуль. Я дам ссылку на ее статью в конце этого урока.

Методы connect и closeDatabase должны быть довольно понятными, однако вам может быть интересно, зачем нам также нужна функция clearDatabase.

Когда дело доходит до модульного тестирования, каждый тест должен начинаться на пустом холсте, что означает, что при запуске нового теста в базе данных не должно быть существующих данных. Поэтому каждый раз, когда тест заканчивается, мы будем вызывать функцию clearDatabase.

Шаг 4. Создайте тестовый файл

Во-первых, в нашей папке с тестами мы будем хранить все файлы, которые включают тесты. Когда дело доходит до именования файлов, важно убедиться, что они написаны следующим образом: fileName.test.js

Перед запуском любого из тестов нам нужно будет подключиться к базе данных в памяти. К счастью для нас, мы уже создали и экспортировали способ сделать это в tests/db.js. Точно так же нам нужно отключить и удалить базу данных после запуска всех тестов. Наконец, после каждого отдельного теста мы должны очищать базу данных.

Теперь все, что нам нужно сделать, это добавить это в начало каждого тестового файла:

const db = require('./db')
beforeAll(async () => await db.connect())
afterEach(async () => await db.clearDatabase())
afterAll(async () => await db.closeDatabase())

Шаг 5. Анатомия теста в шутку

Теперь, когда все настроено, мы можем сосредоточиться на нашем первом тесте для функции createRestaurant.

Jest предлагает способ организации наших тестов. Рассмотрим следующий пример для тестирования асинхронной функции, которая принимает целое число и возвращает число как целое число и строку:

describe('First Group Of Tests', () => {
    it('First Test', async done => {
        const result = await numberFunc(10)
        expect(result.word).toBe("ten")
        expect(result.number).toBeGreaterThan(10)
        done()
    })
    it('Second Test', async done => {
        const result = await numberFunc()
        expect(result).toBeNull()
        done()
    })
})

Ключевые моменты этого:

  • describeblock - это способ объединить похожие тесты. Например, если вы тестируете функцию createRestaurant и хотите убедиться, что она может обрабатывать имена различной длины, вы можете поместить все эти тесты в один блок описания.
  • it block представляет каждый отдельный тест.
  • Самая важная часть блока it для асинхронного теста - убедиться, что вы включили done() в конце теста. Если вы этого не сделаете, тест будет завершен до завершения любого асинхронного вызова, и ваши тесты не пройдут. Jest объясняет важность done() в асинхронной функции:

«Jest будет ждать вызова done обратного вызова перед завершением теста».

  • expect очень похож на assert в других языках. Это способ, которым мы можем проверить, что наши функции получают / создают объекты, которые мы хотим от них. Хотя есть много разных способов использования expect, я показал здесь только 3 варианта. Чтобы узнать больше, посетите https://jestjs.io/docs/en/expect.

Шаг 6. Тестирование createRestaurant()

Поскольку тесты выполняются сверху вниз, лучше всего держать самый простой / базовый тест в верхней части файла. Я имею в виду следующее:

В основном порядок тестов в файле имеет значение.

Так что лучше начать с проверки самых основных требований функции, чтобы убедиться, что она работает, прежде чем переходить к более сложным случаям.

Итак, возвращаясь к тестированию createRestaurant(), самый простой тестовый пример - это создание одного ресторана с допустимым названием, местоположением и бюджетом.

Вот наш тестовый файл с первым тестом! Если какой-либо из трех except() завершится неудачно, тогда тест завершится неудачно, и мы узнаем, что что-то в нашей функции сломано. А пока давайте запустим тест и посмотрим, будет ли он работать.

Шаг 7. Выполнение тестов

В свой package.json добавьте тестовый сценарий:

"scripts": {
    "test": "jest --testEnvironment=node --runInBand ./tests"
}

--runInBand нужен для того, чтобы тесты запускались один за другим.

--testEnvironment=node присутствует, поскольку мы находимся в среде узлов.

Что действительно важно, так это указать правильный путь к вашей тестовой папке. Для меня это было ./tests.

Теперь, когда вы запустите npm test в терминале, вы должны увидеть что-то вроде этого:

Это был пример успешного теста. Я покажу вам, как писать тесты на ошибки.

Проверка на ошибки

В том же файле вы можете создать новый блок описания для случаев, когда нам не удается создать новый ресторан. Оглядываясь назад на нашу схему и функцию, можно сказать, что она должна потерпеть неудачу только в том случае, если имя было повторено или если стоимость, связанная с рестораном, не является одним из значений в перечислении.

В первом тесте мы проверяем, что при повторении имени будет выдана ошибка. Мы уже протестировали успешное создание одного ресторана, поэтому можем быть уверены, что линия 33 будет работать без сбоев.

Однако строка 35 должна выдать ошибку, поскольку имя «Первый» уже принадлежит другому ресторану. Поскольку это асинхронная функция, которая должна вызывать ошибку, нам нужно добавить rejects.toThrow() после ожидаемого.

Во втором тесте мы проверяем, возникает ли ошибка, если третий аргумент createRestaurant() не является строковым значением в перечислении затрат.

Если вы снова запустите тесты, вы увидите следующее:

Поздравляю! Вы успешно протестировали свой сервер Node.js и Mongoose с помощью Jest.

Благодарим за прочтение. Если вы в чем-то не уверены, оставьте комментарий. Это моя первая статья, так что, скорее всего, я не справился идеально, поэтому, пожалуйста, дайте мне знать, если вы заметите какие-либо проблемы 😌

Если вы хотите сделать с jest больше, например, использовать макеты для имитации поведения внешних функций, прочтите этот блог 🥳

использованная литература

Https://dev.to/paulasantamaria/testing-node-js-mongoose-with-an-in-memory-database-32np