В этой серии статей мы рассмотрим, как издеваться над Jest.

Jest Mocking — Часть 1: Функция
Jest Mocking — Часть 2: Модуль
Jest Mocking — Часть 3: Таймер
Jest Mocking — Часть 4: React Component

Для турецких читателей: Serinin Türkçe haline kendi blogum üzerinden ulaşabilirsiniz.

Коды вы можете найти в статье на Github.

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

Что такое мокап и какую проблему он решает?

На самом деле макет — это имитация чего-либо в программном обеспечении.

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

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

Таким образом, мы можем называть макетом каждую из этих имитаций, таких как поддельные данные, поддельные функции, поддельные API.

Макетные типы

Если вы уже имели дело с тестированием, возможно, вы сталкивались с использованием таких терминов, как заглушка, подделка, фиктивный вместо mock. В книге Джерарда Месароса используется термин Тестовый дубль для поддельного объекта, который заменяет настоящий объект, чтобы избежать путаницы. Как дублеры для актеров в фильмах. И делит его на шесть категорий:

  • Dummy: используется для заполнения параметров, которые мы должны передать функции, но на самом деле не используется.
  • Подделка: содержит упрощенную, но работающую версию кода. Примером этого может быть чтение из объекта, содержащего тестовые данные, вместо обращения к базе данных для получения хэшей паролей.
  • Заглушка: используется для возврата запланированных ответов во время вызовов функций во время тестирования.
  • Шпион: защищает исходную реализацию и хранит дополнительную информацию, например, сколько раз вызываются функции, с какими параметрами и какие ответы возвращаются.
  • Mock: используется для планирования того, какой ответ вернут функции, которые, как мы ожидаем, будут вызываться с определенными аргументами. Он выдает ошибку, если вызывается с неожиданными аргументами.

Тем не менее, мы изо всех сил пытаемся классифицировать на практике. Мы будем называть их все как mock.

Введение

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

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

// File: productApi.js
import axios from "axios";

async function getProduct(productId) {
  const response = await axios.get(
    `https://dummyjson.com/products/${productId}`
  );

  return response.data;
}

function mapProduct(data) {
  const mappedData = { ...data };

  mappedData.hotDeal = data.discountPercentage > 80;
  mappedData.isRunningOut = data.stock < 5;

  return mappedData;
}

export { getProduct, mapProduct };

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

// File: productTest.js
import axios from "axios";
import { getProduct, mapProduct } from "./productApi.js";

axios.get = async () => {
  return {
    data: {
      id: 999,
      title: "Fake iPhone 11",
      description: "An apple mobile which is nothing like apple",
      price: 549,
      discountPercentage: 99.99,
      rating: 4.69,
      stock: 94,
      brand: "Apple",
      category: "smartphones",
      thumbnail: "https://dummyjson.com/image/i/products/1/thumbnail.jpg",
      images: ["https://dummyjson.com/image/i/products/1/1.jpg"],
    },
  };
};

async function test() {
  const product = await getProduct(3);
  const mappedProduct = mapProduct(product);

  if (!mappedProduct.hotDeal) {
    throw new Error("mapProduct method works incorrectly. take a look at the code.");
  } else {
    console.log("mapProduct method works correctly.");
  }
}

test();

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

Насмешливая функция

Теперь мы можем продолжить Jest. Мы используем jest.fn(implementation?) для создания фиктивной функции.

// File: mockFunction.test.js
test("playground", () => {
  const mockFunction = jest.fn();
  console.log("mockFunction:", mockFunction);
});

