•4 min read•Tutorial
Testing in Deno: Built-in Tools and Best Practices
Master Deno's built-in testing framework. Learn how to write effective tests without external dependencies
Testing in Deno
Deno includes a powerful testing framework out of the box. No need for Jest, Mocha, or other test runners. Here's how to test effectively in Deno.
Basic Test Structure
// math.ts
export function add(a: number, b: number): number {
return a + b;
}
export function multiply(a: number, b: number): number {
return a * b;
}
// math.test.ts
import { assertEquals } from "@std/assert";
import { add, multiply } from "./math.ts";
Deno.test("add() adds two numbers", () => {
assertEquals(add(2, 3), 5);
assertEquals(add(-1, 1), 0);
assertEquals(add(0, 0), 0);
});
Deno.test("multiply() multiplies two numbers", () => {
assertEquals(multiply(2, 3), 6);
assertEquals(multiply(-2, 3), -6);
assertEquals(multiply(0, 5), 0);
});
Run tests:
deno test
Assertions
import {
assertEquals,
assertNotEquals,
assertExists,
assertStrictEquals,
assertThrows,
assertRejects,
} from "@std/assert";
Deno.test("assertions", () => {
// Equality
assertEquals(1 + 1, 2);
assertNotEquals(1, 2);
// Existence
assertExists("hello");
// Strict equality (checks reference)
const obj = { a: 1 };
assertStrictEquals(obj, obj);
// Exceptions
assertThrows(() => {
throw new Error("error");
});
});
Deno.test("async assertions", async () => {
// Async rejection
await assertRejects(async () => {
throw new Error("async error");
});
});
Testing Async Code
import { assertEquals } from "@std/assert";
async function fetchUser(id: number) {
const response = await fetch(`https://api.example.com/users/${id}`);
return response.json();
}
Deno.test("fetchUser() returns user data", async () => {
const user = await fetchUser(1);
assertEquals(user.id, 1);
assertExists(user.name);
});
Mocking
import { assertEquals } from "@std/assert";
import { stub } from "@std/testing/mock";
Deno.test("mocking example", () => {
const obj = {
method: () => "original",
};
using methodStub = stub(obj, "method", () => "mocked");
assertEquals(obj.method(), "mocked");
});
// After test, stub is automatically cleaned up
Testing HTTP Servers
import { Application, Router } from "oak";
import { assertEquals } from "@std/assert";
function createApp() {
const app = new Application();
const router = new Router();
router.get("/", (ctx) => {
ctx.response.body = { message: "Hello" };
});
app.use(router.routes());
return app;
}
Deno.test("GET / returns greeting", async () => {
const app = createApp();
// Start server
const controller = new AbortController();
const serverPromise = app.listen({
port: 8001,
signal: controller.signal,
});
// Make request
const response = await fetch("http://localhost:8001/");
const data = await response.json();
assertEquals(data.message, "Hello");
// Cleanup
controller.abort();
await serverPromise;
});
Testing with Permissions
Deno.test({
name: "test requiring read permission",
permissions: { read: true },
fn: async () => {
const data = await Deno.readTextFile("./test.txt");
assertExists(data);
},
});
Deno.test({
name: "test requiring specific permissions",
permissions: {
read: ["."],
net: ["api.example.com"],
},
fn: async () => {
// Test code
},
});
Test Organization
// Group related tests
Deno.test("User operations", async (t) => {
await t.step("create user", () => {
// Test create
});
await t.step("update user", () => {
// Test update
});
await t.step("delete user", () => {
// Test delete
});
});
Test Coverage
# Run with coverage
deno test --coverage=coverage
# Generate coverage report
deno coverage coverage --lcov --output=coverage.lcov
# View in browser (with lcov tools)
genhtml coverage.lcov --output-directory coverage-html
Benchmarking
Deno.bench("add() performance", () => {
add(1, 2);
});
Deno.bench({
name: "multiply() performance",
fn: () => {
multiply(5, 10);
},
});
Run benchmarks:
deno bench
Best Practices
- Name tests descriptively
// ❌ Bad
Deno.test("test1", () => {});
// ✅ Good
Deno.test("calculateTotal() returns sum of prices with tax", () => {});
- Test one thing per test
// ✅ Good
Deno.test("validates email format", () => {
assertEquals(isValidEmail("test@example.com"), true);
});
Deno.test("rejects invalid email", () => {
assertEquals(isValidEmail("invalid"), false);
});
- Use setup/teardown
Deno.test("database operations", async (t) => {
// Setup
const db = await setupTestDatabase();
await t.step("insert record", async () => {
// Test
});
await t.step("query record", async () => {
// Test
});
// Teardown
await db.close();
});
Conclusion
Deno's built-in testing is powerful:
- No external dependencies
- Simple API
- Built-in assertions
- Coverage reporting
- Benchmarking included
Write tests early, test often, ship confidently!
👨💻
Jordan Patel
Web Developer & Technology Enthusiast