Go sqlmock与查询不匹配,但查询相同,日志输出显示相同
我正在尝试使用Gorm和sqlmock为一些代码编写测试。我想为我的insert函数编写测试,但现在为了让更新正常工作,我又费尽心思了 工作流的第一部分只是从数据库中查询记录。我无法让它与我的SQL匹配,即使日志输出显示它们是相同的 以下是错误消息: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
(/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,因为它并不真正值得头痛。这是我在这个主题上能找到的最有用的东西。谢谢对于大型查询,手动转义查询几乎是不可能的。这是最有用的答案。谢谢