/* OUTPUT:
  mockFunction: [Function: mockConstructor] {
    _isMockFunction: true,
    getMockImplementation: [Function (anonymous)],
    mock: [Getter/Setter],
    mockClear: [Function (anonymous)],
    mockReset: [Function (anonymous)],
    mockRestore: [Function (anonymous)],
    mockReturnValueOnce: [Function (anonymous)],
    mockResolvedValueOnce: [Function (anonymous)],
    mockRejectedValueOnce: [Function (anonymous)],
    mockReturnValue: [Function (anonymous)],
    mockResolvedValue: [Function (anonymous)],
    mockRejectedValue: [Function (anonymous)],
    mockImplementationOnce: [Function (anonymous)],
    withImplementation: [Function: bound withImplementation],
    mockImplementation: [Function (anonymous)],
    mockReturnThis: [Function (anonymous)],
    mockName: [Function (anonymous)],
    getMockName: [Function (anonymous)]
  }
*/

Мы можем переопределить методы с помощью фиктивных функций.

// File: overridePackageMethod.test.js
import axios from "axios";

test("playground", () => {
  axios.get = jest.fn();
  console.log("mock Implementation:", axios.get.toString());
});

/* OUTPUT:
  mock Implementation: function () {
    return fn.apply(this, arguments);
  }
*/

Также мы можем издеваться над встроенными методами объекта.

// File: builtInMethod.test.js
test("playground", () => {
  console.log("original Implementation:", Math.random.toString());
  Math.random = jest.fn();
  console.log("mock Implementation:", Math.random.toString());
});

/* OUTPUT:
  original Implementation: function random() { [native code] }
  mock Implementation: function () {
    return fn.apply(this, arguments);
  }
*/

.mock Ресурс

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

  • mock.calls: Список аргументов, с которыми была вызвана функция.
  • mock.results: список результатов вызовов функций. Тип возврата может быть return, throw или incomplete.
  • mock.instances: Список экземпляров, если функция является конструктором.
  • mock.contexts: Список this объектов на момент вызова функции.
  • mock.lastCall: Список аргументов последнего вызова.
// File: manipulateArray.test.js
function manipulateArray(array, manipulateMethod) {
  return array.map((item) => manipulateMethod(item));
}

test("playground", () => {
  const array = [0, 1, 2];
  const mockManipulateMethod = jest.fn((x) => x + 2);
  manipulateArray(array, mockManipulateMethod);

  console.log(
    "mockManipulateMethod's mock property:", 
    mockManipulateMethod.mock
  );
});

/* OUTPUT:
  mockManipulateMethod's mock property: {
    "calls": [[0], [1], [2]],
    "contexts": [null, null, null],
    "instances": [null, null, null],
    "results": [
      { "type": "return", "value": 2 },
      { "type": "return", "value": 3 },
      { "type": "return", "value": 4 },
    ],
    "lastCall": [2]
  }
*/

В остальной части статьи мы увидим .mock на реальных примерах.

Возврат статического значения

У нас есть два разных метода заставить фиктивную функцию возвращать статическое значение:

  • mockReturnValue(value) — определите значение, которое будет возвращаться при всех вызовах.
  • mockReturnValueOnce(value) — определите значение, которое будет возвращено при следующем вызове.
// File: mockReturns.test.js
test("playground", () => {
  const mockFunction = jest
    .fn()
    .mockReturnValue("other calls")
    .mockReturnValueOnce("first call")
    .mockReturnValueOnce("second call");

  for (let index = 0; index < 5; index++) {
    console.log("mockedProduct", mockFunction());
  }
});

/* OUTPUT:
  mockedProduct first call
  mockedProduct second call
  mockedProduct other calls
  mockedProduct other calls
  mockedProduct other calls
*/

Рассмотрим еще два примера.

Допустим, в первом примере у нас есть функция для чтения значения из локального хранилища. Мы создадим макет метода window.localStorage.getItem, чтобы проверить, возвращает ли наш метод правильное значение, когда мы даем ему ключ.

// File: getFromLocalStorage.test.js
function getFromLocalStorage(key) {
  return window.localStorage.getItem(key);
}

