RxJS Redux-Observables Test retryWhen внутри эпоса

Я изо всех сил пытаюсь проверить оператор retryWhen в redux-observable epic. На основе этого примера, взятого из docs, я разветвил это jsbin, где я пытаюсь проверить случай, когда ответ терпит неудачу 2 раза, и после этого он возвращает действительный ответ.

Ниже приведены некоторые части кода. Для всей реализации используйте этот jsbin

let RetryStrategy = attempts => attempts
    .zip(Observable.range(1, 4))
    .flatMap(([error, i]) => {
        if (i > 3) {
            return Observable.throw('Network error occured')
        }
        return Observable.timer(i * 1000)
    })


const fetchFooEpic = (action$, store, call = indirect.call) =>
    action$.ofType('FETCH_FOO')
        .mergeMap(action =>
            call(api.fetchFoo, action.payload.id)
                .map(payload => ({ type: 'FETCH_FOO_FULFILLED', payload }))
                .retryWhen(RetryStrategy)
                .takeUntil(action$.ofType('FETCH_FOO_CANCELLED'))
                .catch(error => of({
                    type: 'FETCH_FOO_REJECTED',
                    payload: error.xhr.response,
                    error: true
                }))
        );

describe('fetchFooEpic', () => {
    ...
    it.only('handles errors correctly', () => {
        const badResponse = { message: 'BAD STUFF' };
        const response = { id: 123, name: 'Bilbo' };

        expectEpic(fetchFooEpic, {
            expected: ['-----a|', {
                a: { type: 'FETCH_FOO_FULFILLED', payload: response }
            }],
            action: ['(a|)', {
                a: { type: 'FETCH_FOO', payload: { id: 123 } }
            }],
            response: ['-#-#-a|', {
                a: response
            }, { xhr: { badResponse } }],
            callArgs: [api.fetchFoo, 123]
        });
    });
    ...

});

Если вы проверите ответ в jsbin, фактическое действие всегда будет массивом empty.


person stelioschar    schedule 07.08.2017    source источник
comment
Привет! Пробовали ли вы тестировать retryWhen без всего остального и всех абстракций? Кому-то может быть сложно помочь вам, как мне, или им потребуется потратить нетривиальное количество времени, чтобы все понять.   -  person jayphelps    schedule 07.08.2017
comment
@jayphelps спасибо за быстрый ответ. Проблема, по-видимому, заключается в том, что при использовании retry или retryWhen повторяется попытка выполнить все действие с самого начала, а не просто повторить внутреннюю наблюдаемую ajax (внутри switchMap). Я сделал много тестов на основе приведенного выше примера, и, как вы можете видеть здесь если вы попытаетесь изменить количество повторных попыток (.retry(3)) или изменить маркер ответа (response: ['--#a|'), вы увидите, что фактический кадр представляет собой произведение кадров и количества повторных попыток.   -  person stelioschar    schedule 09.08.2017


Ответы (1)


У меня была аналогичная проблема, когда я пытался протестировать Angular HttpInterceptor, который пытается до трех раз с задержкой между попытками. Как вы упомянули в комментариях, retryWhen повторно подписывается на наблюдаемое после каждой ошибки. Это означает, что если у вас есть наблюдаемая ошибка (например, cold('#|')), вы всегда будете получать ошибку в retryWhen, потому что она повторно подписывается на одну и ту же ошибку, наблюдаемую при каждой повторной попытке.

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

  class MultiObservable extends Observable<any> {
    constructor(observables: Observable<any>[]) {
      let subscriptionIdx = 0;
      super((subscriber: Subscriber<any>) => 
               observables[subscriptionIdx++].subscribe(subscriber));
    }
  }

В своем тесте я использую его следующим образом:

  const testObservable = new MultiObservable([
    cold('#|', null, { status: 400 }),
    cold('a|')
  ]);

  next.handle.and.returnValue(testObservable);
  const actual = interceptor.intercept(req, next);
  expect(actual).toBeObservable(cold('---a|'));

Я надеюсь, что у кого-то еще будет менее хакерское решение, но теперь это работает для меня.

person Robert Mitchell    schedule 15.09.2017