Test sanitizers
The deno test runner offers several sanitizers that catch tests misbehaving in ways assertions don't see: leaking async operations, unclosed resources, and unexpected process exits.
Resource sanitizer
The resource sanitizer ensures that all I/O resources created during a test are closed, to prevent leaks.
I/O resources are things like Deno.FsFile handles, network connections, fetch bodies, timers, and other resources that are not automatically garbage collected.
You should always close resources when you are done with them. For example, to close a file:
const file = await Deno.open("hello.txt");
// Do something with the file
file.close(); // <- Always close the file when you are done with it
To close a network connection:
const conn = await Deno.connect({ hostname: "example.com", port: 80 });
// Do something with the connection
conn.close(); // <- Always close the connection when you are done with it
To close a fetch body:
const response = await fetch("https://example.com");
// Do something with the response
await response.body?.cancel(); // <- Always cancel the body when you are done with it, if you didn't consume it otherwise
As of Deno 2.8 this sanitizer is off by default. Opt in with sanitizeResources: true, or with one of the global mechanisms described in Enabling sanitizers globally.
Deno.test({
name: "no leaks allowed",
async fn() {
using file = await Deno.open("hello.txt");
// ...
},
sanitizeResources: true,
});
Async operation sanitizer
The async operation sanitizer ensures that all async operations started in a test are completed before the test ends. This is important because if an async operation is not awaited, the test will end before the operation is completed, and the test will be marked as successful even if the operation may have actually failed.
You should always await all async operations in your tests. For example:
Deno.test({
name: "async operation test",
async fn() {
await new Promise((resolve) => setTimeout(resolve, 1000));
},
});
As of Deno 2.8 this sanitizer is off by default. Opt in with sanitizeOps: true, or with one of the global mechanisms described below.
Deno.test({
name: "no leaked ops allowed",
async fn() {
await someAsyncWork();
},
sanitizeOps: true,
});
Enabling sanitizers globally
If you want the pre-2.8 behavior (resource and op sanitizers on for every test), you can re-enable them at any of five scopes. Higher-precedence settings override lower ones.
-
Per-test (highest precedence):
Deno.test({ name: "strict", sanitizeOps: true, sanitizeResources: true, fn() {/* … */}, }); -
Per-module with
Deno.test.sanitizer():Deno.test.sanitizer({ ops: true, resources: true }); Deno.test("uses module-level sanitizers", () => {/* … */}); -
CLI flags:
--sanitize-opsand--sanitize-resources. -
Environment variables:
DENO_TEST_SANITIZE_OPS=1andDENO_TEST_SANITIZE_RESOURCES=1. -
deno.json(lowest precedence):{ "test": { "sanitizeOps": true, "sanitizeResources": true } }
Exit sanitizer
The exit sanitizer ensures that tested code doesn’t call Deno.exit(), which could signal a false test success.
This sanitizer is enabled by default, but can be disabled with sanitizeExit: false.
Deno.test({
name: "false success",
fn() {
Deno.exit(0);
},
sanitizeExit: false,
});
// This test never runs, because the process exits during "false success" test
Deno.test({
name: "failing test",
fn() {
throw new Error("this test fails");
},
});
Last updated on
© 2018–2025 the Deno authors
Licensed under the MIT License.
https://docs.deno.com/runtime/test/sanitizers