🎉 Introducing Edgio v6 which supports Node.js v16. Learn how to upgrade. 🎉
Edgio
Edgio

EdgeJS Unit Testing

Edgio provides an EdgeJS testing utility to facilitate in unit-testing your Edgio router logic, helping to mock and run your routes in a test environment just as they would be handled live on your production site.

Configuration

If not already configured for your project, we require using jest for unit-testing your Edgio router logic. It is also recommended to use nock for mocking HTTP requests your application may make.

Bash
1npm i -D jest babel-jest nock @babel/core @babel/preset-env @babel/preset-typescript

Create babel.config.js in the root of your project:

JavaScript
1module.exports = {
2 presets: [['@babel/preset-env', { targets: { node: 'current' } }], '@babel/preset-typescript'],
3}

You can find more information around configuring Jest with their Getting Started guide.

Testing

At the top of each unit test, import the following:

JavaScript
1// reference to your Edgio router
2import routes from '../routes'
3
4// router helper functions
5import { runRoute, appHost, backendHost, staticHost } from '@edgio/core/test-utils'
6
7// http mocking library
8import nock from 'nock'

Assertions

By default, importing @edgio/core/test-utils will automatically add the following assertions to Jest’s expect function:

  • toHaveHeader(name, [value])
  • toHaveBody(string|RegExp)
  • toBeCachedByTheEdge()
  • toBeCachedByTheEdgeFor(seconds)
  • toBeCachedByTheBrowser()
  • toBeCachedByTheBrowserFor(seconds)
  • toBeCachedByTheServiceWorker()
  • toBeCachedByTheServiceWorkerFor(seconds)
  • toBeServedStale()
  • toBeServedStaleFor(seconds)

Route Testing

To test a specific route handler, import runRoute from @edgio/core/testing-utils. This function accepts your router instance, and the path of the route to run.

JavaScript
1import { runRoute } from '@edgio/core/test-utils'
2import router from '../src/routes'
3
4...
5
6it('should run the /foo route', () => {
7 const handler = jest.fn()
8 router.match('/foo', handler)
9
10 const response = await runRoute(router, '/foo')
11
12 expect(handler).toBeCalled()
13 expect(response.request.path).toEqual('/foo')
14})

For extended route testing, you can import createRouteMock to set the path, method, headers, or body of the request:

JavaScript
1import { runRoute, createRouteMock } from '@edgio/core/test-utils'
2import router from '../src/routes'
3
4...
5
6it('should run the /search route', () => {
7 const handler = jest.fn()
8 router.post('/search', handler)
9
10 const response = await runRoute(router, createRouteMock({
11 path: '/search',
12 method: 'POST',
13 body: '{"query": "foo"}'
14 }))
15
16 expect(handler).toBeCalled()
17 expect(response.request.path).toEqual('/search')
18})

Host Mocking

If the route being tested has an upstream request or serves a static file, you will want to mock these requests and responses. This decouples your unit tests from your upstream and application logic, focusing just on how the router responds to the given request. For this, we use nock along with appHost, backendHost, and staticHost imported from @edgio/core/testing-utils.

These functions reference the backend entries defined in your edgio.config.js file.

Mocking appHost Example

If your route sends a response from your application, such as renderWithApp or using NextRoutes, NuxtRoutes, etc., use appHost() to reference the host and port for mocking the request and response.

JavaScript
1// src/router.ts
2...
3export default new Router()
4 .get('/collections/:path*', ({ cache }) => {
5 cache({
6 edge: {
7 maxAgeSeconds: 60 * 60
8 }
9 })
10 })
11 .fallback(({ renderWithApp }) => renderWithApp())

…

JavaScript
1// routes.test.ts
2it('should cache the collections page at the edge for 1 hour', async () => {
3 nock(`http://${appHost()}`)
4 .get('/collections/c1')
5 .reply(200, '')
6
7 const result = await runRoute(routes, '/collections/c1')
8 expect(result).toBeCachedByTheEdgeFor(60 * 60)
9})

Mocking backendHost Example

Routes that use proxy to fetch from a backend can be mocked using backendHost(name), where name is the key used for the backend defined in edgio.config.js.

JavaScript
1// edgio.config.js
2module.exports = {
3 routes: './src/routes.ts',
4 backends: {
5 origin: {
6 domainOrIp: 'api.example.com',
7 hostHeader: 'api.example.com',
8 },
9 },
10}
11
12// src/routes.ts
13export default new Router().get('/collections/:path*', ({ cache, proxy }) => {
14 cache({
15 edge: {
16 maxAgeSeconds: 60 * 60,
17 },
18 })
19 proxy('origin')
20})

…

JavaScript
1// routes.test.ts
2it('should cache the collections page at the edge for 1 hour', async () => {
3 nock(`http://${backendHost('origin')}`)
4 .get('/collections/c1')
5 .reply(200, '')
6
7 const result = await runRoute(routes, '/collections/c1')
8 expect(result).toBeCachedByTheEdgeFor(60 * 60)
9})

Mocking staticHost Example

For serving static assets, mock the asset host using staticHost().

JavaScript
1// src/routes.ts
2export default new Router().get('/icons/:path*', ({ cache, serveStatic }) => {
3 cache({
4 edge: {
5 maxAgeSeconds: 60 * 60,
6 },
7 })
8 serveStatic('assets/icons/:path*')
9})

…

JavaScript
1// routes.test.ts
2it('should cache the static asset at the edge for 1 hour', async () => {
3 nock(`http://${staticHost()}`)
4 .get('/assets/icons/user.png')
5 .reply(200, '')
6
7 const result = await runRoute(routes, '/icons/user.png')
8 expect(result).toBeCachedByTheEdgeFor(60 * 60)
9})

Example Tests

For a more detailed example of EdgeJS unit testing, check out our Edgio Templates for a full implementation.