How toTest Middleware in React Router

If you're already using React Router middleware, you may want to ensure it works as expected. This article will show you how to test a React Router middleware using Bun Test, though the same approach works with any test runner.

We'll write a test for the middleware from the "How to Use Middleware in React Router" tutorial.

Setting Up the Test

First, create a test file and import the middleware function:

import { test, expect, mock } from "bun:test";
import { sessionMiddleware, getSession } from "./session";

Since middleware functions require a request object, a params object, a router context provider instance, and a next function, we need to mock them.

let response = await sessionMiddleware({ request, params, context }, next);

Now, let's define the necessary mocks:

import { unstable_RouterContextProvider } from "react-router";

test("returns a response", async () => {
  let request = new Request("https://example.com");
  let params = {};
  let context = new unstable_RouterContextProvider();

  let next = mock().mockImplementation(() => new Response(null));

  let response = await sessionMiddleware({ request, params, context }, next);

  expect(response).toBeInstanceOf(Response);
});

The unstable_RouterContextProvider class from React Router allows middleware to store values that other middleware, loaders, or actions can use.

Ensuring the Session is Set

Now, let's update our test to check that the session is correctly stored in the context provider.

import { isSession } from "react-router";

test("sets the session instance in context", async () => {
  let request = new Request("https://example.com");
  let params = {};
  let context = new unstable_RouterContextProvider();

  let next = mock().mockImplementation(() => new Response(null));

  let response = await sessionMiddleware({ request, params, context }, next);

  let session = getSession(context);

  expect(isSession(session)).toBeTrue();
});

Verifying the next Function is Called

Middleware should always call the next function. Let's test that:

test("calls the next function", async () => {
  let request = new Request("https://example.com");
  let params = {};
  let context = new unstable_RouterContextProvider();

  let next = mock().mockImplementation(() => new Response(null));

  let response = await sessionMiddleware({ request, params, context }, next);

  expect(next).toHaveBeenCalledTimes(1);
});

Checking if the Session is Committed

Since the middleware commits the session, we need to verify that the response includes the Set-Cookie header.

test("commits the session in the response headers", async () => {
  let request = new Request("https://example.com");
  let params = {};
  let context = new unstable_RouterContextProvider();

  let next = mock().mockImplementation(() => new Response(null));

  let response = await sessionMiddleware({ request, params, context }, next);

  expect(response.headers.get("Set-Cookie")).toBeString();
});

Here, we're only confirming that Set-Cookie is set, not whether the session data is correct. However, it's a solid starting point.

Running the Tests

You can run the tests using:

bun test

For the full implementation of the middleware and test cases, check the GitHub repository of Remix Utils.