golang-testing

テスト駆動開発とGoコードの高品質を保証するための包括的なテスト戦略。

9 stars

Best use case

golang-testing is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

テスト駆動開発とGoコードの高品質を保証するための包括的なテスト戦略。

Teams using golang-testing should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/golang-testing/SKILL.md --create-dirs "https://raw.githubusercontent.com/j7-dev/everything-github-copilot/main/docs/ja-JP/skills/golang-testing/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/golang-testing/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How golang-testing Compares

Feature / Agentgolang-testingStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

テスト駆動開発とGoコードの高品質を保証するための包括的なテスト戦略。

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Go テスト

テスト駆動開発(TDD)とGoコードの高品質を保証するための包括的なテスト戦略。

## いつ有効化するか

- 新しいGoコードを書くとき
- Goコードをレビューするとき
- 既存のテストを改善するとき
- テストカバレッジを向上させるとき
- デバッグとバグ修正時

## 核となる原則

### 1. テスト駆動開発(TDD)ワークフロー

失敗するテストを書き、実装し、リファクタリングするサイクルに従います。

```go
// 1. テストを書く(失敗)
func TestCalculateTotal(t *testing.T) {
    total := CalculateTotal([]float64{10.0, 20.0, 30.0})
    want := 60.0
    if total != want {
        t.Errorf("got %f, want %f", total, want)
    }
}

// 2. 実装する(テストを通す)
func CalculateTotal(prices []float64) float64 {
    var total float64
    for _, price := range prices {
        total += price
    }
    return total
}

// 3. リファクタリング
// テストを壊さずにコードを改善
```

### 2. テーブル駆動テスト

複数のケースを体系的にテストします。

```go
func TestAdd(t *testing.T) {
    tests := []struct {
        name string
        a, b int
        want int
    }{
        {"positive numbers", 2, 3, 5},
        {"negative numbers", -2, -3, -5},
        {"mixed signs", -2, 3, 1},
        {"zeros", 0, 0, 0},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got := Add(tt.a, tt.b)
            if got != tt.want {
                t.Errorf("Add(%d, %d) = %d; want %d",
                    tt.a, tt.b, got, tt.want)
            }
        })
    }
}
```

### 3. サブテスト

サブテストを使用した論理的なテストの構成。

```go
func TestUser(t *testing.T) {
    t.Run("validation", func(t *testing.T) {
        t.Run("empty email", func(t *testing.T) {
            user := User{Email: ""}
            if err := user.Validate(); err == nil {
                t.Error("expected validation error")
            }
        })

        t.Run("valid email", func(t *testing.T) {
            user := User{Email: "test@example.com"}
            if err := user.Validate(); err != nil {
                t.Errorf("unexpected error: %v", err)
            }
        })
    })

    t.Run("serialization", func(t *testing.T) {
        // 別のテストグループ
    })
}
```

## テスト構成

### ファイル構成

```text
mypackage/
├── user.go
├── user_test.go          # ユニットテスト
├── integration_test.go   # 統合テスト
├── testdata/             # テストフィクスチャ
│   ├── valid_user.json
│   └── invalid_user.json
└── export_test.go        # 内部のテストのための非公開のエクスポート
```

### テストパッケージ

```go
// user_test.go - 同じパッケージ(ホワイトボックステスト)
package user

func TestInternalFunction(t *testing.T) {
    // 内部をテストできる
}

// user_external_test.go - 外部パッケージ(ブラックボックステスト)
package user_test

import "myapp/user"

func TestPublicAPI(t *testing.T) {
    // 公開APIのみをテスト
}
```

## アサーションとヘルパー

### 基本的なアサーション

```go
func TestBasicAssertions(t *testing.T) {
    // 等価性
    got := Calculate()
    want := 42
    if got != want {
        t.Errorf("got %d, want %d", got, want)
    }

    // エラーチェック
    _, err := Process()
    if err != nil {
        t.Fatalf("unexpected error: %v", err)
    }

    // nil チェック
    result := GetResult()
    if result == nil {
        t.Fatal("expected non-nil result")
    }
}
```

