Writing Tests
The extension provides in-editor tools that make writing tests faster: code snippets, CodeLens buttons, and full support for data-driven patterns.
This page is about the extension’s raw test() authoring surface. For durable
API behavior that should become specs, docs, reports, and agent context, start
with HTTP Contracts and Workflows.
Use raw test() when you need the full TypeScript escape hatch.
Snippets
Type a prefix in any .ts or .js file and press Tab to expand.
| Prefix | What it generates |
|---|---|
gb-scratch / gbs | Complete scratch test (import + HTTP + assertions) |
gb-t / gbt | Simple test |
gb-ti / gbti | Simple test with import |
gb-tm / gbtm | Test with meta (tags, name) |
gb-flow / gbf | Builder-style multi-step test() |
gb-each / gbe | test.each inline data-driven |
gb-each-csv / gbec | test.each + fromCsv |
gb-each-yaml / gbey | test.each + fromYaml builder |
gb-pick / gbp | test.pick named examples |
gb-pick-json / gbpj | test.pick + JSON file |
gb-pick-dir / gbpd | test.pick + fromDir.merge |
gb-config / gbc | configure() shared config |
gb-plugin / gbpl | defineClientFactory + configure plugins |
gb-i / gbi | import { test } from "@glubean/sdk" |
gb-id / gbid | Import test + data loaders |
gb-ic / gbic | Import configure |
gb-ip / gbip | Import configure + defineClientFactory |
gb-expect / gbx | ctx.expect(...).orFail() assertion guard |
gb-assert / gba | ctx.assert() structured assertion |
gb-flow is a legacy-friendly snippet name for a builder-style test() chain.
It is different from the SDK workflow() API, which composes contract cases as
durable business behavior.
CodeLens: test.pick buttons
When you use test.pick(), CodeLens buttons appear above the definition letting you run specific examples.
- 5 or fewer examples — each key appears as a separate button:
Run "normal",Run "edge", etc. - More than 5 — a single
Pick example... (N)button opens a QuickPick list with multi-select. The list includes “Run All” at the top. - Always available —
Run (random)runs without specifying a key, letting the SDK pick one.
Keys are resolved from inline object literals, JSON file imports, or fromDir.merge("./data/") calls. If resolution fails, you still get the Run (random) button.
CodeLens: Open data
Data loader calls (fromCsv(), fromYaml(), fromDir(), JSON imports) show an Open data button above the call.
- Single file — opens the data file in a bottom split, keeping focus on your test code.
- Directory — shows
Open data (N files)with the file count; clicking opens the first file. - Invalid path — shows a warning instead.
The button auto-refreshes when files in **/data/** change.
CodeLens: Results (N)
Each test definition shows a Results (N) button with the count of saved history results. Click to open the latest result in the Result Viewer. If no results exist yet, clicking prompts you to run the test first.
Data-driven patterns
These snippets focus on extension behavior. In real project files, create a
shared client with configure() and import it into the test file.
test.each — array of cases
Run the same test logic with different inputs. Each row appears as a child node in Test Explorer.
import { configure, test } from "@glubean/sdk";
const { http: api } = configure({
http: { prefixUrl: "{{BASE_URL}}" },
});
export const statusCodes = test.each([
{ id: 1, expected: 200 },
{ id: 999, expected: 404 },
])("get-item-$id", async (ctx, { id, expected }) => {
const res = await api.get(`items/${id}`);
ctx.expect(res).toHaveStatus(expected);
});Data can come from files too: test.each(fromCsv("./data/items.csv")).
test.pick — named examples (key-value)
Why: Separate test logic from test data. Write the verification once, let the team add cases by editing data files — no code changes needed. Each case gets its own CodeLens button so you can run and debug one at a time.
const cases = await fromDir.merge<UserCase>("./data/users/");
export const getUser = test.pick(cases)(
{ id: "get-user-$_pick", name: "Get user: $_pick" },
async (ctx, { id, expected }) => {
const res = await api.get(`users/${id}`);
ctx.expect(res).toHaveStatus(expected);
},
);Data files can be JSON or YAML (with comments). Each team member can add a *.local.json file with personal test cases that won’t conflict with the shared baseline.
For the full data-driven guide — including fromDir.merge, fromCsv,
fromYaml, structured data patterns, personal *.local.json files, and merge
ordering — see Data-Driven and
Local Data.
test.extend aliases
If your team creates custom test functions with test.extend(), the extension discovers them automatically:
// config/browser-test.ts
export const browserTest = test.extend({ /* plugins */ });
// tests/login.test.ts
export const login = browserTest("login", async (ctx) => { ... });Tests defined with browserTest are discovered and appear in Test Explorer alongside regular tests.
Project structure
explore/ vs tests/
| Directory | Purpose | CI behavior |
|---|---|---|
explore/ | Quick experiments, AI-generated drafts, bug reproduction | Requires --profile explore or explicit path |
tests/ | Committed regression suite | Default glubean run (the local profile) |
Test Explorer groups them separately when both directories exist. The default
glubean init template creates tests/ and contracts/; add explore/ plus
an explore profile when your team wants a committed draft lane.
Contracts and workflows are still the main source-of-truth layer. The extension
helps most with local test() discovery, data navigation, and result
inspection; use the CLI or MCP for the full project run.
When a test is stable, move it from explore/ to tests/. That is the only
change needed for the default local/CI profile to pick it up.
config/ and data/
config/— sharedconfigure.tsfor base URL, auth, pluginsdata/— JSON, YAML, CSV files for data-driven tests
What’s next?
- Running Tests — All the ways to execute tests
- Result Viewer — Inspect traces, assertions, and events
- Generate with AI — Let AI write the first draft