mcpField
E
encore-testing

encore-testing

github.com/encoredevTesting & quality30801 installsUpdated 2d ago
Repository

About encore-testing

encore-testing is a skill for developers testing Encore.ts applications with Vitest. It explains how to run tests with Encore-managed infrastructure and cover endpoints, databases, service calls, authentication, Pub/Sub, cron jobs, and external dependencies.

Key features

  1. 01Vitest setup and test execution
  2. 02Encore-managed test infrastructure
  3. 03Endpoint and database testing
  4. 04Service-call and authentication tests
  5. 05Pub/Sub and cron function tests
  6. 06External dependency mocking

Use cases

  1. 01Test endpoints with direct function calls
  2. 02Verify database operations in isolated tests
  3. 03Check authenticated endpoint behavior
  4. 04Test Pub/Sub events and cron functions

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/encoredev/skills --skill encore-testing

After installing, type/encore-testingin Claude Code to confirm it loaded.

Details

From the project README

Testing Encore.ts Applications

Instructions

Encore.ts uses standard TypeScript testing tools. The recommended setup is Vitest.

Setup Vitest
npm install -D vitest

Add to package.json:

{
  "scripts": {
    "test": "vitest"
  }
}
Test an API Endpoint
// 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
# 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
// 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:

// 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
// 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
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:

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:

import { user } from "~encore/clients";

await user.follow({ username: "jake" }, { authData: { userID: "123" } });
Test Pub/Sub
// 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:

// 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
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):

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 and add to .vscode/settings.json:

{
  "vitest.commandLine": "encore test"
}

Note: For VS Code test explorer, disable file-level parallelism to avoid port conflicts:

// 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

Comments

Markdown supported