如何用Jest模拟pRetry定时器以加速函数测试?
Got it, let's fix your test setup. The main issues here are not controlling the random failure case (so your function might succeed early) and misusing Jest's fake timers with async promises. Here's a step-by-step solution to get your test running under 500ms and verify those console logs:
First, Understand the Problem
Your myFunc uses pRetry with retries:7, which means it will attempt the function 8 total times (1 initial try + 7 retries). Each failure triggers that console.log line. To see the full log count, we need to force every attempt to fail, and skip waiting for the real 500ms timeouts.
Solution Code
First, let's update your test with proper mocking and fake timer handling:
Test File
const myFunc = require('./path-to-your-function'); // Import your function describe('myFunc retry behavior', () => { beforeEach(() => { // 1. Turn on Jest's fake timers to replace real setTimeout jest.useFakeTimers(); // 2. Force Math.random() to always return <0.5, so every attempt fails jest.spyOn(Math, 'random').mockReturnValue(0.4); // 3. Spy on console.log to count how many times it's called jest.spyOn(console, 'log').mockImplementation(() => {}); }); afterEach(() => { // Clean up mocks and restore real timers after each test jest.restoreAllMocks(); jest.useRealTimers(); }); it('should fail after all retries and log 8 times (initial + 7 retries)', async () => { // Start the function but don't await it yet — we need to trigger timers first const funcPromise = myFunc(); // Run all pending timers instantly (skips the 500ms waits) jest.runAllTimers(); // Now wait for the promise to resolve/reject await expect(funcPromise).rejects.toThrow('Calculations failed....'); // Verify the log was called 8 times (matches total attempts) expect(console.log).toHaveBeenCalledTimes(8); }); });
Why Your Original Test Didn't Work
- Uncontrolled Randomness: Without mocking
Math.random(), your function might randomly succeed on the first try (or any retry), so you wouldn't see all logs. - Incorrect Fake Timer Usage: If you
await myFunc()directly, Jest waits for real timers to finish. Instead, you need to start the promise, trigger all fake timers, then wait for the promise to settle. - No Log Verification: You weren't checking the
console.logcalls, so you couldn't confirm the retry count.
Alternative: If You Want Exactly 7 Logs
If you meant to see 7 logs (only retry attempts, not the initial try), adjust your pRetry config to retries:6 (so total attempts =7). Or modify the test assertion to expect(console.log).toHaveBeenCalledTimes(7) if that's what your requirement is.
Troubleshooting Tip
If jest.runAllTimers() doesn't work for some reason (rare with pRetry), you can manually advance timers in a loop to simulate each retry:
// Replace jest.runAllTimers() with this: for (let i = 0; i < 7; i++) { jest.advanceTimersByTime(500); // Match your minTimeout await new Promise(resolve => process.nextTick(resolve)); // Let async code run }
This test will run in milliseconds, no real waits needed, and you'll get the exact log count you're expecting.
内容的提问来源于stack exchange,提问作者Max Kurtz

