Install any skill in seconds. Free to start, no credit card required.
Get Started Free →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.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 80% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 120% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 102% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 299% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 154% | 0% |
Instructions for AI coding agents on automating unit test creation using consistent software testing patterns in this Go project.
> Ensures high code quality and reliability. Tests are self-documenting, reducing cognitive load for reviewers and maintainers.
> Uniform structure across tests ensures predictable, familiar code that team members can navigate efficiently.
> Table-driven and data-driven approaches minimize boilerplate code when adding new test cases, making it simple to expand coverage.
> 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.
> Unit tests should execute quickly to provide rapid feedback during development and continuous integration.
> Each unit test should be self-contained and not rely on the state or behavior of other tests.
> Unit tests should produce deterministic results every time they are run, regardless of the environment or order of execution.
> Unit tests should have clear pass/fail outcomes without requiring manual inspection.
> 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.
> Defines the input parameters or conditions for the test.
> Captures the actual output or result produced by the code under test.
> 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.
> Each row in the table represents a distinct test case with its own set of inputs and expected outputs.
> 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.
> Test data can be stored in external files (e.g., JSON, CSV) and loaded at runtime.
> 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.
> Set up the necessary preconditions and inputs for the test.
> Execute the function or method being tested.
> Verify that the actual output matches the expected output.
Test fixtures provide a consistent and reusable setup and teardown mechanism for test cases.
> Initialize common objects or state needed for multiple tests.
> Clean up resources or reset state after each test.
Identify new functions in pkg/ or internal/ (e.g., pkg/<package>/<file>.go).
Create new tests in the same package (e.g., pkg/<package>/<file>_test.go).
Include comprehensive edge cases:
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) |
> Use the standard Go testing package.
> Include testing and github.com/google/go-cmp/cmp for comparisons.
> Use t.Parallel() to run tests in parallel.
> Consolidate test cases for a single function into one TestXxx(t *testing.T) function using table-driven testing.
This approach:
> Use cmp.Equal for value comparisons and errors.Is for error checking.
Use these templates for new unit tests. Replace placeholders with actual values.
go// SPDX-License-Identifier: Apache-2.0 package <package> import ( "errors" "testing" "github.com/google/go-cmp/cmp" )
gofunc 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>) } }) } }
go// 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>) } }
gofunc 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) } }) } }
gofunc 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) } }) } }
goimport ( "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.
json { "tests": [ { "name": "case-description-1", "in": { "input": <value> }, "want": { "expected": <value> } }, { "name": "case-description-2", "in": { "input": <value> }, "want": { "expected": <value> } } ] }
Other measured skills in the registry, with their headline benchmark lift.