HTML / CSSJavaScriptNode jsПаттерны проектированияПрактические

Модульное тестирование (Unit testing)

Что такое модульное тестирование?

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

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

Преимущества Модульное тестирования:

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

Недостатки модульного тестирования:

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

Лучшие практики модульного тестирования

  • Модульный тест должен быть независимыми. В случае каких-либо улучшений или изменений требований, это не должно повлиять на модульные тесты.
  • Соблюдайте четкие и последовательные соглашения об именах для ваших модульных тестов.
  • В случае изменения кода в каком-либо модуле убедитесь, что для модуля существует соответствующий test case, и модуль проходит тесты перед изменением реализации.
1function add(a,b){
2 return a+b
3}
4
5test('Функция add должна возвращать 3',()=>{
6 expect(sum(1, 2)).toBe(3);
7})

Что такое модульный тест ?

Модульный тест - это автоматический тест, который:

  • Проверяет небольшой фрагмент кода (также известный как модуль);
  • Выполняет это быстро;
  • Делает это изолированно;

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

Test-Driven Development (TDD)

В конце 1990-х годов Кент Бек разработал технику «разработка через тестирование» (Test-Driven Development, TDD), как часть экстремального программирования. Эта техника для построения ПО, которая управляет процессом разработки через написание тестов. В сущности, повторяет три простых правила:

  • Сначала пишется тест
  • Затем пишется код под этот тест
  • Рефакторинг нового и старого кода, чтобы улучшить качество кода

Процесс начинается заново пока не получится желаемый результат.

tdd

Написание теста первым дает два преимущества:

  1. Это способ получить само-тестируемый код
  2. Думая сначала о тесте вы заставляете себя думать об интерфейсе самого кода. Эта фокусировка на интерфейсе и на том как вы используете класс помогает вам разделить интерфейс от реализации.

Самая большая ошибка при использовании данной методологии — это пренебрежение третьим шагом, рефакторинг. Это приводит к тому, что код будет “грязным” (но по крайней мере, будут тесты).

Behaviour Driven Development BDD

BDD (Behaviour Driven Development) или разработка на основе поведения, появилось в процессе эволюции unit-тестирования и разработана Дэном Нортом (Dan North) в 2006г. Как утверждает сам автор, методология должна помочь людям изучить TDD. Она появилось из agile практик и предназначена сделать их более доступными и эффективными для команд-новичков в Agile. Со временем, BDD стало охватывать более широкую картину agile-анализа и автоматическое приемочное тестирование.

Это привело к тому, что сами тесты стали переименовывать в поведение (спецификации), что позволило сфокусироваться на том, что объекту нужно сделать. Таким образом, разработчики стали создавать для себя документацию и записывать названия тестов в виде предложений. Они обнаружили, что созданная документация, стала доступна бизнесу, разработчикам и тестерам.

Считается, что разработка на основе поведения одно из ответвлений Mock-стилей (или Solitary-тест), т.е. тесты преимущественно строятся с использованием дублей.

Позднее, появился стиль написания тестов Given-When-Then, или, как его стали называть, спецификация поведения системы. Идея заключается в том, чтобы разбить написание тестового сценария на три раздела:

  • Дано (Given) — состояние, до того, как вы начнете описывать поведение. можно рассматривать как предварительное условие теста.
  • Когда (When) — поведение, которое вы описываете.
  • Тогда (Then) — изменения, которые вы ожидаете от поведения

Пример:

1Описание: Пользователь продает акции.
2Сценарий: Пользователь запрашивает продажу до закрытия торгов
3Дано (Given): У меня есть 100 акций MSFT и 150 акций APPL и время до закрытия торгов.
4Когда (When): Я прошу продать 20 акций MSFT
5Тогда (Then): У меня должно остаться 80 акций MSFT и 150 акций APPL и заявка на продажу 20 акций должна быть выполнена.

