Created
October 9, 2023 14:45
-
-
Save nickcampbell18/5ed0539929fb8dbc8a12c974c7b00e1f to your computer and use it in GitHub Desktop.
Replacing the Undici global dispatch logic with a symbol-less Jest-compatible implementation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
test("mocking a request", async () => { | |
const mockAgent = new MockAgent() | |
;(global as any).setMockedFetchGlobalDispatcher(mockAgent) | |
await fetch(...) | |
}) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const { TestEnvironment } = require("jest-environment-node") | |
const { fetch: undiciFetch, MockAgent } = require("undici") | |
const v8 = require('node:v8'); | |
module.exports = class extends TestEnvironment { | |
constructor(config, context) { | |
super(config, context) | |
this.mockGlobalFetch() | |
} | |
/** | |
* Replace the global fetch implementation with one which still uses undici, but pulls | |
* in the correct "dispatcher" from the global object. | |
* | |
* Background: | |
* 1. Every call to fetch uses a "dispatcher", i.e. the underlying HTTP client | |
* 2. If you don't explicitly specify it when calling `fetch(...)`, Undici / Node | |
* internally uses a special symbol to find the default global one inside the | |
* global namespace [1] | |
* 3. Jest runs each test in a separate process, so Symbol.for(...) returns a | |
* different value in the test process. | |
* 4. This means that calling `setGlobalDispatcher(...)` in the test has no effect on | |
* the global version that fetch otherwise would use. | |
* 5. This means that your mocks won't apply and won't be cleared up automatically. | |
* | |
* This method overrides the global fetch, to specify our own global dispatcher, and | |
* creates a special method that can be used to replace the dispatcher. | |
* | |
* [1]: https://github.com/nodejs/undici/blob/e5c9d703e63cd5ad691b8ce26e3f9a81c598f2e3/lib/global.js#L5 | |
*/ | |
mockGlobalFetch() { | |
this.global.fetch = (input, init) => undiciFetch(input, { | |
dispatcher: this.global["mockAgent"], | |
...init, | |
}) | |
this.global.setMockedFetchGlobalDispatcher = (agent) => | |
Object.defineProperty(this.global, "mockAgent", { | |
value: agent, | |
writable: true, | |
enumerable: false, | |
configurable: false | |
}) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@nickcampbell18 This saved my day, thank you!