Есть ли способ правильно смоделировать повторный выбор селекторов для модульного тестирования? - PullRequest
1 голос
/ 15 апреля 2019

У меня довольно сложная структура селекторов в моем проекте (некоторые селекторы могут иметь до 5 уровней вложенности), поэтому некоторые из них очень сложно протестировать с передачей входного состояния, и я хотел бы вместо этого имитировать входные селекторы.Однако я обнаружил, что это на самом деле невозможно.

Вот самый простой пример:

// selectors1.js
export const baseSelector = createSelector(...);

-

// selectors2.js
export const targetSelector = createSelector([selectors1.baseSelector], () => {...});

Что бы я хотел иметь в своемнабор тестов:

beforeEach(() => {
  jest.spyOn(selectors1, 'baseSelector').mockReturnValue('some value');
});

test('My test', () => {
  expect(selectors2.targetSelector()).toEqual('some value');
});

Но этот подход не будет работать, так как targetSelector получает ссылку на selectors1.baseSelector во время инициализации selectors2.js и макет назначается selectors1.baseSelector после него.

Сейчас я вижу 2 рабочих решения:

  1. Смоделируйте весь selectors1.js модуль с jest.mock, однако, он не будет работать, если мне нужно будет изменить selectors1.baseSelectorвывод для некоторых конкретных случаев
  2. Оберните все селекторы зависимостей следующим образом:

export const targetSelector = createSelector([(state) => selectors1.baseSelector(state)], () => {...});

Но мне не нравится этот подход по очевидным причинам.

Итак, вопрос в следующем: есть ли шанс правильно сменить селекторы для проверки юнитов?

Ответы [ 2 ]

3 голосов
/ 18 апреля 2019

Дело в том, что реселект основан на концепции композиции. Таким образом, вы создаете один селектор из многих других. На самом деле вам нужно проверить не весь селектор, а последнюю функцию, выполняющую эту работу. Если нет, тесты будут дублировать друг друга, как если бы у вас были тесты для селектора 1, а селектор1 используется в селекторе 2, тогда вы автоматически протестируете оба из них в тестах селектора 2.

Для достижения:

  • меньше издевается
  • нет необходимости специально имитировать результат составных селекторов
  • без дублирования теста

проверить только функцию результата селектора. Доступен по selector.resultFunc.

Так, например:

const selector2 = createSelector(selector1, (data) => ...);

// tests

const actual = selector2.resultFunc([returnOfSelector1Mock]);
const expected = [what we expect];
expect(actual).toEqual(expected)

Резюме

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

1 голос
/ 17 апреля 2019

Этого можно добиться, выполнив макет всего модуля selectors1.js, но импорт также находится внутри теста, чтобы иметь доступ к проверенной функциональности

Предположим, ваш selectors1.js выглядит как

import { createSelector } from 'reselect';

// selector
const getFoo = state => state.foo;

// reselect function
export const baseSelector = createSelector(
  [getFoo],
  foo => foo
);

и selectors2.js выглядит как

import { createSelector } from 'reselect';
import selectors1 from './selectors1';

export const targetSelector = createSelector(
  [selectors1.baseSelector],
  foo => {
    return foo.a;
  }
);

Тогда вы могли бы написать тест вроде

import { baseSelector } from './selectors1';
import { targetSelector } from './selectors2';

// This mocking call will be hoisted to the top (before import)
jest.mock('./selectors1', () => ({
  baseSelector: jest.fn()
}));

describe('selectors', () => {
  test('foo.a = 1', () => {
    const state = {
      foo: {
        a: 1
      }
    };
    baseSelector.mockReturnValue({ a: 1 });
    expect(targetSelector(state)).toBe(1);
  });

  test('foo.a = 2', () => {
    const state = {
      foo: {
        a: 1
      }
    };
    baseSelector.mockReturnValue({ a: 2 });
    expect(targetSelector(state)).toBe(2);
  });
});
Вызов функции

jest.mock будет поднят в верхнюю часть модуля, чтобы смоделировать модуль selectors1.js Когда вы import / require selectors1.js, вы получите смоделированную версию baseSelector, которую вы можете контролировать ее поведение перед запуском теста

...