test-helper
Write comprehensive, idiomatic tests following best practices and project conventions. Use this when writing unit tests, integration tests, or test fixtures. Helps ensure proper test structure, mocking, assertions, and coverage.
$ Installer
git clone https://github.com/kcaldas/genie /tmp/genie && cp -r /tmp/genie/pkg/skills/internal/skills/test-helper ~/.claude/skills/genie// tip: Run this command in your terminal to install the skill
SKILL.md
name: test-helper description: Write comprehensive, idiomatic tests following best practices and project conventions. Use this when writing unit tests, integration tests, or test fixtures. Helps ensure proper test structure, mocking, assertions, and coverage.
Test Helper Skill
You are an expert at writing high-quality tests that are maintainable, readable, and thorough. Use this skill when:
- Writing new tests
- Improving existing tests
- Setting up test fixtures or mocks
- Debugging failing tests
- Ensuring test coverage
Testing Philosophy
Core Principles
- Tests are Documentation: Tests should clearly show how code is intended to be used
- Test Behavior, Not Implementation: Focus on what the code does, not how
- Arrange-Act-Assert: Structure tests with clear setup, execution, and verification
- One Concept Per Test: Each test should verify a single behavior
- Fast and Isolated: Tests should run quickly and independently
Go Testing Patterns
Basic Test Structure
func TestFunctionName(t *testing.T) {
// Arrange: Set up test data and dependencies
input := "test data"
expected := "expected result"
// Act: Execute the function being tested
result, err := FunctionName(input)
// Assert: Verify the results
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result != expected {
t.Errorf("got %v, want %v", result, expected)
}
}
Table-Driven Tests
func TestFunctionName(t *testing.T) {
tests := []struct {
name string
input string
expected string
wantErr bool
}{
{
name: "valid input",
input: "test",
expected: "result",
wantErr: false,
},
{
name: "invalid input",
input: "",
expected: "",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := FunctionName(tt.input)
if (err != nil) != tt.wantErr {
t.Errorf("error = %v, wantErr %v", err, tt.wantErr)
return
}
if result != tt.expected {
t.Errorf("got %v, want %v", result, tt.expected)
}
})
}
}
Mock Interfaces
type MockService struct {
DoWorkFunc func(input string) (string, error)
}
func (m *MockService) DoWork(input string) (string, error) {
if m.DoWorkFunc != nil {
return m.DoWorkFunc(input)
}
return "", nil
}
Test Fixtures
func setupTest(t *testing.T) (*Component, func()) {
// Setup
component := NewComponent()
// Return cleanup function
return component, func() {
// Cleanup
component.Close()
}
}
func TestWithFixture(t *testing.T) {
component, cleanup := setupTest(t)
defer cleanup()
// Use component in test
}
TypeScript/JavaScript Testing Patterns
Jest/Vitest Structure
describe('FunctionName', () => {
it('should handle valid input', () => {
// Arrange
const input = 'test';
const expected = 'result';
// Act
const result = functionName(input);
// Assert
expect(result).toBe(expected);
});
it('should throw on invalid input', () => {
expect(() => functionName('')).toThrow();
});
});
Mocking with Jest
jest.mock('../service');
describe('Component', () => {
beforeEach(() => {
jest.clearAllMocks();
});
it('should call service', async () => {
const mockService = jest.fn().mockResolvedValue('result');
const component = new Component(mockService);
await component.doWork();
expect(mockService).toHaveBeenCalledWith(expect.any(String));
});
});
Best Practices
Test Organization
- Group Related Tests: Use describe/subtests for related scenarios
- Descriptive Names: Test names should explain what is being tested and expected outcome
- Test Files: Keep test files next to the code they test (
file.go→file_test.go) - Test Packages: Use
package_testfor integration tests, same package for unit tests
What to Test
- ✅ Public API: All exported functions and methods
- ✅ Edge Cases: Empty inputs, nil values, boundaries
- ✅ Error Paths: How errors are handled and propagated
- ✅ Integration: How components work together
- ❌ Private Implementation: Don't test internal details
- ❌ Third-Party Code: Mock external dependencies
Common Patterns to Check
Test for Nil/Empty Inputs
func TestHandlesNilInput(t *testing.T) {
_, err := Process(nil)
if err == nil {
t.Error("expected error for nil input")
}
}
Test Concurrent Access
func TestConcurrentAccess(t *testing.T) {
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
// Concurrent operations
}()
}
wg.Wait()
}
Test Context Cancellation
func TestContextCancellation(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
cancel() // Cancel immediately
err := Operation(ctx)
if err != context.Canceled {
t.Errorf("expected Canceled error, got %v", err)
}
}
Workflow
When asked to write tests:
-
Understand the Code
- Read the function/method being tested
- Identify inputs, outputs, and side effects
- Note error conditions and edge cases
-
Plan Test Cases
- Happy path (normal operation)
- Edge cases (boundaries, special values)
- Error cases (invalid inputs, failures)
- Concurrent/async scenarios if applicable
-
Write Tests
- Start with the happy path
- Add edge cases
- Cover error scenarios
- Use table-driven tests for multiple similar cases
-
Verify Coverage
- Run tests:
go test -v - Check coverage:
go test -cover - Detailed report:
go test -coverprofile=coverage.out
- Run tests:
-
Review and Refactor
- Ensure tests are readable
- Remove duplication
- Verify test names are descriptive
Project-Specific Conventions
Check the existing test files to identify:
- Naming conventions (
Test_,test,should_) - Assertion libraries (testify, assert)
- Mock frameworks
- Test helpers and utilities
- Setup/teardown patterns
Common Gotchas
- Test Isolation: Tests should not depend on each other
- Random Data: Use fixed test data for reproducibility
- Time: Mock time.Now() for time-dependent tests
- External Services: Always mock network calls
- Cleanup: Use defer or cleanup functions to avoid test pollution
When you finish writing tests, invoke the Skill tool with an empty skill name to clear this context.
Repository

kcaldas
Author
kcaldas/genie/pkg/skills/internal/skills/test-helper
11
Stars
0
Forks
Updated6d ago
Added1w ago