### カスタムヘルパー関数

```go
// ヘルパーとしてマーク(スタックトレースに表示されない)
func assertEqual(t *testing.T, got, want interface{}) {
    t.Helper()
    if got != want {
        t.Errorf("got %v, want %v", got, want)
    }
}

func assertNoError(t *testing.T, err error) {
    t.Helper()
    if err != nil {
        t.Fatalf("unexpected error: %v", err)
    }
}

// 使用例
func TestWithHelpers(t *testing.T) {
    result, err := Process()
    assertNoError(t, err)
    assertEqual(t, result.Status, "success")
}
```

### ディープ等価性チェック

```go
import "reflect"

func assertDeepEqual(t *testing.T, got, want interface{}) {
    t.Helper()
    if !reflect.DeepEqual(got, want) {
        t.Errorf("got %+v, want %+v", got, want)
    }
}

func TestStructEquality(t *testing.T) {
    got := User{Name: "Alice", Age: 30}
    want := User{Name: "Alice", Age: 30}
    assertDeepEqual(t, got, want)
}
```

## モッキングとスタブ

### インターフェースベースのモック

```go
// 本番コード
type UserStore interface {
    GetUser(id string) (*User, error)
    SaveUser(user *User) error
}

type UserService struct {
    store UserStore
}

// テストコード
type MockUserStore struct {
    users map[string]*User
    err   error
}

func (m *MockUserStore) GetUser(id string) (*User, error) {
    if m.err != nil {
        return nil, m.err
    }
    return m.users[id], nil
}

func (m *MockUserStore) SaveUser(user *User) error {
    if m.err != nil {
        return m.err
    }
    m.users[user.ID] = user
    return nil
}

// テスト
func TestUserService(t *testing.T) {
    mock := &MockUserStore{
        users: make(map[string]*User),
    }
    service := &UserService{store: mock}

    // サービスをテスト...
}
```

### 時間のモック

```go
// プロダクションコード - 時間を注入可能にする
type TimeProvider interface {
    Now() time.Time
}

type RealTime struct{}

func (RealTime) Now() time.Time {
    return time.Now()
}

type Service struct {
    time TimeProvider
}

// テストコード
type MockTime struct {
    current time.Time
}

func (m MockTime) Now() time.Time {
    return m.current
}

func TestTimeDependent(t *testing.T) {
    mockTime := MockTime{
        current: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
    }
    service := &Service{time: mockTime}

    // 固定時間でテスト...
}
```

### HTTP クライアントのモック

```go
type HTTPClient interface {
    Do(req *http.Request) (*http.Response, error)
}

type MockHTTPClient struct {
    response *http.Response
    err      error
}

func (m *MockHTTPClient) Do(req *http.Request) (*http.Response, error) {
    return m.response, m.err
}

func TestAPICall(t *testing.T) {
    mockClient := &MockHTTPClient{
        response: &http.Response{
            StatusCode: 200,
            Body:       io.NopCloser(strings.NewReader(`{"status":"ok"}`)),
        },
    }

    api := &APIClient{client: mockClient}
    // APIクライアントをテスト...
}
```

## HTTPハンドラーのテスト

### httptest の使用

```go
func TestHandler(t *testing.T) {
    handler := http.HandlerFunc(MyHandler)

    req := httptest.NewRequest("GET", "/users/123", nil)
    rec := httptest.NewRecorder()

    handler.ServeHTTP(rec, req)

    // ステータスコードをチェック
    if rec.Code != http.StatusOK {
        t.Errorf("got status %d, want %d", rec.Code, http.StatusOK)
    }

    // レスポンスボディをチェック
    var response map[string]interface{}
    if err := json.NewDecoder(rec.Body).Decode(&response); err != nil {
        t.Fatalf("failed to decode response: %v", err)
    }

    if response["id"] != "123" {
        t.Errorf("got id %v, want 123", response["id"])
    }
}
```

### ミドルウェアのテスト

