From 3811a3c1a034b99b69d369b9b15d8e3efd9985bb Mon Sep 17 00:00:00 2001 From: Zhongwei Li Date: Sat, 29 Nov 2025 18:15:18 +0800 Subject: [PATCH] Initial commit --- .claude-plugin/plugin.json | 14 +++ README.md | 3 + commands/go-test-single.md | 21 ++++ plugin.lock.json | 49 ++++++++ skills/go-tdd-patterns/SKILL.md | 198 ++++++++++++++++++++++++++++++++ 5 files changed, 285 insertions(+) create mode 100644 .claude-plugin/plugin.json create mode 100644 README.md create mode 100644 commands/go-test-single.md create mode 100644 plugin.lock.json create mode 100644 skills/go-tdd-patterns/SKILL.md diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..cb8c443 --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,14 @@ +{ + "name": "nk-go-development", + "description": "Go-specific TDD patterns, test runners, table-driven tests, and testify usage", + "version": "1.0.0", + "author": { + "name": "Cole Kennedy" + }, + "skills": [ + "./skills" + ], + "commands": [ + "./commands" + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..f89bb25 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# nk-go-development + +Go-specific TDD patterns, test runners, table-driven tests, and testify usage diff --git a/commands/go-test-single.md b/commands/go-test-single.md new file mode 100644 index 0000000..398feac --- /dev/null +++ b/commands/go-test-single.md @@ -0,0 +1,21 @@ +--- +description: Run a single Go test by name with verbose output +--- + +# Run Single Go Test + +Run a specific Go test function. Provide the test name and optionally the package path. + +**Usage:** +```bash +# Test in current directory +go test -v -run $ARGUMENTS + +# Test in specific package +go test -v ./path/to/package -run $ARGUMENTS + +# With failfast +go test -v --failfast -run $ARGUMENTS +``` + +Ask for the test name if not provided in the request, then execute the test with verbose output and show the results. diff --git a/plugin.lock.json b/plugin.lock.json new file mode 100644 index 0000000..48cde4c --- /dev/null +++ b/plugin.lock.json @@ -0,0 +1,49 @@ +{ + "$schema": "internal://schemas/plugin.lock.v1.json", + "pluginId": "gh:colek42/claude-plugins:nk-go-development", + "normalized": { + "repo": null, + "ref": "refs/tags/v20251128.0", + "commit": "b8107922d5896882079b6293b06a450a99a55722", + "treeHash": "f91009b2d87fea9491706cae6817b374e5e7bee59490f1c820408b02fc81025a", + "generatedAt": "2025-11-28T10:15:45.397207Z", + "toolVersion": "publish_plugins.py@0.2.0" + }, + "origin": { + "remote": "git@github.com:zhongweili/42plugin-data.git", + "branch": "master", + "commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390", + "repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data" + }, + "manifest": { + "name": "nk-go-development", + "description": "Go-specific TDD patterns, test runners, table-driven tests, and testify usage", + "version": "1.0.0" + }, + "content": { + "files": [ + { + "path": "README.md", + "sha256": "8446c4d684110815129531a7f7352da776cadaa3214f26b509ead70213254ba8" + }, + { + "path": ".claude-plugin/plugin.json", + "sha256": "910f3c038965effa37756af9a0f8808e1855abcb150c3ca9f0903db647d05e21" + }, + { + "path": "commands/go-test-single.md", + "sha256": "1fa071631506d0e104cd8c9d4a6e76d7367851e00534e90d76afdddb9e772bee" + }, + { + "path": "skills/go-tdd-patterns/SKILL.md", + "sha256": "23a6b2bd3817a221641bcddb1620c7a091ce89dc2346e4005b822851c54a8b50" + } + ], + "dirSha256": "f91009b2d87fea9491706cae6817b374e5e7bee59490f1c820408b02fc81025a" + }, + "security": { + "scannedAt": null, + "scannerVersion": null, + "flags": [] + } +} \ No newline at end of file diff --git a/skills/go-tdd-patterns/SKILL.md b/skills/go-tdd-patterns/SKILL.md new file mode 100644 index 0000000..c93c7e1 --- /dev/null +++ b/skills/go-tdd-patterns/SKILL.md @@ -0,0 +1,198 @@ +# Go TDD Patterns & Best Practices + +This skill provides Go-specific testing patterns and conventions. + +## When to Use + +Activates when: +- Writing Go code +- Creating or modifying Go tests +- Reviewing Go test coverage +- Refactoring Go code + +## Test Organization + +### File Naming +- Unit tests: `*_test.go` +- Integration tests: `e2e_test.go` or `integration_test.go` +- Place tests alongside the code they test + +### Test Function Naming +```go +func TestFunctionName(t *testing.T) // Basic test +func TestFunctionName_Scenario(t *testing.T) // Specific scenario +func TestFunctionName_EdgeCase(t *testing.T) // Edge case +``` + +## Table-Driven Tests + +Use table-driven tests for multiple inputs/scenarios: + +```go +func TestValidation(t *testing.T) { + tests := []struct { + name string + input string + want bool + wantErr error + }{ + { + name: "valid input", + input: "test", + want: true, + wantErr: nil, + }, + { + name: "empty input", + input: "", + want: false, + wantErr: ErrEmptyInput, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := Validate(tt.input) + + if tt.wantErr != nil { + require.ErrorIs(t, err, tt.wantErr) + return + } + + require.NoError(t, err) + assert.Equal(t, tt.want, got) + }) + } +} +``` + +## Testify Assertions + +### require vs assert + +**Use `testify/require`** - Stop test execution on failure: +```go +require.NoError(t, err) // Stop if error +require.NotNil(t, result) // Stop if nil +require.Equal(t, expected, actual) // Stop if not equal +require.ErrorIs(t, err, ErrExpected) // Stop if wrong error +``` + +**Use `testify/assert`** - Continue test execution: +```go +assert.Equal(t, expected, actual) // Continue on failure +assert.True(t, condition) // Continue on failure +assert.Contains(t, slice, element) // Continue on failure +``` + +**Pattern:** Use `require` for prerequisites, `assert` for multiple checks + +## Mock External Dependencies + +Mock external dependencies for unit tests: +- Network calls +- Filesystem operations +- Time-dependent code +- External services + +```go +type mockClient struct { + mock.Mock +} + +func (m *mockClient) FetchData(ctx context.Context) ([]byte, error) { + args := m.Called(ctx) + return args.Get(0).([]byte), args.Error(1) +} +``` + +## Test Error Cases + +Always test error cases and edge conditions: + +```go +func TestHandler_Errors(t *testing.T) { + tests := []struct { + name string + setup func(*mockDeps) + wantErr error + }{ + { + name: "database connection failed", + setup: func(m *mockDeps) { + m.db.On("Connect").Return(ErrConnFailed) + }, + wantErr: ErrConnFailed, + }, + { + name: "invalid input", + setup: func(m *mockDeps) { + // No setup needed + }, + wantErr: ErrInvalidInput, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := newMockDeps() + if tt.setup != nil { + tt.setup(m) + } + + err := Handler(m) + require.ErrorIs(t, err, tt.wantErr) + }) + } +} +``` + +## Integration Test Patterns + +Use build tags for integration tests: + +```go +//go:build integration +// +build integration + +package mypackage_test + +func TestIntegration_RealDatabase(t *testing.T) { + // Integration test code +} +``` + +Run with: `go test -tags=integration ./...` + +## Test Coverage Standards + +- Minimum coverage: 90% lines and branches +- Every exported function must have tests +- Critical paths need both positive and negative tests +- Edge cases must be explicitly tested + +## Running Tests + +```bash +# Run all tests +go test -v ./... + +# Run with race detector +go test -v -race ./... + +# Run with coverage +go test -v -cover ./... + +# Run single test +go test -v ./path/to/package -run TestName + +# Run with failfast +go test -v --failfast ./... +``` + +## Test Documentation + +Tests serve as documentation: +- Use descriptive test names +- Use table-driven test `name` field to describe scenario +- Add comments only for complex setup or non-obvious behavior