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

13 go go-gorm go-sqlmock

我正在尝试使用 sqlmock 使用 Gorm 为某些代码编写测试。我想出为我的插入功能编写测试,但现在我正在努力让更新工作。

工作流的第一部分只是从数据库中查询记录。即使日志输出显示它们相同,我也无法让它与我的 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"
Run Code Online (Sandbox Code Playgroud)

我也尝试使用 ExpectQuery 的 ExpectExec 插入。

    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())
            }
        }

    }
Run Code Online (Sandbox Code Playgroud)

我也试过:

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)
Run Code Online (Sandbox Code Playgroud)

任何人都知道我在这里做错了什么?

* 更新 *

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

        mock.ExpectExec(`SELECT \* FROM "storage_pools"`).
            WithArgs(c.givenPool.PoolId).WillReturnResult(sqlmock.NewResult(1, 1))
Run Code Online (Sandbox Code Playgroud)
[2020-01-13 10:32:21]  call to Query 'SELECT * FROM "storage_pools"  WHERE "storage_pools"."deleted_at" IS NULL AND ((poolid = ?)) ORDER BY "storage_pools"."id" ASC LIMIT 1' with args [{Name: Ordinal:1 Value:1}], was not expected, next expectation is: ExpectedExec => expecting Exec or ExecContext which:
  - matches sql: 'SELECT \* FROM "storage_pools"'
  - is with arguments:
    0 - 1
  - should return Result having:
      LastInsertId: 1
      RowsAffected: 1
Run Code Online (Sandbox Code Playgroud)

这确实有效,但现在我遇到了一个新问题。对于初学者 Gorm 出于某种原因正在执行 2 个选择语句......第一个工作并找到行,第二个查询没有找到相同的行。我在这里不知所措。即将放弃这个图书馆。我本可以在我们尝试让它工作的时间里写我自己的。

        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)
Run Code Online (Sandbox Code Playgroud)

小智 22

尝试这个:

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`))
Run Code Online (Sandbox Code Playgroud)

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

  • 如果您解释说“.ExpectQuery()”正在将字符串解析为正则表达式并且需要转义字符,那就太好了 (3认同)

Dmi*_*ski 10

mock.ExpectExec()函数不执行简单的字符串比较。相反,它使用输入字符串作为 RegExp 来匹配查询。

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

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

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

提示:您可以使用https://www.regex-escape.com/preg_quote-online.php或其他网站在线转义您的字符串

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

如果有人对其进行了无害的更改(例如添加额外的空格字符),则测试可能会给您误报结果。另一方面,全文匹配不会捕获与 SQL 不兼容的数据库架构更改。

我最终为我的项目完成了这个设置:

与运行单元测试mock.ExpectExec()中包含的基本子INSERT INTO history。这使得测试不那么脆弱。同时我们还在本次测试中进行了大量检查以验证代码执行流程:

  1. SQL 参数的数量
  2. 这些 SQL 参数的值
  3. 确保使用 mock.ExpectationsWereMet() 执行 SQL 命令

最重要的是,我们必须为我们的 SQL 查询运行集成测试。这是确保我们的 SQL 正确且与最新的数据库更改保持同步的唯一方法。

PS 避免*在选择中。明确使用字段名称。

更新1:

小心字符串大小写。“SELECT”和“select”是两个不同的字符串。

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

// insert
sqlMock.ExpectExec("INSERT INTO eeo").
        WithArgs("2018-12-31", "John Dow", "title"}).
        WillReturnResult(sqlmock.NewResult(mock.EeoID, 1))

// select
rows := sqlmock.NewRows([]string{"req_id", "state"})
sqlMock.ExpectQuery("select").WithArgs(mock.CandidateID).WillReturnRows(rows)
Run Code Online (Sandbox Code Playgroud)