Golang中如何解决测试代码拆分到多个文件的问题
Golang中如何解决测试代码拆分到多个文件的问题 我为我的API编写了大量的测试,这些测试都放在一个文件中,并且一切运行正常。现在我想将这些测试分离到单独的文件中,但无论我尝试什么方法,正如你在代码仓库中看到的,我使用了处理程序模块,因此无法将测试放在处理程序模块中,因为存在循环依赖。此外,为了使测试通过,必须连接到数据库,因此所有函数都必须建立数据库连接。
将测试移动到单独的文件夹中,我也未能成功。
让我们考虑一个最简单的例子:这是我的 server_test.go 文件。
package main
var (
client *mongo.Client
appDB db.AppRepository
mongoDatabase *mongo.Database
configDB connstring.ConnString
s3Endpoint string
)
func TestMain(m *testing.M) {
// Set up resources before running tests
setup()
// Run the tests
code := m.Run()
teardown()
os.Exit(code)
}
func copyFile(src, dst string) {
input, err := os.ReadFile(src)
if err != nil {
logrus.Errorf("Failed to read the file: %v", err)
return
}
err = os.WriteFile(dst, input, 0644)
if err != nil {
logrus.Errorf("Failed to copy the file: %v", err)
return
}
}
func removeFile(filename string) {
err := os.Remove(filename)
if err != nil {
logrus.Errorf("Failed to remove the file: %v", err)
return
}
}
func setup() {
viper.SetConfigType("env")
viper.SetConfigName(".env")
// set the configuration file path
viper.AddConfigPath(".")
// read in the configuration file
if err := viper.ReadInConfig(); err != nil {
panic(err)
}
// Create a single database connection
flagMap := map[string]interface{}{
"migration": true,
"rollback": false,
"user_name": "admin",
"user_password": "password",
}
s3Endpoint = viper.GetString("S3_ENDPOINT")
client, configDB = mongod.ConnectToDatabase(viper.GetString("MONGODB_URL_TESTS"), flagMap)
appDB = mongod.NewAppRepository(&configDB, client)
mongoDatabase = client.Database(configDB.Database)
copyFile("LICENSE", "testapp.dmg")
copyFile("LICENSE", "testapp.pkg")
}
func teardown() {
adminsCollection := mongoDatabase.Collection("admins")
filter := bson.M{"username": "admin"}
// Delete the admin user from the collection
_, err := adminsCollection.DeleteOne(context.TODO(), filter)
if err != nil {
logrus.Errorf("Failed to remove admin user: %v", err)
}
log.Println("Successfully removed admin user.")
client.Disconnect(context.Background())
log.Println("MongoDB is disconnected.")
removeFile("testapp.dmg")
removeFile("testapp.pkg")
}
func TestHealthCheck(t *testing.T) {
router := gin.Default()
w := httptest.NewRecorder()
handler := handler.NewAppHandler(client, appDB, mongoDatabase)
router.GET("/health", func(c *gin.Context) {
handler.HealthCheck(c)
})
req, _ := http.NewRequest("GET", "/health", nil)
// Serve the request using the Gin router.
router.ServeHTTP(w, req)
// Check the response status code.
assert.Equal(t, http.StatusOK, w.Code)
// Check the response body.
expected := `{"status":"healthy"}`
assert.Equal(t, expected, w.Body.String())
}
如何正确地将 TestHealthCheck 函数移动到 tests/info_test.go 中?当然,需要将数据库连接参数传递给它……
更多关于Golang中如何解决测试代码拆分到多个文件的问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于Golang中如何解决测试代码拆分到多个文件的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Go中解决测试代码拆分到多个文件的问题,可以通过以下几种方式实现:
方案1:使用测试辅助包(推荐)
创建一个 testhelper 包来管理共享的测试资源:
// testhelper/setup.go
package testhelper
import (
"context"
"log"
"os"
"testing"
"github.com/sirupsen/logrus"
"github.com/spf13/viper"
"go.mongodb.org/mongo-driver/mongo"
"your-project/db"
"your-project/connstring"
"your-project/mongod"
)
var (
Client *mongo.Client
AppDB db.AppRepository
MongoDatabase *mongo.Database
ConfigDB connstring.ConnString
S3Endpoint string
)
func Setup() {
viper.SetConfigType("env")
viper.SetConfigName(".env")
viper.AddConfigPath(".")
if err := viper.ReadInConfig(); err != nil {
panic(err)
}
flagMap := map[string]interface{}{
"migration": true,
"rollback": false,
"user_name": "admin",
"user_password": "password",
}
S3Endpoint = viper.GetString("S3_ENDPOINT")
Client, ConfigDB = mongod.ConnectToDatabase(viper.GetString("MONGODB_URL_TESTS"), flagMap)
AppDB = mongod.NewAppRepository(&ConfigDB, Client)
MongoDatabase = Client.Database(ConfigDB.Database)
copyFile("LICENSE", "testapp.dmg")
copyFile("LICENSE", "testapp.pkg")
}
func Teardown() {
adminsCollection := MongoDatabase.Collection("admins")
filter := bson.M{"username": "admin"}
_, err := adminsCollection.DeleteOne(context.TODO(), filter)
if err != nil {
logrus.Errorf("Failed to remove admin user: %v", err)
}
Client.Disconnect(context.Background())
removeFile("testapp.dmg")
removeFile("testapp.pkg")
}
func copyFile(src, dst string) {
input, err := os.ReadFile(src)
if err != nil {
logrus.Errorf("Failed to read the file: %v", err)
return
}
err = os.WriteFile(dst, input, 0644)
if err != nil {
logrus.Errorf("Failed to copy the file: %v", err)
return
}
}
func removeFile(filename string) {
err := os.Remove(filename)
if err != nil {
logrus.Errorf("Failed to remove the file: %v", err)
return
}
}
方案2:使用 _test 包和全局变量
在单独的测试文件中使用 _test 包:
// tests/info_test.go
package main_test
import (
"context"
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"go.mongodb.org/mongo-driver/mongo"
"your-project/db"
"your-project/handler"
)
// 导出测试辅助函数
func GetTestClient() *mongo.Client {
return client
}
func GetTestAppDB() db.AppRepository {
return appDB
}
func GetTestMongoDatabase() *mongo.Database {
return mongoDatabase
}
func TestHealthCheck(t *testing.T) {
router := gin.Default()
w := httptest.NewRecorder()
handler := handler.NewAppHandler(GetTestClient(), GetTestAppDB(), GetTestMongoDatabase())
router.GET("/health", func(c *gin.Context) {
handler.HealthCheck(c)
})
req, _ := http.NewRequest("GET", "/health", nil)
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
expected := `{"status":"healthy"}`
assert.Equal(t, expected, w.Body.String())
}
方案3:使用测试套件结构体
创建一个测试套件结构体来封装测试依赖:
// tests/suite_test.go
package tests
import (
"context"
"os"
"testing"
"github.com/sirupsen/logrus"
"github.com/spf13/viper"
"go.mongodb.org/mongo-driver/mongo"
"your-project/db"
"your-project/connstring"
"your-project/mongod"
)
type TestSuite struct {
Client *mongo.Client
AppDB db.AppRepository
MongoDatabase *mongo.Database
ConfigDB connstring.ConnString
S3Endpoint string
t *testing.T
}
func NewTestSuite(t *testing.T) *TestSuite {
ts := &TestSuite{t: t}
ts.Setup()
return ts
}
func (ts *TestSuite) Setup() {
viper.SetConfigType("env")
viper.SetConfigName(".env")
viper.AddConfigPath(".")
if err := viper.ReadInConfig(); err != nil {
ts.t.Fatal(err)
}
flagMap := map[string]interface{}{
"migration": true,
"rollback": false,
"user_name": "admin",
"user_password": "password",
}
ts.S3Endpoint = viper.GetString("S3_ENDPOINT")
ts.Client, ts.ConfigDB = mongod.ConnectToDatabase(viper.GetString("MONGODB_URL_TESTS"), flagMap)
ts.AppDB = mongod.NewAppRepository(&ts.ConfigDB, ts.Client)
ts.MongoDatabase = ts.Client.Database(ts.ConfigDB.Database)
ts.copyFile("LICENSE", "testapp.dmg")
ts.copyFile("LICENSE", "testapp.pkg")
}
func (ts *TestSuite) Teardown() {
adminsCollection := ts.MongoDatabase.Collection("admins")
filter := bson.M{"username": "admin"}
_, err := adminsCollection.DeleteOne(context.TODO(), filter)
if err != nil {
logrus.Errorf("Failed to remove admin user: %v", err)
}
ts.Client.Disconnect(context.Background())
ts.removeFile("testapp.dmg")
ts.removeFile("testapp.pkg")
}
func (ts *TestSuite) copyFile(src, dst string) {
input, err := os.ReadFile(src)
if err != nil {
logrus.Errorf("Failed to read the file: %v", err)
return
}
err = os.WriteFile(dst, input, 0644)
if err != nil {
logrus.Errorf("Failed to copy the file: %v", err)
return
}
}
func (ts *TestSuite) removeFile(filename string) {
err := os.Remove(filename)
if err != nil {
logrus.Errorf("Failed to remove the file: %v", err)
return
}
}
// tests/info_test.go
package tests
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"your-project/handler"
)
func TestHealthCheck(t *testing.T) {
ts := NewTestSuite(t)
defer ts.Teardown()
router := gin.Default()
w := httptest.NewRecorder()
handler := handler.NewAppHandler(ts.Client, ts.AppDB, ts.MongoDatabase)
router.GET("/health", func(c *gin.Context) {
handler.HealthCheck(c)
})
req, _ := http.NewRequest("GET", "/health", nil)
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
expected := `{"status":"healthy"}`
assert.Equal(t, expected, w.Body.String())
}
方案4:使用 init() 函数和包级变量
在单独的测试文件中使用 init() 函数:
// tests/info_test.go
package main
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"your-project/handler"
)
func init() {
// 确保 setup 已经运行
if client == nil {
setup()
}
}
func TestHealthCheck(t *testing.T) {
router := gin.Default()
w := httptest.NewRecorder()
handler := handler.NewAppHandler(client, appDB, mongoDatabase)
router.GET("/health", func(c *gin.Context) {
handler.HealthCheck(c)
})
req, _ := http.NewRequest("GET", "/health", nil)
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
expected := `{"status":"healthy"}`
assert.Equal(t, expected, w.Body.String())
}
对于你的具体情况,推荐使用方案1或方案3。方案1通过创建独立的测试辅助包来避免循环依赖,方案3通过测试套件结构体提供了更好的封装和灵活性。这两种方案都能让你将测试代码拆分到多个文件中,同时共享数据库连接和其他测试资源。

