You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用go-sqlmock模拟PostgreSQL的INSERT语句失败,如何解决?

解决Go-Sqlmock匹配GORM自动生成时间戳的测试问题

我在用go-sqlmock为GORM编写的业务代码写单元测试时遇到了问题:业务代码会自动生成时间戳,用sqlmock.AnyArg()匹配动态时间值失败,测试不通过。

业务代码

func (c *ObjectStoreClient) CreateObject(data *file.Object) error {
    tx := Client.DB.Begin()

    err := tx.Create(&data).Error
    if err != nil {
        tx.Rollback()
        return err
    }

    return tx.Commit().Error
}

单元测试代码

func TestFxFS_Create(t *testing.T) {
    asserts := assert.New(t)
    objectID := "7dd48234-8b92-422b-b21e-27f9bd3074a2"

    // Success
    {
        fxfs := NewFS(rootID)
        mock.ExpectBegin()
        mock.ExpectExec("INSERT INTO (.+)").
            WithArgs("7dd48234-8b92-422b-b21e-27f9bd3074a2", "testFile", 0, "dir", "", "", rootID, 1, sqlmock.AnyArg(), sqlmock.AnyArg()).
            WillReturnResult(sqlmock.NewResult(1, 1))
        mock.ExpectCommit()

        data := &file.Object{
            ObjectID: objectID,
            FileInfo: &file.Info{
                FileName:       "testFile",
                FileSize:       0,
                FileType:       "dir",
                FileHash:       "",
                StorageID:      1,
                SourcePath:     "",
                ParentObjectID: rootID,
            },
        }

        resp, err := fxfs.Create(data)

        asserts.Empty(err)
        asserts.NotEmpty(resp)
        asserts.NotEmpty(resp.Object)
    }
}

错误信息

2024/05/03 11:28:49 /home/fanxing/project/clouddrive_Microservices/cmd/file/biz/fxfs/store/fs.go:18 call to Query 'INSERT INTO "objects" ("object_id","info_name","info_size","info_type","info_hash","info_source_path","info_parent_object_id","info_storage_id","info_created_time","info_mod_time") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10) RETURNING "id"' with args [{Name: Ordinal:1 Value:7dd48234-8b92-422b-b21e-27f9bd3074a2} {Name: Ordinal:2 Value:testFile} {Name: Ordinal:3 Value:0} {Name: Ordinal:4 Value:dir} {Name: Ordinal:5 Value:} {Name: Ordinal:6 Value:} {Name: Ordinal:7 Value:7dd48234-8b92-422b-b21e-27f9bd3074e5} {Name: Ordinal:8 Value:1} {Name: Ordinal:9 Value:2024-05-03 11:28:49.107053224 +0800 CST} {Name: Ordinal:10 Value:2024-05-03 11:28:49.107053224 +0800 CST}], was not expected, next expectation is: ExpectedExec => expecting Exec or ExecContext which:
  - matches sql: 'INSERT INTO (.+)'
  - is with arguments:
    0 - 7dd48234-8b92-422b-b21e-27f9bd3074a2
    1 - testFile
    2 - 0
    3 - dir
    4 - 
    5 - 
    6 - 7dd48234-8b92-422b-b21e-27f9bd3074e5
    7 - 1
    8 - {}
    9 - {}
  - should return Result having:
      LastInsertId: 1
      RowsAffected: 1
