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

Golang squirrel含VALUES子句查询的sqlmock单元测试匹配问题

问题根因

Go 原生 map 的遍历顺序是随机的,直接遍历 map 生成 squirrel 的 VALUES 行数据时,每次输出的行顺序都不固定,sqlmock 默认按固定字符串/正则规则匹配 SQL,自然无法命中预期。

解决方案

优先选第一种方案,实现成本最低,维护性最好。

方案1:构建查询时固定遍历顺序(推荐)

在读取 map 数据拼接 VALUES 前,先对 map 的 key 做排序,按固定顺序遍历生成行数据,就能保证每次生成的 SQL 中 VALUES 子句的行顺序完全一致,单测直接写死预期 SQL 即可。
示例代码:

import (
    "sort"
    "github.com/Masterminds/squirrel"
)

func BuildCountryRegionQuery(codeMap map[string]string) squirrel.SelectBuilder {
    // 提取map所有key
    keys := make([]string, 0, len(codeMap))
    for k := range codeMap {
        keys = append(keys, k)
    }
    // 按固定规则排序,这里用字典序,可根据业务需求调整排序逻辑
    sort.Strings(keys)

    // 按排序后的顺序生成VALUES行
    valueRows := make([]interface{}, 0, len(keys))
    for _, k := range keys {
        valueRows = append(valueRows, squirrel.Expr("(?,?)", k, codeMap[k]))
    }

    // 拼接最终查询,PostgreSQL记得用Dollar占位符格式
    return squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar).
        Select("country_code", "region_code").
        FromValues(valueRows, "country_code", "region_code")
}

注意:如果业务本身对 VALUES 的行顺序有明确要求,直接按业务规则排序即可,不需要额外加字典序排序,保证单测顺序和业务逻辑顺序一致就行。字典序排序的性能开销极低,即使map有上百个key,对整体查询性能的影响也可以忽略不计。

方案2:自定义sqlmock匹配规则(业务不要求行顺序时用)

如果业务层面不需要固定 VALUES 的行顺序,就不要硬改生产代码的逻辑,直接给 sqlmock 写自定义匹配器,跳过顺序校验,只校验 SQL 结构和参数集合是否符合预期。
示例代码:

import (
    "reflect"
    "strings"
    "testing"
    "github.com/DATA-DOG/go-sqlmock"
)

func TestBuildQuery(t *testing.T) {
    db, mock, err := sqlmock.New()
    if err != nil {
        t.Fatalf("init mock error: %v", err)
    }
    defer db.Close()

    expectedMap := map[string]string{"us":"us", "ca":"ca", "se":"eu"}
    // 自定义SQL匹配逻辑
    mock.ExpectQuery(func(actualSQL string) bool {
        // 先校验SQL基础结构
        if !strings.Contains(actualSQL, "FROM (VALUES") {
            return false
        }
        // 自行实现解析逻辑,提取actualSQL中VALUES里的所有值对,转成和expectedMap同结构的集合
        actualPairs := parseValuesPairs(actualSQL)
        // 无序对比两个集合是否完全一致
        return reflect.DeepEqual(actualPairs, expectedMap)
    }).WillReturnRows(sqlmock.NewRows([]string{"country_code", "region_code"}))

    // 执行目标查询逻辑
    // ...
}

这种方案不需要改生产代码,但是需要写简单的SQL片段解析逻辑,适合对VALUES顺序无要求的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 09:18:27