Loading...
Loading...
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 skill4agent add sentenz/skills go-unit-testingEnsures 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.
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.
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.
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.
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.
Initialize common objects or state needed for multiple tests.
Clean up resources or reset state after each test.
pkg/internal/pkg/<package>/<file>.gopkg/<package>/<file>_test.go| Command | Description |
|---|---|
| Execute tests with race detection and JUnit report |
| Generate coverage reports (HTML and XML) |
Use the standard Gopackage.testing
Includeandtestingfor comparisons.github.com/google/go-cmp/cmp
Useto run tests in parallel.t.Parallel()
Consolidate test cases for a single function into onefunction using table-driven testing.TestXxx(t *testing.T)
Usefor value comparisons andcmp.Equalfor error checking.errors.Is
// 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.jsonJSON 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>
}
}
]
}