```go
func TestAuthMiddleware(t *testing.T) {
    // ダミーハンドラー
    nextHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
    })

    // ミドルウェアでラップ
    handler := AuthMiddleware(nextHandler)

    tests := []struct {
        name       string
        token      string
        wantStatus int
    }{
        {"valid token", "valid-token", http.StatusOK},
        {"invalid token", "invalid", http.StatusUnauthorized},
        {"no token", "", http.StatusUnauthorized},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            req := httptest.NewRequest("GET", "/", nil)
            if tt.token != "" {
                req.Header.Set("Authorization", "Bearer "+tt.token)
            }
            rec := httptest.NewRecorder()

            handler.ServeHTTP(rec, req)

            if rec.Code != tt.wantStatus {
                t.Errorf("got status %d, want %d", rec.Code, tt.wantStatus)
            }
        })
    }
}
```

### テストサーバー

```go
func TestAPIIntegration(t *testing.T) {
    // テストサーバーを作成
    server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        json.NewEncoder(w).Encode(map[string]string{
            "message": "hello",
        })
    }))
    defer server.Close()

    // 実際のHTTPリクエストを行う
    resp, err := http.Get(server.URL)
    if err != nil {
        t.Fatalf("request failed: %v", err)
    }
    defer resp.Body.Close()

    // レスポンスを検証
    var result map[string]string
    json.NewDecoder(resp.Body).Decode(&result)

    if result["message"] != "hello" {
        t.Errorf("got %s, want hello", result["message"])
    }
}
```

## データベーステスト

### トランザクションを使用したテストの分離

```go
func TestUserRepository(t *testing.T) {
    db := setupTestDB(t)
    defer db.Close()

    tests := []struct {
        name string
        fn   func(*testing.T, *sql.DB)
    }{
        {"create user", testCreateUser},
        {"find user", testFindUser},
        {"update user", testUpdateUser},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            tx, err := db.Begin()
            if err != nil {
                t.Fatal(err)
            }
            defer tx.Rollback() // テスト後にロールバック

            tt.fn(t, tx)
        })
    }
}
```

### テストフィクスチャ

```go
func setupTestDB(t *testing.T) *sql.DB {
    t.Helper()

    db, err := sql.Open("postgres", "postgres://localhost/test")
    if err != nil {
        t.Fatalf("failed to connect: %v", err)
    }

    // スキーマを移行
    if err := runMigrations(db); err != nil {
        t.Fatalf("migrations failed: %v", err)
    }

    return db
}

func seedTestData(t *testing.T, db *sql.DB) {
    t.Helper()

    fixtures := []string{
        `INSERT INTO users (id, email) VALUES ('1', 'test@example.com')`,
        `INSERT INTO posts (id, user_id, title) VALUES ('1', '1', 'Test Post')`,
    }

    for _, query := range fixtures {
        if _, err := db.Exec(query); err != nil {
            t.Fatalf("failed to seed data: %v", err)
        }
    }
}
```

## ベンチマーク

### 基本的なベンチマーク

```go
func BenchmarkCalculation(b *testing.B) {
    for i := 0; i < b.N; i++ {
        Calculate(100)
    }
}

// メモリ割り当てを報告
func BenchmarkWithAllocs(b *testing.B) {
    b.ReportAllocs()
    for i := 0; i < b.N; i++ {
        ProcessData([]byte("test data"))
    }
}
```

### サブベンチマーク

```go
func BenchmarkEncoding(b *testing.B) {
    data := generateTestData()

    b.Run("json", func(b *testing.B) {
        b.ReportAllocs()
        for i := 0; i < b.N; i++ {
            json.Marshal(data)
        }
    })

    b.Run("gob", func(b *testing.B) {
        b.ReportAllocs()
        var buf bytes.Buffer
        enc := gob.NewEncoder(&buf)
        b.ResetTimer()
        for i := 0; i < b.N; i++ {
            enc.Encode(data)
            buf.Reset()
        }
    })
}
```

### ベンチマーク比較

