[英]How to mock AbortController in Jest
I have a Redux
saga that makes several API requests.我有一个
Redux
传奇,它发出了几个 API 请求。 I am using takeLatest
to make sure that any previously running sagas are cancelled if a new action is fired.我正在使用
takeLatest
确保在触发新操作时取消任何以前运行的 sagas。 However this does not cancel in-flight requests and we are running into max connection limit issues.但是,这不会取消进行中的请求,并且我们遇到了最大连接限制问题。
To fix this I am creating an AbortController inside the saga and passing it to each request so they can be aborted if the saga is cancelled (see below):为了解决这个问题,我在 saga 中创建了一个 AbortController 并将其传递给每个请求,以便在取消 saga 时可以中止它们(见下文):
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);
}
However, I'm not sure how to check that abortController.abort()
is called, since AbortController is instantiated inside the saga.但是,我不确定如何检查是否调用了
abortController.abort()
,因为 AbortController 是在 saga 中实例化的。 Is there a way to mock this?有没有办法模拟这个?
You can use jest.spyOn(object, methodName) to create mock for AbortController.prototype.abort
method.您可以使用jest.spyOn(object, methodName)为
AbortController.prototype.abort
方法创建模拟。 Then, execute the saga generator, test it by each step.然后,执行 saga 生成器,按步骤测试。 Simulate the cancellation using
gen.return()
method.使用
gen.return()
方法模拟取消。
My test environment is node
, so I use abortcontroller-polyfill to polyfill AbortController
.我的测试环境是
node
,所以我使用abortcontroller- polyfill 来AbortController
。
Eg例如
saga.ts
: saga.ts
:
import { AbortController, abortableFetch } from 'abortcontroller-polyfill/dist/cjs-ponyfill';
import _fetch from 'node-fetch';
import { SagaIterator } from 'redux-saga';
import { call, cancelled, takeLatest } from 'redux-saga/effects';
const { fetch } = abortableFetch(_fetch);
export function getFoo(abortController) {
return fetch('http://localhost/api/foo', { signal: abortController.signal });
}
export function* doSomething(): SagaIterator {
const abortController = new AbortController();
try {
const fooResponse = yield call(getFoo, abortController);
} catch {
console.log('handle error');
} finally {
if (yield cancelled()) {
abortController.abort();
}
}
}
export function* watchForDoSomethingAction(): SagaIterator {
yield takeLatest('action/type/app/do_something', doSomething);
}
saga.test.ts
: saga.test.ts
:
import { AbortController } from 'abortcontroller-polyfill/dist/cjs-ponyfill';
import { call, cancelled } from 'redux-saga/effects';
import { doSomething, getFoo } from './saga';
describe('66588109', () => {
it('should pass', async () => {
const abortSpy = jest.spyOn(AbortController.prototype, 'abort');
const gen = doSomething();
expect(gen.next().value).toEqual(call(getFoo, expect.any(AbortController)));
expect(gen.return!().value).toEqual(cancelled());
gen.next(true);
expect(abortSpy).toBeCalledTimes(1);
abortSpy.mockRestore();
});
});
test result:测试结果:
PASS src/stackoverflow/66588109/saga.test.ts
66588109
✓ should pass (4 ms)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 75 | 50 | 33.33 | 78.57 |
saga.ts | 75 | 50 | 33.33 | 78.57 | 8,16,25
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 2.801 s
In order to test the AbortController
's abort
function I mocked the global.AbortController
inside my test.为了测试
AbortController
的abort
function 我在我的测试中模拟了global.AbortController
。
Example:例子:
const abortFn = jest.fn(); // @ts-ignore global.AbortController = jest.fn(() => ({ abort: abortFn, })); await act(async () => { //... Trigger the cancel function }); // expect the mock to be called expect(abortFn).toBeCalledTimes(1);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.