// internal/testing/setup.gopackagetestingimport("context""database/sql""testing""time""github.com/DATA-DOG/go-sqlmock""github.com/stretchr/testify/assert""github.com/stretchr/testify/require")// TestSuite provides common testing utilitiestypeTestSuitestruct{t*testing.Tdb*sql.DBmocksqlmock.Sqlmockctxcontext.Contextcancelcontext.CancelFunc}// NewTestSuite creates a new test suitefuncNewTestSuite(t*testing.T)*TestSuite{ctx,cancel:=context.WithTimeout(context.Background(),30*time.Second)return&TestSuite{t:t,ctx:ctx,cancel:cancel,}}// SetupDB sets up a mock databasefunc(ts*TestSuite)SetupDB(){db,mock,err:=sqlmock.New()require.NoError(ts.t,err)ts.db=dbts.mock=mock}// Teardown cleans up test resourcesfunc(ts*TestSuite)Teardown(){ifts.db!=nil{ts.db.Close()}ts.cancel()}// AssertNoError is a helper for common error assertionsfunc(ts*TestSuite)AssertNoError(errerror){ts.t.Helper()assert.NoError(ts.t,err)}// AssertError is a helper for error assertionsfunc(ts*TestSuite)AssertError(errerror){ts.t.Helper()assert.Error(ts.t,err)}
// internal/testing/testdata.gopackagetestingimport("encoding/json""os""path/filepath")// TestDataManager manages test datatypeTestDataManagerstruct{basePathstring}// NewTestDataManager creates a new test data managerfuncNewTestDataManager()*TestDataManager{return&TestDataManager{basePath:"testdata",}}// LoadJSON loads JSON test datafunc(tdm*TestDataManager)LoadJSON(filenamestring,targetinterface{})error{path:=filepath.Join(tdm.basePath,filename)data,err:=os.ReadFile(path)iferr!=nil{returnerr}returnjson.Unmarshal(data,target)}// LoadText loads text test datafunc(tdm*TestDataManager)LoadText(filenamestring)(string,error){path:=filepath.Join(tdm.basePath,filename)data,err:=os.ReadFile(path)iferr!=nil{return"",err}returnstring(data),nil}// CreateTestData creates test datafunc(tdm*TestDataManager)CreateTestData(filenamestring,datainterface{})error{path:=filepath.Join(tdm.basePath,filename)jsonData,err:=json.MarshalIndent(data,""," ")iferr!=nil{returnerr}returnos.WriteFile(path,jsonData,0644)}
// internal/service/user_integration_test.gopackageserviceimport("context""database/sql""testing""time""github.com/DATA-DOG/go-sqlmock""github.com/stretchr/testify/assert""github.com/stretchr/testify/require")funcTestUserService_Integration_CreateUser(t*testing.T){// Arrangedb,mock,err:=sqlmock.New()require.NoError(t,err)deferdb.Close()service:=&UserService{db:db}user:=&User{Name:"John Doe",Email:"john@example.com",}// Set up mock expectationsmock.ExpectBegin()mock.ExpectQuery("INSERT INTO users").WithArgs(user.Name,user.Email).WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(1))mock.ExpectCommit()// Actresult,err:=service.CreateUser(user)// Assertrequire.NoError(t,err)assert.NotNil(t,result)assert.Equal(t,int64(1),result.ID)// Verify all expectations were metassert.NoError(t,mock.ExpectationsWereMet())}funcTestUserService_Integration_GetUser(t*testing.T){// Arrangedb,mock,err:=sqlmock.New()require.NoError(t,err)deferdb.Close()service:=&UserService{db:db}userID:=int64(1)// Set up mock expectationsrows:=sqlmock.NewRows([]string{"id","name","email","created_at"}).AddRow(userID,"John Doe","john@example.com",time.Now())mock.ExpectQuery("SELECT id, name, email, created_at FROM users WHERE id = ?").WithArgs(userID).WillReturnRows(rows)// Actresult,err:=service.GetUser(userID)// Assertrequire.NoError(t,err)assert.NotNil(t,result)assert.Equal(t,userID,result.ID)assert.Equal(t,"John Doe",result.Name)assert.Equal(t,"john@example.com",result.Email)// Verify all expectations were metassert.NoError(t,mock.ExpectationsWereMet())}
// internal/service/coverage_test.gopackageserviceimport("testing""time")funcTestUserService_AllPaths(t*testing.T){service:=NewUserService()// Test successful creationuser:=&User{Name:"John Doe",Email:"john@example.com",}result,err:=service.CreateUser(user)require.NoError(t,err)assert.NotNil(t,result)// Test validation errorinvalidUser:=&User{Name:"",// InvalidEmail:"invalid-email",}_,err=service.CreateUser(invalidUser)assert.Error(t,err)// Test duplicate emailduplicateUser:=&User{Name:"Jane Doe",Email:"john@example.com",// Duplicate}_,err=service.CreateUser(duplicateUser)assert.Error(t,err)// Test successful retrievalretrieved,err:=service.GetUser(result.ID)require.NoError(t,err)assert.Equal(t,result.ID,retrieved.ID)// Test not found_,err=service.GetUser("non-existent")assert.Error(t,err)}
# Run tests with coveragegotest-coverprofile=coverage.out./...
# View coverage reportgotoolcover-html=coverage.out
# Coverage by functiongotoolcover-func=coverage.out
// internal/testing/helpers.gopackagetestingimport("crypto/rand""encoding/hex""testing""time")// RandomString generates a random stringfuncRandomString(lengthint)string{bytes:=make([]byte,length/2)rand.Read(bytes)returnhex.EncodeToString(bytes)}// RandomEmail generates a random emailfuncRandomEmail()string{returnRandomString(8)+"@example.com"}// RandomUser creates a random userfuncRandomUser()*User{return&User{Name:RandomString(10),Email:RandomEmail(),CreatedAt:time.Now(),}}// AssertTimeEqual asserts that two times are equal within a tolerancefuncAssertTimeEqual(t*testing.T,expected,actualtime.Time,tolerancetime.Duration){t.Helper()diff:=expected.Sub(actual)ifdiff<0{diff=-diff}ifdiff>tolerance{t.Errorf("expected time %v, got %v (diff: %v)",expected,actual,diff)}}
// internal/testing/fixtures.gopackagetestingimport("encoding/json""os""path/filepath")// LoadFixture loads a test fixturefuncLoadFixture(filenamestring,targetinterface{})error{path:=filepath.Join("testdata","fixtures",filename)data,err:=os.ReadFile(path)iferr!=nil{returnerr}returnjson.Unmarshal(data,target)}// SaveFixture saves a test fixturefuncSaveFixture(filenamestring,datainterface{})error{path:=filepath.Join("testdata","fixtures",filename)jsonData,err:=json.MarshalIndent(data,""," ")iferr!=nil{returnerr}returnos.WriteFile(path,jsonData,0644)}
# .github/workflows/test.ymlname:Teston:push:branches:[main,develop]pull_request:branches:[main]jobs:test:runs-on:ubuntu-lateststeps:-uses:actions/checkout@v3-name:Set up Gouses:actions/setup-go@v4with:go-version:'1.21'-name:Cache Go modulesuses:actions/cache@v3with:path:~/go/pkg/modkey:${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}restore-keys:|${{ runner.os }}-go--name:Download dependenciesrun:go mod download-name:Run testsrun:go test -v -race -coverprofile=coverage.out ./...-name:Run benchmarksrun:go test -bench=. -benchmem ./...-name:Upload coverage to Codecovuses:codecov/codecov-action@v3with:file:./coverage.out
# 1. Run testsgotest./...
# 2. Run tests with coveragegotest-cover./...
# 3. Run benchmarksgotest-bench=../...
# 4. Run tests with race detectiongotest-race./...
# Test specific packagegotest./internal/service
# Test specific functiongotest-runTestUserService_CreateUser
# Benchmark specific functiongotest-bench=BenchmarkUserService_CreateUser
# Coverage reportgotoolcover-html=coverage.out
// Test function namingfuncTestPackageName_FunctionName_Scenario(t*testing.T){// Arrange// Act// Assert}// Benchmark function namingfuncBenchmarkPackageName_FunctionName(b*testing.B){// Setupb.ResetTimer()// Benchmark code}