encore-testing — Claude Code skill: test Encore.ts apps
Guides your agent through writing and running automated tests for Encore.ts with encore test and vitest or jest. Useful when an endpoint or service needs coverage without hand-rolling test databases.
npx skills add encoredev/skills --skill encore-testing -a claude-code
npx skills add encoredev/skills --skill testing -a claude-codeWhat encore-testing does
The skill covers testing Encore.ts code with standard TypeScript tooling. Vitest is the recommended setup: npm install -D vitest, a test script in package.json, then encore test. Running through encore test is the preferred path because it provisions test databases and keeps infrastructure isolated per test.
Examples walk through the common cases: calling a handler directly with request parameters, asserting on APIError and its not_found code, cleaning tables in beforeEach, service-to-service calls, publishing events, and cron jobs (test the function, not the schedule). Imports from ~encore need a vite.config.ts with an alias pointing at ./encore.gen.
Auth is where it gets fiddly. Mocking getAuthData only affects code in the test process that reads it directly. Calls through ~encore/clients go via the Encore runtime, which never sees the mock, so an auth: true endpoint fails with an error about missing auth. Pass the auth data explicitly instead. In VS Code, file-level parallelism is worth turning off to avoid port conflicts.
What it helps with
- write a first test for an Encore.ts endpoint
- set up
encore testwith vitest - spin up an isolated database per test
- assert on APIError codes and messages
- test an
auth: trueendpoint without a real login
Example prompts for the agent
- write a vitest test for the getUser endpoint with an id parameter
- set up encore test and clean the users table in beforeEach
- test the auth: true endpoint by passing authData explicitly
How to install encore-testing
1Skills CLI (Vercel)
npx skills add encoredev/skills --skill encore-testing -a claude-codenpx skills add encoredev/skills --skill testing -a claude-code2Manually: put the files in place
~/.claude/skills/testing/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/testing/
1Skills CLI (Vercel)
npx skills add encoredev/skills --skill testing -a codex2Manually: put the files in place
.agents/skills/testing/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add encoredev/skills --skill testing -a cursor2Manually: put the files in place
.cursor/skills/testing/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install encoredev/skills testing2Manually: put the files in place
.github/skills/testing/SKILL.md1Skills CLI (Vercel)
npx skills add encoredev/skills --skill testing -a gemini-cli2Platform CLI
gemini skills install https://github.com/encoredev/skills --path encore/testingManually: put the files in place
.devin/skills/testing/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add encoredev/skills --skill testing -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about encore-testing
How do I install encore-testing in Claude?
Use the skills CLI: npx skills add encoredev/skills --skill encore-testing -a claude-code. The skill file lands at ~/.claude/skills/testing/SKILL.md.
What does encore-testing do?
It tells the agent how to write and run Encore.ts tests: calling handlers directly, isolated test databases, mocking external services, and checking errors and auth.
Why does mocking getAuthData fail through ~encore/clients?
Those calls go through the Encore runtime, which does not see the mock, so an auth: true endpoint fails. Pass auth data explicitly: await user.follow({ username: "jake" }, { authData: { userID: "123" } }).
Source text
SKILL.md · Apache-2.0
---
name: encore-testing
description: Write or run automated tests for Encore.ts code with `encore test` and vitest/jest. Covers isolated per-test databases, calling handlers directly, and `describe`/`it`/`expect`.
when_to_use: >-
User wants to add/write/fix a test, write a vitest or jest spec, test an endpoint or service, set up `encore test`, configure isolated test databases, write `beforeEach`/`afterEach` for db cleanup, mock external dependencies, or assert on API request/response behaviour. Trigger phrases: "write a test", "write a vitest test", "add tests for", "vitest", "jest", "encore test", "test the endpoint", "test the service", "integration test", "isolated database".
---
# Testing Encore.ts Applications
## Instructions
Encore.ts uses standard TypeScript testing tools. The recommended setup is Vitest.
### Setup Vitest
```bash
npm install -D vitest
```
Add to `package.json`:
```json
{
"scripts": {
"test": "vitest"
}
}
```
### Test an API Endpoint
```typescript
// api.test.ts
import { describe, it, expect } from "vitest";
import { hello } from "./api";
describe("hello endpoint", () => {
it("returns a greeting", async () => {
const response = await hello();
expect(response.message).toBe("Hello, World!");
});
});
```
### Run Tests
```bash
# Run with Encore (recommended - sets up infrastructure)
encore test
# Or run directly with npm
npm test
```
Using `encore test` is recommended because it:
- Sets up test databases automatically
- Provides isolated infrastructure per test
- Handles service dependencies
### Test with Request Parameters
```typescript
// api.test.ts
import { describe, it, expect } from "vitest";
import { getUser } from "./api";
describe("getUser endpoint", () => {
it("returns the user by ID", async () => {
const user = await getUser({ id: "123" });
expect(user.id).toBe("123");
expect(user.name).toBeDefined();
});
});
```
### Test Database Operations
Encore provides isolated test databases:
```typescript
// user.test.ts
import { describe, it, expect, beforeEach } from "vitest";
import { createUser, getUser, db } from "./user";
describe("user operations", () => {
beforeEach(async () => {
// Clean up before each test
await db.exec`DELETE FROM users`;
});
it("creates and retrieves a user", async () => {
const created = await createUser({ email: "[email protected]", name: "Test" });
const retrieved = await getUser({ id: created.id });
expect(retrieved.email).toBe("[email protected]");
});
});
```
### Test Service-to-Service Calls
```typescript
// order.test.ts
import { describe, it, expect } from "vitest";
import { createOrder } from "./order";
describe("order service", () => {
it("creates an order and notifies user service", async () => {
// Service calls work normally in tests
const order = await createOrder({
userId: "user-123",
items: [{ productId: "prod-1", quantity: 2 }],
});
expect(order.id).toBeDefined();
expect(order.status).toBe("pending");
});
});
```
### Test Error Cases
```typescript
import { describe, it, expect } from "vitest";
import { getUser } from "./api";
import { APIError } from "encore.dev/api";
describe("error handling", () => {
it("throws NotFound for missing user", async () => {
await expect(getUser({ id: "nonexistent" }))
.rejects
.toThrow("user not found");
});
it("throws with correct error code", async () => {
try {
await getUser({ id: "nonexistent" });
} catch (error) {
expect(error).toBeInstanceOf(APIError);
expect((error as APIError).code).toBe("not_found");
}
});
});
```
### Test Authenticated Endpoints
Mock `getAuthData` to call `auth: true` endpoints directly as a given user:
```typescript
import { describe, it, expect, vi } from "vitest";
import * as auth from "~encore/auth";
import { getProfile } from "./api";
it("returns the caller's profile", async () => {
vi.spyOn(auth, "getAuthData").mockImplementation(() => ({ userID: "123" }));
const profile = await getProfile();
expect(profile.id).toBe("123");
});
```
The mock only affects code in the test process that reads `getAuthData()` directly. Calls made through `~encore/clients` go through the Encore runtime, which does not see the mock, so an `auth: true` endpoint called that way fails with "endpoint requires auth but none provided". Pass the auth data explicitly instead:
```typescript
import { user } from "~encore/clients";
await user.follow({ username: "jake" }, { authData: { userID: "123" } });
```
### Test Pub/Sub
```typescript
// notifications.test.ts
import { describe, it, expect, vi } from "vitest";
import { orderCreated } from "./events";
describe("pub/sub", () => {
it("publishes order created event", async () => {
const messageId = await orderCreated.publish({
orderId: "order-123",
userId: "user-456",
total: 9999,
});
expect(messageId).toBeDefined();
});
});
```
### Test Cron Jobs
Test the underlying function, not the cron schedule:
```typescript
// cleanup.test.ts
import { describe, it, expect } from "vitest";
import { cleanupExpiredSessions } from "./cleanup";
describe("cleanup job", () => {
it("removes expired sessions", async () => {
// Create some expired sessions first
await createExpiredSession();
// Call the endpoint directly
await cleanupExpiredSessions();
// Verify cleanup happened
const remaining = await countSessions();
expect(remaining).toBe(0);
});
});
```
### Mocking External Services
```typescript
import { describe, it, expect, vi, beforeEach } from "vitest";
import { sendWelcomeEmail } from "./email";
// Mock external API
vi.mock("./external-email-client", () => ({
send: vi.fn().mockResolvedValue({ success: true }),
}));
describe("email service", () => {
it("sends welcome email", async () => {
const result = await sendWelcomeEmail({ userId: "123" });
expect(result.sent).toBe(true);
});
});
```
### Test Configuration
Create `vite.config.ts` (required for `~encore` imports):
```typescript
import { defineConfig } from "vitest/config";
import path from "path";
export default defineConfig({
resolve: {
alias: {
"~encore": path.resolve(__dirname, "./encore.gen"),
},
},
test: {
globals: true,
environment: "node",
include: ["**/*.test.ts"],
coverage: {
reporter: ["text", "json", "html"],
},
},
});
```
### VS Code Integration
Install the [Vitest extension](https://marketplace.visualstudio.com/items?itemName=vitest.explorer) and add to `.vscode/settings.json`:
```json
{
"vitest.commandLine": "encore test"
}
```
**Note:** For VS Code test explorer, disable file-level parallelism to avoid port conflicts:
```typescript
// vite.config.ts
export default defineConfig({
// ...
test: {
fileParallelism: false, // Disable for VS Code
// ...
},
});
```
Re-enable for CI: `encore test --fileParallelism=true`
### Guidelines
- Use `encore test` to run tests with infrastructure setup
- Each test file gets an isolated database transaction (rolled back after)
- Test API endpoints by calling them directly as functions
- Service-to-service calls work normally in tests
- Mock external dependencies (third-party APIs, email services, etc.)
- Don't mock Encore infrastructure (databases, Pub/Sub) - use the real thing
© encoredev · Apache-2.0 · encoredev/skills