Методология BDD с точки зрения программистов, как утверждает сам ее автор (BDD IS LIKE TDD IF…), не отличается от TDD. Там используются все те же правила, что и в TDD: тест, код, рефакторинг. Отличие заключается в том, что BDD охватывает более широкую публику. Спецификации становятся доступными не только программистам, но и людям, не разбирающимся в коде, но имеющим отношение к разработке ПО. Таким образом, в процесс создания тестов подключается вся команда: аналитики, тестеры, менеджеры.

Пирамида тестирования Майка Кона

пирамида тестирования Майка Кона

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

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

Integration — интеграционное тестирование. Оно проверяет более крупные кусочки системы, то есть это либо объединение нескольких кусочков логики (несколько методов или классов), либо корректность работы с внешним компонентом. Этих тестов как правило меньше, чем Unit, так как они тяжеловеснее.

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

E2E(End-to-End) — тесты, которые проверяют работу пользовательского интерфейса. Они затрагивают логику на всех уровнях приложения, из-за чего их еще называют сквозными. Их как правило в разы меньше, так они наиболее тяжеловесны и должны проверять самые необходимые (используемые) пути.

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

Ключевые понятия юнит-тестирования

Покрытие тестов (Code Coverage) — одна из главных оценок качества тестирования приложения. Это процент кода, который был покрыт тестами (0-100%).

Для оценки покрытия тестами обычно используют дополнительные инструменты: JaCoCo, Cobertura и т.д.

Тестовый сценарий (Test Case) — сценарий, описывающий шаги, конкретные условия и параметры, необходимые для проверки реализации тестируемого кода.

Фикстуры (Fixture) — состояние среды тестирования, которое необходимо для успешного выполнения испытуемого метода. Это заранее заданный набор объектов и их поведения в используемых условиях.

Этапы тестирования

Тест состоит из трёх этапов:

  1. Задание тестируемых данных (фикстур).
  2. Использование тестируемого кода (вызов тестируемого метода).
  3. Проверка результатов и сверка с ожидаемыми.

Этапы тестирования

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

  • Мок (Mock) — объекты, которые настраиваются (например, специфично для каждого теста) и позволяют задать ожидания вызовы методов в виде ответов, которые мы планируем получить. Проверки соответствия ожиданиям проводятся через вызовы к Mock-объектам.

  • Заглушки (Stub) — обеспечивают жестко зашитый ответ на вызовы во время тестирования.

Также они могут сохранять в себе информацию о вызове (например, параметры или количество этих вызовов). Такие иногда называют своим термином — шпион (Spy).

Иногда термины stubs и mock путают: разница в том, что стаб ничего не проверяет, а лишь имитирует заданное состояние. А мок — это объект, у которого есть ожидания. Например, что данный метод класса должен быть вызван определенное число раз. Иными словами, ваш тест никогда не сломается из-за «стаба», а вот из-за мока может.

Среды тестирования

Для JavaScript доступно несколько сред тестирования (фреймворков). Самые популярные из них — Jest, Mocha, Jasmine и др .

Мы будет использовать Jest

Jest

Jest — это test runner, то есть библиотека JavaScript для создания, запуска и структурирования тестов. Jest распространяется в виде пакета NPM, вы можете установить его в любом проекте JavaScript.

Установка

Для установки Jest в ваш проект выполните:

1npm install --save-dev jest

После установки можете обновить секцию scripts вашего package.json:

1{
2"scripts": {
3 "test": "jest"
4 }
5}

С помощью такого простого вызова мы уже можем запустить наши тесты (на самом деле jest потребует существование хотя бы одного теста).

Также можно установить глобально (но так делать я бы не рекомендовал, так как по мне глобальная установка модулей является плохой практикой):

1npm install jest --global

После этого вы можете использовать jest непосредственно из командной строки.

