Test Automation with Deno
work in progressAs previously mentioned, I’ve cautiously come to appreciate Deno’s development tooling. This sometimes includes using its test runner, mostly because it’s both simple and readily available while adhering to common conventions.
Nevertheless, there are a few idiosyncratic details and conventions worth pointing out – if only to reduce friction and lower the barrier to pursuing test-driven development from the get-go. In fact, for our purposes here, I’ll recklessly expand the definition of “testing” to include static verification: formatting and linting for stylistic consistency as well as optional type checking – thus establishing some basic infrastructure for any JavaScript project, regardless of size or ambition.
Tasks
{
"tasks": {
"vet": "deno lint && deno fmt --check",
"verify": "deno check --import-map ./deno.json ./src/index.js"
},
// …
}
These two designations aren’t perfect, but were chosen to avoid overloading terms already reserved for Deno standard commands.
Note that deno check
requires an entry point; unfortunately, we can’t just
point it at our src
directory.
If we require custom TypeScript configuration, e.g. to
avoid TypeScript syntax, we
can pass that to deno check
via --config ./tsconfig.json
.
My shell history typically includes this combo:
$ deno task vet && deno task verify && deno test --parallel
Co-Located Tests
I’ve accepted the value of reduced
distance
by having test modules reside alongside the respective implementation (e.g.
src/util.js
and src/util.test.js
).
Nevertheless, for overarching scenarios, a separate top-level test
directory
can be a useful addition.
Behavior-Driven Development (BDD)
While I’m not a fan of BDD nomenclature and grammar, Deno’s default approach lacks familiar hooks for setup and teardown – so I typically end up using BDD style anyway.
Import Maps
For consistency, we want to define standard-library URLs in one central place, ensuring we’re using the same version throughout. While we might create a proxy module which just selectively re-exports the respective functionality, that quickly becomes annoying and a bit of a maintenance burden.
Deno’s support for import maps makes this pretty simple though:
{
"imports": {
"$deno/": "https://deno.land/std@0.224.0/"
},
// …
}
import { describe, it } from "$deno/testing/bdd.ts";
import {
assertEquals as assertDeep,
assertStrictEquals as assertSame,
} from "$deno/assert/mod.ts";
describe("calculator", () => {
it("supports numbers", () => {
assertSame(5 * 5, 25);
assertDeep([3, 2, 1].toReversed(), [1, 2, 3]);
});
});
deno test
will pick this up automatically, substituting $deno/
within
import
statements accordingly. However, deno check
currently requires
explicitly adding --import-map ./deno.json
and emits spurious warnings about
supposedly invalid top-level keys within that file. 🤷
Note that editors and other tooling might not be aware of import maps within
deno.json
by default.