JestJs:对 Axios 的多个异步 API 调用获得模拟实现给出相同的响应
JestJs: Multiple asynchronous API calls to Axios get mock implementation gives same response
我有一种方法可以从 API 中获取产品数据。我使用 Axios 来调用 API。由于产品数据是相互独立的,所以我一次调用了所有的API,然后使用Promise.all
等待调用完成再继续。例如,考虑具有以下行的函数(未包含在函数语句中)。
const productIds = [ "SKU-1", "SKU-2" ];
const promises = productIds.map<Promise<Product>>(id => axios.get(`/products/${id}`));
Promise.all(promises).then(values => {
// do some stuff here.
});
编写的单元测试用例使用jest mock实现如下。
import http from "axios";
import { sku1, sku2 } from "./test-product-data";
jest.mock("axios", () => ({
create: jest.fn(() => http),
get: jest.fn(() => Promise.resolve()),
}));
const httpMock = http as jest.Mocked<typeof http>;
describe("Update operations!", () => {
beforeAll(() => {
httpMock.get.mockImplementation(url => {
// Call to Get product.
if (url.endsWith("/SKU-1")) {
mockedSuccessResponse.data = Object.assign({}, sku1);
return Promise.resolve(mockedSuccessResponse);
};
if (url.endsWith("/SKU-2")) {
mockedSuccessResponse.data = Object.assign({}, sku2);
return Promise.resolve(mockedSuccessResponse);
};
});
});
});
开玩笑 returns 最后一次 API 调用的响应,即 SKU-2
的响应,即使是 ID 为 SKU-1
的产品。我错过了什么吗?
由于您正在修改同一个对象引用(mockedSuccessResponse.data
),后续更改将覆盖之前的更改。这意味着 url
以 /SKU-2
结尾的模拟对象 (sku2
) 将覆盖 mockedSuccessResponse.data
。当你在 promise.all
中调用 axios.get()
时,他们都解析了 sku2
模拟数据。
解决方案:为不同的条件分支创建不同的模拟对象。
例如
index.ts
:
import axios from 'axios';
interface Product {
id: string;
}
export function main() {
const productIds = ['SKU-1', 'SKU-2'];
const promises = productIds.map<Promise<Product>>((id) => axios.get(`/products/${id}`));
return Promise.all(promises);
}
index.test.ts
:
import http from 'axios';
import { main } from './';
jest.mock('axios', () => ({
create: jest.fn(() => http),
get: jest.fn(),
}));
const httpMock = http as jest.Mocked<typeof http>;
describe('Update operations!', () => {
const sku1 = { id: 'sku-1' };
const sku2 = { id: 'sku-2' };
beforeAll(() => {
httpMock.get.mockImplementation((url: string): any => {
if (url.endsWith('/SKU-1')) {
return Promise.resolve({ data: Object.assign({}, sku1) });
}
if (url.endsWith('/SKU-2')) {
return Promise.resolve({ data: Object.assign({}, sku2) });
}
});
});
test('should pass', async () => {
const actual = await main();
expect(actual).toEqual([{ data: { id: 'sku-1' } }, { data: { id: 'sku-2' } }]);
});
});
测试结果:
PASS examples/70599956/index.test.ts (9.178 s)
Update operations!
✓ should pass (2 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 9.216 s
我有一种方法可以从 API 中获取产品数据。我使用 Axios 来调用 API。由于产品数据是相互独立的,所以我一次调用了所有的API,然后使用Promise.all
等待调用完成再继续。例如,考虑具有以下行的函数(未包含在函数语句中)。
const productIds = [ "SKU-1", "SKU-2" ];
const promises = productIds.map<Promise<Product>>(id => axios.get(`/products/${id}`));
Promise.all(promises).then(values => {
// do some stuff here.
});
编写的单元测试用例使用jest mock实现如下。
import http from "axios";
import { sku1, sku2 } from "./test-product-data";
jest.mock("axios", () => ({
create: jest.fn(() => http),
get: jest.fn(() => Promise.resolve()),
}));
const httpMock = http as jest.Mocked<typeof http>;
describe("Update operations!", () => {
beforeAll(() => {
httpMock.get.mockImplementation(url => {
// Call to Get product.
if (url.endsWith("/SKU-1")) {
mockedSuccessResponse.data = Object.assign({}, sku1);
return Promise.resolve(mockedSuccessResponse);
};
if (url.endsWith("/SKU-2")) {
mockedSuccessResponse.data = Object.assign({}, sku2);
return Promise.resolve(mockedSuccessResponse);
};
});
});
});
开玩笑 returns 最后一次 API 调用的响应,即 SKU-2
的响应,即使是 ID 为 SKU-1
的产品。我错过了什么吗?
由于您正在修改同一个对象引用(mockedSuccessResponse.data
),后续更改将覆盖之前的更改。这意味着 url
以 /SKU-2
结尾的模拟对象 (sku2
) 将覆盖 mockedSuccessResponse.data
。当你在 promise.all
中调用 axios.get()
时,他们都解析了 sku2
模拟数据。
解决方案:为不同的条件分支创建不同的模拟对象。
例如
index.ts
:
import axios from 'axios';
interface Product {
id: string;
}
export function main() {
const productIds = ['SKU-1', 'SKU-2'];
const promises = productIds.map<Promise<Product>>((id) => axios.get(`/products/${id}`));
return Promise.all(promises);
}
index.test.ts
:
import http from 'axios';
import { main } from './';
jest.mock('axios', () => ({
create: jest.fn(() => http),
get: jest.fn(),
}));
const httpMock = http as jest.Mocked<typeof http>;
describe('Update operations!', () => {
const sku1 = { id: 'sku-1' };
const sku2 = { id: 'sku-2' };
beforeAll(() => {
httpMock.get.mockImplementation((url: string): any => {
if (url.endsWith('/SKU-1')) {
return Promise.resolve({ data: Object.assign({}, sku1) });
}
if (url.endsWith('/SKU-2')) {
return Promise.resolve({ data: Object.assign({}, sku2) });
}
});
});
test('should pass', async () => {
const actual = await main();
expect(actual).toEqual([{ data: { id: 'sku-1' } }, { data: { id: 'sku-2' } }]);
});
});
测试结果:
PASS examples/70599956/index.test.ts (9.178 s)
Update operations!
✓ should pass (2 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 9.216 s