Jest tests: тестирование в JavaScript. Инструкция для начинающих

Андрій Денисенко

Jest — это популярный фреймворк для тестирования JavaScript с акцентом на простоту использования. Редакция Highload разобралась, как устроены тесты в Jest и как начать тестировать JavaScript-код начинающим.


Содержание:
Что такое тестирование?
Что такое Jest?
Установка и настройка Jest
Тестирование
Сопоставление
Подготовка нескольких тестов
Итоги

Что такое тестирование?

Тестирование — это процесс проверки работоспособности кода в различных условиях и на разных уровнях.

Процесс тестирования состоит из нескольких этапов. Обычно этапы тестирования представляют в виде пирамиды из трех уровней: модульного, интеграционного и сквозного (E2E, end-to-end) тестирования.

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

Что такое Jest?

Jest — это среда тестирования JavaScript, которая создана с мыслью о простоте.

Она поддерживает не только JavaScript сам по себе, но и проекты с применением Babel, TypeScript, Node.js, React, Angular, Vue и пр.

Что нужно тестировать?

Jest используется для модульного тестирования.

Модульное тестирование (unit testing, юнит-тестирование) — это тестирование небольших блоков кода по мере их написания.

Поиск ошибки в крупном фрагменте кода отнимает много времени. Поэтому возникла концепция «сначала тест» (test-first). Ее суть заключается в том, что сначала пишется тест, а уже потом — тестируемый код. Впоследствии эта концепция выделилась в методологию разработки через тестирование. Благодаря применению такой процедуры вы можете убедиться в том, что код работает, как требуется.

Тестируемой единицей может быть как отдельная функция или метод, так и модуль в целом. Для тестирования выбирают самостоятельно написанные блоки (при написании кода с нуля) или блоки имеющегося кода, для которых не написаны тесты (при работе с существующей системой).

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

Установка и настройка Jest

Установить Jest проще простого. Создайте каталог для тестового проекта, например jest_test:

cd d:\
mkdir jest_test
cd jest_test

Инициируйте npm с ответами по умолчанию:

npm init -y

Будет создан файл package.json с таким содержимым:

{
  "name": "jest_test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

Установите Jest с помощью npm:

npm install --save-dev jest

Рекомендуется использовать именно такой способ установки, а не устанавливать Jest глобально.

Откройте файл package.json и измените секцию scripts:

"scripts": {
    "test": "jest"
  },

Теперь можно приступать к тестированию.

Тестирование

Рассмотрим этапы юнит-тестирования на примере простой функции возведения в квадрат.

1. Создать тест

Создадим файл square.test.js со следующим кодом:

// Импортируем функцию
const square = require('./square');

// Тест
test('Возводит 2 в квадрат, чтобы вернуть 4', () => {
    expect(square(2)).toBe(4);
});

2. Запустить тест (провал)

npm run test

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

Именно так и принято делать: первое тестирование проводится до написания кода, и оно должно провалиться, чтобы мы убедились в том, что тест срабатывает при отсутствии кода.

3. Написать код

Напишем код для тестирования и сохраним его в файле square.js.

// Объявляем функцию
function square(x){
    return x * x;
}
// Экспортируем функцию
module.exports = square;

4. Запустить тест (успех)

npm run test

На этот раз тест будет пройден.

Сопоставление

Теперь функция определена, и мы проверяем, как она работает, с помощью сопоставления. Сопоставление производится с помощью функции expect() и вычислителя. В данном случае это проверка на точное равенство toBe():

expect(square(2)).toBe(4);

Равенство

toBe

Этот вычислитель используется для сравнения примитивных типов или проверки идентичности экземпляров объекта по ссылке.

Для проверки он использует Object.is. В случае примитивных значений это лучше, чем оператор строгого равенства ===.

Обратите внимание, что не следует использовать toBe для чисел с плавающей точкой. Из-за округления 2.0 + 3.0 не обязательно будет равно 5.0. Для таких чисел лучше использовать toBeCloseTo.
toBe используется для тестирования точного совпадения. Если нужно проверить значение объекта, используйте сопоставитель toEqual.

toEqual

Рассмотрим такой тест, чтобы сравнить toBe и toEqual:

function Cat (name, age) {
    this.name = name;
    this.age = age;
};

var myCat = new Cat('Петька', 5)
var herCat = new Cat('Венька', 3)

// Группируем тесты
describe('Кот', () => {
    test('Один и тот же кот - toBe:', () => {
        expect(myCat).toBe(myCat);
    });
    
    test('Один и тот же кот - toEqual:', () => {
        expect(myCat).toEqual(myCat);
    });
    
    test('Два разных кота с разными данными - toBe:', () => {
        expect(herCat).toBe(myCat);
    });
    
    test('Два разных кота с разными данными - toEqual:', () => {
        expect(herCat).toEqual(myCat);
    });
    
    test('Два разных кота с одинаковыми данными - toBe:', () => {
        expect({name: 'Петька', age: 5}).toBe(myCat);
    });
    
    test('Два разных кота с одинаковыми данными - toEqual:', () => {
        expect({name: 'Петька', age: 5}).toEqual(myCat);
    });
});

После запуска тестов получим такой ответ:

Первая пара тестов пройдена, потому что объект сравнивается с самим собой. Он тождествен себе и по ссылке и по значениям.

Вторая пара тестов не пройдена, потому что сравниваются два разных объекта с разными значениями.

В третьей паре объявленный объект myCat сравнивается с новым объектом {name: 'Петька', age: 5}. Поскольку это разные объекты, тест toBe провален, а поскольку значения параметров у этих объектов равны, тест toEqual пройден.

toStrictEqual

Используется для тестирования объектов с одинаковыми типами и структурой.

В отличие от toEqual, проверяет ключи со значением undefined и проверяет массивы на разреженность.

Сравните результаты нестрогого и строгого сравнения:

test('Undefined equal', () => {
    expect({x: undefined, y: 'second'}).toEqual({y: 'second'});
});

test('Undefined strict equal', () => {
    expect({x: undefined, y: 'second'}).toStrictEqual({y: 'second'});
});

Отрицание

not

Когда требуется инвертировать условие, используется not. Например, так можно провести тест и убедиться, что строка не в верхнем регистре.

str = 'строка';

test('неравенство строк в разных регистрах', () => {
    expect(str).not.toEqual(str.toUpperCase());
});

Сравнение чисел

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

test('3 + 2', () => {
    const val = 3 + 2;
    expect(val).toBeGreaterThan(3);
    expect(val).toBeGreaterThanOrEqual(5);
    expect(val).toBeLessThan(10);
    expect(val).toBeLessThanOrEqual(6);
});

Обратите внимание, что несмотря на четыре проверки, мы видим, что выполнен только один тест. Так произошло потому, что все проверки помещены в один блок теста.

Истинность, нуль и определенность

Булевская ложность в JavaScript представлена значениями false, 0, '', null, undefined и NaN. Все остальные значения с этой точки зрения трактуются как истинные. Но иногда во время тестирования нужно не только определять истинность значения, но и различать false, null и undefined и NaN.

toBeTruthy

Используется, когда вам важно не значение, а только его истинность в булевском контексте.

Тест будет пройден при любом значении, кроме false, 0, '', null, undefined и NaN.

toBeFalsy

Используется, когда вам важно не значение, а только его ложность в булевском контексте.

Тест будет пройден только при одном из следующих значений: false, 0, '', null, undefined и NaN.

toBeNull

Тест будет пройден только при значении null.

toBeDefined

Используется, чтобы узнать, определено ли значение.

toBeUndefined

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

toBeNaN

Определяет, является ли значение NaN.

Пример

Рассмотрим тест, в котором применяются функции определения различных рассмотренных значений применительно к значению undefined.

var v = undefined;

describe('Истинность, нуль и определенность', () => {
    test('Expect undefined not.toBeTruthy', () => {
        expect(v).not.toBeTruthy();
    });
    
    test('Expect undefined toBeFalsy', () => {
        expect(v).toBeFalsy();
    });
    
    test('Expect undefined not.toBeNull', () => {
        expect(v).not.toBeNull();
    });

    test('Expect undefined not.toBeDefined', () => {
        expect(v).not.toBeDefined();
    });

    test('Expect undefined toBeUndefined', () => { 
        expect(v).toBeUndefined();
    });

    test('Expect undefined not.toBeNaN', () => {
        expect(v).not.toBeNaN();
    });
});

Вхождение в массив

toContain

Проверяет, содержится ли указанный элемент в массиве. Для проверки используется строгое равенство, как в toBe.

toContainEqual

Проверяет, содержится ли в массиве элемент с определенной структурой и значениями. Проверяется равенство значений, а не эквивалентность объектов.

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

var winter = ['декабрь', 'январь', 'февраль'];
var spring = ['март', 'апрель', 'май'];
var summer = ['июнь', 'июль', 'август'];
var autumn = ['сентябрь', 'октябрь', 'ноябрь'];
var seasons = [winter, spring, summer, autumn];

describe('Вхождение элементов в массив', () => {
    
    test('Есть ли среди сезонов весна?', () => {
        expect(seasons).toContain(spring);
    });
    
    test("Есть ли среди сезонов ['март', 'апрель', 'май']?", () => {
        expect(seasons).toContain(['март', 'апрель', 'май']);
    });
    
    test('Есть ли среди сезонов весна (Equal)?', () => {
        expect(seasons).toContainEqual(spring);
    });
    
    test("Есть ли среди сезонов ['март', 'апрель', 'май'] (Equal)?", () => {
        expect(seasons).toContainEqual(['март', 'апрель', 'май']);
    });    
});

Не пройден лишь один тест — второй: два различных объекта сравнивались по ссылке.

Сравнение со строками и регулярными выражениями

toMatch

Определяет, содержит ли аргумент указанную строку или соответствует ли он заданному регулярному выражению.

test('pineapple contains apple', () => {
    expect('pineapple').toMatch('apple');
});

test('pineapple contains pine', () => {
    expect('pineapple').toMatch(/pine/);
});

Подготовка нескольких тестов

Иногда требуется провести одинаковую подготовку перед выполнением нескольких тестов. Так бывает, в частности, когда невозможно жестко закодировать данные и нужно получать их асинхронно. Jest предоставляет для этого функции beforeEach и afterEach.

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

beforeEach(() => {
  initProductDatabase();
});

afterEach(() => {
  clearProductDatabase();
});

test('Среди продуктов есть яблоко', () => {
  expect(getProducts()).toContainEqual('яблоко');
});

test('Среди продуктов есть помидор', () => {
  expect(getProducts()).toContainEqual('помидор');
});

Тогда в определенный момент в базе может найтись яблоко, но не окажется помидора, и мы получим следующий результат:

Если же нужно проводить подготовку перед всеми тестами и проводить дополнительную работу последних, применяются функции beforeAll и afterAll.

В этом случае в приведенном выше фрагменте кода можно заменить beforeEach на beforeAll, а afterEach — на afterAll.

Бывает, что нужно провести некоторые подготовительные действия для всех тестов, а некоторые — только для отдельных. В таком случае функции подготовки можно включать в блок describe. При этом функции before..., находящиеся вне блока describe, будут выполняться раньше, чем те, которые в него включены.

Итоги

Написание тестов требует времени, но это время окупается при правильном подходе к тестированию. Фреймворк Jest предоставляет удобные средства для тестирования кода на JavaScript, а также является простым и понятным.

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

В заключении, в качестве хорошего дополнения к материалу советуем посмотреть вот это тематическое видео:

Останні статті

Обучение Power BI – какие онлайн курсы аналитики выбрать

Сегодня мы поговорим о том, как выбрать лучшие курсы Power BI в Украине, особенно для…

13.01.2024

Work.ua назвал самые конкурентные вакансии в IТ за 2023 год

В 2023 году во всех крупнейших регионах конкуренция за вакансию выросла на 5–12%. Не исключением…

08.12.2023

Украинская IT-рекрутерка создала бесплатный трекер поиска работы

Unicorn Hunter/Talent Manager Лина Калиш создала бесплатный трекер поиска работы в Notion, систематизирующий все этапы…

07.12.2023

Mate academy отправит работников в 10-дневный оплачиваемый отпуск

Edtech-стартап Mate academy принял решение отправить своих работников в десятидневный отпуск – с 25 декабря…

07.12.2023

Переписки, фото, история браузера: киевский программист зарабатывал на шпионаже

Служба безопасности Украины задержала в Киеве 46-летнего программиста, который за деньги устанавливал шпионские программы и…

07.12.2023

Как вырасти до сеньйора? Девелопер создал популярную подборку на Github

IT-специалист Джордан Катлер создал и выложил на Github подборку разнообразных ресурсов, которые помогут достичь уровня…

07.12.2023