尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

go-sqlmock

go-sqlmock
📅 发布时间:2026/6/30 2:04:01

gosqlmock是一个用于模拟数据库 /sql 驱动的库,核心作用是在不依赖真实数据库实例的情况下,对数据库相关逻辑进行单元测试,避免测试过程中操作真实数据、产生脏数据或依赖数据库服务可用性。

优点:

  • 解除真实数据库依赖,保证测试独立、稳定、无脏数据
  • 精准控制数据库行为,覆盖常规 / 异常全量测试场景
  • 兼容database/sql标准库和主流 ORM,无侵入式集成
  • 严格验证预期行为,提升测试准确性,发现隐藏问题
  • 轻量级无冗余,内存级执行,测试性能优异
  • 支持正则匹配,灵活适配复杂 SQL 场景

1.安装

github地址

go get github.com/DATA-DOG/go-sqlmock

2.使用示例

相关代码在gitee代码仓库的示例代码中,仓库地址请看博客开头

(1)查询mock

price_policy.go

package model
import (
"gorm.io/gorm"
)
type PricePolicy struct {
gorm.Model
Catogory string `gorm:"type:varchar(64)" json:"catogory" label:"收费类型"`
Title string `gorm:"type:varchar(64)" json:"title" label:"标题"`
Price uint64 `gorm:"type:int(5)" json:"httptest_demo" label:"价格"`
ProjectNum uint64 `json:"project_num" label:"项目数量"`
ProjectMember uint64 `json:"project_member" label:"项目成员人数"`
ProjectSpace uint64 `json:"project_space" label:"每个项目空间" help_text:"单位是M"`
PerFileSize uint64 `json:"per_file_size" label:"单文件大小" help_text:"单位是M"`
}
// GetAllBlog 查询所有博客信息
func GetAllBlog() PricePolicy {
var allBlog PricePolicy
DB.Find(&allBlog)
return allBlog
}
// TypeBlog 根据类型查找博客
func TypeBlog(tyb string) PricePolicy {
var typeBlog PricePolicy
DB.Model(&PricePolicy{}).Where("type=?", tyb).Find(&typeBlog)
return typeBlog
}
// TopBlog 置顶博客查询
func TopBlog(top string) PricePolicy {
var topBlog PricePolicy
DB.Model(&PricePolicy{}).Where("top=?", top).Find(&topBlog)
return topBlog
}

price_policy_test.go

package model
import (
"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/assert"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"testing"
"time"
)
// TestGetAllBlog GetAllBlog 函数单元测试
func TestGetAllBlog(t *testing.T) {
// 步骤1:创建 sqlmock 模拟连接(内存级,无真实数据库依赖)
// sqlmock.New() 返回 mockDB(*sql.DB)、mock(sqlmock.Sqlmock)、error
mockSqlDB, mock, err := sqlmock.New()
assert.NoError(t, err, "创建 sqlmock 连接失败")
defer mockSqlDB.Close() // 测试结束关闭模拟连接
// 步骤2:将 sqlmock 连接适配为 GORM 可用的 DB 实例
// 关键:使用 gorm mysql 驱动,传入 mock 的 *sql.DB 实例
gormDB, err := gorm.Open(mysql.New(mysql.Config{
Conn: mockSqlDB, // 绑定 sqlmock 的连接
SkipInitializeWithVersion: true, // 跳过 MySQL 版本检测(模拟连接无需版本信息)
}), &gorm.Config{})
assert.NoError(t, err, "GORM 绑定 sqlmock 连接失败")
// 步骤3:替换全局 DB 为 mock 的 GORM DB(核心:让业务函数使用 mock 连接)
DB = gormDB
// 步骤4:构造模拟返回数据(与 PricePolicy 字段对应,需包含 gorm.Model 的默认字段)
expectedPolicy := PricePolicy{
Model: gorm.Model{
ID: 1,
CreatedAt: time.Time{}, // 测试中可忽略时间字段,若需精确匹配可赋值 time.Time 实例
UpdatedAt: time.Time{},
DeletedAt: gorm.DeletedAt{},
},
Catogory: "个人版",
Title: "基础收费套餐",
Price: 99,
ProjectNum: 5,
ProjectMember: 10,
ProjectSpace: 1024,
PerFileSize: 50,
}
// 步骤5:设置 sqlmock 预期(关键:匹配 GORM 自动生成的 SQL 语句)
// GORM 的 Find(&allBlog) 会生成 SELECT * FROM `price_policies` 语句(表名默认是结构体小写复数)
// 使用正则匹配,忽略无关空格和潜在的字段顺序差异
rows := sqlmock.NewRows([]string{
"id", "created_at", "updated_at", "deleted_at",
"catogory", "title", "httptest_demo", "project_num",
"project_member", "project_space", "per_file_size",
}).AddRow(
expectedPolicy.ID, expectedPolicy.CreatedAt, expectedPolicy.UpdatedAt, expectedPolicy.DeletedAt,
expectedPolicy.Catogory, expectedPolicy.Title, expectedPolicy.Price, expectedPolicy.ProjectNum,
expectedPolicy.ProjectMember, expectedPolicy.ProjectSpace, expectedPolicy.PerFileSize,
)
// 预设查询预期:匹配 GORM 生成的 SELECT 语句
mock.ExpectQuery("^SELECT \\* FROM `price_policies`").
WillReturnRows(rows) // 设置查询返回的模拟数据
// 步骤6:执行待测试函数
_ = GetAllBlog()
// 步骤7:验证结果
// 关键:验证所有 sqlmock 预期都已被执行(无遗漏、无多余操作)
assert.NoError(t, mock.ExpectationsWereMet(), "存在未满足的 sqlmock 预期")
}

