Skip to content

Mocking HTTP services

Tested code might rely on remote HTTP services which need to be mocked for unit testing. SDT provides a method for conveniently generating HTTP services responding to incoming requests based on a simple configuration.

Example

Let's start with an example displaying the bare minimum necessary to set up an HTTP service for use in tested code.

javascript
import { createHttpServer } from "@hitchy/server-dev-tools";

describe( "Testing my code", () => {
	let server;

    beforeAll( async() => {
		server = await createHttpServer( {
            "/foo": { text: "bar" },
        } );
    } );

    afterAll( () => {
		server.close();
    } );

	// TODO add your testing code including starting/stopping Hitchy as necessary
} );

Is Hitchy required?

The HTTP service itself does not require some Hitchy runtime to be started and stopped. Because of that, the example above is explicitly omitting any code setting up Hitchy for testing code relying on it.

Basics

The asynchronous method createHttpServer() is invoked to create an HTTP service ready for responding to requests based on provided configuration. It may be invoked multiple times to have multiple services to work with in parallel. Either created service is available locally, only, on a dedicated port.

API

The method takes a configuration as sole argument.

The resulting HTTP service is based on Node's native HTTP server. It comes with a few extensions, though:

baseUrl

This property provides the base URL of the created HTTP service. It can be used e.g. for compiling request URLs or for passing it in configurations of tested code.

setOverlay

This method takes a configuration as argument which is shallowly merged with the one provided on creating the HTTP service. Requests configured in this configuration is preferred over the one provided on creating the service. It is designed to adjust a running service's behavior temporarily to meet a test's particular expectations.

Mind the process!

Usually, testing Hitchy-based code does not spawn new processes. HTTP services get created in same process as your test runner, too. That's why using overlays to temporarily adjust a running HTTP service's configuration is working.

Keep this in mind in case your test or the tested code is spawning processes explicitly.

javascript
it( "behaves properly", async() => {
	server.setOverlay( {
        "/foo": { text: "baz" },
    } );
	
	// requests sent to `server` respond with updated configuration now
} );

On setting another overlay, the previous overlay gets replaced:

javascript
it( "behaves properly", async() => {
	server.setOverlay( customConfig );

	// requests sent to `server` respond with updated configuration now

	server.setOverlay( customAltConfig );

	// requests sent to `server` respond with latest update to the configuration
} );

When invoked without any argument, the original configuration is restored:

javascript
it( "behaves properly", async() => {
	server.setOverlay( {
        "/foo": { text: "baz" },
    } );

	// requests sent to `server` respond with updated configuration now

	server.setOverlay();

	// requests sent to `server` respond with original configuration again
} );

An overlay may provide a falsy response configuration for an endpoint to respond to some request matching that endpoint in the same way as if there was no matching endpoint.

Configuration

The HTTP service configuration is an object mapping endpoints into response configurations used whenever a matching request is received.

Endpoint

An endpoint is a pathname optionally prefixed with an HTTP request method.

text
POST /api/foo/bar

A request is considered matching when both method and pathname are matching. The HTTP request method is optional in which case any method is matching.

text
/api/foo/bar

If both cases match in a configuration, the one with the method prefix is preferred over the one lacking it.

Response configuration

A response configuration is either

  • an object with optional properties describing how to respond to a matching request or
  • a function invoked on every matching request to deliver that set of properties.

On providing a function, it is invoked with both the request descriptor and the response manager as arguments. Because of that, some callback can e.g. adjust the response in its own ways. By returning false instead of a response configuration, the response is considered as completely sent to the client already.

body

This property provides a string or Buffer sent as payload in response to a matching request.

Convenience feature

This property is one out of multiple options, and it must not be mixed with other such properties.

css

This property provides a string sent as CSS in response to a matching request.

Convenience feature

This property is one out of multiple options, and it must not be mixed with other such properties.

file

This property names a local file to be read. Its content is delivered in response to a matching request. The file's extension is used to pick a content type reported to the requesting client, falling back to application/octet-stream.

Convenience feature

This property is one out of multiple options, and it must not be mixed with other such properties.

html

This property provides a string sent as HTML in response to a matching request.

Convenience feature

This property is one out of multiple options, and it must not be mixed with other such properties.

javascript

This property provides a string sent as Javascript in response to a matching request.

Convenience feature

This property is one out of multiple options, and it must not be mixed with other such properties.

json

This property provides some data sent as JSON in response to a matching request.

Convenience feature

This property is one out of multiple options, and it must not be mixed with other such properties.

status

This property describes the HTTP status code to be used in response to a matching request. It defaults to 200.

text

This property provides a string sent as plain text in response to a matching request.

Convenience feature

This property is one out of multiple options, and it must not be mixed with other such properties.

headers

This is an object mapping response header names into either header's value.

Example

javascript
const server = await createHttpServer( {
    "/api/foo/bar": {
		text: "baz"
    },
    "POST /api/foo/bar": {
		json: { baz: "boo" },
        headers: {
			"x-assigned-id": 12345678
        }
    },
    "DELETE /api/foo/bar": () => ( {} ),
    "PUT /api/foo/bar": ( req, res ) => {
		res.end( "foo" );
		return false;
    },
} );