mocaron
v1.1.0
Published
Simple express mock server with a flexible API inspired by fetch-mock
Downloads
754
Maintainers
Readme
Installation
# npm
npm install -D mocaron
# yarn
yarn add -D mocaron
# pnpm
pnpm add -D mocaron
Quick Start
import { MockServer } from "mocaron";
const mockServer = new MockServer({ port: 3000 });
await mockServer.start();
mockServer.get("/test", { status: 200, body: { message: "Hello World" } });
const response = await fetch("http://localhost:3000/test");
console.log(response.status); // 200
console.log(await response.json()); // { message: "Hello World" }
await mockServer.stop();
Usage
Starting and stopping the mock server
See constructor()
start()
stop()
import { MockServer } from "mocaron";
const mockServer = new MockServer({ port: 3000 });
await mockServer.start();
// ...
await mockServer.stop();
Registering a mock
Register a mock using mock()
.
mockServer.mock(
{ path: "/test", method: "GET" },
{ status: 200, body: { message: "Hello World" } },
);
const response = await fetch("http://localhost:3000/test");
console.log(response.status); // 200
console.log(await response.json()); // { message: "Hello World" }
Method specific mocks
You can also register mocks that only match a specific HTTP method.
See get()
post()
put()
patch()
delete()
mockServer
.get("/test", { status: 200, body: { message: "Hello World" } })
.post("/test", { status: 201, body: { message: "Created" } })
.put("/test", { status: 200, body: { message: "Replaced" } })
.patch("/test", { status: 200, body: { message: "Updated" } })
.delete("/test", { status: 204 });
Unmatched requests
If a request does not match any of the registered mocks the server will respond with a 404 status code.
const response = await fetch("http://localhost:3000/test");
console.log(response.status); // 404
Ambiguous mocks
If two or more mocks match the same request the server will respond with a 404 status code.
mockServer.mock({ path: "/foo" }, "foo").mock({ path: "/foo" }, "bar");
const response = await fetch("http://localhost:3000/foo");
console.log(response.status); // 404
You can override this behavior by passing the overwrite
option to the last matching mock.
mockServer
.mock({ path: "/foo" }, "foo")
.mock({ path: "/foo" }, "bar", { overwrite: true });
const response = await fetch("http://localhost:3000/foo");
console.log(response.status); // 200
console.log(await response.text()); // bar
Resetting the mock server
Calling reset()
will reset the mock server to its initial state.
mockServer.get("/test", { status: 200 });
let response = await fetch("http://localhost:3000/test");
console.log(response.status); // 200
mockServer.reset();
response = await fetch("http://localhost:3000/test");
console.log(response.status); // 404
Testing
Set up the mock server for each test using start()
, stop()
and reset()
.
import { MockServer } from "mocaron";
import { beforeAll, afterAll, beforeEach, test, assert } from "my-test-library";
const mockServer = new MockServer({ port: 3000 });
beforeAll(() => mockServer.start());
afterAll(() => mockServer.stop());
beforeEach(() => mockServer.reset());
Test that a mock has been called using hasBeenCalledWith()
.
test("mock has been called", async () => {
mockServer.get("/test", { status: 200 });
await fetch("http://localhost:3000/test");
assert(mockServer.hasBeenCalledWith({ path: "/test" }));
});
Test that a mock has been called a specific number of times using hasBeenCalledTimes()
.
test("mock has been called 3 times", async () => {
mockServer.get("/test", { status: 200 });
await fetch("http://localhost:3000/test");
await fetch("http://localhost:3000/test");
await fetch("http://localhost:3000/test");
assert(mockServer.hasBeenCalledTimes(3, { path: "/test" }));
});
Custom assertions using calls()
.
test("custom assertion", async () => {
mockServer.get("/test", { status: 200 });
await fetch("http://localhost:3000/test");
assert(mockServer.calls().length === 1);
assert(mockServer.calls()[0].request.path === "/test");
});
API
MockServer
ExpectationMessage
Options
Request
Matcher
MatcherObj
MatcherFn
Response
ResponseObj
ResponseFn
MockOptions
Mock
Call
MockServer
constructor(options): MockServer
Create a new MockServer
instance.
| Param | Type | Default |
| ------- | --------------------- | ------- |
| options | Options
| - |
Example
const mockServer = new MockServer({ port: 3000 });
start(): Promise<void>
Start the mock server.
Example
await mockServer.start();
stop(): Promise<void>
Stop the mock server.
Example
await mockServer.stop();
port(): number
Get the port the mock server is running on.
Example
const port = mockServer.port();
console.log(port); // 3000
mock(matcher, response, options): MockServer
Register a mock.
| Param | Type | Default |
| -------- | ----------------------------------------------- | ------- |
| matcher | string
| RegExp
| Matcher
| - |
| response | string
| number
| Response
| - |
| options | MockOptions
| {}
|
If matcher
is a string
or RegExp
, it will be used to match the request path.
If response
is a string
, it will be used as the response body.
If response
is a number
, it will be used as the response status code.
Returns the MockServer
instance.
Example
mockServer.mock({ path: "/test" }, { status: 204 });
const response = await fetch("http://localhost:3000/test");
console.log(response.status); // 204
get(matcher, response, options): MockServer
Register a mock that only responds to requests using the HTTP GET
method.
| Param | Type | Default |
| -------- | --------------------------------------------------- | ------- |
| matcher | string
| RegExp
| MatcherObj
| - |
| response | string
| number
| Response
| - |
| options | MockOptions
| {}
|
If matcher
is a string
or RegExp
, it will be used to match the request path.
If response
is a string
, it will be used as the response body.
If response
is a number
, it will be used as the response status code.
Returns the MockServer
instance.
Example
mockServer.get("/test", {
status: 200,
body: { message: "Hello World" },
});
const response = await fetch("http://localhost:3000/test");
console.log(response.status); // 200
console.log(await response.json()); // { message: "Hello World" }
post(matcher, response, options): MockServer
Register a mock that only responds to requests using the HTTP POST
method.
| Param | Type | Default |
| -------- | --------------------------------------------------- | ------- |
| matcher | string
| RegExp
| MatcherObj
| - |
| response | string
| number
| Response
| - |
| options | MockOptions
| {}
|
If matcher
is a string
or RegExp
, it will be used to match the request path.
If response
is a string
, it will be used as the response body.
If response
is a number
, it will be used as the response status code.
Returns the MockServer
instance.
Example
mockServer.post("/test", {
status: 201,
body: { message: "Hello World" },
});
const response = await fetch("http://localhost:3000/test", {
method: "POST",
body: JSON.stringify({ message: "Hello World" }),
});
console.log(response.status); // 201
console.log(await response.json()); // { message: "Hello World" }
put(matcher, response, options): MockServer
Register a mock that only responds to requests using the HTTP PUT
method.
| Param | Type | Default |
| -------- | --------------------------------------------------- | ------- |
| matcher | string
| RegExp
| MatcherObj
| - |
| response | string
| number
| Response
| - |
| options | MockOptions
| {}
|
If matcher
is a string
or RegExp
, it will be used to match the request path.
If response
is a string
, it will be used as the response body.
If response
is a number
, it will be used as the response status code.
Returns the MockServer
instance.
Example
mockServer.put("/test", {
status: 200,
body: { message: "Hello World" },
});
const response = await fetch("http://localhost:3000/test", {
method: "PUT",
body: JSON.stringify({ message: "Hello World" }),
});
console.log(response.status); // 200
console.log(await response.json()); // { message: "Hello World" }
patch(matcher, response, options): MockServer
Register a mock that only responds to requests using the HTTP PATCH
method.
| Param | Type | Default |
| -------- | --------------------------------------------------- | ------- |
| matcher | string
| RegExp
| MatcherObj
| - |
| response | string
| number
| Response
| - |
| options | MockOptions
| {}
|
If matcher
is a string
or RegExp
, it will be used to match the request path.
If response
is a string
, it will be used as the response body.
If response
is a number
, it will be used as the response status code.
Returns the MockServer
instance.
Example
mockServer.patch("/test", {
status: 200,
body: { message: "Hello World" },
});
const response = await fetch("http://localhost:3000/test", {
method: "PATCH",
body: JSON.stringify({ message: "Hello World" }),
});
console.log(response.status); // 200
console.log(await response.json()); // { message: "Hello World" }
delete(matcher, response, options): MockServer
Register a mock that only responds to requests using the HTTP DELETE
method.
| Param | Type | Default |
| -------- | --------------------------------------------------- | ------- |
| matcher | string
| RegExp
| MatcherObj
| - |
| response | string
| number
| Response
| - |
| options | MockOptions
| {}
|
If matcher
is a string
or RegExp
, it will be used to match the request path.
If response
is a string
, it will be used as the response body.
If response
is a number
, it will be used as the response status code.
Returns the MockServer
instance.
Example
mockServer.delete("/test", { status: 204 });
const response = await fetch("http://localhost:3000/test", {
method: "DELETE",
});
console.log(response.status); // 204
mocks(): readonly Mock[]
Get all registered mocks.
Returns an array of Mock
objects.
Example
mockServer.mock({ path: "/test" }, { status: 204 });
const mocks = mockServer.mocks();
console.log(mocks);
// [{ matcher: { path: "/test" }, response: { status: 204 } }]
calls(): readonly Call[]
Get all registered calls.
Returns an array of Call
objects.
Example
mockServer.mock({ path: "/test" }, { status: 204 });
await fetch("http://localhost:3000/test");
const calls = mockServer.calls();
console.log(calls);
// [{ matcher: { path: "/test" }, request: <express.Request> }]
hasBeenCalledWith(matcher): boolean
Check if the route has been called with the given matcher
.
| Param | Type | Default |
| ------- | --------------------------------------------- | ------- |
| matcher | string
| RegExp
| Matcher
| - |
If matcher
is a string
or RegExp
, it will be used to match the request path.
Returns true
if the route has been called with the given matcher
, false
otherwise.
Example
mockServer.get("/test", { status: 200 });
console.log(mockServer.hasBeenCalledWith({ path: "/test" })); // false
await fetch("http://localhost:3000/test");
console.log(mockServer.hasBeenCalledWith({ path: "/test" })); // true
hasBeenCalledTimes(times, matcher): boolean
Check if the route has been called a certain number of times with the given matcher
.
| Param | Type | Default |
| ------- | --------------------------------------------- | ------- |
| times | number
| - |
| matcher | string
| RegExp
| Matcher
| - |
If matcher
is a string
or RegExp
, it will be used to match the request path.
Returns true
if the route has been called times
times with the given matcher
, false
otherwise.
Example
mockServer.get("/test", { status: 200 });
console.log(mockServer.hasBeenCalledTimes(0, { path: "/test" })); // true
console.log(mockServer.hasBeenCalledTimes(1, { path: "/test" })); // false
await fetch("http://localhost:3000/test");
console.log(mockServer.hasBeenCalledTimes(0, { path: "/test" })); // false
console.log(mockServer.hasBeenCalledTimes(1, { path: "/test" })); // true
countCalls(matcher): number
Count the number of times the server was called with the given matcher
.
| Param | Type | Default |
| ------- | --------------------------------------------- | ------- |
| matcher | string
| RegExp
| Matcher
| - |
If matcher
is a string
or RegExp
, it will be used to match the request path.
Returns the number of times the server has been called with the given matcher
.
Example
mockServer.get("/test", { status: 200 });
console.log(mockServer.countCalls({ path: "/test" })); // 0
await fetch("http://localhost:3000/test");
console.log(mockServer.countCalls({ path: "/test" })); // 1
reset(): void
Reset all mocks and calls.
Example
mockServer.get("/test", { status: 200 });
await fetch("http://localhost:3000/test");
console.log(mockServer.mocks());
// [{ matcher: { path: "/test", method: "GET" }, response: { status: 200 } }]
console.log(mockServer.calls());
// [{ matcher: { path: "/test", method: "GET" }, request: <express.Request> }]
mockServer.reset();
console.log(mockServer.mocks()); // []
console.log(mockServer.calls()); // []
resetMocks(): void
Reset all mocks.
Example
mockServer.get("/test", { status: 200 });
console.log(mockServer.mocks());
// [{ matcher: { path: "/test", method: "GET" }, response: { status: 200 } }]
mockServer.resetMocks();
console.log(mockServer.mocks()); // []
resetCalls(): void
Reset all calls.
Example
mockServer.get("/test", { status: 200 });
await fetch("http://localhost:3000/test");
console.log(mockServer.calls());
// [{ matcher: { path: "/test", method: "GET" }, request: <express.Request> }]
mockServer.resetCalls();
console.log(mockServer.calls()); // []
ExpectationMessage
hasBeenCalledWith(mockServer, matcher): string
Format an expectation message for hasBeenCalledWith()
.
| Param | Type | Default |
| ---------- | --------------------------- | ------- |
| mockServer | MockServer
| - |
| matcher | Matcher
| - |
Returns a string with the formatted expectation message.
Example
if (!mockServer.hasBeenCalledWith(matcher)) {
throw new Error(ExpectationMessage.hasBeenCalledWith(mockServer, matcher));
}
hasBeenCalledTimes(mockServer, times, matcher): string
Format an expectation message for hasBeenCalledTimes()
.
| Param | Type | Default |
| ---------- | --------------------------- | ------- |
| mockServer | MockServer
| - |
| times | number | - |
| matcher | Matcher
| - |
Returns a string with the formatted expectation message.
Example
if (!mockServer.hasBeenCalledTimes(mockServer, 2, matcher)) {
throw new Error(
ExpectationMessage.hasBeenCalledTimes(mockServer, 2, matcher),
);
}
Options
Object with the following properties:
| Property | Type | Description |
| -------- | -------- | ------------------------------ |
| port | number
| port to run the mock server on |
Request
Type alias for express.Request
with the body
property typed as Buffer
| undefined
.
type Request = express.Request<{}, unknown, Buffer | undefined>;
Matcher
Type alias for MatcherObj
| MatcherFn
.
type Matcher = MatcherObj | MatcherFn;
MatcherObj
Object with the following properties:
| Property | Type | Description |
| -------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| method | string
| undefined
| HTTP method to match against |
| path | string
| RegExp
| undefined
| path to match against |
| query | Request["query"]
| undefined
| query parameters to match against.Parameters explicitly set to undefined
will not match when provided. |
| headers | Record<string, string \| undefined>
| undefined
| headers to match against.Headers explicitly set to undefined
will not match when provided. |
| body | string
| object
| undefined
| body to match against.If an object
is given it will be compared to the request body parsed as JSON. |
MatcherFn
Function that takes a Request
and returns whether the request should match.
type MatcherFn = (req: Request) => boolean;
Response
Type alias for ResponseObj
| ResponseFn
.
type Response = ResponseObj | ResponseFn;
ResponseObj
Object with the following properties:
| Property | Type | Description |
| -------- | --------------------------------------- | ---------------------------------------------------------------------------------------- |
| status | number
| undefined
| status code to respond with (defaults to 200
) |
| headers | Record<string, string>
| undefined
| headers to respond with |
| body | string
| object
| undefined
| body to respond with.If an object
is given it will be converted to a JSON string. |
| delay | number
| undefined
| delay in milliseconds before responding |
ResponseFn
Function or async function that takes a Request
and returns a ResponseObj
.
type ResponseFn = (req: Request) => ResponseObj | Promise<ResponseObj>;
MockOptions
Object with the following properties:
| Property | Type | Description |
| --------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------ |
| overwrite | boolean
| undefined
| when set to true
,previous ambiguous mocks matching the same request will be overwritten |
Mock
Object with the following properties:
| Property | Type | Description |
| -------- | ----------------------------- | -------------------------------------------------- |
| matcher | Matcher
| matcher to match against the request |
| response | Response
| response the server will respond with when matched |
| options | MockOptions
| see MockOptions
|
Call
Object with the following properties:
| Property | Type | Description |
| -------- | --------------------- | ----------------------------------- |
| request | Request
| request the server was called with |
| matcher | Matcher
| matcher the request matched against |