test("should get data from local storage correctly", () => {
  const key = "testKey";
  const value = "testValue";

  const mockLocalStorageGet = jest.fn();

  Object.defineProperty(window, "localStorage", {
    value: {
      getItem: mockLocalStorageGet,
    },
  });

  // we want mock value to be returned when mock localstorage is called.
  mockLocalStorageGet.mockReturnValue(value);
  // another usage: window.localStorage.getItem.mockReturnValue(value)

  getFromLocalStorage(key);

  expect(jest.isMockFunction(window.localStorage.getItem)).toBe(true)
  expect(mockLocalStorageGet.mock.lastCall[0]).toBe(key);
  expect(mockLocalStorageGet.mock.results[0].value).toBe(value);
});

/* OUTPUT:
  PASS  getFromLocalStorage.test.js
    ✓ should get data from local storage correctly (3 ms)
*/

Во втором примере мы хотим рассчитать оставшееся время до будущей даты. Если мы не имитируем его, тест будет проходить до даты окончания. Мы не должны допускать хрупких испытаний. Вот почему мы смоделируем значение, возвращаемое new Date(), и убедимся, что оно предшествует дате окончания.

// File: getRemainingTime.test.js
function getRemainingTime(endDate, startDate = new Date()) {
  let delta = (endDate.getTime() - startDate.getTime()) / 1000;

  return {
    remainingDays: Math.floor(delta / (60 * 60 * 24)),
    remainingHours: Math.floor((delta / (60 * 60)) % 24),
    remainingMinutes: Math.floor((delta / 60) % 60),
    remainingSeconds: Math.floor(delta % 60),
  };
}

test("should return remaining data when give future date", () => {
  const endDate = new Date(2023, 1, 1);
  const mockCurrDate = new Date(2022, 10, 16, 16, 9, 25);

  // we want the mock date to be returned when the constructor is called.
  global.Date = jest.fn().mockReturnValue(mockCurrDate);

  expect(getRemainingTime(endDate)).toEqual({
    remainingDays: 76,
    remainingHours: 7,
    remainingMinutes: 50,
    remainingSeconds: 35,
  });
});

/* OUTPUT:
  PASS  getRemainingTime.test.js
    ✓ should return remaining data when give future date (3 ms)
*/

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

Возврат динамического значения

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

  • mockImplementation(func): этот метод устанавливает реализацию фиктивной функции, которая будет использоваться для всех вызовов.
  • mockImplementationOnce(func): этот метод устанавливает реализацию фиктивной функции, которая будет использоваться для одного вызова. После того, как фиктивная функция будет вызвана один раз, она вернется к своему поведению по умолчанию (чтобы вернуть undefined).
  • withImplementation(func, callback): этот метод задает реализацию фиктивной функции, которая будет использоваться для всех вызовов, происходящих в рамках предоставленной функции callback.

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

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

// File: manipulateArray.test.js
function manipulateArray(array, manipulateMethod) {
  return array.map((item) => manipulateMethod(item));
}

test("playground", () => {
  const array = [0, 1, 2];
  const manipulateMethod = jest.fn().mockImplementation((x) => x + 2);
  manipulateArray(array, manipulateMethod);

  console.log(manipulateMethod.mock.results);
});

/* OUTPUT:
  [
    { type: 'return', value: 2 },
    { type: 'return', value: 3 },
    { type: 'return', value: 4 }
  ]
*/

Во втором примере мы видим, как мы можем изменить реализацию в конкретном блоке.

// File: withImplementation.test.js
test("playground", () => {
  const mockMethod = jest.fn(() => "outside callback");

  console.log(mockMethod());

  mockMethod.withImplementation(
    // temporary implementation for the mock function
    () => "inside callback",
    // the scope in which the implementation will be applied
    () => {
      console.log(mockMethod());
    }
  );

  console.log(mockMethod());
});

/* OUTPUT:
  outside callback
  inside callback
  outside callback
*/

Имитация асинхронных функций

