Skip to main content
skill CX Codex

encore-go-testing — Claude Code skill: tests and install

Write and run Encore Go tests with encore test and Go’s standard testing package. It covers handlers, service calls, databases, errors, and test structure.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add encoredev/skills --skill encore-go-testing -a claude-code
npx skills add encoredev/skills --skill go-testing -a claude-code
Open on GitHub

What encore-go-testing does

This skill guides tests for Encore Go applications. It uses encore test for the full suite, a selected package, or verbose output, while test files call handlers directly and assert returned values. The examples cover request parameters, service-to-service calls, Encore errors, event publishing, and the function behind a cron job.

It also shows database checks with sqldb.Exec, table-driven cases, and nested tests built with t.Run. Error handling uses errs.Error and its codes. encore test sets up test databases, isolates infrastructure for each test, and handles service dependencies, so the documented workflow does not treat plain go test as the preferred runner.

What it helps with

  • write a test for an Encore Go handler
  • check an API response with request parameters
  • verify database create-and-read flows
  • assert an Encore NotFound error
  • organize table-driven and nested tests

Example prompts for the agent

  • Write a *_test.go file for GetUser and check the response for ID 123.
  • Add a user creation test that stores a record, retrieves it, and checks its email.
  • Run the Encore test suite with verbose output using encore test -v ./....

How to install encore-go-testing

1Skills CLI (Vercel)

npx skills add encoredev/skills --skill encore-go-testing -a claude-code
npx skills add encoredev/skills --skill go-testing -a claude-code

2Manually: put the files in place

~/.claude/skills/go-testing/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/go-testing/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about encore-go-testing

How do I install encore-go-testing in Claude Code?

Run npx skills add encoredev/skills --skill encore-go-testing -a claude-code, then check ~/.claude/skills/go-testing/SKILL.md.

What does encore-go-testing do?

It helps create and run Encore Go tests with encore test and the standard testing package. Its examples cover handlers, databases, service calls, errors, events, and cron logic.

Why use encore test instead of go test for Encore Go?

encore test sets up test databases, provides isolated infrastructure per test, and handles service dependencies.

Source text

SKILL.md · Apache-2.0
---
name: encore-go-testing
description: Write or run automated tests for Encore Go code with `encore test` and the standard library `testing` package. Covers isolated per-test databases, calling handlers directly, and `*testing.T` patterns.
when_to_use: >-
  User wants to add/write/fix a test in Go, write a `*_test.go` file, test an endpoint or service, set up `encore test`, configure isolated test databases, write `t.Cleanup`/`t.Helper` for db cleanup, mock external dependencies, or assert on API request/response behaviour. Trigger phrases: "write a Go test", "add tests for", "go test", "encore test", "test the endpoint", "test the service", "integration test", "isolated database".
---

# Testing Encore Go Applications

## Instructions

Encore Go uses standard Go testing with `encore test`.

### Run Tests

```bash
# Run all tests with Encore (recommended)
encore test ./...

# Run tests for a specific package
encore test ./user/...

# Run with verbose output
encore test -v ./...
```

Using `encore test` instead of `go test` is recommended because it:
- Sets up test databases automatically
- Provides isolated infrastructure per test
- Handles service dependencies

### Test an API Endpoint

```go
// hello/hello_test.go
package hello

import (
    "context"
    "testing"
)

func TestHello(t *testing.T) {
    ctx := context.Background()
    
    resp, err := Hello(ctx)
    if err != nil {
        t.Fatalf("unexpected error: %v", err)
    }
    
    if resp.Message != "Hello, World!" {
        t.Errorf("expected 'Hello, World!', got '%s'", resp.Message)
    }
}
```

### Test with Request Parameters

```go
// user/user_test.go
package user

import (
    "context"
    "testing"
)

func TestGetUser(t *testing.T) {
    ctx := context.Background()
    
    user, err := GetUser(ctx, &GetUserParams{ID: "123"})
    if err != nil {
        t.Fatalf("unexpected error: %v", err)
    }
    
    if user.ID != "123" {
        t.Errorf("expected ID '123', got '%s'", user.ID)
    }
}
```

### Test Database Operations

Encore provides isolated test databases:

```go
// user/user_test.go
package user

import (
    "context"
    "testing"
    
    "encore.dev/storage/sqldb"
)

func TestCreateUser(t *testing.T) {
    ctx := context.Background()
    
    // Clean up
    _, _ = sqldb.Exec(ctx, db, "DELETE FROM users")
    
    // Create user
    created, err := CreateUser(ctx, &CreateUserParams{
        Email: "[email protected]",
        Name:  "Test User",
    })
    if err != nil {
        t.Fatalf("failed to create user: %v", err)
    }
    
    // Retrieve and verify
    retrieved, err := GetUser(ctx, &GetUserParams{ID: created.ID})
    if err != nil {
        t.Fatalf("failed to get user: %v", err)
    }
    
    if retrieved.Email != "[email protected]" {
        t.Errorf("expected email '[email protected]', got '%s'", retrieved.Email)
    }
}
```

### Test Service-to-Service Calls

```go
// order/order_test.go
package order

import (
    "context"
    "testing"
)

func TestCreateOrder(t *testing.T) {
    ctx := context.Background()
    
    // Service calls work normally in tests
    order, err := CreateOrder(ctx, &CreateOrderParams{
        UserID: "user-123",
        Items: []OrderItem{
            {ProductID: "prod-1", Quantity: 2},
        },
    })
    if err != nil {
        t.Fatalf("failed to create order: %v", err)
    }
    
    if order.Status != "pending" {
        t.Errorf("expected status 'pending', got '%s'", order.Status)
    }
}
```

### Test Error Cases

```go
package user

import (
    "context"
    "errors"
    "testing"
    
    "encore.dev/beta/errs"
)

func TestGetUser_NotFound(t *testing.T) {
    ctx := context.Background()
    
    _, err := GetUser(ctx, &GetUserParams{ID: "nonexistent"})
    if err == nil {
        t.Fatal("expected error, got nil")
    }
    
    // Check error code
    var e *errs.Error
    if errors.As(err, &e) {
        if e.Code != errs.NotFound {
            t.Errorf("expected NotFound, got %v", e.Code)
        }
    } else {
        t.Errorf("expected errs.Error, got %T", err)
    }
}
```

### Test Pub/Sub

```go
// notifications/notifications_test.go
package notifications

import (
    "context"
    "testing"
    
    "myapp/events"
)

func TestPublishOrderCreated(t *testing.T) {
    ctx := context.Background()
    
    msgID, err := events.OrderCreated.Publish(ctx, &events.OrderCreatedEvent{
        OrderID: "order-123",
        UserID:  "user-456",
        Total:   9999,
    })
    if err != nil {
        t.Fatalf("failed to publish: %v", err)
    }
    
    if msgID == "" {
        t.Error("expected message ID, got empty string")
    }
}
```

### Test Cron Jobs

Test the underlying function, not the cron schedule:

```go
// cleanup/cleanup_test.go
package cleanup

import (
    "context"
    "testing"
)

func TestCleanupExpiredSessions(t *testing.T) {
    ctx := context.Background()
    
    // Create some expired sessions first
    createExpiredSession(ctx)
    
    // Call the endpoint directly
    err := CleanupExpiredSessions(ctx)
    if err != nil {
        t.Fatalf("cleanup failed: %v", err)
    }
    
    // Verify cleanup happened
    count := countSessions(ctx)
    if count != 0 {
        t.Errorf("expected 0 sessions, got %d", count)
    }
}
```

### Table-Driven Tests

```go
func TestValidateEmail(t *testing.T) {
    tests := []struct {
        name    string
        email   string
        wantErr bool
    }{
        {"valid email", "[email protected]", false},
        {"missing @", "userexample.com", true},
        {"empty", "", true},
        {"valid with subdomain", "[email protected]", false},
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            err := validateEmail(tt.email)
            if (err != nil) != tt.wantErr {
                t.Errorf("validateEmail(%q) error = %v, wantErr %v", tt.email, err, tt.wantErr)
            }
        })
    }
}
```

### Test with Subtests

