Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 64 additions & 19 deletions examples/tutorials/bdd.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
last_modified: 2026-02-03
last_modified: 2026-05-13
title: "Behavior-Driven Development (BDD)"
description: "Implementing Behavior-Driven Development with Deno's Standard Library's BDD module. Create readable, well organised tests with effective assertions."
url: /examples/bdd_tutorial/
Expand Down Expand Up @@ -262,35 +262,80 @@ These hooks are useful for:
- Setting up and tearing down database connections
- Creating and cleaning up shared resources

Here's an example of how you might use `beforeAll` and `afterAll`:

```ts
describe("Database operations", () => {
let db: Database;

beforeAll(async () => {
// Connect to the database once before all tests
db = await Database.connect(TEST_CONNECTION_STRING);
await db.migrate();
Here's a runnable example that tests a small HTTP service. Spinning a server up
and down for every single test would be wasteful — and slow, once you have a
handful of cases — so the server starts once in `beforeAll` and shuts down once
in `afterAll`:

```ts title="user_api_test.ts"
import { afterAll, beforeAll, describe, it } from "jsr:@std/testing/bdd";
import { assertEquals } from "jsr:@std/assert";

describe("User API", () => {
let server: Deno.HttpServer;
let baseUrl: string;

beforeAll(() => {
// Start the test server once before any test runs. Port 0 asks the
// OS for a free port, so parallel test files don't collide.
server = Deno.serve({ port: 0, onListen() {} }, (req) => {
const { pathname } = new URL(req.url);
if (pathname === "/users/1") {
return Response.json({ id: 1, name: "Ada" });
}
return new Response("Not Found", { status: 404 });
});
const { port } = server.addr as Deno.NetAddr;
baseUrl = `http://localhost:${port}`;
});

afterAll(async () => {
// Disconnect after all tests are complete
await db.close();
// Shut the server down once, after every test has run. Without
// this, `deno test` would report a resource leak.
await server.shutdown();
});

it("should insert a record", async () => {
const result = await db.insert({ name: "Test" });
assertEquals(result.success, true);
it("returns a known user", async () => {
const res = await fetch(`${baseUrl}/users/1`);
assertEquals(res.status, 200);
assertEquals(await res.json(), { id: 1, name: "Ada" });
});

it("should retrieve a record", async () => {
const record = await db.findById(1);
assertEquals(record.name, "Test");
it("returns 404 for unknown users", async () => {
const res = await fetch(`${baseUrl}/users/999`);
assertEquals(res.status, 404);
await res.body?.cancel();
});
});
```

The server binds to a local port, so the test needs network permission to reach
itself:

```sh
deno test --allow-net=0.0.0.0,localhost user_api_test.ts
```

```console
running 1 test from ./user_api_test.ts
User API ...
returns a known user ... ok (4ms)
returns 404 for unknown users ... ok (1ms)
User API ... ok (6ms)

ok | 1 passed (2 steps) | 0 failed (11ms)
```

:::caution

Anything you create in `beforeAll` is shared by every test in the block. If one
test mutates that shared state — adds a row, changes a field, leaves a handler
registered — the next test starts from the changed state, not the original. When
tests need a clean slate, set up the cheap, per-test state in `beforeEach` and
leave only the expensive, read-mostly setup in `beforeAll`.

:::

## Gherkin vs. JavaScript-style BDD

If you're familiar with Cucumber or other BDD frameworks, you might be expecting
Expand Down
67 changes: 59 additions & 8 deletions runtime/getting_started/first_project.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
last_modified: 2025-03-10
last_modified: 2026-05-14
title: Making a Deno project
description: "Step-by-step guide to creating your first Deno project. Learn how to initialize a project, understand the basic file structure, run TypeScript code, and execute tests using Deno's built-in test runner."
oldUrl: /runtime/manual/getting_started/first_steps/
Expand Down Expand Up @@ -35,21 +35,72 @@ my_project

A `deno.json` file is created to
[configure your project](/runtime/fundamentals/configuration/), and two
TypeScript files are created; `main.ts` and `main_test.ts`. The `main.ts` file
is where you'll write your application code, on initial creation it will contain
a simple program which adds two numbers together. The `main_test.ts` file is
where you can write tests, initially it will contain a test for your addition
program.
TypeScript files are created: `main.ts` and `main_test.ts`. Let's look at what
`deno init` put in each of them, so the rest of this guide makes sense.

`main.ts` exports an `add` function and, when run as the entry point, prints the
result of calling it. The `import.meta.main` guard means this top-level call
only runs when you execute the file directly — not when another module imports
the `add` function from it:

```ts title="main.ts"
export function add(a: number, b: number): number {
return a + b;
}

// Learn more at https://docs.deno.com/runtime/manual/examples/module_metadata#concepts
if (import.meta.main) {
console.log("Add 2 + 3 =", add(2, 3));
}
```

`main_test.ts` imports the `add` function and asserts it returns the expected
result. `Deno.test` is built into the runtime, so there's no test framework to
install:

```ts title="main_test.ts"
import { assertEquals } from "@std/assert";
import { add } from "./main.ts";

Deno.test(function addTest() {
assertEquals(add(2, 3), 5);
});
```

`deno.json` is the project's
[configuration file](/runtime/fundamentals/configuration/). The generated one
declares a `dev` task that re-runs `main.ts` whenever a file changes, and pins
`@std/assert` from [JSR](https://jsr.io/@std/assert) so the test above can
resolve it:

```json title="deno.json"
{
"tasks": {
"dev": "deno run --watch main.ts"
},
"imports": {
"@std/assert": "jsr:@std/assert@1"
}
}
```

## Run your project

You can run this program with the following command:
`cd` into the new directory and run `main.ts` with `deno run`:

```bash
$ deno main.ts
$ cd my_project
$ deno run main.ts
Add 2 + 3 = 5
```

For an iterative workflow, use the generated `dev` task instead. It runs the
program in watch mode, restarting it every time you save a file:

```bash
deno task dev
```

## Run your tests

Deno has a [built in test runner](/runtime/fundamentals/testing/). You can write
Expand Down
Loading