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

git checkout step3-checking_state_and_behaviour

Как видите, наш тест проверяет состояние преобразованного объекта, а также поведение для его получения.

import transformType1 from '../main/transformers/type1Transformer';
import transformType2 from '../main/transformers/type2Transformer';
import transform from '../main';

jest.mock('../main/transformers/type1Transformer');
jest.mock('../main/transformers/type2Transformer');

beforeEach(() => {
  transformType1.mockReset();
  transformType2.mockReset();
});

describe('index.js', () => {
  it('transforms the incoming raw object', () => {
    const rawObject = {
      name: 'NAME',
      content: [
        {
          type: 'type1',
          value: 'prop1',
        },
        {
          type: 'type2',
          value: 'PROP2',
        },
      ],
    };

    transformType1.mockImplementation(jest.fn(() => 'fakeType1Transformation'));
    transformType2.mockImplementation(jest.fn(() => 'fakeType2Transformation'));

    const result = transform(rawObject);

    // checking by state
    expect(result.name).toBe('name');
    expect(result.content[0].value).toBe('fakeType1Transformation');
    expect(result.content[1].value).toBe('fakeType2Transformation');

    // checking by behaviour
    expect(transformType1).toBeCalledWith(rawObject.content[0]);
    expect(transformType2).toBeCalledWith(rawObject.content[1]);
  });
});

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

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

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

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

Давайте перейдем к следующему шагу:

git checkout step4-checking_only_state

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

Гораздо проще, надежнее и с лучшей обратной связью.

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

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

Давайте рассмотрим это в последнем посте, так что следите за обновлениями!