Jest-тестирование контекста / шпионажа над ложными переменными, созданными вне функций (на уровне класса) - PullRequest
1 голос
/ 07 марта 2019

Я пытаюсь провести какое-то тестирование в Jest, но застрял в шутке.Мне удалось заставить тест работать, но только изменив мою реализацию (из-за чего я чувствую себя грязно).

Вот тест:

import * as postmark from 'postmark';
jest.mock('postmark');

const mockGetServers = jest.fn();
const AccountClient = jest.fn(() => {
  return {
    getServers: mockGetServers
  };
});
postmark.AccountClient = AccountClient;

import accountApi from './account-api';

describe('account-api', () => {
  describe('listServers', () => {
    it('calls postmark listServers', async () => {
      await accountApi.listServers();

      expect(mockGetServers).toHaveBeenCalledTimes(1);
    });
  });
});

Вот рабочая реализация:

import * as postmark from 'postmark';
const accountToken = 'some-token-number';

const listServers = async () => {
  try {
    const accountClient = postmark.AccountClient(accountToken);
    const servers = await accountClient.getServers();
    return servers;
  } catch (e) {
    console.log('ERROR', e);
  }
};

export default {
  listServers
}

Вот оригинальная реализация:

import * as postmark from 'postmark';
const accountToken = 'some-token-number';
const accountClient = postmark.AccountClient(accountToken);

const listServers = async () => {
  try {
    const servers = await accountClient.getServers();
    return servers;
  } catch (e) {
    console.log('ERROR', e);
  }
};

export default {
  listServers
}

Единственное изменение - это то, где в коде создается accountClient (внутри или снаружи функции listServers).Первоначальная реализация была бы завершена, и Jest сообщал бы, что макет не был вызван.

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

Что отличается от того, как я создал accountClient, что означает, что в тесте можно шпионить за имитатором?Есть ли способ, которым я могу издеваться (и шпионить) за объектом, который создается на уровне класса, а не на уровне функции?

Спасибо

Ответы [ 2 ]

1 голос
/ 08 марта 2019

Я что-то упускаю из-за того, как шутка работает под капотом?

Две вещи на заметку:

  1. ES6 import вызовы подняты в верхнюю часть текущего диапазона
  2. babel-jest поднимает вызовы на jest.mock наверх их кодового блока (прежде всего, включая любые вызовы ES6 import в блоке)

Чем отличается способ создания учетной записиКлиента от клиента, что означает, что в тесте можно шпионить за пробой?

В обоих случаях это выполняется первым:

jest.mock('postmark');

... который автоматически смоделирует модуль postmark.

Затем запускается:

import accountApi from './account-api';

В оригинальной реализации эта строка запускается:

const accountClient = postmark.AccountClient(accountToken);

... который фиксирует результат вызова postmark.AccountClient и сохраняет его в accountClient. Автоматическое макетирование postmark будет иметь заглушку AccountClient с фиктивной функцией, которая возвращает undefined, поэтому accountClient будет установлено на undefined.

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

Затем во время теста запускается эта строка:

await accountApi.listServers();

В оригинальной реализации этот вызов заканчивается выполнением этого:

const servers = await accountClient.getServers();

... который падает до catch, поскольку accountClient не определено, ошибка регистрируется, и тест продолжается до тех пор, пока не произойдет сбой в этой строке:

expect(mockGetServers).toHaveBeenCalledTimes(1);

... так как mockGetServers никогда не вызывали.

С другой стороны, в рабочей реализации это работает:

const accountClient = postmark.AccountClient(accountToken);
const servers = await accountClient.getServers();

... и так как postmark подвергается насмешке по этому пункту, он использует макет и тест проходит.


Есть ли способ, которым я могу издеваться (и шпионить) за объектом, который создается на уровне класса, а не на уровне функции?

Да.

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

Один из самых простых способов сделать это - настроить макет с фабрикой модулей во время вызова на jest.mock, так как он поднимается и запускается первым.

Вот обновленный тест, который работает с оригинальной реализацией :

import * as postmark from 'postmark';
jest.mock('postmark', () => {  // use a module factory
  const mockGetServers = jest.fn();
  const AccountClient = jest.fn(() => {
    return {
      getServers: mockGetServers  // NOTE: this returns the same mockGetServers every time
    };
  });
  return {
    AccountClient
  }
});

import accountApi from './account-api';

describe('account-api', () => {
  describe('listServers', () => {
    it('calls postmark listServers', async () => {
      await accountApi.listServers();

      const mockGetServers = postmark.AccountClient().getServers;  // get mockGetServers
      expect(mockGetServers).toHaveBeenCalledTimes(1);  // Success!
    });
  });
});
1 голос
/ 07 марта 2019

Я думаю, вы можете посмотреть на proxyquire .

import * as postmark from 'postmark';
import * as proxyquire from 'proxyquire';

jest.mock('postmark');

const mockGetServers = jest.fn();
const AccountClient = jest.fn(() => {
  return {
    getServers: mockGetServers
  };
});
postmark.AccountClient = AccountClient;

import accountApi from proxyquire('./account-api', postmark);

describe('account-api', () => {
  describe('listServers', () => {
    it('calls postmark listServers', async () => {
      await accountApi.listServers();

      expect(mockGetServers).toHaveBeenCalledTimes(1);
    });
  });
});

Обратите внимание, что я не тестировал эту реализацию; может потребоваться настройка.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...