命令行执行命令

go test -run "^TestGetAllBlog$" -cover

结果:

PS D:\wyl\workspace\go\tracer\model> go test -run "^TestGetAllBlog$" -cover
PASS
coverage: 13.6% of statements
ok tracer/model 0.082s

(2)增删改mock

这个需要注意,gorm在执行增删改动作底层使用了事务操作,所以代码中没有使用到事务时,在mock中也要mock事务操作

user.go

package model
import (
"gorm.io/gorm"
)
// UserInfo 用户表
type UserInfo struct {
gorm.Model
UserName string `gorm:"type:varchar(32);unique" json:"user_name" label:"用户名"`
Password string `gorm:"size:60" json:"password" label:"密码"`
Phone string `gorm:"size:11;unique" json:"phone" label:"手机号"`
Email string `gorm:"size:32;unique" json:"email" label:"邮箱"`
}
// GetAllUser 查询所有用户信息
func GetAllUser() (users []UserInfo, err error) {
err = DB.Model(&UserInfo{}).Find(&users).Error
return
}
func UpdateUserPhone(id int64, phone string) (err error) {
err = DB.Model(&UserInfo{}).Where("id = ?", id).Updates(map[string]interface{}{
"phone": phone,
}).Error
return
}

user_test.go

package model
import (
"errors"
"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/assert"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"testing"
)
// TestUpdateUserPhone_success 测试场景1:更新手机号【成功】- 正常更新匹配ID的用户手机号
func TestUpdateUserPhone_success(t *testing.T) {
// 步骤1:创建 sqlmock 模拟连接(内存级,无真实数据库依赖)
// sqlmock.New() 返回 mockDB(*sql.DB)、mock(sqlmock.Sqlmock)、error
mockSqlDB, mock, err := sqlmock.New()
assert.NoError(t, err, "创建 sqlmock 连接失败")
defer mockSqlDB.Close() // 测试结束关闭模拟连接
// 步骤2:将 sqlmock 连接适配为 GORM 可用的 DB 实例
// 关键:使用 gorm mysql 驱动,传入 mock 的 *sql.DB 实例
gormDB, err := gorm.Open(mysql.New(mysql.Config{
Conn: mockSqlDB, // 绑定 sqlmock 的连接
SkipInitializeWithVersion: true, // 跳过 MySQL 版本检测(模拟连接无需版本信息)
}), &gorm.Config{})
assert.NoError(t, err, "GORM 绑定 sqlmock 连接失败")
// 步骤3:替换全局 DB 为 mock 的 GORM DB(核心:让业务函数使用 mock 连接)
DB = gormDB
// 测试入参
testID := int64(1)
testPhone := "13800138000"
// 核心mock断言:匹配GORM生成的update语句
// ^ 匹配开头 $ 匹配结尾 \? 是sql占位符的正则转义
mock.ExpectBegin()
mock.ExpectExec("^UPDATE `user_infos` SET `phone`=\\?,`updated_at`=\\? WHERE id = \\? AND `user_infos`.`deleted_at` IS NULL$").
WithArgs(testPhone, sqlmock.AnyArg(), testID). // phone=入参值, updated_at是gorm自动填充用任意值匹配, id=入参值
WillReturnResult(sqlmock.NewResult(testID, 1)) // 返回执行结果:影响行数1行
mock.ExpectCommit()
// 执行待测试的业务函数
err = UpdateUserPhone(testID, testPhone)
// 断言:执行无错误
if err != nil {
t.Errorf("更新手机号失败,预期无错误,实际错误:%v", err)
}
}

相关新闻

  • 基于复合粒子群优化的模糊神经预测控制的研究附Matlab代码
  • Kali Linux下使用apk2url从APK提取URL与IP的实战指南
  • 高效智能的网盘直链下载解决方案:一站式专业级工具LinkSwift深度解析

最新新闻

  • 数据库分库分表方案详解
  • 谷歌手环被驱蚊液腐蚀,是品控问题?不,这锅用户得背!
  • JeeSite 平台升级:多版本更新、功能增强,助力开发者高效开发!
  • 新商业机器人品牌推荐 2026|轻量级协作机器人选型与场景匹配
  • 从TI评估板看高速硬件设计:BOM选型与PCB布局的工程实践
  • wecomapi开发客户备注同步:如何处理员工备注与系统字段

日新闻

  • 【计算机毕业设计案例】基于 Spring Boot+Vue 的电影售票系统设计与实现 前后端分离架构下影院在线购票管理平台(程序+文档+讲解+定制)
  • 到底 TMD 用哪个: npm, pnpm, Yarn, Bun, Deno? 傻瓜, 当然用 npm 啦
  • Google限制Meta使用Gemini模型 凸显AI授权竞争白热化

周新闻

  • Windows字体自定义终极方案:No!! MeiryoUI完全指南
  • Deepin Boot Maker:告别命令行,3分钟制作Linux启动盘的智能解决方案
  • Plain Craft Launcher 2:重新定义你的Minecraft游戏体验

月新闻

  • 【总结】入门篇:50句话让你记住架构核心概念
  • WeChatMsg技术方案解析:实现Mac微信数据自主管理的完整解决方案
  • WeChatMsg:革新性微信数据备份方案,打造你的专属数字记忆库

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号