skills/go-unit-testing/SKILL.md
Automates unit test creation for Go projects using the standard testing package with consistent software testing patterns including In-Got-Want, Table-Driven Testing, and AAA patterns. Use when creating, modifying, or reviewing unit tests, or when the user mentions unit tests, test coverage, or Go testing.
npx skillsauth add sentenz/skills go-unit-testingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Instructions for AI coding agents on automating unit test creation using consistent software testing patterns in this Go project.
Readability
Ensures high code quality and reliability. Tests are self-documenting, reducing cognitive load for reviewers and maintainers.
Consistency
Uniform structure across tests ensures predictable, familiar code that team members can navigate efficiently.
Scalability
Table-driven and data-driven approaches minimize boilerplate code when adding new test cases, making it simple to expand coverage.
Debuggability
Scoped traces and detailed assertion messages pinpoint failures quickly during continuous integration and local testing.
The FIRST principles for unit testing focus on creating effective and maintainable tests.
Fast
Unit tests should execute quickly to provide rapid feedback during development and continuous integration.
Independent
Each unit test should be self-contained and not rely on the state or behavior of other tests.
Repeatable
Unit tests should produce deterministic results every time they are run, regardless of the environment or order of execution.
Self-Validating
Unit tests should have clear pass/fail outcomes without requiring manual inspection.
Timely
Unit tests should be written and executed early in the development process to catch issues as soon as possible.
The In-Got-Want pattern structures each test case into three clear sections.
In
Defines the input parameters or conditions for the test.
Got
Captures the actual output or result produced by the code under test.
Want
Specifies the expected output or result that the test is verifying against.
Table-driven testing organizes test cases in a tabular format, allowing multiple scenarios to be defined concisely.
Test Case Structure
Each row in the table represents a distinct test case with its own set of inputs and expected outputs.
Iteration
The test framework iterates over each row, executing the same test logic with different data.
Data-driven testing separates test data from test logic, enabling the same test logic to be executed with multiple sets of input data.
External Data Sources
Test data can be stored in external files (e.g., JSON, CSV) and loaded at runtime.
Reusability
The same test logic can be reused with different datasets, enhancing maintainability and coverage.
The AAA pattern structures each test case into three clear phases.
Arrange
Set up the necessary preconditions and inputs for the test.
Act
Execute the function or method being tested.
Assert
Verify that the actual output matches the expected output.
Test fixtures provide a consistent and reusable setup and teardown mechanism for test cases.
Setup
Initialize common objects or state needed for multiple tests.
Teardown
Clean up resources or reset state after each test.
Identify
Identify new functions in pkg/ or internal/ (e.g., pkg/<package>/<file>.go).
Add/Create
Create new tests in the same package (e.g., pkg/<package>/<file>_test.go).
Test Coverage Requirements
Include comprehensive edge cases:
Apply Templates
Structure all tests using the template pattern.
| Command | Description |
| ----------------------- | -------------------------------------------------- |
| make go-test-unit | Execute tests with race detection and JUnit report |
| make go-test-coverage | Generate coverage reports (HTML and XML) |
Test Framework
Use the standard Go
testingpackage.
Include Imports
Include
testingandgithub.com/google/go-cmp/cmpfor comparisons.
Parallelism
Use
t.Parallel()to run tests in parallel.
Test Organization
Consolidate test cases for a single function into one
TestXxx(t *testing.T)function using table-driven testing.
This approach:
Assertions
Use
cmp.Equalfor value comparisons anderrors.Isfor error checking.
Use these templates for new unit tests. Replace placeholders with actual values.
// SPDX-License-Identifier: Apache-2.0
package <package>
import (
"errors"
"testing"
"github.com/google/go-cmp/cmp"
)
func Test<FunctionName>(t *testing.T) {
t.Parallel()
// In-Got-Want
type in struct {
/* input fields */
}
type want struct {
/* expected output fields */
err error
}
// Table-Driven Testing
tests := []struct {
name string
in in
want want
}{
{
name: "case-description-1",
in: in{
/* input values */
},
want: want{
/* expected output */
err: nil,
},
},
{
name: "case-description-2",
in: in{
/* input values */
},
want: want{
/* expected output */
err: nil, // or specific error
},
},
// add more cases as needed
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Arrange
// additional setup as needed
// Act
got, err := <Function>(tt.in.<input>)
// Assert
if !errors.Is(err, tt.want.err) {
t.Errorf("<Function>() error = %v, want err %v", err, tt.want.err)
}
if !cmp.Equal(got, tt.want.<value>) {
t.Errorf("<Function>(%+v) = %v, want %v", tt.in, got, tt.want.<value>)
}
})
}
}
// testFixture holds common test state and provides setup/teardown.
type testFixture struct {
t *testing.T
// Add common fields for test state
object *<Type>
}
// newTestFixture creates and initializes a test fixture.
func newTestFixture(t *testing.T) *testFixture {
t.Helper()
// Setup
return &testFixture{
t: t,
object: New<Type>(),
}
}
// teardown cleans up resources after test completion.
func (f *testFixture) teardown() {
f.t.Helper()
// Teardown
if f.object != nil {
f.object.Close()
}
}
func Test<FunctionName>WithFixture(t *testing.T) {
t.Parallel()
// Arrange
f := newTestFixture(t)
defer f.teardown()
input := <input_value>
// Act
got, err := f.object.<Function>(input)
// Assert
if err != nil {
t.Errorf("<Function>() unexpected error: %v", err)
}
if !cmp.Equal(got, <expected>) {
t.Errorf("<Function>() = %v, want %v", got, <expected>)
}
}
func Test<FunctionName>Error(t *testing.T) {
t.Parallel()
// In-Got-Want
type in struct {
/* invalid input fields */
}
type want struct {
err error
}
// Table-Driven Testing
tests := []struct {
name string
in in
want want
}{
{
name: "nil-input-returns-error",
in: in{
/* nil or invalid input */
},
want: want{
err: resource.Err<ErrorName>,
},
},
{
name: "invalid-value-returns-error",
in: in{
/* invalid value */
},
want: want{
err: resource.Err<ErrorName>,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Arrange
// setup if needed
// Act
_, err := <Function>(tt.in.<input>)
// Assert
if !errors.Is(err, tt.want.err) {
t.Errorf("<Function>() error = %v, want err %v", err, tt.want.err)
}
})
}
}
func Test<FunctionName>BoundaryValues(t *testing.T) {
t.Parallel()
// In-Got-Want
type in struct {
input <input_type>
}
type want struct {
value <output_type>
err error
}
// Table-Driven Testing
tests := []struct {
name string
in in
want want
}{
{
name: "minimum-value",
in: in{input: <MIN_VALUE>},
want: want{value: /* expected */, err: nil},
},
{
name: "maximum-value",
in: in{input: <MAX_VALUE>},
want: want{value: /* expected */, err: nil},
},
{
name: "zero-value",
in: in{input: 0},
want: want{value: /* expected */, err: nil},
},
{
name: "negative-value",
in: in{input: -1},
want: want{value: /* expected */, err: nil},
},
{
name: "overflow-value",
in: in{input: math.MaxFloat64},
want: want{value: 0, err: resource.ErrOverflow},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Arrange
// setup if needed
// Act
got, err := <Function>(tt.in.input)
// Assert
if !errors.Is(err, tt.want.err) {
t.Errorf("<Function>() error = %v, want err %v", err, tt.want.err)
}
if !cmp.Equal(got, tt.want.value) {
t.Errorf("<Function>(%v) = %v, want %v", tt.in.input, got, tt.want.value)
}
})
}
}
import (
"encoding/json"
"os"
"path/filepath"
"testing"
"github.com/google/go-cmp/cmp"
)
// testCase represents a single test case loaded from JSON.
type testCase struct {
Name string `json:"name"`
In struct {
Input <input_type> `json:"input"`
} `json:"in"`
Want struct {
Expected <output_type> `json:"expected"`
} `json:"want"`
}
// testData represents the JSON test data structure.
type testData struct {
Tests []testCase `json:"tests"`
}
func Test<FunctionName>DataDriven(t *testing.T) {
t.Parallel()
// Load test data from JSON file
testdataPath := filepath.Join("testdata", "<function>_test.json")
data, err := os.ReadFile(testdataPath)
if err != nil {
t.Fatalf("failed to read test data: %v", err)
}
var td testData
if err := json.Unmarshal(data, &td); err != nil {
t.Fatalf("failed to parse test data: %v", err)
}
for _, tc := range td.Tests {
t.Run(tc.Name, func(t *testing.T) {
// Arrange
input := tc.In.Input
expected := tc.Want.Expected
// Act
got, err := <Function>(input)
// Assert
if err != nil {
t.Errorf("<Function>() unexpected error: %v", err)
}
if !cmp.Equal(got, expected) {
t.Errorf("<Function>(%v) = %v, want %v", input, got, expected)
}
})
}
}
tests/data/<function>_test.json
JSON file containing test cases.
{
"tests": [
{
"name": "case-description-1",
"in": {
"input": <value>
},
"want": {
"expected": <value>
}
},
{
"name": "case-description-2",
"in": {
"input": <value>
},
"want": {
"expected": <value>
}
}
]
}
development
Creates and maintains Architecture Decision Records (ADRs) following a structured format with State, Context, Decision, Considered, Consequences, Implementation, and References sections. Supports single-option decisions, multi-option decisions within one decision scope, multiple complementary decisions, and deferred decisions. Use when creating, updating, or reviewing architectural decisions, or when the user mentions ADR, architecture decisions, technical decisions, or design records.
tools
Performs end-to-end threat modeling for OT/ICS systems from Microsoft Threat Modeling Tool (TMT) threat-list exports (`*.csv`) and model files (`*.tm7`). Uses TMT and STRIDE for initial threat enumeration, then enriches each threat with OT/ICS context, MITRE ATT&CK for ICS mappings, MITRE EMB3D device-property threat enrichment for embedded field devices, CWE weakness classification, CVSS v4.0 scoring, Likelihood of Exploit, Risk-based Prioritization via a Risk Matrix, minimum-capable Threat Actor assignment, Risk Treatment decisions, and OT impact categories ranging from Denial of View to Physical Damage to Property.
development
Automates fuzz test creation for Go projects using Go's native fuzzing engine with consistent software testing patterns. Use when creating fuzz tests, mutation testing, or when the user mentions fuzzing, coverage-guided testing, or property-based testing.
development
Automates benchmark test creation for Go projects using the standard testing package with consistent software testing patterns. Use when creating performance benchmarks, profiling tests, or when the user mentions benchmarking, performance testing, or optimization.