【问题标题】:How to mock method to pass test in golang如何在golang中模拟通过测试的方法
【发布时间】:2019-09-07 18:07:25
【问题描述】:

我正在为 api(golang) 设置单元测试。
它似乎使用嘲笑。但我不明白如何编码才能成功。

article
  ├ client
  ├ api
  │  ├ main.go
  │  ├ contoroller
  │  │    ├ contoroller.go
  │  │    └ contoroller_test.go
  │  ├ service
  │  │    ├ service.go
  │  │    └ service_test.go
  │  ├ dao
  │  │    ├ dao.go
  │  │    └ dao_test.go
  │  ├ s3
  │  │    ├ s3.go
  │  │    └ s3_test.go
  │  ├ go.mod 
  │  ├ go.sum
  │  └ Dockerfile
  ├ nginx
  └ docker-compose.yml

现在我正在尝试设置dao_test.go 但它失败了,因为dao.go 调用了来自s3.dao 的方法。

dao_test.go

package dao

// import

type DaoSuite struct {
    suite.Suite
    db   *sql.DB
    mock sqlmock.Sqlmock
    dao  *Dao
    s3   *s3.S3
}

func (s *DaoSuite) SetupTest() {

    var err error
    s.db, s.mock, err = sqlmock.New()
    s.Require().NoError(err)
    s.dao = NewDao(s.db, s.s3)
}

func (s *DaoSuite) TestDeleteArticleDao() {

    // some method

    // here test fails because DeleteArticleDao calls method from another package.
    s.dao.DeleteArticleDao("1")

}

func (s *DaoSuite) TearDownTest() {
    s.db.Close()
    s.Assert().NoError(s.mock.ExpectationsWereMet())
}

dao.go

package dao

// import

type Dao struct {
    database *sql.DB
    s3       *s3.S3
}

func NewDao(database *sql.DB, s3 *s3.S3) *Dao {
    objs := &Dao{database: database, s3: s3}
    return objs
}

func (d *Dao) DeleteArticleDao(id string) {
    //generate imageName

    //here calls method in package s3
    //here test fails 
    d.s3.DeleteS3Image(imageName)

}

s3.go

package s3

//import

type S3 struct {
    APPID  string
    SECRET string
}

type DaoInterface interface {
    DeleteS3Image(imageName util.ImageName) error
}

func NewS3(appid, secret string) *S3 {
    objs := &S3{APPID: appid, SECRET: secret}
    return objs
}


func (objs *S3) DeleteS3Image(imageName util.ImageName) error {
    // method
}

完整的源代码在这里(fix-test-dao):
https://github.com/jpskgc/article/tree/fix-test-dao

我希望在dao_test.go 中测试成功。
但实际上它失败了,因为dao.go 调用了来自s3 package 的方法。
我想知道如何在包 s3 中模拟 DeleteS3Image 以避免错误和成功测试。

这是在dao_test.go 运行go test -v 时出现的错误。

$ go test -v
--- FAIL: TestDaoSuite (0.00s)
    --- FAIL: TestDaoSuite/TestDeleteArticleDao (0.00s)
        dao_test.go:221: 
                Error Trace:    dao_test.go:221
                                                        suite.go:122
                                                        panic.go:522
                                                        panic.go:82
                                                        signal_unix.go:390
                                                        s3.go:66
                                                        dao.go:74
                                                        dao_test.go:156
                Error:          Received unexpected error:
                                there is a remaining expectation which was not matched: ExpectedBegin => expecting database transaction Begin
                Test:           TestDaoSuite/TestDeleteArticleDao
        suite.go:61: test panicked: runtime error: invalid memory address or nil pointer dereference

【问题讨论】:

标签: unit-testing go mocking


【解决方案1】:

在您的设置中,您确实调用了s.dao = NewDao(s.db, s.s3),但是您从未将s.s3 初始化为任何东西,因此s.dao.s3 仍然是nil,这就是d.s3.DeleteS3Image(imageName) 出现恐慌的原因。


在 Go 中,为了能够模拟方法,调用方法的值必须是接口,而不是具体类型。换句话说,不可能在 Go 中模拟一个具体的方法。

所以使用这样的类型:

type Dao struct {
    database *sql.DB
    s3       *s3.S3
}

你只是不能模拟s3

您可以做的是将s3 字段的类型更改为接口类型,您已经准备好一个(s3.DaoInterface)。

type Dao struct {
    database *sql.DB
    s3       s3.DaoInterface
}

现在您可以模拟s3 字段。

剩下的就是让您实现模拟并确保在测试设置期间将s3 字段设置为模拟实现的实例。

type MockS3 struct{}

func (MockS3) DeleteS3Image(imageName util.ImageName) error {
    // do whatever
    return nil
}

func (s *DaoSuite) SetupTest() {

    var err error
    s.db, s.mock, err = sqlmock.New()
    s.Require().NoError(err)
    s.dao = NewDao(s.db, s.s3)
    s.dao.s3 = MockS3{} // <- don't forget about me
}

如何实现模拟取决于您,但如果您是模拟新手,我建议您查看 https://github.com/golang/mock 以帮助您生成模拟。

【讨论】:

  • 感谢您的建议。当我遵循这个建议时,我在 dao_test.go 中遇到了编译错误问题。请看stackoverflow.com/questions/57840793/…
  • @jpskgc.v5 您可以在我的示例中看到我在非指针MockS3 上实现了DeleteS3Image,因此我可以使用MockS3{}。在您的版本中,您已经在指针 *MockDaoInterface 上实现了该方法,因此您必须使用 &amp;MockDaoInterface{}
猜你喜欢
  • 2017-09-29
  • 2021-10-15
  • 1970-01-01
  • 2022-09-28
  • 1970-01-01
  • 1970-01-01
  • 2017-08-07
  • 2019-06-06
  • 1970-01-01
相关资源
最近更新 更多