代码之家  ›  专栏  ›  技术社区  ›  VivekN

用jest和假定时器测试递归轮询函数

  •  1
  • VivekN  · 技术社区  · 6 年前

    我已经创建了一个轮询服务,它递归地调用一个api,如果满足某些条件,那么在api成功时,它会再次保持轮询。

    /**
       * start a timer with the interval specified by the user || default interval
       * we are using setTimeout and not setinterval because a slow back end server might take more time than our interval time and that would lead to
       * a queue of ajax requests with no response at all.
       * -----------------------------------------
       * This function would call the api first time and only on the success response of the api we would poll again after the interval
       */
      runPolling() {
        const { url, onSuccess, onFailure, interval } = this.config;
        const _this = this;
        this.poll = setTimeout(() => {
          /* onSuccess would be handled by the user of service which would either return true or false
          * true - This means we need to continue polling
          * false - This means we need to stop polling
          */
          api
            .request(url)
            .then(response => {
              console.log('I was called', response);
              onSuccess(response);
            })
            .then(continuePolling => {
              _this.isPolling && continuePolling ? _this.runPolling() : _this.stopPolling();
            })
            .catch(error => {
              if (_this.config.shouldRetry && _this.config.retryCount > 0) {
                onFailure && onFailure(error);
                _this.config.retryCount--;
                _this.runPolling();
              } else {
                onFailure && onFailure(error);
                _this.stopPolling();
              }
            });
        }, interval);
      }
    

    在尝试为它编写测试用例时,我不太确定如何模拟假计时器和axios api响应。

    这就是我目前所拥有的

    import PollingService from '../PollingService';
    import { statusAwaitingProduct } from '@src/__mock_data__/getSessionStatus';
    import mockAxios from 'axios';
    
    describe('timer events for runPoll', () => {
        let PollingObject,
        pollingInterval = 3000,
        url = '/session/status',
        onSuccess = jest.fn(() => {
          return false;
        });
        beforeAll(() => {
          PollingObject = new PollingService({
            url: url,
            interval: pollingInterval,
            onSuccess: onSuccess
          });
        });
        beforeEach(() => {
          jest.useFakeTimers();
        });
        test('runPolling should be called recursively when onSuccess returns true', async () => {
          expect.assertions(1);
          const mockedRunPolling = jest.spyOn(PollingObject, 'runPolling');
          const mockedOnSuccess = jest.spyOn(PollingObject.config, 'onSuccess');
          mockAxios.request.mockImplementation(
            () =>
              new Promise(resolve => {
                resolve(statusAwaitingProduct);
              })
          );
    
          PollingObject.startPolling();
          expect(mockedRunPolling).toHaveBeenCalledTimes(1);
          expect(setTimeout).toHaveBeenCalledTimes(1);
          expect(mockAxios.request).toHaveBeenCalledTimes(0);
          expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), pollingInterval);
    
          jest.runAllTimers();
          expect(mockAxios.request).toHaveBeenCalledTimes(1);
          expect(mockedOnSuccess).toHaveBeenCalledTimes(1);
          expect(PollingObject.isPolling).toBeTruthy();
          expect(mockedRunPolling).toHaveBeenCalledTimes(2);
        });
      });
    });
    

    有人能帮忙吗? 谢谢

    1 回复  |  直到 6 年前
        1
  •  4
  •   Brian Adams    6 年前

    发行

    你的测试可能还有其他问题,但我会回答你问的具体问题 expect(mockedOnSuccess).toHaveBeenCalledTimes(1); 失败于 0 times :

    jest.runAllTimers 将同步运行任何挂起的计时器回调,直到没有剩余时间为止。这将执行用调度的匿名函数 setTimeout 在内部 runPolling . 当匿名函数执行时,它将调用 api.request(url) 这就是将要发生的一切 . 匿名函数中的所有其他内容都包含在 then get queued in the PromiseJobs Jobs Queue introduced with ES6 . 到那时这些工作都不会执行 开玩笑。运行计时器

    expect(mockAxios.request).toHaveBeenCalledTimes(1); 然后就过去了 api.请求(url) 已经执行。

    expect(mockedOnSuccess)to havebeen calledTimes(1); 然后就失败了 然后 本该调用它的回调仍在 承诺工作 排队,尚未执行。

    解决方案

    解决方法是确保作业排队 在断言之前有机会逃跑吗 mockedOnSuccess 被叫来了。

    承诺工作 async 测试 Jest await Promise.resolve(); 承诺工作 并允许队列中的任何挂起作业首先执行:

    test('runPolling should be called recursively when onSuccess returns true', async () => {
      ...
      jest.runAllTimers();
      await Promise.resolve();  // allow any pending jobs in PromiseJobs to execute
      expect(mockAxios.request).toHaveBeenCalledTimes(1);
      expect(mockedOnSuccess).toHaveBeenCalledTimes(1); // SUCCESS
      ...
    }
    

    设置超时 因此,没有办法回报一个让考试等待的承诺。

    还请注意,您有多个链接 然后 承诺工作 在你的测试中多次。

    here .