drux41001 2019-09-07 00:59
浏览 124
已采纳

如何在Golang中模拟通过测试的方法

I'm setting unit test for api(golang).
It seems to use mocking. But I don't understand how to code to success.

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

Now I'm trying to set dao_test.go But it fails because dao.go calls method from 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
}

The full source code is here(fix-test-dao):
https://github.com/jpskgc/article/tree/fix-test-dao

I expect the test success in dao_test.go.
But the actual is it fails because dao.go calls method from s3 package.
I want to know how to mock DeleteS3Image in package s3 to avoid error and success test.

Here is the error when running go test -v at 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
  • 写回答

1条回答 默认 最新

  • duanbo2048 2019-09-07 10:07
    关注

    In your setup you do call s.dao = NewDao(s.db, s.s3) however you've never initialized s.s3 to anything, so s.dao.s3 remains nil and that's why d.s3.DeleteS3Image(imageName) panics.


    In Go to be able to mock a method, the value on which the method is called must be an interface, not a concrete type. Put another way, it is not possible to mock a concrete method in Go.

    So with a type like this:

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

    you simply cannot mock s3.

    What you can do, is change the type of the s3 field to an interface type, you already have one ready (s3.DaoInterface).

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

    now you can mock the s3 field.

    What's left is for you to implement your mock and make sure that the s3 field is set to an instance of the mock implementation during test setup.

    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
    }
    

    It is up to you how you implement the mock, but if you're new to mocks I would recommend you take a look at https://github.com/golang/mock to help you with generating mocks.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 求差集那个函数有问题,有无佬可以解决
  • ¥15 【提问】基于Invest的水源涵养
  • ¥20 微信网友居然可以通过vx号找到我绑的手机号
  • ¥15 寻一个支付宝扫码远程授权登录的软件助手app
  • ¥15 解riccati方程组
  • ¥15 display:none;样式在嵌套结构中的已设置了display样式的元素上不起作用?
  • ¥15 使用rabbitMQ 消息队列作为url源进行多线程爬取时,总有几个url没有处理的问题。
  • ¥15 Ubuntu在安装序列比对软件STAR时出现报错如何解决
  • ¥50 树莓派安卓APK系统签名
  • ¥65 汇编语言除法溢出问题