Mocking fetch
#
“Joyful” Pyodide dev means sitting in NodeJS tooling.
But we’re writing a browser app, which is going to issue HTTP requests using fetch
.
How do we replace the server?
We’ll start the process of using mocks.
Vitest config file#
Our path to mocking begins with a small Vitest configuration file at vitest.config.js
in the top-level folder:
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
setupFiles: ["./test/setup.js"],
},
});
It’s one job: point at a “setup” file which will mock our fetch
request.
:::{note} Vitest, mocking, and MSW
The Vitest mocking docs recommend using Mock Service Worker.
Alas, that begins a rabbit hole.
MSW doesn’t support NodeJS native fetch).
It instead relies on node-fetch
, which requires absolute URLs.
:::
Vitest setup test#
We’re about to write some JavaScript to mock fetch
requests and responses.
Let’s do that by…writing a test!
Put this in tests/setup.test.js
:
import {expect, test} from "vitest";
import {mockFetch} from "./setup.js";
test("File mapping works", async () => {
const response = await mockFetch("./__init__.py");
expect(response.ok).to.be.true;
const responseText = await response.text();
expect(responseText).to.contain("from pathlib");
});
The test fails, as we haven’t started tests/setup.js
yet.
Vitest setup file#
Let’s now write our Vitest setup file at tests/setup.js
:
import {vi} from "vitest";
import {readFileSync} from "node:fs";
const INIT_PATH = "src/pyodide_components/__init__.py";
const INIT_CONTENT = readFileSync(INIT_PATH, "utf-8");
const FILES = {
"./__init__.py": INIT_CONTENT,
};
export async function mockFetch(url) {
if (url.includes("pyodide")) {
return {
ok: true,
status: 200,
};
}
const fileText = FILES[url];
return {
ok: true,
status: 200,
text: async () => fileText
};
}
vi.stubGlobal("fetch", mockFetch);
This setup file is run whenever a test sessions starts up. It’s useful for global initialization. Several notable points:
vi
installsmockFetch
in the place offetch
mockFetch
returns the file content from certain matched URLs
Implementation in worker.js
#
With this code in worker.js
:
export async function initialize() {
let loaderContent;
const pyodide = await loadPyodide();
const response = await fetch("./__init__.py");
if (response.ok) {
loaderContent = await response.text();
} else {
console.log("Failed to fetch loader.py");
}
return pyodide;
}
…our test now passes.
You can see the await fetch("./__init__.py")
line.
When this is run under NodeJS and Vitest, the mock takes over.
No HTTP request is sent.
Instead, the contents of src/pyodide_components/__init__.py
are read from disk and returned to the fetch
.
Wrapup#
We covered a lot of NodeJS weirdness in this step:
fetch
and versions of NodeJSVitest configuration files
Vitest setup files
Installing mocks (and writing tests for them)
Truth be told, it’s about the same as getting up-to-speed with pytest
and network mocking.
Ultimately, it’s worth the investment.