seam logo

Running Cloudflare Worker Tests with Ava

2023-01-21

Hold on, are you just looking for a fully working Cloudflare Worker with Ava tests? Check out the repo that contains all the code from this post, if you’re interested on how to change an existing project or understand how it all fits together, read on!

At Seam, we use Ava for almost anything. Ava is a very simple testing system with a lot of power, in particular we love how you can instrument tests using t.teardown and use shared workers to pool and distribute shared resources such as a postgres database.

Wrangler, the main cli tool for writing Cloudflare Workers, uses vitest by default. Vitest is cool, but is more focused on frontend web application development rather than NodeJS backend development.

Let’s start by initializing a wrangler project with wrangler init, paying special care to “Just Say No” to vitest.

If everything went well, you should be able to run wrangler dev -l to run a local wrangler instance and curl to get your hello world.

We can now set up Ava to test our Cloudflare Worker, first you’ll need to set up an ava.config.js that can run Typescript.

1// ava.config.js
2module.exports = {
3  files: ["tests/**/*.test.ts"],
4  extensions: ["ts"],
5  require: ["esbuild-register"]
6}

Make sure to get esbuild set up, esbuild is our transpiler for Typescript, you can install it and Ava with npm add --save-dev ava esbuild esbuild-runner.

Now that Ava is set up, let’s create a fixture that starts a Cloudflare Worker server on a unique port (so our tests can run in parallel). We’re also going to create an Axios instance that’s all hooked up to make requests to our server. This is optional but we find it makes test code super clean.

We wrote a module called ava-wrangler-fixture that makes it so you don’t have to do this. To use ava-wrangler-fixture, just npm add --save-dev ava-wrangler-fixture and do…

import { getTestServer } from "ava-wrangler-fixture"

1// tests/fixtures/get-test-server.ts
2
3import { unstable_dev } from "wrangler"
4import defaultAxios from "axios"
5import { ExecutionContext } from "ava"
6
7export const getTestServer = async (t: ExecutionContext<unknown>) => {
8  const wrangler = await unstable_dev(
9    "src/index.ts",
10    {},
11    {
12      disableExperimentalWarning: true,
13      testMode: true,
14    }
15  )
16
17  const server_url = `http://127.0.0.1:${wrangler.port}`
18
19  // when the test is done, stop the wrangler server
20  t.teardown(() => {
21    wrangler.stop()
22  })
23
24  const axios = defaultAxios.create({ baseURL: server_url })
25
26  return { wrangler, server_url, axios }
27}

The code above uses Wrangler’s builtin unstable_dev server to start a wrangler server on a random port, we then return the server_url and an axios instance to interface that server. Pretty easy!

It’s common to also throw other fixturing into your get-test-server.ts file, for example, maybe you want to set up your keystore with some default values, or seed your d1 database. This is all possible (though a bit tricky in Wrangler’s current state); we’ll have more posts on it soon!

Let’s write a test!

Ok everything is now set up for us to write our first server test!

1// index.test.ts
2
3import test from "ava"
4import { getTestServer } from "./fixtures/get-test-server"
5
6test(`GET / should return "Hello World"`, async (t) => {
7  const { axios } = await getTestServer(t)
8
9  const res = await axios.get("/")
10
11  t.is(res.data, "Hello World!")
12})

Now run your test with npx ava ./tests/index.test.ts, if everything worked you should see:

To add more tests, just drop more *.test.ts files in your test directory. Your tests will all run in parallel if you run npx ava, for good measure, we can add this to our package.json like so:

Now you can run all your tests with npm run test. This is also a pattern that a lot of developers will recognize, which should make contributing to your project much easier!

Wrapping Up

Ava is an awesome testing tool, combining it with Cloudflare Workers gives you a really nice, fast development environment. Check out a working sample of all the code in this post here, and check out more awesome open-source projects from Seam!