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

GORM软删除sqlmock测试UPDATE语句匹配失败报错排查

GORM软删除单元测试sqlmock匹配失败问题

问题场景

编写Workspace软删除逻辑的单元测试时,其余CRUD测试均可正常运行,仅软删除触发的UPDATE语句mock校验不通过。

相关代码

单元测试用例

func TestDeleteWorkspace(t *testing.T) {
    conn, mock, repository, err := setup()
    defer conn.Close()
    assert.NoError(t, err)

    uid := uuid.New()

    // mock.ExpectBegin()
    mock.ExpectQuery(regexp.QuoteMeta(`UPDATE "workspaces" SET`)).WithArgs(sqlmock.AnyArg(), uid)
    // mock.ExpectCommit()

    var e bool
    e, err = repository.Delete(uid)
    assert.NoError(t, err)
    assert.True(t, e)

    err = mock.ExpectationsWereMet()
    assert.NoError(t, err)
}

仓储层Delete实现

func (r *WorkspaceRepository) Delete(id any) (bool, error) {
    if err := r.db.Delete(&model.Workspace{}, "id = ?", id).Error; err != nil {
        return false, nil
    }

    return true, nil
}

Workspace模型定义

模型使用GORM内置软删除特性:

type Workspace struct {
    ID        uuid.UUID      `gorm:"type:uuid;default:uuid_generate_v4()" json:"id"`
    Name      string         `gorm:"not null,type:text" json:"name"`
    CreatedAt time.Time      `gorm:"autoCreateTime" json:"create_time"`
    UpdatedAt time.Time      `gorm:"autoUpdateTime" json:"update_time"`
    DeletedAt gorm.DeletedAt `gorm:"index,->" json:"-"`
}

软删除实际生成的SQL语句:

UPDATE "workspaces" SET "deleted_at"='2022-07-04 09:09:20.778' WHERE id = 'c4610193-b43a-4ed7-9ed6-9d67b3f97502' AND "workspaces"."deleted_at" IS NULL

报错信息

测试运行返回错误:

workspace_test.go:169: 
    Error Trace:    workspace_test.go:169
    Error:          Received unexpected error:
                    there is a remaining expectation which was not matched: ExpectedQuery => expecting Query, QueryContext or QueryRow which:
                      - matches sql: 'UPDATE "workspaces" SET'
                      - is with arguments:
                        0 - 28e7aa46-7a22-4dc7-b3ce-6cf02af525ca
                        1 - {}

根因分析

mock配置存在3处错误:

  • 匹配方法类型错误:UPDATE/DELETE/INSERT这类写操作,GORM会调用驱动的Exec接口执行,ExpectQuery仅能匹配SELECT这类走Query接口的读操作,因此定义的期望始终无法被命中,最终提示存在未匹配的剩余期望。
  • 事务流程期望缺失:GORM默认对所有写操作开启隐式事务,注释掉ExpectBegin()和ExpectCommit()会导致事务流程校验不通过。
  • 参数匹配数量不足:GORM自动拼接的deleted_at IS NULL软删除条件会额外携带一个空值参数,仅配置2个参数的匹配规则无法覆盖全部入参。

修复方案

调整测试用例的mock配置即可:

func TestDeleteWorkspace(t *testing.T) {
    conn, mock, repository, err := setup()
    defer conn.Close()
    assert.NoError(t, err)

    uid := uuid.New()

    // 补全事务期望
    mock.ExpectBegin()
    // 写操作改用ExpectExec匹配,补充参数匹配规则,定义执行返回结果
    mock.ExpectExec(regexp.QuoteMeta(`UPDATE "workspaces" SET`)).
        WithArgs(sqlmock.AnyArg(), uid, sqlmock.AnyArg()).
        WillReturnResult(sqlmock.NewResult(1, 1))
    mock.ExpectCommit()

    var e bool
    e, err = repository.Delete(uid)
    assert.NoError(t, err)
    assert.True(t, e)

    err = mock.ExpectationsWereMet()
    assert.NoError(t, err)
}

配置说明:动态生成的时间值、软删除条件携带的空值都用sqlmock.AnyArg()匹配即可,不需要硬编码固定值,可兼容不同GORM版本的参数生成逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 04:31:11