Creating a Mock Agent
A mock agent in Mentoss is an undici Dispatcher that intercepts HTTP requests made by undici and returns responses that you’ve defined in your mock servers. This allows you to test Node.js applications that use undici without making real network requests.
What is undici?
undici is a high-performance HTTP client for Node.js. It’s used internally by Node.js for fetch() and is also available as a standalone library. If you’re testing code that uses undici’s request(), stream(), or pipeline() methods, or Node.js’s native fetch() function, you’ll want to use MockAgent.
Create a new MockAgent instance
To get started, import the MockAgent class and create a new instance. The only required argument is an object with the following property:
servers(required) - an array ofMockServerinstances to use for mocking requests
Here’s an example:
import { MockServer, MockAgent } from "mentoss";
const server = new MockServer("https://api.example.com");
const agent = new MockAgent({ servers: [server],});With your mock agent created, you can now use it with Node.js fetch() or undici.
Use the mock agent with Node.js fetch()
Node.js has a native fetch() function (available in Node.js 18+) that accepts a dispatcher option. You can use MockAgent as the dispatcher to mock requests:
import { MockServer, MockAgent } from "mentoss";import { expect } from "chai";
describe("My API with Node.js fetch()", () => { const server = new MockServer("https://api.example.com"); const agent = new MockAgent({ servers: [server], });
// reset the server after each test afterEach(() => { agent.clearAll(); });
it("should return a 200 status code", async () => { // set up the route to test server.get("/ping", { status: 200, body: "pong" });
// make the request using the mock agent const response = await fetch("https://api.example.com/ping", { dispatcher: agent, });
// check the response expect(response.status).to.equal(200);
// read the body const bodyText = await response.text(); expect(bodyText).to.equal("pong"); });});Use the mock agent with undici
The MockAgent class implements the undici Dispatcher interface, which means you can use it anywhere undici accepts a dispatcher. Here’s an example using undici’s request() method:
import { MockServer, MockAgent } from "mentoss";import { request } from "undici";import { expect } from "chai";
describe("My API with undici", () => { const server = new MockServer("https://api.example.com"); const agent = new MockAgent({ servers: [server], });
// reset the server after each test afterEach(() => { agent.clearAll(); });
it("should return a 200 status code", async () => { // set up the route to test server.get("/ping", { status: 200, body: "pong" });
// make the request using the mock agent const { statusCode, body } = await request( "https://api.example.com/ping", { dispatcher: agent, }, );
// check the response expect(statusCode).to.equal(200);
// read the body const bodyText = await body.text(); expect(bodyText).to.equal("pong"); });});Differences from FetchMocker
The MockAgent class is similar to FetchMocker but has some key differences:
What’s the same:
- Both use an array of
MockServerinstances to define routes - Both provide the same testing helpers:
called(),allRoutesCalled(),uncalledRoutes, andassertAllRoutesCalled() - Both provide a
clearAll()method to reset the servers
What’s different:
MockAgentdoes not supportbaseUrl- undici and Node.js fetch requests always use absolute URLsMockAgentdoes not supportcredentials- credentials are only relevant for browser contextsMockAgentimplements the undiciDispatcherinterface withdispatch(),close(), anddestroy()methodsMockAgentis specifically designed for use with undici and Node.jsfetch(), not browserfetch()
Testing helpers
Like FetchMocker, MockAgent provides several testing helpers to verify that your requests were made correctly:
const server = new MockServer("https://api.example.com");server.get("/users", { status: 200, body: [] });server.post("/users", { status: 201 });
const agent = new MockAgent({ servers: [server] });
// make a requestawait request("https://api.example.com/users", { dispatcher: agent });
// check if a specific request was madeagent.called("https://api.example.com/users"); // trueagent.called({ method: "POST", url: "https://api.example.com/users" }); // false
// check if all routes were calledagent.allRoutesCalled(); // false (POST not called)
// get a list of uncalled routesagent.uncalledRoutes; // ["POST https://api.example.com/users -> 201"]
// throw an error if not all routes were calledagent.assertAllRoutesCalled(); // throws ErrorClosing the agent
When you’re done with the agent, you can close it to prevent new requests:
await agent.close();// orawait agent.destroy(); // alias for close()After closing, any new dispatch attempts will fail with an error.