jefflau / jest-fetch-mock

Jest mock for fetch
MIT License
883 stars 116 forks source link

Add supprot for AbortSignal #117

Open Gozala opened 5 years ago

Gozala commented 5 years ago

Fetch request can be cancelled through AbortController API & cross-fetch library used here is wrapper around node-fetch library which does support it

It would be really helpful if AbortController was exposed by this library to allow testing cases where requests may be cancelled.

jefflau commented 5 years ago

Would this help you to do what you'd like it to do?

https://github.com/jefflau/jest-fetch-mock/pull/112

Gozala commented 5 years ago

Would this help you to do what you'd like it to do?

112

Not really, I still need to get AbortController instance from somewhere e.g. (https://github.com/mysticatea/abort-controller) I'm suggesting to expose AbortController along with Request, Response & other globals.

akmjenkins commented 5 years ago

@Gozala I'm probably missing something, but shouldn't your AbortController instance come from the code your testing, not the testing library?

Gozala commented 5 years ago

@Gozala I'm probably missing something, but shouldn't your AbortController instance come from the code your testing, not the testing library?

@akmjenkins if fetch, Headers, Response, Request all come from here I would expect AbortController to do the same

https://github.com/jefflau/jest-fetch-mock/blob/454fcc956de67175567ced958c28b0c33bbd0f10/src/index.js#L49-L52

akmjenkins commented 5 years ago

Those are coming from cross-fetch, not jest-fetch-mock. More importantly, those are part of the specification that need to be supplied by fetch. An AbortController is more generic and, while fetch can make use of an AbortController (by listening to it's signal), it's not required for fetch to function (unlike Headers, Response, and Request).

I still fail to see the use case - if you want to test an aborted fetch, then you should already have an implementation of AbortController. Right now jest-fetch-mock still doesn't support testing aborted fetches, but this is just me advocating for my PR :smile:

jbarker47 commented 3 years ago

I have a Redux saga that makes several API requests. I am using takeLatest to make sure that any previously running sagas are cancelled if a new triggering action is fired. However this does not cancel any in-flight requests and we are running into max connection limit issues. To fix this I am adding an AbortController inside the saga and passing it to with each request so that it can be aborted if the current saga is cancelled (see example below):

export function* doSomething(action: Action): SagaIterator {
    const abortController = new AbortController();

    try {
        const fooResponse: FooResponse = yield call(getFoo, ..., abortController);
        ...
        const barResponse: BarResponse = yield call(getBar, ..., abortController);
    }
    catch {
        .. handle error
    }
    finally {
        if (yield cancelled()) {
            abortController.abort(); // Cancel the API call if the saga was cancelled
        }
    }
}

export function* watchForDoSomethingAction(): SagaIterator {
  yield takeLatest('action/type/app/do_something', doSomething);
}

function* rootSaga(): SagaIterator {
  const allSagas = [
    ...,
    watchForDoSomethingAction,
  ];
  yield all(allSagas.map((saga) => call(saga)));
}

export default rootSaga;  // --> sagaMiddleware.run(rootSaga);

However, in this scenario I'm not sure how to test that abortController.abort() is called, since AbortController is instantiated inside the saga. Is there a way to mock this at the test level?