N(odejs m)ock - Nock

Не перестаю находить что-то новое

Спустя 2 года работы в проекте, обнаружил у нас несколько тестов с использованием фреймворка nock. Он нужен чтобы мокать сетевые вызовы к другим сервисам (аналог wiremock).

Как это работает

Каждый мок сначала создаётся, а затем донастраивается цепочкой вызовов (builder pattern). Давай разберём на простом примере:

nock('https://some-remote-service.net',
    {
      reqheaders: {
        authorization: 'bart',
        'user-agent': 'my-super-service',
        host: 'some-remote-service.net',
      },
    })
  .post('/api/endpoint', { first_name: 'bart', last_name: 'simpson' })
  .times(1)
  .reply(200, {});

// вызываем тестируемый код тут

expect(nock.isDone()).toBeTruthy();

nock() - мы говорим фреймворку слушать запросы к хосту https://some-remote-service.net. Вторым параметром мы передаём набор http-заголовков, которые должны быть в запросе.

.post() - мы ожидаем запрос типа POST по пути /api/endpoint и с телом запроса { first_name: 'bart', last_name: 'simpson' }.

times() - сколько раз нужно ответить на запрос.

reply() - кот ответа и тело ответа если нужно.

nock.isDone() - проверяет что все настроенные вызовы были сделаны. Это мне прям очень понравилось, потому что у меня не раз были ситуации, когда вызов рельного сервиса или библиотеки поменялся, а тесты с моками продолжали работать. Потому что замокано было что-то не то или не так, но результат оказывался правильный по счастливому стечению обстоятельств.

Пример проверки retry-логики

it('retries on error', async () => {
  jest.setTimeout(10000);
  nock(serviceUrl, requestOptions)
    .get(firstEndpoint)
    .times(4)
    .reply(404);
  nock(serviceUrl, requestOptions)
    .post(secondEndpoint, requestBody)
    .times(4)
    .reply(500, {});

  // вызываем тестирвемый код тут

  expect(nock.isDone()).toBeTruthy();
});

Всё примерно то же самое, только вместо “хорошего” ответа 200, мы отвечаем 404 на GET по первому пути и 500 на POST по второму. И так 4 раза. nock.isDone() проверит, что каждый из запросов был вызван как минимум 4 раза. Пришлось поднять дефолтный таймаут для этого теста используя jest.setTimeout() (мы используем jest в качестве основного тестового фреймворка).

Как это отлаживать

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

nock(serviceUrl, requestOptions)
  .get(firstEndpoint)
  .times(4)
  .reply(404)
  .log(console.log);

Если добавить к моку (ноку?) .log(console.log), он будет выводить в консоль информацию о попытках сматчить сетевой запрос:

console.log node_modules/nock/lib/interceptor.js:332
    matching https://some-remote-service.net/api/endpoint to POST https://some-remote-service.net:443/api/endpoint: true

true в конце строки означает что ожидаемый запрос был получен.

Если nock.isDone() по неведомой причине возвращает false в конце теста, можно посмотреть, какие из моков не были вызваны:

if (nock.isDone()) {
  console.log(nock.pendingMocks())
}

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

comments powered by Disqus