Дразнить интерфейс Flow.js с помощью Jest? - PullRequest
0 голосов
/ 16 мая 2019

Как интерфейс Flow.js можно смоделировать с помощью Jest ? К моему удивлению, я нигде не нашел, чтобы эта проблема была решена.

Я довольно новичок в обоих случаях, но единственный (непроверенный) вариант, который я вижу, - это создать класс, который наследует от интерфейса, а затем смоделировать реализующий класс. Это кажется довольно обременительным, и я не верю, что мог бы поместить реализующие классы (которые на самом деле должны были бы быть смоделированы) в папки __mocks__, ожидаемые Jest, и все еще получить ожидаемое поведение.

Есть предложения? Есть ли более подходящий инструмент для насмешек?

Обновление

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

Вот упрощенный пример класса, который я буду тестировать в этом сценарии. UpdateResources будет тестируемым классом, в то время как ResourceServer и ResourceRepository являются интерфейсами для служб, над которыми я хотел бы посмеяться и «шпионить» за:

// @flow

import type { ResourceServer } from '../ResourceServer';
import type { ResourceRepository } from '../ResourceRepository';

/**
 * Use case for updating resources
 */
export default class UpdateResources {
  resourceServer: ResourceServer;
  resourceRepository: ResourceRepository;

  constructor(resourceServer: ResourceServer, resourceRepository: ResourceRepository) {
    this.resourceServer = resourceServer;
    this.resourceRepository = resourceRepository;
  }

  async execute(): Promise<boolean> {
    const updatesAvailable = await this.resourceServer.checkForUpdates();

    if (updatesAvailable) {
      const resources = await this.resourceServer.getResources();
      await this.resourceRepository.saveAll(resources);
    }

    return updatesAvailable;
  }
}

Решение

Подход, к которому я пришел, который, кажется, работает достаточно хорошо для моих целей, состоит в том, чтобы создать фиктивную реализацию интерфейса в каталоге __mocks__, которая предоставляет объекты jest.fn для всех реализованных методов. Затем я создаю эти ложные реализации с new и пропускаю любое использование jest.mock().

__ издевается __ / MockResourceServer.js

import type { ResourceServer } from '../ResourceServer';

export default class MockResourceServer implements ResourceServer {

  getResources =  jest.fn(() => Promise.resolve({}));

  checkForUpodates = jest.fn(() => Promise.resolve(true));
}

__ издевается __ / MockResourceRepository.js

import type { ResourceRepository } from '../ResourceRepository';

export default class MockResourceRepository implements ResourceRepository {
  saveAll = jest.fn(() => Promise.resolve());
}

__ __ тесты / UpdateResources.test.js

import UpdateResources from '../UpdateResources';
import MockResourceRepository from '../../__mocks__/MockResourceRepository';
import MockResourceServer from '../../__mocks__/MockResourceServer';

describe('UpdateResources', () => {

  describe('execute()', () => {
    const mockResourceServer = new MockResourceServer();
    const mockResourceRepository = new MockResourceRepository();

    beforeEach(() => {
      jest.clearAllMocks();
    });

    it('should check the ResourceServer for updates', async () => {
      const updateResources = new UpdateResources(mockResourceServer, mockResourceRepository);
      await updateResources.execute();
      expect(mockResourceServer.checkForUpdates).toHaveBeenCalledTimes(1);
    });

    it('should save to ResourceRepository if updates are available', async () => {
      mockResourceServer.load.mockResolvedValue(true);
      const updateResources = new UpdateResources(mockResourceServer, mockResourceRepository);
      await updateResources.execute();
      expect(mockResourceRepository.saveAll).toHaveBeenCalledTimes(1);
    });

    it('should NOT save to ResourceRepository if NO updates are available', async () => {
      mockResourceServer.load.mockResolvedValue(false);
      const updateResources = new UpdateResources(mockResourceServer, mockResourceRepository);
      await updateResources.execute();
      expect(mockResourceRepository.saveAll).not.toHaveBeenCalled();
    });
  });
});

Если кто-нибудь может предложить какие-либо улучшения, я открыт!

1 Ответ

0 голосов
/ 30 июня 2019

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

Лично мне нравится создавать фиктивную реализацию, котораяможет быть построено путем подачи имитационных ответов.Затем его можно использовать повторно в любом тестовом примере, создав его непосредственно в этом тестовом примере с точными ответами, которые он должен предоставить.То есть, вы «пишете» макет с тем, что он должен сказать, вводя ответы во время строительства.Разница между ним и вашей имитирующей реализацией заключается в том, что если он не имеет ответа, он генерирует исключение и не проходит тест.Вот статья, которую я написал, которая показывает этот метод: https://dev.to/yawaramin/interfaces-for-scaling-and-testing-javascript-1daj

При использовании этой техники контрольный пример может выглядеть следующим образом:

it('should save to ResourceRepository if updates are available', async () => {
  const updateResources = new UpdateResources(
    new MockResourceServer({
      checkForUpdates: [true],
      getResources: [{}],
    }),
    new MockResourceRepository({
      saveAll: [undefined],
    }),
  );

  const result = await updateResources.execute();
  expect(result).toBeTruthy();
});

Что мне нравится в этих приколах, так это то, что всеответы являются явными и показывают последовательность происходящих вызовов.

...