Deno mock fetch implementation to be used
in testing. This module allows one to intercept calls to the global fetch API
and control the behaviour accordingly.
- Intercept calls to the global
fetchAPI - Intercept multiple types of requests at once, based on:
- Request Origin
- Request Path
- Request Query string
- Request Body
- Request Headers
- Intercept request indefinitely
- Intercept request a finite number of times
- Simulate a request time delay
- Support for falling back to calling a real API for defined hostnames
- All global
fetchAPI inputs are supported - Advanced methods for matching requests:
stringRegExpFunction
- Throw custom error support
- Set default headers
- Auto-generated headers:
content-length
Set up a basic fetch interceptor.
import { MockFetch } from "https://deno.land/x/[email protected]/mod.ts";
const mockFetch = new MockFetch();
mockFetch
// Intercept `GET https://example.com/hello`
.intercept("https://example.com/hello", { method: "GET" })
// Respond with status `200` and text `hello`
.response("hello", { status: 200 });Call the matching URL:
const response = await fetch("https://example.com/hello");
const text = await response.text();
console.log(response.status); // 200
console.log(text); // "hello"By default, subsequent calls to the same URL will reject with a
MockNotMatchedError:
// Rejects with MockNotMatchedError
await fetch("https://example.com/hello");This behaviour can be changed as demonstrated in the examples.
I want to:
- Intercept a request containing a Query String
- Intercept a request indefinitely
- Intercept a request a set number of times
- Intercept a request with a delay
- Default to calling the original URL if a mock is not matched
- Default to calling specified URLs if a mock is not matched
- Deactivate calling original URLs
- Activate fetch interceptions
- Deactivate fetch interceptions
- Check if fetch interceptions are active
- Close and clean up the Mock Fetch instance
- Get the number of times requests have been intercepted
- View registered mock metadata
- Intercept a request based on method RegExp
- Intercept a request based on method Function
- Intercept a request based on URL RegExp
- Intercept a request based on URL Function
- Intercept a request based on body
- Intercept a request based on headers object
- Intercept a request based on headers instance
- Intercept a request based on headers array
- Intercept a request based on headers function
- Throw a custom error upon fetch call
- Set default headers
- Autogenerate
content-lengthheader - Intercept requests alongside superdeno
Set up the interceptor.
import { MockFetch } from "https://deno.land/x/[email protected]/mod.ts";
const mockFetch = new MockFetch();
mockFetch
.intercept("https://example.com/hello?foo=bar", { method: "GET" })
.response("hello", { status: 200 });Call the matching URL:
const response = await fetch("https://example.com/hello?foo=bar");
const text = await response.text();
console.log(response.status); // 200
console.log(text); // "hello"Set up the interceptor, using the persist method on the MockScope instance.
import { MockFetch } from "https://deno.land/x/[email protected]/mod.ts";
const mockFetch = new MockFetch();
mockFetch
.intercept("https://example.com/hello", { method: "GET" })
.response("hello", { status: 200 })
.persist();Call the matching URL:
const response = await fetch("https://example.com/hello");
const text = await response.text();
console.log(response.status); // 200
console.log(text); // "hello"Call the matching URL again:
const response = await fetch("https://example.com/hello");
const text = await response.text();
console.log(response.status); // 200
console.log(text); // "hello"Set up the interceptor, using the times method on the MockScope instance.
import { MockFetch } from "https://deno.land/x/[email protected]/mod.ts";
const mockFetch = new MockFetch();
mockFetch
.intercept("https://example.com/hello", { method: "GET" })
.response("hello", { status: 200 })
// Will intercept matching requests twice
.times(2);Call the matching URL:
const response = await fetch("https://example.com/hello");
const text = await response.text();
console.log(response.status); // 200
console.log(text); // "hello"Call the matching URL again:
const response = await fetch("https://example.com/hello");
const text = await response.text();
console.log(response.status); // 200
console.log(text); // "hello"Call the matching URL a final time:
// Rejects with MockNotMatchedError
await fetch("https://example.com/hello");Set up the interceptor, using the delay method on the MockScope instance.
import { MockFetch } from "https://deno.land/x/[email protected]/mod.ts";
const mockFetch = new MockFetch();
mockFetch
.intercept("https://example.com/hello", { method: "GET" })
.response("hello", { status: 200 })
// Delay 1000ms before returning the response
.delay(1000);Call the matching URL:
const response = await fetch("https://example.com/hello");
// 1000ms later...
const text = await response.text();
console.log(response.status); // 200
console.log(text); // "hello"Sometimes, one might want to default to calling the original URL if a mock is
not matched. This can be done with the activateNetConnect method on the
MockFetch instance.
import { MockFetch } from "https://deno.land/x/[email protected]/mod.ts";
const mockFetch = new MockFetch();
mockFetch.activateNetConnect();
mockFetch
.intercept("https://example.com/hello", { method: "GET" })
.response("hello", { status: 200 });Call the matching URL:
const response = await fetch("https://example.com/hello");
const text = await response.text();
console.log(response.status); // 200
console.log(text); // "hello"Call the same URL:
const response = await fetch("https://example.com/hello");
const text = await response.text();
console.log(response.status); // 200
console.log(text); // Some html from the actual endpointIn addition, one might want to default to calling the original URL for certain
hostnames if a mock is not matched. This can be done by passing matchers to the
activateNetConnect method on the MockFetch instance.
import { MockFetch } from "https://deno.land/x/[email protected]/mod.ts";
const mockFetch = new MockFetch();
// Allow calls to `example.com` upon an unmatched request.
// This can be called multiple to times to register multiple hostnames
mockFetch.activateNetConnect("example.com");Call a non-matching URL:
const response = await fetch("https://example.com/hello");
const text = await response.text();
console.log(response.status); // 200
console.log(text); // Some html from the actual endpointCall another non-matching URL:
const response = await fetch("https://another-example.com/hello");
// Rejects with MockNotMatchedError
await fetch("https://example.com/hello");Deactivate calling original URLs by calling the deactivateNetConnect on the
MockFetch instance.
import { MockFetch } from "https://deno.land/x/[email protected]/mod.ts";
const mockFetch = new MockFetch();
mockFetch.activateNetConnect();
// Do work...
mockFetch.deactivateNetConnect();Activate fetch interceptions by calling the activate method on the MockFetch
instance.
import { MockFetch } from "https://deno.land/x/[email protected]/mod.ts";
const mockFetch = new MockFetch();
mockFetch.activate();Deactivate fetch interceptions by calling the deactivate method on the
MockFetch instance.
import { MockFetch } from "https://deno.land/x/[email protected]/mod.ts";
const mockFetch = new MockFetch();
mockFetch.deactivate();Check if fetch interceptions are active by checking the value of the
isMockActive field on the MockFetch instance.
import { MockFetch } from "https://deno.land/x/[email protected]/mod.ts";
const mockFetch = new MockFetch();
console.log(mockFetch.isMockActive); // trueClose and clean up the Mock Fetch instance by calling the close method on the
MockFetch instance.
import { MockFetch } from "https://deno.land/x/[email protected]/mod.ts";
const mockFetch = new MockFetch();
mockFetch.close();Get the number of times requests have been intercepted by checking the value of
the calls field on the MockFetch instance.
import { MockFetch } from "https://deno.land/x/[email protected]/mod.ts";
const mockFetch = new MockFetch();
console.log(mockFetch.calls); // 0
mockFetch
.intercept("https://example.com/hello?foo=bar", { method: "GET" })
.response("hello", { status: 200 });
const response = await fetch("https://example.com/hello");
const text = await response.text();
console.log(response.status); // 200
console.log(text); // "hello"
console.log(mockFetch.calls); // 1View registered mock scope metadata by checking the value of the metadata
field on the MockScope instance.
import { MockFetch } from "https://deno.land/x/[email protected]/mod.ts";
const mockFetch = new MockFetch();
console.log(mockFetch.calls); // 0
const mockScope = mockFetch
.intercept("https://example.com/hello?foo=bar", { method: "GET" })
.response("hello", { status: 200 });
console.log(mockScope.metadata); // MockRequestSet up the interceptor.
import { MockFetch } from "https://deno.land/x/[email protected]/mod.ts";
const mockFetch = new MockFetch();
mockFetch
.intercept("https://example.com/hello", { method: /GET/ })
.response("hello", { status: 200 });Call the matching URL:
const response = await fetch("https://example.com/hello");
const text = await response.text();
console.log(response.status); // 200
console.log(text); // "hello"Set up the interceptor.
import { MockFetch } from "https://deno.land/x/[email protected]/mod.ts";
const mockFetch = new MockFetch();
mockFetch
.intercept("https://example.com/hello", {
method: (input) => input === "GET",
})
.response("hello", { status: 200 });Call the matching URL:
const response = await fetch("https://example.com/hello");
const text = await response.text();
console.log(response.status); // 200
console.log(text); // "hello"Set up the interceptor.
import { MockFetch } from "https://deno.land/x/[email protected]/mod.ts";
const mockFetch = new MockFetch();
mockFetch
.intercept(new RegExp("https\\:\\/\\/example\\.com\\/hello\\?foo\\=bar"))
.response("hello", { status: 200 });Call the matching URL:
const response = await fetch("https://example.com/hello?foo=bar");
const text = await response.text();
console.log(response.status); // 200
console.log(text); // "hello"Set up the interceptor.
import { MockFetch } from "https://deno.land/x/[email protected]/mod.ts";
const mockFetch = new MockFetch();
mockFetch
.intercept((input) => input === "https://example.com/hello?foo=bar")
.response("hello", { status: 200 });Call the matching URL:
const response = await fetch("https://example.com/hello?foo=bar");
const text = await response.text();
console.log(response.status); // 200
console.log(text); // "hello"Set up the interceptor.
import { MockFetch } from "https://deno.land/x/[email protected]/mod.ts";
const mockFetch = new MockFetch();
mockFetch
.intercept("https://example.com/hello", {
method: "POST",
body: "hello",
})
.response("there", { status: 200 });Call the matching URL:
const response = await fetch("https://example.com/hello", {
method: "POST",
body: "hello",
});
const text = await response.text();
console.log(response.status); // 200
console.log(text); // "there"Note, the following body types are also supported:
stringRegExp(input: string) => booleanBlobArrayBufferLikeFormDataURLSearchParams
Set up the interceptor.
import { MockFetch } from "https://deno.land/x/[email protected]/mod.ts";
const mockFetch = new MockFetch();
mockFetch
.intercept("https://example.com/hello", {
headers: {
hello: "there",
foo: /bar/,
hey: (input: string) => input === "ho",
},
})
.response("hello", { status: 200 });Call the matching URL:
const response = await fetch("https://example.com/hello", {
headers: new Headers({
hello: "there",
foo: "bar",
hey: "ho",
}),
});
const text = await response.text();
console.log(response.status); // 200
console.log(text); // "hello"Set up the interceptor.
import { MockFetch } from "https://deno.land/x/[email protected]/mod.ts";
const mockFetch = new MockFetch();
mockFetch
.intercept("https://example.com/hello", {
headers: new Headers({
hello: "there",
another: "one",
}),
})
.response("hello", { status: 200 });Call the matching URL:
const response = await fetch("https://example.com/hello", {
headers: new Headers({
hello: "there",
another: "one",
}),
});
const text = await response.text();
console.log(response.status); // 200
console.log(text); // "hello"Set up the interceptor.
import { MockFetch } from "https://deno.land/x/[email protected]/mod.ts";
const mockFetch = new MockFetch();
mockFetch
.intercept("https://example.com/hello", {
headers: [
["hello", "there"],
["foo", /bar/],
["hey", (input: string) => input === "ho"],
],
})
.response("hello", { status: 200 });Call the matching URL:
const response = await fetch("https://example.com/hello", {
headers: new Headers({
hello: "there",
foo: "bar",
hey: "ho",
}),
});
const text = await response.text();
console.log(response.status); // 200
console.log(text); // "hello"Set up the interceptor.
import { MockFetch } from "https://deno.land/x/[email protected]/mod.ts";
const mockFetch = new MockFetch();
mockFetch
.intercept("https://example.com/hello", {
headers: (headers) => headers.get("hello") === "there",
})
.response("hello", { status: 200 });Call the matching URL:
const response = await fetch("https://example.com/hello", {
headers: new Headers({
hello: "there",
}),
});
const text = await response.text();
console.log(response.status); // 200
console.log(text); // "hello"Set up the interceptor and defined an error using the following documentation as a guide.
import { MockFetch } from "https://deno.land/x/[email protected]/mod.ts";
const mockFetch = new MockFetch();
mockFetch
.intercept((input) => input === "https://example.com/hello")
.throwError(new TypeError("Network error"));Call the matching URL:
await fetch("https://example.com/hello"); // Throws the defined error: new TypeError("Network error")Set up the interceptor.
import { MockFetch } from "https://deno.land/x/[email protected]/mod.ts";
const mockFetch = new MockFetch();
const mockInterceptor = mockFetch
.intercept("https://example.com/hello")
.defaultResponseHeaders({ foo: "bar" })
.response("hello", { status: 200 });Call the matching URL:
const response = await fetch("https://example.com/hello");
const text = await response.text();
console.log(response.status); // 200
console.log(response.headers); // { "content-type", "text/plain;charset=UTF-8", foo: "bar" }
console.log(text); // "hello"import { MockFetch } from "https://deno.land/x/[email protected]/mod.ts";
const mockFetch = new MockFetch();
const mockInterceptor = mockFetch
.intercept("https://example.com/hello")
.responseContentLength()
.response("hello", { status: 200 });Call the matching URL:
const response = await fetch("https://example.com/hello");
const text = await response.text();
console.log(response.status); // 200
console.log(response.headers); // { "content-type", "text/plain;charset=UTF-8", "content-length": "5" }
console.log(text); // "hello"To work alongside superdeno, one must setup calls to 127.0.0.1 before
continuing.
import { MockFetch } from "https://deno.land/x/[email protected]/mod.ts";
import { superdeno } from "https://deno.land/x/superdeno/mod.ts";
import { opine } from "https://deno.land/x/[email protected]/mod.ts";
const app = opine();
app.get("/user", (req, res) => {
res.setStatus(200).json({ name: "Deno" });
});
// Setup mock before calling superdeno
const mockFetch = new MockFetch();
mockFetch.activateNetConnect("127.0.0.1");
superdeno(app)
.get("/user")
.expect("Content-Type", /json/)
.expect("Content-Length", "15")
.expect(200)
.end((err, res) => {
if (err) throw err;
});To browse API documentation:
- Go to https://deno.land/x/deno_mock_fetch.
- Click "View Documentation".
Contributions, issues and feature requests are very welcome. If you are using this package and fixed a bug for yourself, please consider submitting a PR!
Further details can be found in the Contributing guide.
The following limitations are known:
The following with throw an InvalidArgumentError:
import { MockFetch } from "https://deno.land/x/[email protected]/mod.ts";
const mockFetch = new MockFetch();
mockFetch.intercept("https://example.com/hello", {
method: "POST",
body: new ReadableStream(),
});
// Throws an `InvalidArgumentError`Trailers are currently not supported for the following reasons:
- Not returned in Deno fetch responses
- Limited support and documentation
This module is 100% free and open-source, under the MIT license.
This modules has been inspired by the following: