создатель действия не возвращает значение для потока в мраморном тесте - PullRequest
0 голосов
/ 27 февраля 2019

У меня есть следующий Epic, который хорошо работает в приложении, но я не могу заставить мой мраморный тест работать.Я вызываю создателя действия в map, и он возвращает правильный объект в поток, но в тесте я получаю пустой поток обратно.

export const updateRemoteFieldEpic = action$ =>
  action$.pipe(
    ofType(UPDATE_REMOTE_FIELD),
    filter(({ payload: { update = true } }) => update),
    mergeMap(({ payload }) => {
      const { orderId, fields } = payload;
      const requiredFieldIds = [4, 12]; //  4 = Name, 12 = Client-lookup
      const requestData = {
        id: orderId,
        customFields: fields
          .map(field => {
            return (!field.value && !requiredFieldIds.includes(field.id)) ||
              field.value
              ? field
              : null;
          })
          .filter(Boolean)
      };

      if (requestData.customFields.length > 0) {
        return from(axios.post(`/customfields/${orderId}`, requestData)).pipe(
          map(() => queueAlert("Draft Saved")),
          catchError(err => {
            const errorMessage =
              err.response &&
              err.response.data &&
              err.response.data.validationResult
                ? err.response.data.validationResult[0]
                : undefined;
            return of(queueAlert(errorMessage));
          })
        );
      }
      return of();
    })
  );

При успешном ответе от сервера я вызываю queueAlert действиеcreator.

export const queueAlert = (
  message,
  position = {
    vertical: "bottom",
    horizontal: "center"
  }
) => ({
  type: QUEUE_ALERT,
  payload: {
    key: uniqueId(),
    open: true,
    message,
    position
  }
});

и вот мой тестовый пример

describe("updateRemoteFieldEpic", () => {
  const sandbox = sinon.createSandbox();
  let scheduler;
  beforeEach(() => {
    scheduler = new TestScheduler((actual, expected) => {
      expect(actual).toEqual(expected);
    });
  });
  afterEach(() => {
    sandbox.restore();
  });
  it("should return success message", () => {
    scheduler.run(ts => {
      const inputM = "--a--";
      const outputM = "--b--";
      const values = {
        a: updateRemoteField({
          orderId: 1,
          fields: [{ value: "test string", id: 20 }],
          update: true
        }),
        b: queueAlert("Draft Saved")
      };

      const source = ActionsObservable.from(ts.cold(inputM, values));
      const actual = updateRemoteFieldEpic(source);

      const axiosStub = sandbox
        .stub(axios, "post")
        .returns([]);

      ts.expectObservable(actual).toBe(outputM, values);
      ts.flush();
      expect(axiosStub.called).toBe(true);
    });
  });
});

выходной поток в actual возвращает пустой массив, который я пытался вернуть из карты, наблюдаемой создателем действия, вызвавшего сбой приложения, посколькуожидаемый объект действия.

1 Ответ

0 голосов
/ 27 февраля 2019

Если заглушить axios.post(...) как [], вы получите from([]) в эпопее - пустую наблюдаемую область, которая не испускает никаких значений.Вот почему ваш mergeMap никогда не называется.Вы можете исправить это, используя вместо этого одноэлементный массив, например, [null] или [{}].


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

Я думаю, что ваша проблема - from(axios.post(...)) в вашем эпосе.Axios возвращает обещание, а RxJS TestScheduler не имеет возможности сделать это синхронным, поэтому expectObservable не будет работать так, как задумано.

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

// api.js

import axios from 'axios';
import { map } from 'rxjs/operators';

export function post(path, data) {
  return from(axios.post(path, options));
}

Как только вы получите эту обертку, вы можете смоделировать функцию, возвращающую постоянную Observable, полностью исключая обещания из картинки.Если вы сделаете это с Jest, вы можете напрямую смоделировать модуль:

import * as api from '../api.js';

jest.mock('../api.js');

// In the test:
api.post.mockReturnValue(of(/* the response */));

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

export const updateRemoteFieldEpic = (action$, state, { api }) =>
  action$.pipe(
    ofType(UPDATE_REMOTE_FIELD),
    filter(({ payload: { update = true } }) => update),
    mergeMap(({ payload }) => {
       // ...
       return api.post(...).pipe(...);
    })
  );

В вашем тесте вы просто передадите поддельный api объект.

...