unit-testing - 如何在golang中模拟通过测试的方法
问题描述
我正在为 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())
}
到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
.
我想知道如何DeleteS3Image
在包 s3 中进行模拟以避免错误和成功测试。
这是运行go test -v
时的错误dao_test.go
。
$ 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
解决方案
在您的设置中,您确实调用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以帮助您生成模拟。
推荐阅读
- reactjs - 如何从 React.js 中的 firebase 获取文件
- python-3.x - 如果在python中关闭,如何自动打开文件
- r - R中带有和不带有NA(ARIMA和Forecast包)的时间序列预测
- javascript - 用对象过滤多维数组
- android - 从bazel 3.6.0切换到3.7.0时如何解决@bazel_tools//tools/android:databinding_exec
- python - 如何在 Pillow 中打开 FileStorage 类型的图像
- docker - Symfony Book,docker-compose 权限被拒绝
- java - RecyclerView 不显示(显示)
- haskell - Haskell:仅将 Map 用于特定的构造函数
- r - 平均绝对比例误差作为插补性能指标