Асинхронные функции, как известно, возвращают Promise. Таким образом, фиктивная функция также должна возвращать обещание. Мы можем сделать это с помощью jest.fn().mockImplementation(() => Promise.resolve(value)) или jest.fn().mockImplementation(() => Promise.reject(value)). Однако Jest предоставляет четыре метода, которые абстрагируют эти реализации:

  • mockResolvedValue(value): всегда возвращает разрешенное обещание с заданным значением.
  • mockResolvedValueOnce(value): возвращает разрешенное обещание с заданным значением для первого вызова, а затем реализацию по умолчанию для следующих вызовов.
  • mockRejectedValue(value): Всегда возвращает отклоненное обещание с заданным значением.
  • mockRejectedValueOnce(value): возвращает отклоненное обещание с заданным значением для первого вызова, а затем реализацию по умолчанию для следующих вызовов.

Давайте посмотрим на пример.

// File: getProduct.test.js
import axios from "axios";

async function getProduct(productId) {
  try {
    const response = await axios.get(`https://dummyjson.com/products/${productId}`);

    return response.data;
  } catch (error) {
    return null;
  }
}

describe("getProduct tests", () => {
  beforeEach(() => {
    axios.get = jest.fn();
  });

  test("should be return product data when request is succesfully", async () => {
    const mockedValue = {
      data: {
        id: 1,
        title: "iPhone 9",
        description: "An apple mobile which is nothing like apple",
        price: 549,
        discountPercentage: 12.96,
        rating: 4.69,
        stock: 94,
        brand: "Apple",
        category: "smartphones",
        thumbnail: "https://dummyjson.com/image/i/products/1/thumbnail.jpg",
        images: [
          "https://dummyjson.com/image/i/products/1/1.jpg",
          "https://dummyjson.com/image/i/products/1/2.jpg",
          "https://dummyjson.com/image/i/products/1/3.jpg",
          "https://dummyjson.com/image/i/products/1/4.jpg",
          "https://dummyjson.com/image/i/products/1/thumbnail.jpg",
        ],
      },
    };
    axios.get.mockResolvedValue(mockedValue);

    const result = await getProduct();

    expect(result).toStrictEqual(mockedValue.data);
  });

  test("should be return product data when request is failed", async () => {
    axios.get.mockRejectedValue(new Error("Error occured when fetching data!"));

    const result = await getProduct();

    expect(result).toStrictEqual(null);
  });
});

/* OUTPUT:
  PASS  sgetProduct.test.js
    getProduct tests
      ✓ should be return product data when request is succesfully (20 ms)
      ✓ should be return product data when request is failed (1 ms)
*/

Очистка фиктивной истории

Иногда мы пишем несколько тестов для проверки результатов методов в разных случаях (как в предыдущем коде). По умолчанию Jest не очищает фиктивные данные. Это также вызывает ошибки. Например:

// File: afterEachless.test.js
describe("playground", () => {
  test("test 1", () => {
    Math.random = jest.fn().mockReturnValue(55);

    console.log("first random value: ", Math.random());
    console.log("second random value: ", Math.random());

    console.log(Math.random.mock.calls.length);
  });

  test("test 2", () => {
    console.log("third random value: ", Math.random());
    console.log("fourth random value: ", Math.random());

    console.log(Math.random.mock.calls.length);
  });
});

/* OUTPUT:
  first random value:  55
  second random value:  55
  2
  third random value:  55
  fourth random value:  55
  4
*/

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

  • mockClear: очищает данные в свойстве .mock.
  • mockReset: в дополнение к mockClear очищает эффект функций типа mockReturnValue и mockImplementation.
  • mockRestore: в дополнение к mockReset он восстанавливает исходную реализацию, когда макет был создан с помощью jest.spyOn.

Если вы хотите очистить историю всех наших фиктивных функций вместо одной фиктивной функции, вы можете использовать методы jest.clearAllMocks, jest.resetAllMocks и jest.restoreAllMocks. Даже если вы не хотите прописывать его в каждом тестовом файле, вы можете активировать правила clearMocks, resetMocks, restoreMocks в файле jest.config.js.

Обычно мы предпочитаем использовать его с afterEach, так как хотим очистить его после тестов. Например:

// File: afterEach.test.js
describe("playground", () => {
  afterEach(() => {
    Math.random.mockRestore();
  });

  test("test 1", () => {
    Math.random = jest.fn().mockReturnValue(55);

    console.log("first random value:", Math.random());
    console.log("second random value:", Math.random());

    console.log(Math.random.mock.calls.length);
  });

  test("test 2", () => {
    console.log("third random value:", Math.random());
    console.log("fourth random value:", Math.random());

    console.log(Math.random.mock.calls.length);
  });
});

/* OUTPUT:
  first random value: 55
  second random value: 55
  2
  third random value: undefined
  fourth random value: undefined
  2
*/

О, неееет. Имитация данных была очищена между тестами. Однако восстановить первоначальную реализацию не удалось. Почему?

Шпион

Если мы переопределим методы фиктивной функции, мы потеряем доступ к исходной реализации. Вместо этого мы можем использовать jest.spyOn(object, methodName).

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

// File: dateNow.test.js
test("playground", () => {
  const dateNowSpy = jest.spyOn(Date, "now");

  console.log("mocked function:", Date.now);
  console.log("first call return value:", Date.now());

  dateNowSpy.mockReturnValueOnce(500);

  console.log("second call return value:", Date.now());
  console.log("third call return value:", Date.now());
});

/* OUTPUT:
  mocked function: [Function: mockConstructor] {
    _isMockFunction: true,
    getMockImplementation: [Function (anonymous)],
    mock: [Getter/Setter],
    mockClear: [Function (anonymous)],
    mockReset: [Function (anonymous)],
    mockRestore: [Function (anonymous)],
    mockReturnValueOnce: [Function (anonymous)],
    mockResolvedValueOnce: [Function (anonymous)],
    mockRejectedValueOnce: [Function (anonymous)],
    mockReturnValue: [Function (anonymous)],
    mockResolvedValue: [Function (anonymous)],
    mockRejectedValue: [Function (anonymous)],
    mockImplementationOnce: [Function (anonymous)],
    withImplementation: [Function: bound withImplementation],
    mockImplementation: [Function (anonymous)],
    mockReturnThis: [Function (anonymous)],
    mockName: [Function (anonymous)],
    getMockName: [Function (anonymous)]
  }
  
  first call return value: 1671971862021
  second call return value: 500
  third call return value: 1671971862027
*/

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

// File: dateNowImplementation.test.js
test("playground", () => {
  jest.spyOn(global.Date, "now");
  console.log("first call return value: ", Date.now());

  global.Date.now.mockImplementation(() => "Hacked by crazyboy!");
  console.log("second call return value: ", Date.now());
  console.log("third call return value: ", Date.now());

  global.Date.now.mockRestore();
  console.log("fourth call return value: ", Date.now());
});

/* OUTPUT:
  first call return value:  1671972138129
  second call return value:  Hacked by crazyboy!
  third call return value:  Hacked by crazyboy!
  fourth call return value:  1671972138151
*/

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

Поддержка типов TypeScript

Если вы используете TypeScript, следующий код выдаст вам сообщение об ошибке, говорящее об отсутствии свойства mock для типа getItem.

test("playground", () => {
  const mockLocalStorageGet = jest.fn();

  Object.defineProperty(window, "localStorage", {
    value: {
      getItem: mockLocalStorageGet,
    },
  });

  console.log(window.localStorage.getItem.mock.results); // TS Error!
});

Чтобы решить эту проблему, вы можете использовать тип jest.Mock или метод jest.mocked (обтекание фиктивными типами).

// usage 1 (my favorite):
const mockedLocalStorageGetItem = jest.mocked(window.localStorage.getItem)

// usage 2:
const mockedLocalStorageGetItem = window.localStorage.getItem as jest.Mock;

Специальные методы ожидания

Обычно мы не проверяем значения свойства .mock вручную. Jest предлагает полезные методы, облегчающие нашу работу.