```go
// 実行: go test -bench=. -benchmem
func BenchmarkStringConcat(b *testing.B) {
    b.Run("operator", func(b *testing.B) {
        for i := 0; i < b.N; i++ {
            _ = "hello" + " " + "world"
        }
    })

    b.Run("fmt.Sprintf", func(b *testing.B) {
        for i := 0; i < b.N; i++ {
            _ = fmt.Sprintf("%s %s", "hello", "world")
        }
    })

    b.Run("strings.Builder", func(b *testing.B) {
        for i := 0; i < b.N; i++ {
            var sb strings.Builder
            sb.WriteString("hello")
            sb.WriteString(" ")
            sb.WriteString("world")
            _ = sb.String()
        }
    })
}
```

## ファジングテスト

### 基本的なファズテスト(Go 1.18+)

```go
func FuzzParseInput(f *testing.F) {
    // シードコーパス
    f.Add("hello")
    f.Add("world")
    f.Add("123")

    f.Fuzz(func(t *testing.T, input string) {
        // パースがパニックしないことを確認
        result, err := ParseInput(input)

        // エラーがあっても、nilでないか一貫性があることを確認
        if err == nil && result == nil {
            t.Error("got nil result with no error")
        }
    })
}
```

### より複雑なファジング

```go
func FuzzJSONParsing(f *testing.F) {
    f.Add([]byte(`{"name":"test","age":30}`))
    f.Add([]byte(`{"name":"","age":0}`))

    f.Fuzz(func(t *testing.T, data []byte) {
        var user User
        err := json.Unmarshal(data, &user)

        // JSONがデコードされる場合、再度エンコードできるべき
        if err == nil {
            _, err := json.Marshal(user)
            if err != nil {
                t.Errorf("marshal failed after successful unmarshal: %v", err)
            }
        }
    })
}
```

## テストカバレッジ

### カバレッジの実行と表示

```bash
# カバレッジを実行してHTMLレポートを生成
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out -o coverage.html

# パッケージごとのカバレッジを表示
go test -cover ./...

# 詳細なカバレッジ
go test -coverprofile=coverage.out -covermode=atomic ./...
```

### カバレッジのベストプラクティス

```go
// Good: テスタブルなコード
func ProcessData(data []byte) (Result, error) {
    if len(data) == 0 {
        return Result{}, ErrEmptyData
    }

    // 各分岐をテスト可能
    if isValid(data) {
        return parseValid(data)
    }
    return parseInvalid(data)
}

// 対応するテストが全分岐をカバー
func TestProcessData(t *testing.T) {
    tests := []struct {
        name    string
        data    []byte
        wantErr bool
    }{
        {"empty data", []byte{}, true},
        {"valid data", []byte("valid"), false},
        {"invalid data", []byte("invalid"), false},
    }
    // ...
}
```

## 統合テスト

### ビルドタグの使用

```go
//go:build integration
// +build integration

package myapp_test

import "testing"

func TestDatabaseIntegration(t *testing.T) {
    // 実際のDBを必要とするテスト
}
```

```bash
# 統合テストを実行
go test -tags=integration ./...

# 統合テストを除外
go test ./...
```

### テストコンテナの使用

```go
import "github.com/testcontainers/testcontainers-go"

func setupPostgres(t *testing.T) *sql.DB {
    ctx := context.Background()

    req := testcontainers.ContainerRequest{
        Image:        "postgres:15",
        ExposedPorts: []string{"5432/tcp"},
        Env: map[string]string{
            "POSTGRES_PASSWORD": "test",
            "POSTGRES_DB":       "testdb",
        },
    }

    container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
        ContainerRequest: req,
        Started:          true,
    })
    if err != nil {
        t.Fatal(err)
    }

    t.Cleanup(func() {
        container.Terminate(ctx)
    })

    // コンテナに接続
    // ...
    return db
}
```

## テストの並列化

### 並列テスト

```go
func TestParallel(t *testing.T) {
    tests := []struct {
        name string
        fn   func(*testing.T)
    }{
        {"test1", testCase1},
        {"test2", testCase2},
        {"test3", testCase3},
    }

    for _, tt := range tests {
        tt := tt // ループ変数をキャプチャ
        t.Run(tt.name, func(t *testing.T) {
            t.Parallel() // このテストを並列実行
            tt.fn(t)
        })
    }
}
```

### 並列実行の制御

```go
func TestWithResourceLimit(t *testing.T) {
    // 同時に5つのテストのみ
    sem := make(chan struct{}, 5)

    tests := generateManyTests()

    for _, tt := range tests {
        tt := tt
        t.Run(tt.name, func(t *testing.T) {
            t.Parallel()

            sem <- struct{}{}        // 獲得
            defer func() { <-sem }() // 解放

            tt.fn(t)
        })
    }
}
```

## Goツール統合

### テストコマンド

```bash
# 基本テスト
go test ./...
go test -v ./...                    # 詳細出力
go test -run TestSpecific ./...     # 特定のテストを実行

# カバレッジ
go test -cover ./...
go test -coverprofile=coverage.out ./...

# レースコンディション
go test -race ./...

# ベンチマーク
go test -bench=. ./...
go test -bench=. -benchmem ./...
go test -bench=. -cpuprofile=cpu.prof ./...

# ファジング
go test -fuzz=FuzzTest

# 統合テスト
go test -tags=integration ./...

# JSONフォーマット(CI統合用)
go test -json ./...
```

### テスト設定

```bash
# テストタイムアウト
go test -timeout 30s ./...

# 短時間テスト(長時間テストをスキップ)
go test -short ./...

# ビルドキャッシュのクリア
go clean -testcache
go test ./...
```

## ベストプラクティス

### DRY(Don't Repeat Yourself)原則

```go
// Good: テーブル駆動テストで繰り返しを削減
func TestValidation(t *testing.T) {
    tests := []struct {
        input string
        valid bool
    }{
        {"valid@email.com", true},
        {"invalid-email", false},
        {"", false},
    }

    for _, tt := range tests {
        t.Run(tt.input, func(t *testing.T) {
            err := Validate(tt.input)
            if (err == nil) != tt.valid {
                t.Errorf("Validate(%q) error = %v, want valid = %v",
                    tt.input, err, tt.valid)
            }
        })
    }
}
```

### テストデータの分離

```go
// Good: テストデータを testdata/ ディレクトリに配置
func TestLoadConfig(t *testing.T) {
    data, err := os.ReadFile("testdata/config.json")
    if err != nil {
        t.Fatal(err)
    }

    config, err := ParseConfig(data)
    // ...
}
```

### クリーンアップの使用

```go
func TestWithCleanup(t *testing.T) {
    // リソースを設定
    file, err := os.CreateTemp("", "test")
    if err != nil {
        t.Fatal(err)
    }

    // クリーンアップを登録(deferに似ているが、サブテストで動作)
    t.Cleanup(func() {
        os.Remove(file.Name())
    })

    // テストを続ける...
}
```

### エラーメッセージの明確化

```go
// Bad: 不明確なエラー
if result != expected {
    t.Error("wrong result")
}

// Good: コンテキスト付きエラー
if result != expected {
    t.Errorf("Calculate(%d) = %d; want %d", input, result, expected)
}

// Better: ヘルパー関数の使用
assertEqual(t, result, expected, "Calculate(%d)", input)
```

## 避けるべきアンチパターン

```go
// Bad: 外部状態に依存
func TestBadDependency(t *testing.T) {
    result := GetUserFromDatabase("123") // 実際のDBを使用
    // テストが壊れやすく遅い
}

// Good: 依存を注入
func TestGoodDependency(t *testing.T) {
    mockDB := &MockDatabase{
        users: map[string]User{"123": {ID: "123"}},
    }
    result := GetUser(mockDB, "123")
}

// Bad: テスト間で状態を共有
var sharedCounter int

func TestShared1(t *testing.T) {
    sharedCounter++
    // テストの順序に依存
}

// Good: 各テストを独立させる
func TestIndependent(t *testing.T) {
    counter := 0
    counter++
    // 他のテストに影響しない
}

// Bad: エラーを無視
func TestIgnoreError(t *testing.T) {
    result, _ := Process()
    if result != expected {
        t.Error("wrong result")
    }
}

// Good: エラーをチェック
func TestCheckError(t *testing.T) {
    result, err := Process()
    if err != nil {
        t.Fatalf("Process() error = %v", err)
    }
    if result != expected {
        t.Errorf("got %v, want %v", result, expected)
    }
}
```

## クイックリファレンス

| コマンド/パターン | 目的 |
|--------------|---------|
| `go test ./...` | すべてのテストを実行 |
| `go test -v` | 詳細出力 |
| `go test -cover` | カバレッジレポート |
| `go test -race` | レースコンディション検出 |
| `go test -bench=.` | ベンチマークを実行 |
| `t.Run()` | サブテスト |
| `t.Helper()` | テストヘルパー関数 |
| `t.Parallel()` | テストを並列実行 |
| `t.Cleanup()` | クリーンアップを登録 |
| `testdata/` | テストフィクスチャ用ディレクトリ |
| `-short` | 長時間テストをスキップ |
| `-tags=integration` | ビルドタグでテストを実行 |

**覚えておいてください**: 良いテストは高速で、信頼性があり、保守可能で、明確です。複雑さより明確さを目指してください。

Related Skills

swift-protocol-di-testing

9
from j7-dev/everything-github-copilot

Protocol-based dependency injection for testable Swift code — mock file system, network, and external APIs using focused protocols and Swift Testing.

pest-testing

9
from j7-dev/everything-github-copilot

Tests applications using the Pest 4 PHP framework. Activates when writing tests, creating unit or feature tests, adding assertions, testing Livewire components, browser testing, debugging test failures, working with datasets or mocking; or when the user mentions test, spec, TDD, expects, assertion, coverage, or needs to verify functionality works.

python-testing

9
from j7-dev/everything-github-copilot

pytest、TDD手法、フィクスチャ、モック、パラメータ化、カバレッジ要件を使用したPythonテスト戦略。

golang-patterns

9
from j7-dev/everything-github-copilot

堅牢で効率的かつ保守可能なGoアプリケーションを構築するための慣用的なGoパターン、ベストプラクティス、規約。

cpp-testing

9
from j7-dev/everything-github-copilot

C++ テストの作成/更新/修正、GoogleTest/CTest の設定、失敗またはフレーキーなテストの診断、カバレッジ/サニタイザーの追加時にのみ使用します。

e2e-testing

9
from j7-dev/everything-github-copilot

Playwright E2E testing patterns, Page Object Model, configuration, CI/CD integration, artifact management, and flaky test strategies.

wpds

9
from j7-dev/everything-github-copilot

Use when building UIs leveraging the WordPress Design System (WPDS) and its components, tokens, patterns, etc.

wp-wpcli-and-ops

9
from j7-dev/everything-github-copilot

Use when working with WP-CLI (wp) for WordPress operations: safe search-replace, db export/import, plugin/theme/user/content management, cron, cache flushing, multisite, and scripting/automation with wp-cli.yml.

wp-rest-api

9
from j7-dev/everything-github-copilot

Use when building, extending, or debugging WordPress REST API endpoints/routes: register_rest_route, WP_REST_Controller/controller classes, schema/argument validation, permission_callback/authentication, response shaping, register_rest_field/register_meta, or exposing CPTs/taxonomies via show_in_rest.

wp-project-triage

9
from j7-dev/everything-github-copilot

Use when you need a deterministic inspection of a WordPress repository (plugin/theme/block theme/WP core/Gutenberg/full site) including tooling/tests/version hints, and a structured JSON report to guide workflows and guardrails.

wp-plugin-development

9
from j7-dev/everything-github-copilot

Use when developing WordPress plugins: architecture and hooks, activation/deactivation/uninstall, admin UI and Settings API, data storage, cron/tasks, security (nonces/capabilities/sanitization/escaping), and release packaging.

wp-playground

9
from j7-dev/everything-github-copilot

Use for WordPress Playground workflows: fast disposable WP instances in the browser or locally via @wp-playground/cli (server, run-blueprint, build-snapshot), auto-mounting plugins/themes, switching WP/PHP versions, blueprints, and debugging (Xdebug).