From backend-golang
Use when writing tests for Go code — table-driven tests with the testing package, testify assertions, httptest for handlers, and interface-based mocking patterns
How this skill is triggered — by the user, by Claude, or both
Slash command
/backend-golang:golang-testingThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
```go
func TestCreateOrder(t *testing.T) {
tests := []struct {
name string
input CreateOrderRequest
wantErr bool
want *Order
}{
{
name: "valid order",
input: CreateOrderRequest{CustomerID: "c1", Items: []Item{{ProductID: "p1", Qty: 1}}},
want: &Order{ID: 1, CustomerID: "c1"},
},
{
name: "empty items",
input: CreateOrderRequest{CustomerID: "c1", Items: nil},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
svc := NewOrderService(newMockRepo())
got, err := svc.Create(context.Background(), tt.input)
if tt.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
assert.Equal(t, tt.want.CustomerID, got.CustomerID)
})
}
}
require for fatal assertions (stops test on failure)assert for non-fatal assertions (continues test)require for setup/preconditions, assert for actual assertionsfunc TestGetOrderHandler(t *testing.T) {
svc := NewOrderService(newMockRepo())
router := NewRouter(svc)
req := httptest.NewRequest(http.MethodGet, "/api/v1/orders/1", nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
var order Order
require.NoError(t, json.NewDecoder(w.Body).Decode(&order))
assert.Equal(t, int64(1), order.ID)
}
testify/mock for complex mocking needspackage order (white-box)order_test.go alongside order.gotestdata/ directory for fixturesUse table-driven tests to cover all four categories within a single test function. Label each case with a category prefix.
func TestCreateOrder(t *testing.T) {
tests := []struct {
name string
input CreateOrderRequest
wantErr bool
errMsg string
}{
// --- Positive ---
{
name: "positive: valid order with single item",
input: CreateOrderRequest{CustomerID: "c1", Items: []Item{{ProductID: "p1", Qty: 2}}},
},
{
name: "positive: valid order with multiple items",
input: CreateOrderRequest{CustomerID: "c1", Items: []Item{{ProductID: "p1", Qty: 1}, {ProductID: "p2", Qty: 3}}},
},
// --- Negative ---
{
name: "negative: empty items returns error",
input: CreateOrderRequest{CustomerID: "c1", Items: nil},
wantErr: true,
errMsg: "items cannot be empty",
},
{
name: "negative: missing customer ID returns error",
input: CreateOrderRequest{CustomerID: "", Items: []Item{{ProductID: "p1", Qty: 1}}},
wantErr: true,
errMsg: "customer ID is required",
},
// --- Edge ---
{
name: "edge: max-length customer ID (255 chars)",
input: CreateOrderRequest{CustomerID: strings.Repeat("c", 255), Items: []Item{{ProductID: "p1", Qty: 1}}},
},
{
name: "edge: zero quantity",
input: CreateOrderRequest{CustomerID: "c1", Items: []Item{{ProductID: "p1", Qty: 0}}},
wantErr: true,
},
// --- Security ---
{
name: "security: SQL injection in customer ID",
input: CreateOrderRequest{CustomerID: "'; DROP TABLE orders; --", Items: []Item{{ProductID: "p1", Qty: 1}}},
wantErr: true,
},
{
name: "security: path traversal in product ID",
input: CreateOrderRequest{CustomerID: "c1", Items: []Item{{ProductID: "../../../etc/passwd", Qty: 1}}},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
svc := NewOrderService(newMockRepo())
_, err := svc.Create(context.Background(), tt.input)
if tt.wantErr {
require.Error(t, err)
if tt.errMsg != "" {
assert.Contains(t, err.Error(), tt.errMsg)
}
return
}
require.NoError(t, err)
})
}
}
func TestGetOrderHandler_Security(t *testing.T) {
tests := []struct {
name string
path string
authHeader string
wantStatus int
}{
{
name: "security: missing auth token returns 401",
path: "/api/v1/orders/1",
authHeader: "",
wantStatus: http.StatusUnauthorized,
},
{
name: "security: expired token returns 401",
path: "/api/v1/orders/1",
authHeader: "Bearer expired-token",
wantStatus: http.StatusUnauthorized,
},
{
name: "security: IDOR access returns 403",
path: "/api/v1/orders/999",
authHeader: "Bearer valid-token-user-1",
wantStatus: http.StatusForbidden,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, tt.path, nil)
if tt.authHeader != "" {
req.Header.Set("Authorization", tt.authHeader)
}
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, tt.wantStatus, w.Code)
assert.NotContains(t, w.Body.String(), "stack trace")
assert.NotContains(t, w.Body.String(), "connection string")
})
}
}
npx claudepluginhub gagandeepp/software-agent-teams --plugin backend-golangGuides completion of development work by verifying tests, detecting environment, and presenting structured options for merge, PR, or cleanup.
Enforces test-driven development: write failing test first, then minimal code to pass. Use when implementing features or bugfixes.
Guides creation and editing of skills using test-driven development with pressure scenarios and subagents to verify agent compliance.