Связано с количеством звонков:

  • toHaveBeenCalled(): Проверяет, была ли вызвана фиктивная функция
  • toHaveBeenCalledTimes(number): Проверяет, вызывалась ли фиктивная функция указанное количество раз.

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

  • toHaveBeenCalledWith(arg1, arg2, …) : Проверяет, была ли вызвана фиктивная функция с указанными аргументами.
  • toHaveBeenLastCalledWith(arg1, arg2, …) : Проверяет, вызывалась ли фиктивная функция в последний раз с указанными аргументами.
  • toHaveBeenNthCalledWith(nthCall, arg1, arg2, ...): Проверяет, вызывалась ли фиктивная функция с указанными аргументами при n-м вызове.

Связано с возвращаемыми значениями вызовов:

  • toHaveReturned() : Проверяет, вернула ли фиктивная функция значение (не выдавая ошибку)
  • toHaveReturnedTimes(number) : Проверяет, возвращала ли фиктивная функция указанное количество раз.
  • toHaveReturnedWith(value) : Проверяет, вернула ли фиктивная функция указанное значение.
  • toHaveLastReturnedWith(value): Проверяет, возвращала ли фиктивная функция в последний раз указанное значение.
  • toHaveNthReturnedWith(nthCall, value): Проверяет, вернула ли фиктивная функция указанное значение при n-м вызове.
// File: expect.test.js
test("playground", async () => {
  const mockAreaCalculate = jest.fn((x, y) => x * y);

  mockAreaCalculate(7, 13);
  mockAreaCalculate(3, 5);
  mockAreaCalculate(5, 8);

  expect(mockAreaCalculate).toHaveBeenCalled();
  expect(mockAreaCalculate).toHaveBeenCalledTimes(3);
  expect(mockAreaCalculate).toHaveBeenCalledWith(7, 13);
  expect(mockAreaCalculate).toHaveBeenLastCalledWith(5, 8);
  expect(mockAreaCalculate).toHaveBeenNthCalledWith(2, 3, 5);

  expect(mockAreaCalculate).toHaveReturned();
  expect(mockAreaCalculate).toHaveReturnedTimes(3);
  expect(mockAreaCalculate).toHaveReturnedWith(7 * 13);
  expect(mockAreaCalculate).toHaveLastReturnedWith(5 * 8);
  expect(mockAreaCalculate).toHaveNthReturnedWith(2, 3 * 5);
});

/* OUTPUT:
PASS  expect.test.js
  ✓ playground
*/

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

  • expect.anything(): соответствует любому значению, кроме null и undefined.
  • expect.any(constructor): соответствует любому значению, созданному данным конструктором.
  • expect.stringContaining(string): Соответствует, если данная строка содержится.
  • expect.stringMatching(string | regexp): Соответствует, если заданная строка или регулярное выражение совпадают.
  • expect.arrayContaining(array): Соответствует, если массив содержит заданное подмножество.
  • expect.objectContaining(object): Соответствует, если объект содержит заданное подмножество.
  • expect.not.stringContaining(string): Соответствует, если значение не является строкой или не содержит заданную строку.
  • expect.not.stringMatching(string | regexp): Соответствует, если значение не является строкой или не соответствует заданной строке или регулярному выражению.
  • expect.not.arrayContaining(array): Соответствует, если массив не содержит заданного подмножества.
  • expect.not.objectContaining(object): Соответствует, если объект не содержит заданного подмножества.
// File: matchers.test.js
test("playground", async () => {
  const calledArguments = [
    [1, 2, 3],
    {
      name: "Sherlock",
      surname: "Holmes",
      job: "Consulting Detective",
      partner: "Dr. Watson",
    },
  ];

  expect(calledArguments).toEqual([
    expect.arrayContaining([1, 3]),
    expect.objectContaining({
      name: expect.anything(),
      job: expect.stringContaining("Detective"),
      partner: expect.any(String),
    }),
  ]);
});

/* OUTPUT:
PASS  matchers.test.js
  ✓ playground
*/

Мы говорили о том, как имитировать методы. В следующей статье мы поговорим о модулях.

Закончим статью моим мемом.

Ресурсы