[0.246ms] [rows:0] INSERT INTO "objects" ("object_id","info_name","info_size","info_type","info_hash","info_source_path","info_parent_object_id","info_storage_id","info_created_time","info_mod_time") VALUES ('7dd48234-8b92-422b-b21e-27f9bd3074a2','testFile',0,'dir','','','7dd48234-8b92-422b-b21e-27f9bd3074e5',1,'2024-05-03 11:28:49.107','2024-05-03 11:28:49.107') RETURNING "id"
2024/05/03 11:28:49 ERROR 未知错误 err="call to Query 'INSERT INTO \"objects\" (\"object_id\",\"info_name\",\"info_size\",\"info_type\",\"info_hash\",\"info_source_path\",\"_parent_object_id\",\"info_storage_id\",\"info_created_time\",\"info_mod_time\") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10) RETURNING \"id\"' with args [{Name: Ordinal:1 Value:7dd48234-8b92-422b-b21e-27f9bd3074a2} {Name: Ordinal:2 Value:testFile} {Name: Ordinal:3 Value:0} {Name: Ordinal:4 Value:dir} {Name: Ordinal:5 Value:} {Name: Ordinal:6 Value:} {Name: Ordinal:7 Value:7dd48234-8b92-422b-b21e-27f9bd3074e5} {Name: Ordinal:8 Value:1} {Name: Ordinal:9 Value:2024-05-03 11:28:49.107053224 +0800 CST} {Name: Ordinal:10 Value:2024-05-03 11:28:49.107053224 +0800 CST}], was not expected, next expectation is: ExpectedExec => expecting Exec or ExecContext which:\n  - matches sql: 'INSERT INTO (.+)\n  - is with arguments:\n    0 - 7dd48234-8b92-422b-b21e-27f9bd3074a2\n    1 - testFile\n    2 - 0\n    3 - dir\n    4 - \n    5 - \n    6 - 7dd48234-8b92-422b-b21e-27f9bd3074e5\n    7 - 1\n    8 - {}\n    9 - {}\n  - should return Result having:\n      LastInsertId: 1\n      RowsAffected: 1"
--- FAIL: TestFxFS_Create (0.00s)
    fs_test.go:174: 
                Error Trace:    /home/fanxing/project/clouddrive_Microservices/cmd/file/biz/fxfs/fs_test.go:174
                Error:          Should be empty, but was create testFile: call to Query 'INSERT INTO "objects" ("object_id","info_name","info_size","info_type","info_hash","info_source_path","info_parent_object_id","info_storage_id","info_created_time","info_mod_time") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10) RETURNING "id"' with args [{Name: Ordinal:1 Value:7dd48234-8b92-422b-b21e-27f9bd3074a2} {Name: Ordinal:2 Value:testFile} {Name: Ordinal:3 Value:0} {Name: Ordinal:4 Value:dir} {Name: Ordinal:5 Value:} {Name: Ordinal:6 Value:} {Name: Ordinal:7 Value:7dd48234-8b92-422b-b21e-27f9bd3074e5} {Name: Ordinal:8 Value:1} {Name: Ordinal:9 Value:2024-05-03 11:28:49.107053224 +0800 CST} {Name: Ordinal:10 Value:2024-05-03 11:28:49.107053224 +0800 CST}], was not expected, next expectation is: ExpectedExec => expecting Exec or ExecContext which:
                                  - matches sql: 'INSERT INTO (.+)'
                                  - is with arguments:
                                    0 - 7dd48234-8b92-422b-b21e-27f9bd3074a2
                                    1 - testFile
                                    2 - 0
                                    3 - dir
                                    4 - 
                                    5 - 
                                    6 - 7dd48234-8b92-422b-b21e-27f9bd3074e5
                                    7 - 1
                                    8 - {}
                                    9 - {}
                                  - should return Result having:
                                      LastInsertId: 1
                                      RowsAffected: 1
                Test:           TestFxFS_Create
    fs_test.go:175: 
                Error Trace:    /home/fanxing/project/clouddrive_Microservices/cmd/file/biz/fxfs/fs_test.go:175
                Error:          Should NOT be empty, but was &{<nil> }
                Test:           TestFxFS_Create
    fs_test.go:176: 
                Error Trace:    /home/fanxing/project/clouddrive_Microservices/cmd/file/biz/fxfs/fs_test.go:176
                Error:          Should NOT be empty, but was <nil>
                Test:           TestFxFS_Create
FAIL
FAIL    drive/cmd/file/biz/fxfs 0.010s
FAIL

解决方案

问题根源有两个,针对性修改即可:

1. 替换ExpectExec为ExpectQuery

PostgreSQL中带RETURNING子句的INSERT操作,GORM会调用Query而非Exec方法执行,所以测试里的mock.ExpectExec需要换成mock.ExpectQuery,同时SQL匹配规则要包含RETURNING部分。

2. 用类型匹配替代AnyArg

sqlmock.AnyArg()在匹配时间戳时可能出现匹配失效,改用sqlmock.AnyOfTypeArgument("timestamp")明确匹配PostgreSQL的时间戳类型,确保动态生成的时间值能被正确识别。

修改后的测试核心代码

mock.ExpectBegin()
// 替换ExpectExec为ExpectQuery,匹配带RETURNING的INSERT语句
mock.ExpectQuery("INSERT INTO (.+) RETURNING (.+)").
    WithArgs("7dd48234-8b92-422b-b21e-27f9bd3074a2", "testFile", 0, "dir", "", "", rootID, 1, 
            sqlmock.AnyOfTypeArgument("timestamp"), sqlmock.AnyOfTypeArgument("timestamp")).
    // 返回模拟的RETURNING结果,对应id字段
    WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(1))
mock.ExpectCommit()

// 最后验证所有mock期望都被执行
if err := mock.ExpectationsWereMet(); err != nil {
    t.Errorf("未满足所有mock期望: %v", err)
}

内容的提问来源于stack exchange,提问作者Fanxing

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 17:55:53