javascript - Cách sử dụng JEST để kiểm tra tiến trình thời gian của các hàm đệ quy
Tôi viết một bài kiểm tra sử dụng JEST. Tôi không biết cách kiểm tra đệ quy promise trong JEST.
Trong bài kiểm tra này, chức năng thử lại thực hiện đệ quy là đối tượng của bài kiểm tra cho đến khi lời hứa được giải quyết.
export function retry<T>(fn: () => Promise<T>, limit: number = 5, interval: number = 1000): Promise<T> {
return new Promise((resolve, reject) => {
fn()
.then(resolve)
.catch((error) => {
setTimeout(() => {
// Reject if the upper limit number of retries is exceeded
if (limit === 1) {
reject(error);
return;
}
// Performs recursive processing of callbacks for which the upper limit number of retries has not been completed
try {
resolve(retry(fn, limit - 1, interval));
} catch (err) {
reject(err);
}
}, interval);
});
});
}
Thực hiện bài kiểm tra sau trên hàm thử lại ở trên.
- retry() được giải quyết trong lần chạy thứ ba. Lần đầu, lần thứ hai và lần thứ ba được gọi mỗi 1000 giây một lần.
Tôi nghĩ nó sẽ như sau khi viết những cái này trong JEST.
jest.useFakeTimers();
describe('retry', () => {
// Timer initialization for each test
beforeEach(() => {
jest.clearAllTimers();
});
// Initialize timer after all tests
afterEach(() => {
jest.clearAllTimers();
});
test('resolve on the third call', async () => {
const fn = jest
.fn()
.mockRejectedValueOnce(new Error('Async error'))
.mockRejectedValueOnce(new Error('Async error'))
.mockResolvedValueOnce('resolve');
// Test not to be called
expect(fn).not.toBeCalled();
// Mock function call firs execution
await retry(fn);
// Advance Timer for 1000 ms and execute for the second time
jest.advanceTimersByTime(1000);
expect(fn).toHaveBeenCalledTimes(2);
// Advance Timer for 1000 ms and execute for the third time
jest.advanceTimersByTime(1000);
expect(fn).toHaveBeenCalledTimes(3);
await expect(fn).resolves.toBe('resolve');
});
});
Kết quả là, nó đã thất bại với lỗi sau.
● retry › resolve on the third call
Timeout - Async callback was not invoked within the 30000ms timeout specified by jest.setTimeout.Error:
> 16 | test('resolve on the third call', async () => {
| ^
17 | jest.useFakeTimers();
18 | const fn = jest
19 | .fn()
Tôi nghĩ rằng điều này sẽ có thể quản lý được trong môi trường JEST liên quan đến lỗi này. Tuy nhiên, về cơ bản, tôi không biết cách kiểm tra xử lý đệ quy promise trong JEST.
Câu trả lời #1
Hàm của bạn rất khó để kiểm tra với bộ hẹn giờ.
Khi bạn gọi await retry(fn); điều này có nghĩa là bạn sẽ chờ cho đến khi retry trả về một giá trị, nhưng setTimeout đã bị chặn cho đến khi bạn gọi jest.advanceTimersByTime(1000); => đây là lý do chính, vì jest.advanceTimersByTime(1000); chưa bao giờ được gọi.
Bạn có thể xem ví dụ của tôi, nó hoạt động tốt với bộ hẹn giờ giả của jest.
test("timing", async () => {
async function simpleTimer(callback) {
await callback();
setTimeout(() => {
simpleTimer(callback);
}, 1000);
}
const callback = jest.fn();
await simpleTimer(callback); // it does not block any things
for (let i = 0; i < 8; i++) {
jest.advanceTimersByTime(1000); // then, this line will be execute
await Promise.resolve(); // allow any pending jobs in the PromiseJobs queue to run
}
expect(callback).toHaveBeenCalledTimes(9); // SUCCESS
});
Tôi nghĩ, bạn có thể bỏ chi tiết bộ đếm thời gian kiểm tra, chỉ cần kiểm tra về logic của bạn: fn đã được gọi 3 lần, cuối cùng nó trả về "resolve"
test("resolve on the third call", async () => {
const fn = jest
.fn()
.mockRejectedValueOnce(new Error("Async error"))
.mockRejectedValueOnce(new Error("Async error"))
.mockResolvedValueOnce("resolve");
// expect.assertions(3);
// Test not to be called
expect(fn).not.toBeCalled();
// Mock function call firs execution
const result = await retry(fn);
expect(result).toEqual("resolve");
expect(fn).toHaveBeenCalledTimes(3);
});
Lưu ý: xóa tất cả các bộ hẹn giờ giả của bạn - jest.useFakeTimers
Câu trả lời số 2
Tài liệu của Jest cho thấy việc thử nghiệm các promise khá đơn giản (https://jestjs.io/docs/en/asynchronous)
Họ đưa ra ví dụ này (giả sử fetchData đang trả về một promise giống như hàm retry của bạn)
test('the data is peanut butter', () => {
return fetchData().then(data => {
expect(data).toBe('peanut butter');
});
});