При помощи вызова команды jest --init в корне проекта, ответив на несколько вопросов, вы получите файл с настройками jest.config.js. Или можно добавить конфигурацию прямиком в ваш package.json. Для этого добавьте в корень json ключ «jest» и в соответствующем ему объекте можете добавлять необходимые вам настройки. Сами опции мы разберем позже. На данном этапе в этом нет необходимости, поскольку jest можно использовать «сходу», без дополнительных конфигураций.

Первый тест

Файлы с расширением *.test.js или *.spec.js будут запущены и обработаны jest по умалчанию.

1test('My first test', () => {
2 expect(Math.max(1, 5, 10)).toBe(10);
3});

Если мы "сломаем" на тест и запстим повторно

1test('My first test', () => {
2 expect(Math.max(1, 5, 10)).toBe(5);
3});

failed tests

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

Теперь давайте разберём код самого теста. Функция test используется для создания нового теста. Она принимает три аргумента (в примере мы использовали вызов с двумя аргументами). Первый — строка с названием теста, его jest отобразит в отчете. Второй — функция, которая содержит логику нашего теста. Также можно использовать 3-й аргумент — таймаут. Он является не обязательным, а его значение по умолчанию составляет 5 секунд. Задаётся в миллисекундах. Этот параметр необходим когда мы работаем с асинхронным кодом и возвращаем из функции теста промис. Он указывает как долго jest должен ждать разрешения промиса. По истечению этого времени, если промис не был разрешен — jest будет считать тест не пройденным. Также вместо test() можно использовать it(). Разницы между такими вызовами нету.

it() это просто алиас на функцию test().

Внутри функции test мы сначала вызываем expect(). Ему мы передаем значение, которое хотим проверить. В нашем случае, это результат вызова Math.max(1, 5, 10). expect() возвращает объект «обертку», у которой есть ряд методов для сопоставления полученного значения с ожидаемым. Один из таких методов мы и использовали — toBe.

Давайте разберем основные из этих методов:

toBe() — подходит, если нам надо сравнивать примитивные значения или является ли переданное значение ссылкой на тот же объект, что указан как ожидаемое значение. Сравниваются значения при помощи Object.is(). В отличие от === это дает возможность отличать 0 от -0, проверить равенство NaN c NaN. toEqual() — подойдёт, если нам необходимо сравнить структуру более сложных типов. Он сравнит все поля переданного объекта с ожидаемым. Проверит каждый элемент массива. И сделает это рекурсивно по всей вложенности.

1test('toEqual with objects', () => {
2 expect({ foo: 'foo', subObject: { baz: 'baz' } })
3 .toEqual({ foo: 'foo', subObject: { baz: 'baz' } }); // Ок
4 expect({ foo: 'foo', subObject: { num: 0 } })
5 .toEqual({ foo: 'foo', subObject: { baz: 'baz' } }); // А вот так ошибка.
6});
7
8test('toEqual with arrays', () => {
9 expect([11, 19, 5]).toEqual([11, 19, 5]); // Ок
10 expect([11, 19, 5]).toEqual([11, 19]); // Ошибка
11});

toContain() — проверят содержит массив или итерируемый объект значение. Для сравнения используется оператор ===.

1const arr = ['apple', 'orange', 'banana'];
2expect(arr).toContain('banana');
3expect(new Set(arr)).toContain('banana');
4expect('apple, orange, banana').toContain('banana');

toContainEqual() — проверяет или содержит массив элемент с ожидаемой структурой.

1expect([{a: 1}, {b: 2}]).toContainEqual({a: 1});

toHaveLength() — проверяет или свойство length у объекта соответствует ожидаемому.

1expect([1, 2, 3, 4]).toHaveLength(4);
2expect('foo').toHaveLength(3);
3expect({ length: 1 }).toHaveLength(1);

toBeNull() — проверяет на равенство с null. toBeUndefined() — проверяет на равенство с undefined. toBeDefined() — противоположность toBeUndefined. Проверяет или значение !== undefined. toBeTruthy() — проверяет или в булевом контексте значение соответствует true. Тоесть любые значения кроме false, null, undefined, 0, NaN и пустых строк. toBeFalsy() — противоположность toBeTruthy(). Проверяет или в булевом контексте значение соответствует false. toBeGreaterThan() и toBeGreaterThanOrEqual() — первый метод проверяет или переданное числовое значение больше, чем ожидаемое >, второй проверяет больше или равно ожидаемому >=. toBeLessThan() и toBeLessThanOrEqual() — противоположность toBeGreaterThan() и toBeGreaterThanOrEqual() toBeCloseTo() — удобно использовать для чисел с плавающей запятой, когда вам не важна точность и вы не хотите, чтобы тест зависел от незначительной разницы в дроби. Вторым аргументом можно передать до какого знака после запятой необходима точность при сравнении.

1const num = 0.1 + 0.2; // 0.30000000000000004
2expect(num).toBeCloseTo(0.3);
3expect(Math.PI).toBeCloseTo(3.14, 2);

toMatch() — проверяет соответствие строки регулярному выражению.

1expect('Banana').toMatch(/Ba/);

toThrow() — используется в случаях, когда надо проверить исключение. Можно проверить как сам факт ошибки, так и проверить на выброс исключения определенного класса, либо по сообщению ошибки, либо по соответствию сообщения регулярному выражению.

1function funcWithError() {
2 throw new Error('some error');
3}
4expect(funcWithError).toThrow();
5expect(funcWithError).toThrow(Error);
6expect(funcWithError).toThrow('some error');
7expect(funcWithError).toThrow(/some/);

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

1expect(true).not.toBe(false);
2expect({ foo: 'bar' }).not.toEqual({});
3
4function funcWithoutError() {}
5expect(funcWithoutError).not.toThrow();

Давайте напишем пару простых тестов.

1const area = (radius) => Math.PI * radius ** 2;
2const circumference = (radius) => 2 * Math.PI * radius;
3
4
5test('Circle area', () => {
6 expect(circle.area(5)).toBeCloseTo(78.54);
7 expect(circle.area()).toBeNaN();
8});
9
10test('Circumference', () => {
11 expect(circle.circumference(11)).toBeCloseTo(69.1, 1);
12 expect(circle.circumference()).toBeNaN();
13});

В этих тестах мы проверили результат работы 2-х методов — area и circumference. При помощи метода toBeCloseTo мы сверились с ожидаемым результатом. В первом случае мы проверили или вычисляемая площадь круга с радиусом 5 приблизительно равна 78.54, при этом разница с полученым значением (оно составит 78.53981633974483) не большая и тест будет засчитан. Во втором мы указали, что нас интересует проверка с точностью до 1 знака после запятой. Также мы вызвали наши методы без аргументов и проверили результат с помощью toBeNaN. Поскольку результат их выполнения будет NaN, то и тесты будут пройдены успешно.

Разберём ещё один пример. Создадим функцию, которая будет фильтровать массив продуктов по цене:

1const byPriceRange = (products, min, max) =>
2 products.filter(item => item.price >= min && item.price <= max);
3
4const products = [
5 { name: 'onion', price: 12 },
6 { name: 'tomato', price: 26 },
7 { name: 'banana', price: 29 },
8 { name: 'orange', price: 38 }
9];
10
11test('Test product filter by range', () => {
12 const FROM = 15;
13 const TO = 30;
14 const filteredProducts = byPriceRange(products, FROM, TO);
15
16 expect(filteredProducts).toHaveLength(2);
17 expect(filteredProducts).toContainEqual({ name: 'tomato', price: 26 });
18 expect(filteredProducts).toEqual([{ name: 'tomato', price: 26 }, { name: 'banana', price: 29 }]);
19 expect(filteredProducts[0].price).toBeGreaterThanOrEqual(FROM);
20 expect(filteredProducts[1].price).toBeLessThanOrEqual(TO);
21 expect(filteredProducts).not.toContainEqual({ name: 'orange', price: 38 });
22});

В этом тесте мы проверям результат работы функии byRangePrice. Сначала мы проверили соответствие длины полученого массива ожидаемой — 2. Следующая проверка требует, чтобы в массиве находился элемент — { name: 'tomato', price: 26 }. Объект в массиве и объект переданный toContainEqual — это два разных объекта, а не ссылка на один и тот же. Но toContainEqual сверит каждое свойство. Так как оба объекта идентичные — проверка пройдет успешно. Далее мы используем toEqual для провеки структуры всего массива и его элементов. Методы toBeGreaterThanOrEqual и toBeLessThanOrEqual помогут нам проверить price первого и второго элемента массива. И, наконец, вызов not.toContainEqual сделает проверку, не содержится ли в массиве элемент — { name: 'orange', price: 38 }, которого по условию там быть не должно.

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

Expect

Если бы мы сами захотели бы написать функцию expect то она выглядела бы так

1const sum = (a, b) => a - b
2const subtract = (a, b) => a - b
3
4let result, expected
5result = sum(3, 7)
6expected = 10
7
8expect(result).toBe(expected)
9
10result = subtract(7, 3)
11expected = 4
12expect(result).toBe(expected)
13
14function expect(actual) {
15 return {
16 toBe(expected) {
17 if (actual !== expected) {
18 throw new Error(`${actual} is not equal to ${expected}`)
19 }
20 }
21 }
22}

Mock-функции

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

Использование фиктивной функции

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

1function forEach(items, callback) {
2 for (let index = 0; index < items.length; index++) {
3 callback(items[index]);
4 }
5}

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

1const mockCallback = jest.fn(x => 42 + x);
2forEach([0, 1], mockCallback);
3
4// The mock function is called twice
5expect(mockCallback.mock.calls.length).toBe(2);
6
7// The first argument of the first call to the function was 0
8expect(mockCallback.mock.calls[0][0]).toBe(0);
9
10// The first argument of the second call to the function was 1
11expect(mockCallback.mock.calls[1][0]).toBe(1);
12
13// The return value of the first call to the function was 42
14expect(mockCallback.mock.results[0].value).toBe(42);

.mock свойство

У всех мок-функций есть особое свойство .mock, где хранятся данные о том как функция была вызвана и что она вернула. Свойство .mock также отслеживает значение this для каждого вызова, так что как правило это можно посмотреть:

1const myMock = jest.fn();
2
3const a = new myMock();
4const b = {};
5const bound = myMock.bind(b);
6bound();
7
8console.log(myMock.mock.instances);
9// > [ <a>, <b> ]

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

1expect(someMockFunction.mock.calls.length).toBe(1);
2
3expect(someMockFunction.mock.calls[0][0]).toBe('first arg');
4
5expect(someMockFunction.mock.calls[0][1]).toBe('second arg');
6
7expect(someMockFunction.mock.results[0].value).toBe('return value');
8
9expect(someMockFunction.mock.instances.length).toBe(2);
10
11expect(someMockFunction.mock.instances[0].name).toEqual('test');

Мок-функции также могут использоваться для внедрения тестовых значений в ваш код во время тестирования:

1const myMock = jest.fn();
2console.log(myMock());
3// > undefined
4
5myMock.mockReturnValueOnce(10).mockReturnValueOnce('x').mockReturnValue(true);
6
7console.log(myMock(), myMock(), myMock(), myMock());
8// > 10, 'x', true, true

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

1const filterTestFn = jest.fn();
2
3// Make the mock return `true` for the first call,
4// and `false` for the second call
5filterTestFn.mockReturnValueOnce(true).mockReturnValueOnce(false);
6
7const result = [11, 12].filter(num => filterTestFn(num));
8
9console.log(result);
10// > [11]
11console.log(filterTestFn.mock.calls[0][0]); // 11
12console.log(filterTestFn.mock.calls[1][0]); // 12

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

1const myMockFn = jest.fn(cb => cb(null, true));
2
3myMockFn((err, val) => console.log(val));
4// > true

Метод mockImplementation полезен, когда вам нужно определить реализацию по умолчанию фиктивной функции, которая создается из другого модуля:

1const foo = jest.fn()
2foo.mockImplementation(() => 42);
3foo();

Когда вам нужно воссоздать сложное поведение фиктивной функции, при котором несколько вызовов функций приводят к разным результатам, используйте метод mockImplementationOnce:

1const myMockFn = jest
2 .fn()
3 .mockImplementationOnce(cb => cb(null, true))
4 .mockImplementationOnce(cb => cb(null, false));
5
6myMockFn((err, val) => console.log(val));
7// > true
8
9myMockFn((err, val) => console.log(val));
10// > false

Когда mock функция исчерпывает реализации, определенные с помощью mockImplementationOnce, она выполнит реализацию по умолчанию, установленную с помощью jest.fn (если она определена):

1const myMockFn = jest
2 .fn(() => 'default')
3 .mockImplementationOnce(() => 'first call')
4 .mockImplementationOnce(() => 'second call');
5
6console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn());
7// > 'first call', 'second call', 'default', 'default'

Для случаев, когда у нас есть методы, которые обычно связаны цепочкой (и, следовательно, всегда нужно возвращать это), у нас есть сладкий API, чтобы упростить это в виде функции .mockReturnThis(), которая также находится во всех моков:

1const myObj = {
2 myMethod: jest.fn().mockReturnThis(),
3};
4
5// is the same as
6
7const otherObj = {
8 myMethod: jest.fn(function () {
9 return this;
10 }),
11};

При желании вы можете указать имя для ваших фиктивных функций, которое будет отображаться вместо «jest.fn()» в выводе ошибок теста. Используйте это, если вы хотите иметь возможность быстро идентифицировать фиктивную функцию, сообщающую об ошибке в вашем тестовом выводе.

1const myMockFn = jest
2 .fn()
3 .mockReturnValue('default')
4 .mockImplementation(scalar => 42 + scalar)
5 .mockName('add42');

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

1const mockFunc = jest.fn()
2//Мок-функция вызывалась хотя бы один раз
3expect(mockFunc).toHaveBeenCalled();
4
5// Мок-функция вызывалась хотя бы один раз с указанными аргументами.
6expect(mockFunc).toHaveBeenCalledWith(arg1, arg2);
7
8// Последний вызов фиктивной функции был вызван с указанными аргументами
9expect(mockFunc).toHaveBeenLastCalledWith(arg1, arg2);
10
11// Все звонки и название макета записываются в виде снимка
12expect(mockFunc).toMatchSnapshot();

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

1const mockFunc = jest.fn()
2// Мок-функция вызывалась хотя бы один раз
3expect(mockFunc.mock.calls.length).toBeGreaterThan(0);
4
5// Мок-функция была вызвана хотя бы один раз с указанными аргументами
6expect(mockFunc.mock.calls).toContainEqual([arg1, arg2]);
7
8// Последний вызов фиктивной функции был вызван с указанными аргументами
9expect(mockFunc.mock.calls[mockFunc.mock.calls.length - 1]).toEqual([
10 arg1,
11 arg2,
12]);
13
14// Первый аргумент последнего вызова фиктивной функции был `42`
15// (обратите внимание, что для этого конкретного утверждения нет сахарного помощника)
16expect(mockFunc.mock.calls[mockFunc.mock.calls.length - 1][0]).toBe(42);
17
18// Снимок проверит, что макет был вызван одинаковое количество раз,
19// в том же порядке, с теми же аргументами. Это также будет утверждаться по имени.
20expect(mockFunc.mock.calls).toEqual([[arg1, arg2]]);
21expect(mockFunc.getMockName()).toBe('a mock name');
Hello