Testing React Router: Part 1

I’m a believer.

I have joined the ranks of those who see the URL as the One Almighty Source of Truth. In this vein, we use React Router to determine which components and data to show.

But even though I’m a believer, I’m also a cynic. Let’s put our faith in the URL to the test.

Why should we test our routing?

With the URL as the source of truth, we can expect the view to significantly change depending on the URL path or query params. Shouldn’t we have tests that ensure the correct components show up? Especially if you have complicated routing: nested routes, query params, optional routes, etc.

Initial exploration

The folks who wrote React Router wrote a set of tests that verify whether a matching route can be found for a given path.

For example, here’s a test that verifies that a path of /users will yield the correct set of matching routes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
routes = [
 RootRoute = {
    childRoutes: [
      UsersRoute = {
        path: 'users',
        indexRoute: (UsersIndexRoute = {}),
        childRoutes: [
          UserRoute = {
            path: ':userID',
...
]

describe('when the location matches an index route', function () {
    it('matches the correct routes', function (done) {
        matchRoutes(routes, createLocation('/users'), function (error, match) {
          expect(match).toExist()
          expect(match.routes).toEqual([ RootRoute, UsersRoute, UsersIndexRoute ])
          done()
        })
    })
    ...
See the full React Router test here.

I couldn’t find much out there on the Interwebz about testing React routes. So, my first step was to see if I could just get React Router’s “matching routes” test suite working for an existing app that has its own simple front-end routing.

It was a bit of a struggle.

The most important part was to convert the routes in routes.js to JSON instead of JSX. This is because React Router’s tests use a matchRoutes testing tool that rely on routes having a certain structure. Their test suite recreates a complicated nest of test routes inside the test itself. If I were writing my own routes test, it would be pretty annoying to have to update a handmade list of routes in the test every time the app’s routes changed. Writing routes as JSON will allow me to simply import my routes from the routes file into the test.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// in routes.js

export const Routes = [
    {
        component: App,
        onEnter: trackUserId,
        childRoutes: [
            {
                path: "/",
                component: "LandingPageComponent"
            },
            {
                path: "/buy",
                component: "BuyComponent"
            },
        ]
    }
]
1
2
3
4
5
6
7
// in __test__/routes_spec.js

import { Routes } from '../routes'

...

matchRoutes(Routes, createLocation('/buy'), function(error, match) { ... })

What to test?

I don’t want my routes test to test React Router’s logic – it’s not my place to make sure that React Router knows how to find a matching route from a given path. I want to test the logic that I’m creating for the app. So, what I do want to test is that all the right information is displayed on the page if I hit the “/” path versus the “/buy” path.

For example, I could check that loading “/buy” adds the SearchWidget and ShoppingCartWidget to the page, and that hitting the root “/” shows the FullPageSplashComponent.

But HOW to do that? Stay tuned, more to come.