```go
func TestUserCRUD(t *testing.T) {
    ctx := context.Background()
    var userID string
    
    t.Run("create", func(t *testing.T) {
        user, err := CreateUser(ctx, &CreateUserParams{
            Email: "[email protected]",
            Name:  "Test",
        })
        if err != nil {
            t.Fatalf("create failed: %v", err)
        }
        userID = user.ID
    })
    
    t.Run("read", func(t *testing.T) {
        user, err := GetUser(ctx, &GetUserParams{ID: userID})
        if err != nil {
            t.Fatalf("read failed: %v", err)
        }
        if user.Email != "[email protected]" {
            t.Errorf("wrong email: %s", user.Email)
        }
    })
    
    t.Run("delete", func(t *testing.T) {
        err := DeleteUser(ctx, &DeleteUserParams{ID: userID})
        if err != nil {
            t.Fatalf("delete failed: %v", err)
        }
    })
}
```

### Test Database Isolation

Create isolated, fully-migrated test databases using `et.NewTestDatabase()`:

```go
import "encore.dev/et"

func TestWithFreshDatabase(t *testing.T) {
    // Creates a new database with all migrations applied
    testDB := et.NewTestDatabase(t, db)

    // Use testDB for queries - it's completely isolated
    _, err := testDB.Exec(ctx, "INSERT INTO users (email) VALUES ($1)", "[email protected]")
    if err != nil {
        t.Fatal(err)
    }
}
```

### Service Instance Isolation

By default, service structs are shared across tests for performance. Enable isolation when tests modify service state:

```go
import "encore.dev/et"

func TestWithServiceIsolation(t *testing.T) {
    // Enable service instance isolation for this test
    et.EnableServiceInstanceIsolation()

    // Now this test gets its own service struct instance
    // preventing state interference with other tests
}
```

### Test Tracing Dashboard

View test execution traces in the development dashboard at `http://localhost:9400` while tests run. This helps diagnose failures by showing:
- Request/response data
- Database queries
- Service-to-service calls
- Errors and stack traces

### Mocking Endpoints and Services

Mock endpoints or entire services for isolated unit testing:

```go
import "encore.dev/et"

func TestWithMockedEndpoint(t *testing.T) {
    // Mock a specific endpoint
    et.MockEndpoint(products.GetPrice, func(ctx context.Context, p *products.PriceParams) (*products.PriceResponse, error) {
        return &products.PriceResponse{Price: 100}, nil
    })

    // Mock an entire service
    et.MockService("products", &mockProductService{})
}
```

### Guidelines

- Use `encore test` to run tests with infrastructure setup
- Each test gets access to real infrastructure (databases, Pub/Sub)
- Test API endpoints by calling them directly as functions
- Service-to-service calls work normally in tests
- Use table-driven tests for testing multiple cases
- Use `et.NewTestDatabase()` for isolated database testing
- Use `et.EnableServiceInstanceIsolation()` when tests modify service state
- Don't mock Encore infrastructure - use the real thing
- Mock external dependencies (third-party APIs, email services, etc.)

© encoredev · Apache-2.0 · encoredev/skills

Similar skills

All skills for Testing and QA →

From the same set25

encore-testing 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. CX Codex +1 791installs encore-frontend A skill that connects React, Next.js, Vue or Svelte apps to an Encore.ts backend: typed client generation, CORS setup and auth tokens. CX Codex +1 763installs encore-migrate Guides step-by-step backend migrations to Encore.ts or Encore Go. It tracks progress via migration-plan.md, separates backend APIs from frontend code, and supports an autonomous mode. CX Codex +1 725installs encore-go-auth This skill helps secure Encore Go endpoints and identify callers. Use it for JWTs, API keys, bearer tokens, or session cookies. CX Codex +1 557installs encore-go-service This skill structures Encore.go services, Go packages, systems, and typed calls between services. Use it when adding a service or applying an agreed layout. CX Codex +1 555installs encore-go-api Teaches the agent to write Encore Go endpoints with the //encore:api annotation: typed request and response structs, path, query, header and cookie params, error codes. CX Codex +1 555installs encore-go-database Guides SQL queries, schema migrations, and PostgreSQL setup in Encore Go services using the sqldb package. CX Codex +1 553installs encore-go-code-review A skill that scans existing Encore Go code for anti-patterns: infrastructure inside functions, missing context, SQL injection risks before you merge. CX Codex +1 553installs

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor