smithery/encoredev

encore-go-testing

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.

Installation

$ npx skills add smithery/encoredev --skill encore-go-testing

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery/encoredev · top by installs.

npx skills add smithery/encoredev

Browse all from smithery/encoredev

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 9,109 B
  • docs SUMMARY.md 63 B

History

  1. First recorded snapshot · 0 installs

SKILL.md

Testing Encore Go Applications

Instructions

Encore Go uses standard Go testing with encore test.

Run Tests

# 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

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

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

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

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

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

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

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

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

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

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:

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:

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.)