NodeJS Jest mock setTimeout -获取超时错误

km0tfn4u  于 2023-03-29  发布在  Node.js
关注(0)|答案(3)|浏览(170)

我正在写一个单元测试,它测试一个在继续之前等待超时的函数,我试过使用

jest.useFakeTimers();
jest.advanceTimersByTime(20000);

但我一直收到错误信息
: Timeout - Async callback was not invoked within the 20000 ms timeout specified by jest.setTimeout.Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Error:

模块文件

async function main()

{

   const promisesArray: Promise<void>[] = [];

   // lots of logic and async calls //

   for (const userRecord of records) {

      promisesArray.push(verifyUserRecord(userRecord.userId));

   }

   await Promise.all(promisesArray);
}

function waitTimeout(ms: number) {
     return new Promise((resolve) => setTimeout(() => resolve(true), ms));
}

 async function verifyUserRecord(userId) {
   
    await waitTimeout(CHECK_EXECUTION_STATUS_TIMEOUT);

    await <some other method call>
   /////////////// more logic /////////////
}

测试文件

describe('main test', () => {
beforeAll(() => {
  // calls to jest spy on //
});
beforeEach(() => {
  jest.useFakeTimers();
  // various mocks //

});
afterEach(() => {
  jest.resetAllMocks();
});
afterAll(() => {
  jest.useRealTimers();
  jest.restoreAllMocks();
});
it('Should successfully run', async () => {
    const p = main();
    jest.advanceTimersByTime(60000);
    jest.runAllTimers(); // <- explicitly tell jest to run all setTimeout, setInterval
    jest.runAllTicks(); // <- explicitly tell jest to run all Promise callback

    await p;

    // validations // 
  });
});

但是当我从测试文件调用main方法时,我得到了上面的错误。当调试时,我看到它到达waitTimeout,然后等待或移动到测试模块中的下一个it。它没有模拟超时,并继续模块文件逻辑。
如何正确测试文件并模拟超时?
先谢了。

ozxc1zmp

ozxc1zmp1#

模拟计时器时使用了错误的API
jest.setTimeout(20000);用于设置jest等待测试完成的最大超时。如果测试时间过长,jest将抛出错误
回到您的问题,因为您使用jest.useFakeTimers();,所以必须显式地告诉jest用尽所有宏任务(setTimeout)和微任务(Promise回调)。
我对你的代码做了一点修改,这样expect就可以工作了

const CHECK_EXECUTION_STATUS_TIMEOUT = 2000;
const records = [{ userId: 1 }, { userId: 2 }];

async function main() {
  const promisesArray: Promise<number>[] = [];

  for (const userRecord of records) {
    promisesArray.push(verifyUserRecord(userRecord.userId));
  }

  return await Promise.all(promisesArray);
}

function waitTimeout(ms: number) {
  return new Promise((resolve) => setTimeout(() => resolve(true), ms));
}

async function verifyUserRecord(userId: number) {
  await waitTimeout(CHECK_EXECUTION_STATUS_TIMEOUT);

  // await <some other method call>
  /////////////// more logic /////////////
  return userId;
}

describe("main", () => {
  beforeEach(() => {
    jest.useFakeTimers(); // <- use fake timer
  });

  afterEach(() => {
    jest.useRealTimers();
  });

  it("should work", async () => {
    const p = main();
    jest.runAllTimers(); // <- explicitly tell jest to run all setTimeout, setInterval
    jest.runAllTicks(); // <- explicitly tell jest to run all Promise callback

    expect(await p).toEqual([1, 2]);
  });
});

输出:

lhcgjxsq

lhcgjxsq2#

您可以使用runOnlyPendingTimers,这将刷新所有挂起的计时器。
用途

jest.useFakeTimers();

// call your function here

jest.runOnlyPendingTimers();
8yparm6h

8yparm6h3#

另一种模拟setTimeout的方法:

jest.spyOn(global, 'setTimeout').mockImplementation((callback) => {
if (typeof callback === 'function') {
    callback();
}
return { hasRef: () => false } as NodeJS.Timeout;
});

相关问题