1

I've got a saga that has some error handling logic in it - I want to test that a call is made three times and provide a response for each invocation. The use case is that the saga retries on the first two errors before giving up, so I need a sequence of response: [fail, fail, success]

it("must succeed after the first two requests are failures", () =>
      expectSaga(
        sagaToTest
      ).provide([
          [
            call(getdata, request),
            throwError(new Error("oops")) // do this twice and succeed on the third invication
          ]
        ])
        .call(getdata, request)
        .call(getdata, request)
        .call(getdata, request)
        .put(actions.itSucceeded("message"))
        .run());
  });

This is straightforward in other testing / mocking libraries but for some reason I can't seem to find the right documentation.

Thanks!

nbpeth
  • 2,967
  • 4
  • 24
  • 34
  • I don't know of a convenient utility but I use static providers with dynamic values (very confusing naming) for this: https://github.com/jfairbank/redux-saga-test-plan/blob/master/docs/integration-testing/mocking/static-providers.md#static-providers-with-dynamic-values – azundo Sep 23 '21 at 21:00
  • disappointing, I've come to the same conclusion. I've been able to verify the number of calls by using a `not` after my expected calls. dynamic providers seems like the only viable option but feels like a hack – nbpeth Sep 23 '21 at 22:12

1 Answers1

0

This library does exactly that https://www.npmjs.com/package/saga-test-stub

You'll need to split your code tho, first encapsulate the throwable call in a separate saga and test it

function* callApi(request: any){
    try {
        const response = call(getdata, request);
        return {sucess:true,response}
    }
    catch (e){
        return {sucess:false}
    }
}

describe('callApi saga', () => {
    let sagaStub: SagaStub;
    beforeEach(() => {
        sagaStub = stub(callApi, {});
    });
    describe('when call to api fails', () => {
        beforeEach(() => {
            jest.spyOn(api,'callApi').mockImplementation(()=> {
                throw new Error()
            });
            it('should return success false', () => {
                expect(saga).toBeDone({sucess:false})
            });
        });
    });
    describe('when call to api works', () => {
        // ...
    });
});

then stub the yielded values from the first saga

describe('sagaToTest', () => {
    let sagaStub: SagaStub;
    beforeEach(() => {
        sagaStub = stub(sagaToTest, {});
        when(sagaStub).yields(call(callApi,{})).doNext(
            {succes: false},
            {succes: false},
            {succes: true, response: 'here you go'},
        )
    });

    it('must succeed after the first two requests are failures', () => {
        expect(sagaStub).toYield(
            call(callApi,{}), //note: this is redundant since it is stubbed
            call(callApi,{}), //note: this is redundant since it is stubbed
            call(callApi,{}), //note: this is redundant since it is stubbed
            put(actions.itSucceeded("message"))
        )
    });
});
  • Please demonstrate [how this tool solves the problem](https://meta.stackoverflow.com/questions/251602/recommending-off-site-resources-when-questions-dont-ask-for-it/251605#251605) in the answer itself. – tjheslin1 Apr 08 '22 at 16:01
  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/late-answers/31489145) – Barry Michael Doyle Apr 11 '22 at 08:57