0

我是go-sqlmock第一次使用,我正在尝试为后期操作编写测试。我正在使用gormgin

  1. 测试给了我一个错误,s.mock.ExpectQuery(regexp.QuoteMeta(....我不是这里的问题。我已经发布了测试和输出。
  2. 另外,(这与1无关)在这个测试中我真的不知道code会是什么,因为它是在api控制器中随机生成的。有没有办法在该code字段中分配一个通用编号。

测试文件

     package unit
    
     import (
        "net/http"
        "net/http/httptest"
        "regexp"
        "testing"
    
        "github.com/DATA-DOG/go-sqlmock"
        "github.com/SamiAlsubhi/go/controllers"
        "github.com/SamiAlsubhi/go/routes"
        "github.com/gin-gonic/gin"
        "github.com/stretchr/testify/assert"
        "github.com/stretchr/testify/require"
        "github.com/stretchr/testify/suite"
        "gorm.io/driver/postgres"
        "gorm.io/gorm"
     )
    
    type Suite struct {
        suite.Suite
        DB     *gorm.DB
        mock   sqlmock.Sqlmock
        router *gin.Engine
    }
    
    func (s *Suite) SetupSuite(t *testing.T) {
        conn, mock, err := sqlmock.New()
        if err != nil || conn == nil {
            t.Errorf("Failed to open mock sql db, got error: %v", err)
        }
        s.mock = mock
    
        dialector := postgres.New(postgres.Config{
            DSN:                  "sqlmock_db_0",
            DriverName:           "postgres",
            Conn:                 conn,
            PreferSimpleProtocol: true,
        })
    
        if db, err := gorm.Open(dialector, &gorm.Config{}); err != nil || db == nil {
            t.Errorf("Failed to open gorm v2 db, got error: %v", err)
        } else {
            s.DB = db
        }
        api := &controllers.API{Db: s.DB}
    
        s.router = routes.SetupRouter(api)
    
    }
    
    func TestSetup(t *testing.T) {
        suite.Run(t, new(Suite))
    
    }
    
    func (s *Suite) AfterTest(_, _ string) {
        require.NoError(s.T(), s.mock.ExpectationsWereMet())
    }
    
    func (s *Suite) Test_GetOTP() {
        var (
            phone = "99999999"
            code  = "123456"
        )
    
        s.mock.ExpectQuery(regexp.QuoteMeta(
            `INSERT INTO "otps" ("phone","code") VALUES ($1,$2) RETURNING "otps"."id"`)).
            WithArgs(phone, code).
            WillReturnRows(sqlmock.NewRows([]string{"id"}).
                AddRow(1))
    
        s.mock.ExpectCommit()
    
        w := httptest.NewRecorder()
        req, err := http.NewRequest("GET", "/api/auth/get-otp/"+phone, nil)
        require.NoError(s.T(), err)
    
        s.router.ServeHTTP(w, req)
        assert.Equal(s.T(), 200, w.Code)
    
        //require.Nil(s.T(), deep.Equal(&model.Person{ID: id, Name: name}, w.Body))
    }

输出。

    --- FAIL: TestSetup (0.00s)
        --- FAIL: TestSetup/Test_GetOTP (0.00s)
            /Users/sami/Desktop/SamiAlsubhi/go/test/unit/suite.go:63: test panicked: runtime error: invalid memory address or nil pointer dereference
                goroutine 26 [running]:
                runtime/debug.Stack()
                    /usr/local/go/src/runtime/debug/stack.go:24 +0x65
                github.com/stretchr/testify/suite.failOnPanic(0xc000001a00)
                    /Users/sami/Desktop/golang/pkg/mod/github.com/stretchr/testify@v1.7.0/suite/suite.go:63 +0x3e
                panic({0x49e96a0, 0x5193810})
                    /usr/local/go/src/runtime/panic.go:1038 +0x215
                github.com/SamiAlsubhi/go/test/unit.(*Suite).AfterTest(0x4abe61b, {0x4becfd0, 0xc000468940}, {0x0, 0x0})
                    /Users/sami/Desktop/SamiAlsubhi/go/test/unit/setup_test.go:60 +0x1c
                github.com/stretchr/testify/suite.Run.func1.1()
                    /Users/sami/Desktop/golang/pkg/mod/github.com/stretchr/testify@v1.7.0/suite/suite.go:137 +0x1b7
                panic({0x49e96a0, 0x5193810})
                    /usr/local/go/src/runtime/panic.go:1038 +0x215
                github.com/SamiAlsubhi/go/test/unit.(*Suite).Test_GetOTP(0xc000468940)
                    /Users/sami/Desktop/SamiAlsubhi/go/test/unit/setup_test.go:69 +0x4f
                reflect.Value.call({0xc000049140, 0xc000010308, 0x13}, {0x4abf50c, 0x4}, {0xc000080e70, 0x1, 0x1})
                    /usr/local/go/src/reflect/value.go:543 +0x814
                reflect.Value.Call({0xc000049140, 0xc000010308, 0xc000468940}, {0xc0003c9e70, 0x1, 0x1})
                    /usr/local/go/src/reflect/value.go:339 +0xc5
                github.com/stretchr/testify/suite.Run.func1(0xc000001a00)
                    /Users/sami/Desktop/golang/pkg/mod/github.com/stretchr/testify@v1.7.0/suite/suite.go:158 +0x4b6
                testing.tRunner(0xc000001a00, 0xc000162000)
                    /usr/local/go/src/testing/testing.go:1259 +0x102
                created by testing.(*T).Run
                    /usr/local/go/src/testing/testing.go:1306 +0x35a
    FAIL
    coverage: [no statements]
    FAIL    github.com/SamiAlsubhi/go/test/unit 0.912s
    FAIL
4

1 回答 1

0

第一个问题的解决方案:
使用时testify/suite,如果为Suitestruct创建了一堆方法,它们会在运行测试时自动执行。话虽如此,这些方法将通过接口过滤器。在 的情况下.SetupSuite,它必须没有参数并且没有返回,才能运行。

第二个问题的解决方案:有一种方法go-sqlmock可以匹配任何类型的数据,使用sqlmock.AnyArg().

固定代码:

    package unit
    
    import (
        "encoding/json"
        "fmt"
        "math/rand"
        "net/http"
        "net/http/httptest"
        "regexp"
        "testing"
    
        "github.com/DATA-DOG/go-sqlmock"
        "github.com/SamiAlsubhi/go/controllers"
        "github.com/SamiAlsubhi/go/routes"
        "github.com/gin-gonic/gin"
        "github.com/stretchr/testify/assert"
        "github.com/stretchr/testify/require"
        "github.com/stretchr/testify/suite"
        "gorm.io/driver/postgres"
        "gorm.io/gorm"
    )
    
    type Suite struct {
        suite.Suite
        DB     *gorm.DB
        mock   sqlmock.Sqlmock
        router *gin.Engine
    }
    
    func (s *Suite) SetupSuite() {
        //t.Logf("setup start")
        conn, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherRegexp))
        if err != nil || conn == nil {
            panic(fmt.Sprintf("Failed to open mock sql db, got error: %v", err))
        }
        s.mock = mock
    
        dialector := postgres.New(postgres.Config{
            DSN:                  "sqlmock_db_0",
            DriverName:           "postgres",
            Conn:                 conn,
            PreferSimpleProtocol: true,
        })
    
        if db, err := gorm.Open(dialector, &gorm.Config{SkipDefaultTransaction: true}); err != nil || db == nil {
            panic(fmt.Sprintf("Failed to open gorm v2 db, got error: %v", err))
        } else {
            s.DB = db
        }
    
        api := &controllers.API{Db: s.DB, IsTesting: true}
        s.router = routes.SetupRouter(api)
    
    }
    
    func TestSetup(t *testing.T) {
    
        suite.Run(t, new(Suite))
    
    }
    
    // func (s *Suite) AfterTest(_, _ string) {
    //  require.NoError(s.T(), s.mock.ExpectationsWereMet())
    // }
    
    func (s *Suite) Test_GetOTP_Non_Existing_Phone() {
        /*
            This to test getting OTP for a phone number that does not exist in the otps table
        */
        phone := fmt.Sprintf("%v", 90000000+rand.Intn(99999999-90000000))
        s.mock.MatchExpectationsInOrder(false)
    
        s.mock.ExpectQuery(regexp.QuoteMeta(`SELECT count(*) FROM "otps" WHERE phone = $1 AND "otps"."deleted_at" IS NULL`)).
            WithArgs(phone).
            WillReturnRows(sqlmock.NewRows([]string{"count"}).
                AddRow(0))
    
        s.mock.ExpectQuery(regexp.QuoteMeta(
            `INSERT INTO "otps" ("created_at","updated_at","deleted_at","phone","code") VALUES ($1,$2,$3,$4,$5) RETURNING "id"`)).
            WithArgs(sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), phone, sqlmock.AnyArg()).
            WillReturnRows(sqlmock.NewRows([]string{"id"}).
                AddRow(1))
    
        w := httptest.NewRecorder()
        req, err := http.NewRequest("GET", "/api/auth/get-otp/"+phone, nil)
        require.NoError(s.T(), err)
    
        s.router.ServeHTTP(w, req)
        assert.Equal(s.T(), 200, w.Code)
        //parse response
        var response gin.H
        err = json.Unmarshal(w.Body.Bytes(), &response)
        require.NoError(s.T(), err)
        _, ok := response["expiry_in"]
        assert.True(s.T(), ok)
    
        require.NoError(s.T(), s.mock.ExpectationsWereMet())
    
    }
于 2022-01-24T08:09:45.490 回答