Go sqlmock与查询不匹配,但查询相同,日志输出显示相同

Go sqlmock与查询不匹配,但查询相同,日志输出显示相同,go,go-gorm,go-sqlmock,Go,Go Gorm,Go Sqlmock,我正在尝试使用Gorm和sqlmock为一些代码编写测试。我想为我的insert函数编写测试,但现在为了让更新正常工作,我又费尽心思了 工作流的第一部分只是从数据库中查询记录。我无法让它与我的SQL匹配,即使日志输出显示它们是相同的 以下是错误消息: (/path/to/my/project/database.go:263) [2020-01-08 10:29:40] Query: could not match actual sql: "SELECT * FROM "storage_pool

我正在尝试使用Gorm和sqlmock为一些代码编写测试。我想为我的insert函数编写测试,但现在为了让更新正常工作,我又费尽心思了

工作流的第一部分只是从数据库中查询记录。我无法让它与我的SQL匹配,即使日志输出显示它们是相同的

以下是错误消息:

(/path/to/my/project/database.go:263)
[2020-01-08 10:29:40]  Query: could not match actual sql: "SELECT * FROM "storage_pools" WHERE "storage_pools"."deleted_at" IS NULL AND ((poolid = ?)) ORDER BY "storage_pools"."id" ASC LIMIT 1" with expected regexp "SELECT * FROM "storage_pools" WHERE "storage_pools"."deleted_at" IS NULL AND ((poolid = ?)) ORDER BY "storage_pools"."id" ASC LIMIT 1"
我还尝试使用ExpectQuery的ExpectExec insert

    for _, c := range cases {

        db, mock, err := sqlmock.New()
        if err != nil {
            t.Fatal(err)
        }

        DB, err := gorm.Open("sqlite3", db)
        if err != nil {
            t.Fatal(err)
        }
        DB.LogMode(true)

        mock.ExpectQuery(`SELECT * FROM "storage_pools" WHERE "storage_pools"."deleted_at" IS NULL AND ((poolid = ?)) ORDER BY "storage_pools"."id" ASC LIMIT 1`)

        err = UpdateStoragePool(DB, &c.givenPool)
        if !reflect.DeepEqual(c.wantedError, err) {
            t.Fatalf("expecting errror %q, got %q", c.wantedError, err)
        }

        // if we didn't have any errors during the tx, check all expectations were met
        if c.wantedError == nil {
            if err := mock.ExpectationsWereMet(); err != nil {
                t.Fatalf(err.Error())
            }
        }

    }
mockedRow := sqlmock.NewRows([]string{"id", "created_at", "updated_at", "poolid"}).AddRow(1, time.Now(), time.Now(), "1")
mockedRow2 := sqlmock.NewRows([]string{"id", "created_at", "updated_at", "poolid"}).AddRow(1, time.Now(), time.Now(), "1")

        // Mock the complete transaction
        mock.ExpectQuery(`SELECT * FROM "storage_pools"  WHERE "storage_pools"."deleted_at" IS NULL AND ((poolid = ?)) ORDER BY "storage_pools"."id" ASC LIMIT 1`).
            WithArgs(c.givenPool.PoolId).
            WillReturnRows(mockedRow)

        mock.ExpectQuery(`SELECT * FROM "storage_pools" WHERE "storage_pools"."deleted_at" IS NULL AND "storage_pools"."id" = ? AND ((poolid = ?)) ORDER BY "storage_pools"."id" ASC`).
            WithArgs(1, c.givenPool.PoolId).
            WillReturnRows(mockedRow2)
我也试过:

mock.ExpectQuery(`SELECT * FROM "storage_pools" WHERE "storage_pools"."deleted_at" IS NULL AND ((poolid = '1')) ORDER BY "storage_pools"."id" ASC LIMIT 1`).WithArgs(1)  

mock.ExpectExec(`SELECT * FROM "storage_pools" WHERE "storage_pools"."deleted_at" IS NULL AND ((poolid = ?)) ORDER BY "storage_pools"."id" ASC LIMIT 1`)  

mock.ExpectExec(`SELECT * FROM "storage_pools" WHERE "storage_pools"."deleted_at" IS NULL AND ((poolid = '1')) ORDER BY "storage_pools"."id" ASC LIMIT 1`).WithArgs(1)
有人知道我做错了什么吗

*更新*

由于某些原因,这不适用于select语句:

        mock.ExpectExec(`SELECT \* FROM "storage_pools"`).
            WithArgs(c.givenPool.PoolId).WillReturnResult(sqlmock.NewResult(1, 1))
这确实有效,但现在我遇到了一个新问题。首先,出于某种原因,Gorm正在做2个select语句。。。第一个查询工作并找到行,第二个查询找不到同一行。我在这里不知所措。就要放弃这个图书馆了。我本可以在我们花时间试图让它工作的时候写我自己的

        db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
        if err != nil {
            t.Fatal(err)
        }

        DB, err := gorm.Open("postgres", db)
        if err != nil {
            t.Fatal(err)
        }
        DB.LogMode(true)

        mockedRow := sqlmock.NewRows([]string{"id", "created_at", "updated_at", "poolid"}).AddRow(1, time.Now(), time.Now(), "1")

        // Mock the complete transaction
        mock.ExpectQuery(`SELECT * FROM "storage_pools"  WHERE "storage_pools"."deleted_at" IS NULL AND ((poolid = ?)) ORDER BY "storage_pools"."id" ASC LIMIT 1`).
            WithArgs(c.givenPool.PoolId).
            WillReturnRows(mockedRow)

        mock.ExpectQuery(`SELECT * FROM "storage_pools" WHERE "storage_pools"."deleted_at" IS NULL AND "storage_pools"."id" = ? AND ((poolid = ?)) ORDER BY "storage_pools"."id" ASC`).
            WithArgs(1, c.givenPool.PoolId).
            WillReturnRows(mockedRow)
mock.ExpectExec()
函数不执行简单的字符串比较。相反,它使用输入字符串作为RegExp来匹配查询

SQL匹配字符串中的某些字符是保留的RegExp字符,应进行转义以匹配SQL

转义后,字符串应如下所示:

从“存储池”中选择\*,其中“存储池”“已删除池”为空,并且\(\(池id \=\?\)\)按“存储池”“id”ASC限制1排序。

提示:您可以使用或其他网站在线转义字符串

附加思想:使用精确SQL匹配的测试可能很脆弱,而不会为精确SQL增加太多额外的值

若有人在测试中做了无害的更改,比如添加了额外的空格字符,那个么测试可能会给出假阳性结果。另一方面,全文匹配不会捕获与SQL不兼容的DB架构更改

我为我的项目完成了以下设置:

使用
mock.ExpectExec()
和基本子字符串(如
INSERT-INTO-history)运行单元测试。这使得测试不那么脆弱。同时,我们仍在本测试中进行大量检查,以验证代码执行流:

  • SQL参数数
  • 这些SQL参数的值
  • 确保使用mock.ExpectationsWereMet()执行SQL命令
  • 除此之外,我们还必须为SQL查询运行集成测试。这是确保我们的SQL是正确的并且是最新的DB更改的唯一方法

    注意:在选择中避免使用
    *
    。请使用字段名明确显示

    更新1:

    小心你的箱子。“选择”和“选择”是两个不同的字符串

    我当前项目中的一些代码片段:

    //插入
    ExpectExec(“插入eeo”)。
    用args(“2018-12-31”,“约翰·道”,“标题”})。
    WillReturnResult(sqlmock.NewResult(mock.EeoID,1))
    //挑选
    行:=sqlmock.NewRows([]字符串{“req_id”,“state”})
    sqlMock.ExpectQuery(“select”).WithArgs(mock.CandidateID).WillReturnRows(rows)
    
    这是一个奇怪的解决方案,但对我来说很有效。可能是sqlmock中的一个bug。复制mockedRow变量并将其插入ExpectQuery

        for _, c := range cases {
    
            db, mock, err := sqlmock.New()
            if err != nil {
                t.Fatal(err)
            }
    
            DB, err := gorm.Open("sqlite3", db)
            if err != nil {
                t.Fatal(err)
            }
            DB.LogMode(true)
    
            mock.ExpectQuery(`SELECT * FROM "storage_pools" WHERE "storage_pools"."deleted_at" IS NULL AND ((poolid = ?)) ORDER BY "storage_pools"."id" ASC LIMIT 1`)
    
            err = UpdateStoragePool(DB, &c.givenPool)
            if !reflect.DeepEqual(c.wantedError, err) {
                t.Fatalf("expecting errror %q, got %q", c.wantedError, err)
            }
    
            // if we didn't have any errors during the tx, check all expectations were met
            if c.wantedError == nil {
                if err := mock.ExpectationsWereMet(); err != nil {
                    t.Fatalf(err.Error())
                }
            }
    
        }
    
    mockedRow := sqlmock.NewRows([]string{"id", "created_at", "updated_at", "poolid"}).AddRow(1, time.Now(), time.Now(), "1")
    mockedRow2 := sqlmock.NewRows([]string{"id", "created_at", "updated_at", "poolid"}).AddRow(1, time.Now(), time.Now(), "1")
    
            // Mock the complete transaction
            mock.ExpectQuery(`SELECT * FROM "storage_pools"  WHERE "storage_pools"."deleted_at" IS NULL AND ((poolid = ?)) ORDER BY "storage_pools"."id" ASC LIMIT 1`).
                WithArgs(c.givenPool.PoolId).
                WillReturnRows(mockedRow)
    
            mock.ExpectQuery(`SELECT * FROM "storage_pools" WHERE "storage_pools"."deleted_at" IS NULL AND "storage_pools"."id" = ? AND ((poolid = ?)) ORDER BY "storage_pools"."id" ASC`).
                WithArgs(1, c.givenPool.PoolId).
                WillReturnRows(mockedRow2)
    
    或者,您可以创建一个mockedRow数组,如下所示:

    mockedRow := []*sqlmock.Rows{
    sqlmock.NewRows([]string{"id", "created_at", "updated_at", "poolid"}).AddRow(1, time.Now(), time.Now(), "1"),
    sqlmock.NewRows([]string{"id", "created_at", "updated_at", "poolid"}).AddRow(1, time.Now(), time.Now(), "1"),
    }
    And use it as WillReturnRows(mockedRow[0]) and WillReturnRows(mockedRow[1])
    
    试试这个:

    mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "storage_pools" WHERE "storage_pools"."deleted_at" IS NULL AND ((poolid = ?)) ORDER BY "storage_pools"."id" ASC LIMIT 1`))
    

    将查询放入此函数regexp.QuoteMeta()。

    我无法使用“插入历史记录”方法使其工作。这种方法适用于我的insert测试,但由于某些原因,我无法使它适用于select语句。如何处理select语句?我还没有让ExpectExec开始工作。无论我做了多少,我都无法使查询匹配。另外,如何确认使用ExpectExec返回的行,因为它不支持WillReturnRows。@Crashk1d Reg EX区分大小写。确保在测试中使用案例“选择”(而不是“选择”)您使用的是什么数据库?我绝对不能让ExpectQuery(“选择”)工作。尝试了各种可能的排列,直到我脸色发青。这在我的插入函数测试中很好,但在这里不行。我也在使用Gorm,我认为这是我在这里感到沮丧的原因。在我使用mysql和Gorm mysql驱动程序之间,不是最棒的驱动程序,我在这里讨论一些东西。戈姆在幕后做了很多事。sqlmock的错误消息和日志记录还有很多需要改进的地方。同意,我已经从GORM日志中复制粘贴了确切的查询,但仍然无法匹配。不适用于MySQL和Sqlite3。在这一点上,我可能只是摆脱了整个gorm,而使用简单的SQL,因为它并不真正值得头痛。这是我在这个主题上能找到的最有用的东西。谢谢对于大型查询,手动转义查询几乎是不可能的。这是最有用的答案。谢谢