用 Jest 模拟基于承诺的请求

Posted

技术标签:

【中文标题】用 Jest 模拟基于承诺的请求【英文标题】:Mocking promised based request with Jest 【发布时间】:2017-02-26 01:48:59 【问题描述】:

我正在尝试使用 Jest 对函数进行单元测试,但在处理 jest 模拟模块(相当于 nodejs 世界中的 rewire 或 proxyquire)时遇到了一些麻烦。

我实际上是在尝试使用一些参数在模拟模块上调用间谍。这是我要测试的功能。

注意:当前测试只涉及“fetch(...)”部分,我正在尝试测试是否使用 good 参数调用了 fetch。

export const fetchRemote = slug => 
    return dispatch => 
        dispatch(loading());
        return fetch(Constants.URL + slug)
            .then(res => res.json())
            .then(cmp => 
                if (cmp.length === 1) 
                    return dispatch(setCurrent(cmp[0]));
                
                return dispatch(computeRemote(cmp));
            );
    ;
;

返回的函数充当闭包,因此“捕获”了我要模拟的节点获取外部模块。

这是我试图通过绿色的测试:

it('should have called the fetch function wih the good const parameter and slug', done => 
            const slug = 'slug';
            const spy = jasmine.createSpy();
            const stubDispatch = () => Promise.resolve(json: () => []);
            jest.mock('node-fetch', () => spy);
            const dispatcher = fetchRemote(slug);
            dispatcher(stubDispatch).then(() => 
                expect(spy).toHaveBeenCalledWith(Constants.URL + slug);
                done();
            );
        );

编辑:第一个答案对编写测试有很大帮助,我现在有以下一个:

it('should have called the fetch function wih the good const parameter and slug', done => 
            const slug = 'slug';
            const stubDispatch = () => null;
            const spy = jest.mock('node-fetch', () => Promise.resolve(json: () => []));
            const dispatcher = fetchRemote(slug);
            dispatcher(stubDispatch).then(() => 
                expect(spy).toHaveBeenCalledWith(Constants.URL + slug);
                done();
            );
        );

但是现在,这是我遇到的错误:

 console.error node_modules/core-js/modules/es6.promise.js:117
      Unhandled promise rejection [Error: expect(jest.fn())[.not].toHaveBeenCalledWith()

      jest.fn() value must be a mock function or spy.
      Received:
        object: "addMatchers": [Function anonymous], "autoMockOff": [Function anonymous], "autoMockOn": [Function anonymous], "clearAllMocks": [Function anonymous], "clearAllTimers": [Function anonymous], "deepUnmock": [Function anonymous], "disableAutomock": [Function anonymous], "doMock": [Function anonymous], "dontMock": [Function anonymous], "enableAutomock": [Function anonymous], "fn": [Function anonymous], "genMockFn": [Function bound getMockFunction], "genMockFromModule": [Function anonymous], "genMockFunction": [Function bound getMockFunction], "isMockFunction": [Function isMockFunction], "mock": [Function anonymous], "resetModuleRegistry": [Function anonymous], "resetModules": [Function anonymous], "runAllImmediates": [Function anonymous], "runAllTicks": [Function anonymous], "runAllTimers": [Function anonymous], "runOnlyPendingTimers": [Function anonymous], "runTimersToTime": [Function anonymous], "setMock": [Function anonymous], "unmock": [Function anonymous], "useFakeTimers": [Function anonymous], "useRealTimers": [Function anonymous]]

【问题讨论】:

【参考方案1】:

首先你需要在testing async code时返回一个promise。你的间谍需要返回一个已解决或被拒绝的承诺。

it('should have called the fetch function wih the good const parameter and slug', done => 
  const slug = 'successPath';
  const stubDispatch = () => Promise.resolve( json: () => [] );
  spy = jest.mock('node-fetch', (path) => 
    if (path === Constants.URL + 'successPath') 
      return Promise.resolve('someSuccessData ')
     else 
      return Promise.reject('someErrorData')
    
  );
  const dispatcher = fetchRemote(slug);
  return dispatcher(stubDispatch).then(() => 
    expect(spy).toHaveBeenCalledWith(Constants.URL + slug);
    done();
  );
);

【讨论】:

我在这个实现中看不到间谍:/。不过谢谢你的回答 你试过expect(spy.mock)而不是expect(spy)吗? 你必须从你的测试中返回调度员。 我试过了,但没办法。似乎节点模块根本没有被嘲笑。当我在没有后端的情况下运行测试时,它会发送请求并抛出错误 好的,那么如何将fetch 导入到您的模块中,还是使用原生JS 实现?

以上是关于用 Jest 模拟基于承诺的请求的主要内容,如果未能解决你的问题,请参考以下文章

刷新承诺队列?

使用 Jest 模拟请求标头模块

使用正确类型使用 Jest 和 Typescript 模拟 Express 请求

模拟服务工作者无法拦截网络请求

用 Jest 测试 Axios

Jest 使用 Mock Service Worker (MSW) 或其他模拟离线网络?