繁体   English   中英

如何在 Jest 中模拟 AbortController

[英]How to mock AbortController in Jest

我有一个Redux传奇,它发出了几个 API 请求。 我正在使用takeLatest确保在触发新操作时取消任何以前运行的 sagas。 但是,这不会取消进行中的请求,并且我们遇到了最大连接限制问题。

为了解决这个问题,我在 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);
}

但是,我不确定如何检查是否调用了abortController.abort() ,因为 AbortController 是在 saga 中实例化的。 有没有办法模拟这个?

您可以使用jest.spyOn(object, methodName)AbortController.prototype.abort方法创建模拟。 然后,执行 saga 生成器,按步骤测试。 使用gen.return()方法模拟取消。

我的测试环境是node ,所以我使用abortcontroller- polyfill 来AbortController

例如

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

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();
  });
});

测试结果:

 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

为了测试AbortControllerabort function 我在我的测试中模拟了global.AbortController

例子:

 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM