Back to Blog
4 min readTutorial

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

  1. Name tests descriptively
// ❌ Bad
Deno.test("test1", () => {});

// ✅ Good
Deno.test("calculateTotal() returns sum of prices with tax", () => {});
  1. 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);
});
  1. 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