Golang单元测试中用于模拟数据库的依赖注入

huangapple go评论71阅读模式
英文:

Golang dependency injection for mock database in unit testing

问题

我正在创建一个使用Golang接口的模拟数据库,以测试我的处理程序函数。然而,在我的单元测试环境中,我似乎没有正确执行依赖注入。我创建了一个setup_test.go文件来设置我的模拟数据库环境。在下面的代码片段中,我将介绍我的依赖注入的流程。

  1. main.go
package main

type application struct {
	DB repository.DatabaseRepo
}

func main() {
	app := application{}

	conn, err := app.connectToDB()
	if err != nil {
		log.Fatal("Failed to connect to PostgreSQL:", err)
	}

	defer func() {
		conn.Close(context.Background())
	}()

	app.DB = &dbrepo.PostgresDBRepo{DB: conn}

	// 将app.DB传递给routes函数进行依赖注入
	mux := routes.Routes(app.DB)
package repository

type DatabaseRepo interface {
	Connection() *pgx.Conn
	GetCountByUsername(ctx context.Context, username string) (int, error)
}
  1. routes.go
package routes

func Routes(app repository.DatabaseRepo) http.Handler {
    mux := chi.NewRouter()

    signUpH := user.New(app)
	mux.Post("/signup", utils.MakeHTTPHandler(signUpH.SignUp))
}
  1. SignUp.go
    代码在调用数据库的这一行失败了。我在setup_test.go的第4点中设置了模拟数据库。我得到的错误消息是"panic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation ...]"。
package user

type application struct {
	DB repository.DatabaseRepo
}

// 依赖注入Repository(数据库)
func New(app repository.DatabaseRepo) *application {
	return &application{DB: app}
}

func (app application) SignUp(w http.ResponseWriter, req *http.Request) error {
    // 其他逻辑

    // 当我运行`go run .`时,它打印出的是*dbrepo.PostgresDBRepo
    // 当我运行`go test -v ./...`时,它打印出的是<nil>
    // 但预期类型应该是*dbrepo.TestDBRepo
    fmt.Printf("app.DB的类型:%T\n", app.DB)

    // 在单元测试中的这一行失败了
    userCount, err := app.DB.GetCountByUsername(ctx, Username)

    // 其他逻辑
}
  1. setup_test.go
package main

var app application

func TestMain(m *testing.M) {

	app.DB = &dbrepo.TestDBRepo{}

    fmt.Printf("app.DB的类型:%T\n", app.DB) // 输出类型是*dbrepo.TestDBRepo

	os.Exit(m.Run())
}

我看到许多类似的模拟数据库的单元测试使用这种方法,但它们通常在同一个main包中进行,如果你有嵌套的文件夹和包,就不会出现这个问题。

接下来我可以尝试什么?

英文:

I am creating a mock database with Golang interfaces to test my handler function. However, I don't seem to be performing the dependency injection correctly in my unit testing environment. I have created a setup_test.go file to set up my mock database environment. In the code snippets below, I will go over the flow of how my dependency injection works.

  1. main.go
package main

type application struct {
	DB repository.DatabaseRepo
}

func main() {
	app := application{}

	conn, err := app.connectToDB()
	if err != nil {
		log.Fatal(&quot;Failed to connect to PostgreSQL:&quot;, err)
	}

	defer func() {
		conn.Close(context.Background())
	}()

	app.DB = &amp;dbrepo.PostgresDBRepo{DB: conn}

	// Passing app.DB to routes function for DI
	mux := routes.Routes(app.DB)
package repository

type DatabaseRepo interface {
	Connection() *pgx.Conn
	GetCountByUsername(ctx context.Context, username string) (int, error)
}
  1. routes.go
package routes

func Routes(app repository.DatabaseRepo) http.Handler {
    mux := chi.NewRouter()

    signUpH := user.New(app)
	mux.Post(&quot;/signup&quot;, utils.MakeHTTPHandler(signUpH.SignUp))
}
  1. SignUp.go <br>
    The code fails at this line which is calling the database. I have set up my mock database in point 4 in setup_test.go. This is the error message I am getting "panic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation ...]".
package user

type application struct {
	DB repository.DatabaseRepo
}

// Dependency Injection of Repository (Database)
func New(app repository.DatabaseRepo) *application {
	return &amp;application{DB: app}
}

func (app application) SignUp(w http.ResponseWriter, req *http.Request) error {
    // some other logic

    // When I run `go run .`, this prints out to be *dbrepo.PostgresDBRepo
    // When I run `go test -v ./...`, this prints out to be &lt;nil&gt; 
    // but the expected type should be *dbrepo.TestDBRepo
    fmt.Printf(&quot;Type of app.DB: %T\n&quot;, app.DB)

    // fails on this line in unit testing
    userCount, err := app.DB.GetCountByUsername(ctx, Username)

    // some other logic
}
  1. setup_test.go
package main

var app application

func TestMain(m *testing.M) {

	app.DB = &amp;dbrepo.TestDBRepo{}

    fmt.Printf(&quot;Type of app.DB: %T\n&quot;, app.DB) // output type is *dbrepo.TestDBRepo

	os.Exit(m.Run())
}

I have seen many unit testing for similar mock database using this approach but they usually do it within the same package main which will not have this issue if you have nested folders and packages like mine.

What can I try next?

答案1

得分: 0

似乎解决这个问题的唯一方法是在依赖于这个测试环境(在我的情况下是模拟数据库)的包中创建多个TestMain,正如https://groups.google.com/g/golang-nuts/c/SxEkZhWl3QA中所述。也许在将来,Golang会将这个问题纳入考虑,使得TestMain可以集成到不同的包中。

英文:

(Posted answer on behalf of the question author to move it to the answers section).

It seems like the only way to solve this issue is to create multiple TestMain in packages that rely on this test environment (in my case, the mock database) as stated in https://groups.google.com/g/golang-nuts/c/SxEkZhWl3QA. Maybe in future this concern will be added into Golang where TestMain can be integrated to different packages.

huangapple
  • 本文由 发表于 2023年3月27日 22:42:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/75857122.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定