go-table-driven-tests

安装量: 34
排名: #19959

安装

npx skills add https://github.com/tigrisdata/skills --skill go-table-driven-tests

Go Table-Driven Tests Use this skill when writing or modifying Go table-driven tests. It ensures tests follow established patterns. Core Principles One test function, many cases - Define test cases in a slice and iterate with t.Run() Explicit naming - Each case has a name field that becomes the subtest name Structured inputs - Use struct fields for inputs, expected outputs, and configuration Helper functions - Use t.Helper() in test helpers for proper line reporting Environment guards - Skip integration tests when credentials are unavailable Table Structure Pattern func TestFunctionName ( t * testing . T ) { tests := [ ] struct { name string // required: subtest name input Type // function input want Type // expected output wantErr error // expected error (nil for success) errCheck func ( error ) bool // optional: custom error validation setupEnv func ( ) func ( ) // optional: env setup, returns cleanup } { { name : "descriptive case name" , input : "test input" , want : "expected output" , } , // ... more cases } for _ , tt := range tests { t . Run ( tt . name , func ( t * testing . T ) { // test implementation using tt fields } ) } } Field Guidelines Field Required Purpose name Yes Subtest name - be descriptive and specific input / args Varies Input values for the function under test want / want Varies Expected output values (e.g., wantErr , wantResult ) errCheck No Custom error validation function setupEnv No Environment setup function returning cleanup Naming Conventions Test function: Test or Test_ Subtest names: lowercase, descriptive, spaces allowed Input fields: match parameter names or use input / args Output fields: prefix with want (e.g., want , wantErr , wantResult ) Common Patterns 1. Basic Table Test func TestWithRegion ( t * testing . T ) { tests := [ ] struct { name string region string } { { "auto region" , "auto" } , { "us-west-2" , "us-west-2" } , { "eu-central-1" , "eu-central-1" } , } for _ , tt := range tests { t . Run ( tt . name , func ( t * testing . T ) { o := & Options { } WithRegion ( tt . region ) ( o ) if o . Region != tt . region { t . Errorf ( "Region = %v, want %v" , o . Region , tt . region ) } } ) } } 2. Error Checking with wantErr func TestNew_errorCases ( t * testing . T ) { tests := [ ] struct { name string input string wantErr error } { { "empty input" , "" , ErrInvalidInput } , { "invalid input" , "!!!" , ErrInvalidInput } , } for _ , tt := range tests { t . Run ( tt . name , func ( t * testing . T ) { _ , err := Parse ( tt . input ) if ! errors . Is ( err , tt . wantErr ) { t . Errorf ( "error = %v, want %v" , err , tt . wantErr ) } } ) } } 3. Custom Error Validation with errCheck func TestNew_customErrors ( t * testing . T ) { tests := [ ] struct { name string setupEnv func ( ) func ( ) wantErr error errCheck func ( error ) bool } { { name : "no bucket name returns ErrNoBucketName" , setupEnv : func ( ) func ( ) { return func ( ) { } } , wantErr : ErrNoBucketName , errCheck : func ( err error ) bool { return errors . Is ( err , ErrNoBucketName ) } , } , } for _ , tt := range tests { t . Run ( tt . name , func ( t * testing . T ) { cleanup := tt . setupEnv ( ) defer cleanup ( ) _ , err := New ( context . Background ( ) ) if tt . wantErr != nil { if tt . errCheck != nil { if ! tt . errCheck ( err ) { t . Errorf ( "error = %v, want %v" , err , tt . wantErr ) } } } } ) } } 4. Environment Setup with setupEnv func TestNew_envVarOverrides ( t * testing . T ) { tests := [ ] struct { name string setupEnv func ( ) func ( ) options [ ] Option wantErr error } { { name : "bucket from env var" , setupEnv : func ( ) func ( ) { os . Setenv ( "TIGRIS_STORAGE_BUCKET" , "test-bucket" ) return func ( ) { os . Unsetenv ( "TIGRIS_STORAGE_BUCKET" ) } } , wantErr : nil , } , { name : "bucket from option overrides env var" , setupEnv : func ( ) func ( ) { os . Setenv ( "TIGRIS_STORAGE_BUCKET" , "env-bucket" ) return func ( ) { os . Unsetenv ( "TIGRIS_STORAGE_BUCKET" ) } } , options : [ ] Option { func ( o * Options ) { o . BucketName = "option-bucket" } , } , wantErr : nil , } , } for _ , tt := range tests { t . Run ( tt . name , func ( t * testing . T ) { cleanup := tt . setupEnv ( ) defer cleanup ( ) _ , err := New ( context . Background ( ) , tt . options ... ) if tt . wantErr != nil && ! errors . Is ( err , tt . wantErr ) { t . Errorf ( "error = %v, want %v" , err , tt . wantErr ) } } ) } } Integration Test Guards For tests requiring real credentials, use a skip helper: // skipIfNoCreds skips the test if Tigris credentials are not set. // Use this for integration tests that require real Tigris operations. func skipIfNoCreds ( t * testing . T ) { t . Helper ( ) if os . Getenv ( "TIGRIS_STORAGE_ACCESS_KEY_ID" ) == "" || os . Getenv ( "TIGRIS_STORAGE_SECRET_ACCESS_KEY" ) == "" { t . Skip ( "skipping: TIGRIS_STORAGE_ACCESS_KEY_ID and TIGRIS_STORAGE_SECRET_ACCESS_KEY not set" ) } } func TestCreateBucket ( t * testing . T ) { tests := [ ] struct { name string bucket string options [ ] BucketOption wantErr error } { { name : "create snapshot-enabled bucket" , bucket : "test-bucket" , options : [ ] BucketOption { WithEnableSnapshot ( ) } , } , } for _ , tt := range tests { t . Run ( tt . name , func ( t * testing . T ) { skipIfNoCreds ( t ) // test implementation } ) } } Test Helpers Use t.Helper() in helper functions for proper line number reporting: func setupTestBucket ( t * testing . T , ctx context . Context , client * Client ) string { t . Helper ( ) skipIfNoCreds ( t ) bucket := "test-bucket-" + randomSuffix ( ) err := client . CreateBucket ( ctx , bucket ) if err != nil { t . Fatalf ( "failed to create test bucket: %v" , err ) } return bucket } func cleanupTestBucket ( t * testing . T , ctx context . Context , client * Client , bucket string ) { t . Helper ( ) err := client . DeleteBucket ( ctx , bucket , WithForceDelete ( ) ) if err != nil { t . Logf ( "warning: failed to cleanup test bucket %s: %v" , bucket , err ) } } Checklist When writing table-driven tests: Table struct has name field as first field Each test case has a descriptive name Input fields use clear naming (match parameters or use input ) Expected output fields prefixed with want Iteration uses t.Run(tt.name, func(t testing.T) { ... }) Error checking uses errors.Is() for error comparison Environment setup includes cleanup in defer Integration tests use skipIfNoCreds(t) helper Test helpers use t.Helper() for proper line reporting Test file is *_test.go and lives next to the code it tests Best Practices Detailed Error Messages Include both actual and expected values in error messages for clear failure diagnosis: t . Errorf ( "got %q, want %q" , actual , expected ) Note: t.Errorf is not an assertion - the test continues after logging. This helps identify whether failures are systematic or isolated to specific cases. Maps for Test Cases Consider using a map instead of a slice for test cases. Map iteration order is non-deterministic, which ensures test cases are truly independent: tests := map [ string ] struct { input string want string } { "empty string" : { input : "" , want : "" } , "single character" : { input : "x" , want : "x" } , "multi-byte glyph" : { input : "🎉" , want : "🎉" } , } for name , tt := range tests { t . Run ( name , func ( t * testing . T ) { got := process ( tt . input ) if got != tt . want { t . Errorf ( "got %q, want %q" , got , tt . want ) } } ) } Parallel Testing Add t.Parallel() calls to run test cases in parallel. The loop variable is automatically captured per iteration: func TestFunction ( t * testing . T ) { tests := [ ] struct { name string input string } { // ... test cases } for _ , tt := range tests { t . Run ( tt . name , func ( t * testing . T ) { t . Parallel ( ) // marks this subtest as parallel // test implementation } ) } } References Go Wiki: TableDrivenTests - Official Go community best practices for table-driven testing Go Testing Package - Standard library testing documentation Prefer Table Driven Tests - Dave Cheney's guide on when and why to use table-driven tests over traditional test structures

返回排行榜