This file provides context for AI agents working on gotest2.
gotest2 is a Go test generator that supports modern Go features, specifically generics. It is an alternative to gotests.
- Parser:
internal/parserusesgolang.org/x/tools/go/packagesto parse Go code. Key structs:FunctionInfo,Receiver,Field. - Generator:
internal/generatorusestext/templateandembedto generate test files. - Templates:
internal/generator/templatescontains broken-down templates:header.tmpl: Package info.standard.tmpl: For non-generic functions.generic.tmpl: For generic functions (wrapper strategy).body.tmpl: Entry point.
We use a Wrapper Strategy for generics:
TestXxx(Caller): Defines test cases and loops over them. Calls the helper. Contains concrete types (e.g.,intas default).testXxx(Helper): Generic function that implementation the assertion logic.
cmd/gotest2/main.go: CLI entry point.internal/generator/generator.go: Core generation logic.internal/parser/parser.go: Parsing logic.
Run tests with:
go test -v ./...Integration tests are in example/.
We have defined standard workflows in .agent/workflows/ to automate common tasks:
- Verify Changes:
.agent/workflows/verify.md - Regenerate Examples:
.agent/workflows/regenerate.md - Add New Feature:
.agent/workflows/new_feature.md
To verify any new changes:
- Regenerate Example Tests:
go run cmd/gotest2/main.go ./example/...
- Run Tests:
go test -v ./example - Implement Manual Test Case:
Open the generated
_test.gofile (e.g.,example/standard_test.go) and REPLACE the// TODO: Add test casesblock with a REAL test case that exercises the feature you are testing (e.g., verifyingInithook execution or asserting return values). Run the test again to confirm your logic passes. - Verify Parallel Execution:
Ensure
t.Parallel()is present in generated files and tests pass. - Dogfooding:
Generate tests for internal packages to ensure self-hosting capability.
go run cmd/gotest2/main.go -- internal/parser/parser.go go test -v ./internal/parser
Generated test cases now support Init and Cleanup hooks:
Init(t *testing.T, tt *TestCase): Runs before the function call.Cleanup(t *testing.T, tt *TestCase): Runs after the function call (via defer). Useful for setting up/tearing down mocks or fixtures.
To override the default reflect.DeepEqual check, provide a Validate function in your test case:
- Signature:
func(t *testing.T, got1 T1, got2 T2..., tt *TestCase) error - If
Validatefails (returns error), the test fails. - If
Validatesucceeds (returns nil), default checks are skipped.
Generate fuzz targets using the --fuzz flag:
go run cmd/gotest2/main.go --fuzz -- example/fuzz.goThis generates FuzzXxx functions for any fuzz-compatible inputs.
Override default templates:
go run cmd/gotest2/main.go --template custom.tmpl -- example/standard.goGenerated tests for generic functions include a Generic Runner (runTestXxx[T any]) and a main test function (TestXxx). You can easily add test cases for different concrete types by adding t.Run blocks in the main test function.
Example generics_test.go:
func TestGenericSum(t *testing.T) {
// ...
// Integer cases
t.Run("int", func(t *testing.T) {
runTestGenericSum[int](t, []testGenericSumTestCase[int]{
{
name: "Positive int",
args: struct{ a, b int }{1, 2},
want: testGenericSumWants[int]{want0: 3},
},
})
})
// Float cases
t.Run("float64", func(t *testing.T) {
runTestGenericSum[float64](t, []testGenericSumTestCase[float64]{
{
name: "Mixed float",
args: struct{ a, b float64 }{1.5, 2.5},
want: testGenericSumWants[float64]{want0: 4.0},
},
})
})
}This pattern ensures your generic logic works correctly across all intended types.