Bun Moc 获取
在 Bun 获取请求 。 适合测试和调试.
目录
安装
bun add @aryzing/bun-mock-fetch
API 密码
mockFetch
mockFetch(requestMatcher: RequestMatcher, response: Response | ResponseFn): void
注册一个模拟。 何时 fetch 调用,请求匹配 requestMatcher页:1 response 返回,而不是提出真正的网络请求。 每次通话都增加了一个新的模拟器. 如果多个模拟符合请求,则最近注册的模拟优先.
clearFetchMocks
clearFetchMocks(): void
删除所有已注册的模拟并恢复原始内置 fetch。清空模拟不会改变 通过 或者说 记录 设置.
json
json(data: unknown, init?: ResponseInit): Response
创建一个 Response 数据序列化为 JSON 和 Content-Type: application/json 设置。 可选 init 已转交 Response 构造器并可用于设置自定义状态代码或附加头.
import { json, mockFetch } from "@aryzing/bun-mock-fetch";
// 200 JSON response.
mockFetch(/example/, json({ message: "Hello, world!" }));
// 201 JSON response.
mockFetch(/example/, json({ id: 1 }, { status: 201 }));
// 422 JSON error response.
mockFetch(/example/, json({ error: "Invalid input" }, { status: 422 }));
setPassthrough
setPassthrough(value: boolean): void
控制不匹配任何模拟的请求是否转发给本地内置 fetch。默认启用。 设定为 false 以无与伦比的请求取代.
setLogging
setLogging(value: boolean): void
启用或禁用调试日志 。 默认禁用 .
配置
请求匹配者
import { mockFetch } from "@aryzing/bun-mock-fetch";
// Simple string matching.
mockFetch("https://example.com", new Response());
// Using minimatch.
mockFetch("https://example.com/foo/**", new Response());
// Using regex.
mockFetch(/example/, new Response());
// Using a matcher function.
mockFetch((input, init) => {
// Check input examples.
if (typeof input === "string") return input.includes("example");
if (input instanceof URL) return input.host.includes("example");
if (input instanceof Request) return input.url.includes("example");
// Check init example.
return init?.method === "POST";
}, new Response());
// Using a detailed matcher object. All properties are optional.
mockFetch(
{
// Must match this string, glob, or regex.
url: "https://example.com",
// Must match this method (case-insensitive).
method: "POST",
// Must include these headers (case-insensitive) and match their values.
headers: {
"Content-Type": "application/json",
},
},
new Response(),
);
回应
import { json, mockFetch } from "@aryzing/bun-mock-fetch";
// Text response.
mockFetch(/example/, new Response("Hello, world!"));
// JSON response.
mockFetch(/example/, json({ message: "Hello, world!" }));
// Response function.
mockFetch(/example/, async ({ mockedRequest, input, init, nativeFetch }) => {
console.log(mockedRequest); // The matched mock request config.
console.log(input); // The 1st arg. to `fetch`.
console.log(init); // The 2nd arg. to `fetch`.
// Delayed response example, useful for spinners and timeouts.
await Bun.sleep(1000);
// Proxy the request or otherwise fetch data using the native built-in fetch.
const quote = await nativeFetch("https://example.com/quote-of-the-day").then(
(r) => r.text(),
);
// Configure response based on request.
const name =
(await (input instanceof Request ? input : new Request("", init)).text()) ||
"guest";
return new Response(`Hello ${name}! ${quote}`);
});
实例
用自己的模拟测试
import { clearFetchMocks, mockFetch } from "@aryzing/bun-mock-fetch";
afterEach(() => {
clearFetchMocks();
});
test("first test", async () => {
mockFetch("https://api.example.com/data", new Response("first"));
expect(await exampleApiClient.getData()).toBe("first");
});
test("second test", async () => {
mockFetch("https://api.example.com/data", new Response("second"));
expect(await exampleApiClient.getData()).toBe("second");
});
使用共享模拟器的测试
import { clearFetchMocks, mockFetch } from "@aryzing/bun-mock-fetch";
beforeAll(() => {
mockFetch("https://foo.example.com/data", new Response("foo"));
mockFetch("https://bar.example.com/data", new Response("bar"));
});
afterAll(() => {
clearFetchMocks();
});
test("first test", async () => {
expect(await exampleFooClient()).toBe("foo");
});
test("second test", async () => {
expect(await exampleFooClient()).toBe("foo");
expect(await exampleBarClient()).toBe("bar");
});
test("third test", async () => {
expect(await exampleBarClient()).toBe("bar");
});
开发过程中的模拟网络呼叫
import { sdk } from "@example/sdk";
import { json, mockFetch } from "@aryzing/bun-mock-fetch";
mockFetch("https://sdk.example.com/auth", json({ token: "abc...def" }));
mockFetch("https://sdk.example.com/shopping-cart", json({ items: mockItems }));
await sdk.init(); // E.g., internally calls `/auth`
const items = await sdk.getShoppingCart();
render(items); // E.g., a UI framework
开发过程中调试 API 调用
允许日志向控制台打印每个被截取的请求 。 通道还在,所以所有无与伦比的请求仍然正常地通向真正的网络.
import { setLogging } from "@aryzing/bun-mock-fetch";
setLogging(true);
// All fetch calls will now be logged to the console, e.g.:
// [@aryzing/bun-mock-fetch]: Mocked fetch called with path: https://api.example.com/data
// [@aryzing/bun-mock-fetch]: No matching mock found for request: https://api.example.com/data
// [@aryzing/bun-mock-fetch]: Using built-in fetch for request: https://api.example.com/data
截取特定终点并检查其请求而不影响他人:
import { json, mockFetch, setLogging } from "@aryzing/bun-mock-fetch";
setLogging(true);
mockFetch("https://api.example.com/flaky-endpoint", async ({ input, init }) => {
console.log("request headers:", init?.headers);
return json({ debug: true });
});
代理真实反应
使用 nativeFetch 将请求转发给真正的网络,并修改答复后再返回.
import { mockFetch } from "@aryzing/bun-mock-fetch";
// Inject a field into every response from an endpoint.
mockFetch(
"https://api.example.com/users",
async ({ input, init, nativeFetch }) => {
const response = await nativeFetch(input, init);
const data = await response.json();
return Response.json({ ...data, injected: true });
},
);
// Add an auth header to outgoing requests before forwarding.
mockFetch(
"https://api.example.com/**",
async ({ input, init, nativeFetch }) => {
return nativeFetch(input, {
...init,
headers: { ...init?.headers, Authorization: "Bearer token123" },
});
},
);