Оригинальная ссылка:Jest Tutorial for Beginners: Getting Started With Jest for JavaScript Testing (2019)
Что такое тест?
На жаргоне,Тестирование означает проверку того, что наш код соответствует некоторым ожиданиям.. Например, названныйtransformerФункция получаетВходПосле этого вернитеожидаемый результат.
Существует много типов тестов, но в общих чертахСуществует три основных типа тестов:
- модульный тест
- Интеграционное тестирование
- тестирование пользовательского интерфейса
Учебник Jest, описанный в этой статье, охватываетмодульный тест, но в конце статьи также есть много ресурсов для других типов тестов, которые может изучить каждый.
Что такое шутка?
Jest — это библиотека тестирования JavaScript для создания, выполнения и сборки тестовых случаев. Вы можете установить и использовать его как пакет npm в любом проекте. Jest в настоящее время является самым популярным исполнителем тестов и используется по умолчанию при создании приложений React.
Откуда мне знать, что тестировать?
упомянултестовое задание, даже самый простой блок кода может оказаться непосильным для новичков. Наиболее часто задаваемый вопрос: «Откуда мне знать, что тестировать?». Если вы пишете веб-приложение, способ тестирования взаимодействия с пользователем на странице за страницей — хорошее место для начала. Но веб-приложения также представляют собой блоки кода, состоящие из множества функций и модулей, которые также необходимо тестировать. Обычно бывает два случая:
- ты взял на себяустаревший кодТестовые случаи не были написаны
- вы должны начать с нулявнедрить новую функцию
Что мы можем сделать по этому поводу? Для двух приведенных выше сценариев вы можете поставитьтестовое заданиенаписано как часть кода. Я про эти коды для проверкипроизводит ли данная функция ожидаемый результат. Типичный тестовый поток выглядит следующим образом:
- Введите тестируемую функцию
- дать функции вход
- определить ожидаемый результат
- Проверьте, возвращает ли функция ожидаемый результат
это все. Не так страшно смотреть на тест вот так:Вход - Ожидаемый результат - Результаты проверки. Хорошо, теперь о Jest, который почти точно определяет то, что мы только что сказали.
Создать проект
Для каждого проекта JavaScript требуется среда NPM (убедитесь, что Node установлен в системе). Далее мы создаем новую папку и инициализируем проект.
mkdir getting-started-with-jest && cd $_
npm init -y
Далее устанавливаем Jest:
npm i jest --save-dev
Затем мы настраиваем сценарий NPM, чтобы он мог выполнять наши тестовые примеры из командной строки. Открытьpackage.json, назовите команду, выполняющую Jest, "test":
"scripts": {
"test": "jest"
},
Теперь вы можете начать!
Спецификация и разработка через тестирование
Разработчики любят творческую свободу. Но когда дело доходит до серьезных вещей, в большинстве случаев привилегий не так много. Обычно мы должны следоватьТехнические характеристики, а значит, письменное или устное описание конструкции.
В этом руководстве мы привели к менеджеру проекта, есть довольно простая спецификация. Очень важные потребности клиентовФункция, которая отфильтровывает объекты, которые нам нужны в массиве..
Для каждого объекта в массиве мы должны проверить его свойство «url»,Соответствует ли значение атрибута элементу, который мы дали. В итоговом массиве результатовСодержит все члены объекта, которые мы сопоставили. Чтобы стать опытным разработчиком JavaScript, вам нужно следоватьразработка через тестированиережим, который требует, чтобы перед тем, как вы начнете писать код,Сначала напишите неудачные тестовые случаи.
По умолчанию Jest будетtestsНайдите тестовые файлы в папке проекта. Создадим новую папку:
cd getting-started-with-jest
mkdir __tests__
следующий вtestsСоздать файл в папкеfilterByTerm.spec.js. Вам может быть интересно, почему имя файла содержит «.spec»? На самом деле это соглашение, заимствованное из Ruby, спецификация для маркировки файлов как конкретных функций.
Начните тестирование прямо сейчас!
Структура теста и первый неудачный тест
Что ж, теперь первый тестовый пример Jest является общим. открыть файлfilterByTerm.spec.js, создайте тестовый блок:
describe("Filter function", () => {
// test stuff
});
Наш первый друг звонилdescribe, этот метод Jest используется для содержания одного или нескольких связанных тестов. Каждый раз, когда вы начинаете писать новый набор тестов для функции, заворачивайте его вdescribeвнутри. Этот метод принимает два параметра: описание набора тестов и функцию обратного вызова, обертывающую фактический тестовый пример.
Далее введем еще одну функциюtestТеперь здесь определяется фактический блок тестового кода:
describe("Filter function", () => {
test("it should filter by a search term (link)", () => {
// actual test
});
});
Теперь вы готовы писать тесты. Помните, что тестирование — этовойти,функцияа такжеОжидаемые результатыпредмет. Во-первых, мы определяем простой ввод — массив, содержащий элементы объекта:
describe("Filter function", () => {
test("it should filter by a search term (link)", () => {
const input = [
{ id: 1, url: "https://www.url1.dev" },
{ id: 2, url: "https://www.url2.dev" },
{ id: 3, url: "https://www.link3.dev" }
];
});
});
Давайте определим нашуОжидаемые результаты. Согласно спецификации,Тестируемая функция должна удалять объекты, свойство URL которых не соответствует заданному поисковому запросу.. Например, наш поисковый запрос — «ссылка», а ожидаемый результат — массив, содержащий только один член объекта:
describe("Filter function", () => {
test("it should filter by a search term (link)", () => {
const input = [
{ id: 1, url: "https://www.url1.dev" },
{ id: 2, url: "https://www.url2.dev" },
{ id: 3, url: "https://www.link3.dev" }
];
const output = [{ id: 3, url: "https://www.link3.dev" }];
});
});
Теперь пришло время написать фактический тестовый код. Мы собираемся использовать Jestexpectфункция исопоставительчтобы убедиться, что наш гипотетический (в настоящее время) вызов функции возвращает ожидаемый результат. Код приведен ниже:
expect(filterByTerm(input, "link")).toEqual(output);
Или разложить код и выделить часть, которая вызывает функцию:
filterByTerm(input, "link");
В тесте Jest мы оборачиваем тестовую функцию вexpectвнутри и спичкасопоставитель(Функция Jest для проверки ввода), чтобы завершить тест. Полный тестовый код приведен ниже:
describe("Filter function", () => {
test("it should filter by a search term (link)", () => {
const input = [
{ id: 1, url: "https://www.url1.dev" },
{ id: 2, url: "https://www.url2.dev" },
{ id: 3, url: "https://www.link3.dev" }
];
const output = [{ id: 3, url: "https://www.link3.dev" }];
expect(filterByTerm(input, "link")).toEqual(output);
});
});
(Для получения дополнительной информации о сопоставителях Jest см.Документация здесь)
Теперь запустите волну тестов:
npm test
Вы увидите, что тест не пройден:
FAIL __tests__/filterByTerm.spec.js
Filter function
✕ it should filter by a search term (2ms)
● Filter function › it should filter by a search term (link)
ReferenceError: filterByTerm is not defined
9 | const output = [{ id: 3, url: "https://www.link3.dev" }];
10 |
> 11 | expect(filterByTerm(input, "link")).toEqual(output);
| ^
12 | });
13 | });
14 |
"ReferenceError: filterByTerm не определен" Смысл очевиден, не определенfilterByTerm, давайте исправим это дальше.
Исправьте тест (и снова провалите тест)
мы не достиглиfilterByTermШерстяная ткань. Для удобства мы поместили определение этой функции вместе с нашим тестовым примером. Конечно,В реальных новых проектах тестовые случаи и тестируемые функции часто находятся в разных файлах, при тестировании функций их нужно импортировать из других файлов..
filterByTermВнутри функции нам нужно использовать собственный фильтр метода массива, чтобы отфильтровать нужные нам члены:
function filterByTerm(inputArr, searchTerm) {
return inputArr.filter(function(arrayElement) {
return arrayElement.url.match(searchTerm);
});
}
Объясните, как работает следующая функция: мы проверяем, соответствует ли значение свойства «url» каждого члена объекта во входном массивеmatchРегулярное выражение в методе совпадает. Вот полный код:
function filterByTerm(inputArr, searchTerm) {
return inputArr.filter(function(arrayElement) {
return arrayElement.url.match(searchTerm);
});
}
describe("Filter function", () => {
test("it should filter by a search term (link)", () => {
const input = [
{ id: 1, url: "https://www.url1.dev" },
{ id: 2, url: "https://www.url2.dev" },
{ id: 3, url: "https://www.link3.dev" }
];
const output = [{ id: 3, url: "https://www.link3.dev" }];
expect(filterByTerm(input, "link")).toEqual(output);
});
});
Теперь снова выполните тест:
npm test
Вы видели это, прошли!
PASS __tests__/filterByTerm.spec.js
Filter function
✓ it should filter by a search term (link) (4ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 0.836s, estimated 1s
отличный. Но все ли кончено? еще нет. Как сделать так, чтобы вызов функции снова не работал? Затем мы вызываем следующую функцию с условием поиска в верхнем регистре:
function filterByTerm(inputArr, searchTerm) {
return inputArr.filter(function(arrayElement) {
return arrayElement.url.match(searchTerm);
});
}
describe("Filter function", () => {
test("it should filter by a search term (link)", () => {
const input = [
{ id: 1, url: "https://www.url1.dev" },
{ id: 2, url: "https://www.url2.dev" },
{ id: 3, url: "https://www.link3.dev" }
];
const output = [{ id: 3, url: "https://www.link3.dev" }];
expect(filterByTerm(input, "link")).toEqual(output);
expect(filterByTerm(input, "LINK")).toEqual(output); // New test
});
});
Выполнить тест... не удалось. Давай, давай еще поправляйся.
Тест исправления: совместимость с поисковыми запросами в верхнем регистре
filterByTermСледует также учитывать условия поиска, написанные с заглавной буквы. То есть, даже если содержимое поиска указано в верхнем регистре, соответствующий объект соответствия должен быть возвращен в форме без учета регистра.
filterByTerm(inputArr, "link");
filterByTerm(inputArr, "LINK");
Чтобы проверить эту ситуацию, нам нужно ввести новый тест:
expect(filterByTerm(input, "LINK")).toEqual(output); // New test
Для того, чтобы пройти тест, нам нужно немного подкорректироватьmatchРегулярное выражение для метода:
//
return arrayElement.url.match(searchTerm);
//
Вместо того, чтобы использовать поисковую систему напрямую, мы можем построитьНечувствительные к регистру регулярных выражений. То есть строковое выражение, не зависящее от регистра. Вот фиксированный код:
function filterByTerm(inputArr, searchTerm) {
const regex = new RegExp(searchTerm, "i");
return inputArr.filter(function(arrayElement) {
return arrayElement.url.match(regex);
});
}
Вот полный тестовый код:
describe("Filter function", () => {
test("it should filter by a search term (link)", () => {
const input = [
{ id: 1, url: "https://www.url1.dev" },
{ id: 2, url: "https://www.url2.dev" },
{ id: 3, url: "https://www.link3.dev" }
];
const output = [{ id: 3, url: "https://www.link3.dev" }];
expect(filterByTerm(input, "link")).toEqual(output);
expect(filterByTerm(input, "LINK")).toEqual(output);
});
});
function filterByTerm(inputArr, searchTerm) {
const regex = new RegExp(searchTerm, "i");
return inputArr.filter(function(arrayElement) {
return arrayElement.url.match(regex);
});
}
Выполните здесь, и вы увидите, что он проходит. острый! В качестве ссылки вы можете написать новый тест, который проверяет следующие условия:
- Проверьте, является ли поисковый запрос "uRI"
- Тест на пустые условия поиска. Как функция справляется с этим?
Как вы собираетесь создавать эти тесты?
В следующем разделе мы рассмотрим еще одну важную тему тестирования:покрытие кода.
покрытие кода
чтопокрытие кода? Прежде чем говорить об этом, давайте внесем несколько изменений в код. В корневом каталоге проекта создайте файл с именемsrcпапку, а затем создайте файл с именемfilterByTerm.jsдокумент. Мы здесьexportиз этой функции:
mkdir src && cd _$
touch filterByTerm.js
Ниже находится файлfilterByTerm.jsСодержание:
function filterByTerm(inputArr, searchTerm) {
if (!searchTerm) throw Error("searchTerm cannot be empty");
const regex = new RegExp(searchTerm, "i");
return inputArr.filter(function(arrayElement) {
return arrayElement.url.match(regex);
});
}
module.exports = filterByTerm;
Теперь предположим, что я новый коллега в вашей компании. Я ничего не знаю о тестировании и, не зная нашей среды разработки, добавилесли заявление:
function filterByTerm(inputArr, searchTerm) {
if (!searchTerm) throw Error("searchTerm cannot be empty");
if (!inputArr.length) throw Error("inputArr cannot be empty"); // new line
const regex = new RegExp(searchTerm, "i");
return inputArr.filter(function(arrayElement) {
return arrayElement.url.match(regex);
});
}
module.exports = filterByTerm;d
Мы добавили новую строку кода в filterByTerm, но она не тестировалась. Вы не узнаете, пока я не скажу вам: «Вот новое утверждение для проверки».что тестировать. Почти невозможно знать все пути, по которым пойдет наш код, поэтомуНужен инструмент, который поможет нам обнаружить эти слепые пятна.
Этот вид инструмента называетсяпокрытие кода, это мощный инструмент в нашем наборе инструментов. Jest имеет встроенный инструмент покрытия кода, который можно активировать двумя способами:
- Указывается в командной строке с флагом "--coverage"
- Ручная настройка в package.json
Перед выполнением тестов покрытия убедитесь, чтоtests/filterByTerm.spec.jsсерединадобавлен фильтр по терминуфункция.
const filterByTerm = require("../src/filterByTerm");
// ...
Сохраните файл и выполните тест покрытия:
npm test -- --coverage
Получены следующие результаты:
PASS __tests__/filterByTerm.spec.js
Filter function
✓ it should filter by a search term (link) (3ms)
✓ it should filter by a search term (uRl) (1ms)
✓ it should throw when searchTerm is empty string (2ms)
-----------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
-----------------|----------|----------|----------|----------|-------------------|
All files | 87.5 | 75 | 100 | 100 | |
filterByTerm.js | 87.5 | 75 | 100 | 100 | 3 |
-----------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
Это хорошее резюме нашего покрытия функциональными тестами. мы видимСтрока 3 не переопределяется. Теперь давайте проверим мой недавно добавленный оператор if, чтобы добиться 100% покрытия кода.
Если вы хотите проверять покрытие кода каждый раз при тестировании, вы можете настроить jest следующим образом в package.json:
"scripts": {
"test": "jest"
},
"jest": {
"collectCoverage": true
},
Либо привести его в команду в виде флага:
"scripts": {
"test": "jest"
},
"jest": {
"collectCoverage": true,
"coverageReporters": ["html"]
},
Теперь каждый раз, когда вы запускаете npm test, вы будете видеть в своем проекте файл с именемcoverageпапка:getting-started-with-jest/coverage/. В этом каталоге вы увидите кучу файлов и/coverage/index.htmlЭто индексная страница этих файлов, которая является кратким отчетом о покрытии кода.
Нажмите на имя файла, и вы увидите точную непроверенную строку кода:
Простой. Благодаря инструментам покрытия кода вы знаете, где протестировать свой код.
Как протестировать Реакт?
React — очень популярная библиотека JavaScript для создания динамических пользовательских интерфейсов. Jest работает гладко при тестировании приложений React (и Jest, и React разработаны инженерами Facebook). Jest также является средством запуска тестов по умолчанию при создании программ React.
Если вы хотите узнать, как тестировать компоненты React, ознакомьтесь сТестирование компонентов React: наиболее подробное руководствоэтот учебник. Этот учебник охватываеткомпоненты модульного тестирования, компоненты классов, функциональные компоненты с хуками и новый Act API.
Резюме (куда идти дальше)
Тестирование — большая и увлекательная тема. Существует множество типов тестов и множество библиотек тестов на выбор. В этом руководстве по Jest мы узнали, как настроить отчеты о покрытии Jest, как организовать и написать простой модульный тест и как протестировать код JavaScript.
Если вы хотите узнать отестирование пользовательского интерфейсаС точки зрения вещей, я настоятельно рекомендую вам проверить этот учебник《Учебное пособие: Сквозное тестирование JavaScript с помощью Cypress》
Также рекомендую прочитать Гарри ПерсиваляTest-Driven Development with PythonЭта книга, хотя и не имеет прямого отношения к JavaScript. Учебное пособие содержит все советы и рекомендации по тестированию, а также подробное введение во все различные типы тестирования.
Если вы готовы и хотите изучить автоматизированное тестирование и непрерывную интеграцию, рекомендуется прочитать«Автоматизированное тестирование и непрерывная интеграция в JavaScript»этот учебник.
Вы можете найти код для этого руководства на Github:getting-started-with-jest, который включает в себя практический код, изложенный в тексте.
Спасибо за прочтение!
(над)