单元测试-go-sqlmock

发布时间:2026/9/7 0:26:04

单元测试-go-sqlmock
项目demo地址go-test本文主要针对单元测试工具其他工具请看专栏内其它博客。go-sqlmock介绍gosqlmock是一个用于模拟数据库 /sql 驱动的库核心作用是在不依赖真实数据库实例的情况下对数据库相关逻辑进行单元测试避免测试过程中操作真实数据、产生脏数据或依赖数据库服务可用性。优点解除真实数据库依赖保证测试独立、稳定、无脏数据精准控制数据库行为覆盖常规 / 异常全量测试场景兼容database/sql标准库和主流 ORM无侵入式集成严格验证预期行为提升测试准确性发现隐藏问题轻量级无冗余内存级执行测试性能优异支持正则匹配灵活适配复杂 SQL 场景1.安装github地址go get github.com/DATA-DOG/go-sqlmock2.使用示例相关代码在gitee代码仓库的示例代码中仓库地址请看博客开头1查询mockprice_policy.gopackagemodelimport(gorm.io/gorm)typePricePolicystruct{gorm.Model Catogorystringgorm:type:varchar(64) json:catogory label:收费类型Titlestringgorm:type:varchar(64) json:title label:标题Priceuint64gorm:type:int(5) json:httptest_demo label:价格ProjectNumuint64json:project_num label:项目数量ProjectMemberuint64json:project_member label:项目成员人数ProjectSpaceuint64json:project_space label:每个项目空间 help_text:单位是MPerFileSizeuint64json:per_file_size label:单文件大小 help_text:单位是M}// GetAllBlog 查询所有博客信息funcGetAllBlog()PricePolicy{varallBlog PricePolicy DB.Find(allBlog)returnallBlog}// TypeBlog 根据类型查找博客funcTypeBlog(tybstring)PricePolicy{vartypeBlog PricePolicy DB.Model(PricePolicy{}).Where(type?,tyb).Find(typeBlog)returntypeBlog}// TopBlog 置顶博客查询funcTopBlog(topstring)PricePolicy{vartopBlog PricePolicy DB.Model(PricePolicy{}).Where(top?,top).Find(topBlog)returntopBlog}price_policy_test.gopackagemodelimport(github.com/DATA-DOG/go-sqlmockgithub.com/stretchr/testify/assertgorm.io/driver/mysqlgorm.io/gormtestingtime)// TestGetAllBlog GetAllBlog 函数单元测试funcTestGetAllBlog(t*testing.T){// 步骤1创建 sqlmock 模拟连接内存级无真实数据库依赖// sqlmock.New() 返回 mockDB(*sql.DB)、mock(sqlmock.Sqlmock)、errormockSqlDB,mock,err:sqlmock.New()assert.NoError(t,err,创建 sqlmock 连接失败)defermockSqlDB.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 连接DBgormDB// 步骤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结果PSD:\wyl\workspace\go\tracer\model go test-run^TestGetAllBlog$-cover PASS coverage: 13.6% of statements ok tracer/model 0.082s2增删改mock这个需要注意gorm在执行增删改动作底层使用了事务操作所以代码中没有使用到事务时在mock中也要mock事务操作user.gopackagemodelimport(gorm.io/gorm)// UserInfo 用户表typeUserInfostruct{gorm.Model UserNamestringgorm:type:varchar(32);unique json:user_name label:用户名Passwordstringgorm:size:60 json:password label:密码Phonestringgorm:size:11;unique json:phone label:手机号Emailstringgorm:size:32;unique json:email label:邮箱}// GetAllUser 查询所有用户信息funcGetAllUser()(users[]UserInfo,errerror){errDB.Model(UserInfo{}).Find(users).Errorreturn}funcUpdateUserPhone(idint64,phonestring)(errerror){errDB.Model(UserInfo{}).Where(id ?,id).Updates(map[string]interface{}{phone:phone,}).Errorreturn}user_test.gopackagemodelimport(errorsgithub.com/DATA-DOG/go-sqlmockgithub.com/stretchr/testify/assertgorm.io/driver/mysqlgorm.io/gormtesting)// TestUpdateUserPhone_success 测试场景1更新手机号【成功】- 正常更新匹配ID的用户手机号funcTestUpdateUserPhone_success(t*testing.T){// 步骤1创建 sqlmock 模拟连接内存级无真实数据库依赖// sqlmock.New() 返回 mockDB(*sql.DB)、mock(sqlmock.Sqlmock)、errormockSqlDB,mock,err:sqlmock.New()assert.NoError(t,err,创建 sqlmock 连接失败)defermockSqlDB.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 连接DBgormDB// 测试入参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()// 执行待测试的业务函数errUpdateUserPhone(testID,testPhone)// 断言执行无错误iferr!nil{t.Errorf(更新手机号失败预期无错误实际错误%v,err)}}funcTestUpdateUserPhone_error(t*testing.T){// 步骤1创建 sqlmock 模拟连接内存级无真实数据库依赖// sqlmock.New() 返回 mockDB(*sql.DB)、mock(sqlmock.Sqlmock)、errormockSqlDB,mock,err:sqlmock.New()assert.NoError(t,err,创建 sqlmock 连接失败)defermockSqlDB.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 连接DBgormDB// 测试入参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入参值WillReturnError(errors.New(更新手机号失败))mock.ExpectRollback()// 执行待测试的业务函数_UpdateUserPhone(testID,testPhone)}命令行执行命令go test -run “^TestUpdateUserPhone_success$” -cover结果PSD:\wyl\workspace\go\tracer\model go test-run^TestUpdateUserPhone_success$-cover PASS coverage: 9.1% of statements ok tracer/model 0.082sgo test -run “^TestUpdateUserPhone_error$” -cover结果2026/01/1117:05:36D:/wyl/workspace/go/tracer/model/user.go:24更新手机号失败[0.506ms][rows:0]UPDATEuser_infosSETphone13800138000,updated_at2026-01-1117:05:36.624 WHERE id1ANDuser_infos.deleted_atIS NULL PASS coverage:9.1%of statements ok tracer/model0.086s

相关新闻

云克隆吃透犬角膜基质细胞的培养与制备难点,用全链路可控工艺、高标准质控、稳定的细胞性状,规避实验试错成本

云克隆吃透犬角膜基质细胞的培养与制备难点,用全链路可控工艺、高标准质控、稳定的细胞性状,规避实验试错成本

2026/8/31 7:59:28

犬角膜基质细胞(Corneal Stromal Cells,CSC)作为大型哺乳动物眼科研究的核心原代模型,角膜结构、基质特性、病理反应和人类高度接近,是角膜愈合、瘢痕形成、抗炎机制、眼科新药筛选、角膜生物材料验证的“黄金实验细胞…

多云架构下,Atlas 怎么统一管理证书生命周期?

多云架构下,Atlas 怎么统一管理证书生命周期?

2026/8/23 0:40:47

现在企业的数字证书早就不是一两张的事了。公有云、私有云、容器平台、CDN、物联网设备、内部业务系统,每个地方都在用证书,每个证书都有自己的到期时间。 大多数企业都是证书快到期了才开始续期,这样就导致很多证书容易被遗忘导致一系列的问…

2026.7.8:2026年7月最新安装教程之docker compose 安装最新版neo4j

2026.7.8:2026年7月最新安装教程之docker compose 安装最新版neo4j

2026/9/1 21:55:54

2026年7月最新安装教程之docker compose 安装最新版neo4j 一、Neo4j和图数据库简介 neo4j是基于Java语言编写图形数据库。图是一组节点和连接这些节点的关系。图形数据库也被称为图形数据库管理系统或GDBMS。 Neo4j的是一种流行的图形数据库。 其他的图形数据库是Oracle NoSQL…

2026 AI视觉与物联网开发板选购指南:从MCU到Jetson的档位解析

2026 AI视觉与物联网开发板选购指南:从MCU到Jetson的档位解析

2026/9/7 0:01:24

2026 年已经过了一大半,如果你现在正准备入手 AI 视觉或物联网开发板,我建议你先别急着下单。市面上从几十块的 ESP32 到几千块的英伟达 Jetson,价格差了近百倍,宣传话术却几乎一样,都告诉你“能跑 AI、能做视觉、能搞…

基于Vue的企业门户网站管理系统的设计与实现

基于Vue的企业门户网站管理系统的设计与实现

2026/9/7 0:01:24

目 录 摘 要 Abstract 目 录 1 引言 1.1 选题背景 1.2 研究现状 1.3 目的和意义 1.4 论文结构安排 1.5本章小结 2 开发环境与技术 2.1 MySQL数据库 2.2 Java语言技术 2.3 Spring Boot框架 2.4 Vue.js 2.5 本章小节 3 系统分析 …

BS EN 13814-1-2019游乐设施安全标准:设计与制造核心要点解析

BS EN 13814-1-2019游乐设施安全标准:设计与制造核心要点解析

2026/9/7 0:01:24

简介:BS EN 13814-1:2019是英国采纳欧洲标准EN 13814-1:2019的正式版本,由BSI标准出版,重点规定游乐设施和游乐设备在设计与制造环节的安全准则,与BS EN 13814-2:2019、BS EN 13814-3:2019共同取代旧版BS EN 13814:2004。该标准面…

UL 1642锂电池安全标准全解析:测试项目、认证流程与避坑指南

UL 1642锂电池安全标准全解析:测试项目、认证流程与避坑指南

2026/9/7 0:01:24

简介:UL 1642是锂电池安全领域的重要规范,本中文版资源适合锂电池制造商、检测机构工程师及产品认证相关人员阅读,用于理解电池在设计与制造层面的安全要求、测试方法与合规要点。资源共1个PDF文件,压缩包大小834KB,便…

基于YOLOv8和PyQt5的麦穗稻穗检测识别系统设计与实现

基于YOLOv8和PyQt5的麦穗稻穗检测识别系统设计与实现

2026/9/7 0:01:24

这次我们来看一个把目标检测算法和桌面端工具结合得很典型的项目:基于 YOLOv8 PyQt5 的麦穗稻穗检测识别系统。这个项目本身不是新概念,但它的价值在于落地形态很完整。YOLOv8 负责核心的麦穗稻穗目标检测,PyQt5 负责提供可视化的桌面交互界…

ChCore操作系统实验全解析:从启动到虚拟内存与异常处理

ChCore操作系统实验全解析:从启动到虚拟内存与异常处理

2026/9/6 23:51:23

简介:面向操作系统课程设计与实践备考的完整实验方案,围绕上海交通大学Chcore操作系统教学环境,覆盖内存管理、系统调用与缺页异常两大核心模块。文档对分页机制、页表管理、内存分配与回收、内存保护、换页流程以及系统调用、缺页处理、页替…

中国人民大学杨琳团队《Nature Communications》 | 全球潮汐湿地土壤有机碳时空格局与环境驱动:一项2009-2020年的全球评估

中国人民大学杨琳团队《Nature Communications》 | 全球潮汐湿地土壤有机碳时空格局与环境驱动:一项2009-2020年的全球评估

2026/9/6 1:19:56

本文首发于“生态学者”!从“湿地面积”到“土壤碳密度”:为什么需要重新认识潮汐湿地蓝碳变化?潮汐湿地位于陆地与海洋的交汇地带,包括红树林、盐沼和潮滩,是全球重要的蓝碳生态系统。其土壤能够长期储存大量有机碳&a…

adb抓包

adb抓包

2026/9/6 1:19:56

前言 本文介绍如何通过 tcpdump 在 Android 手机上抓取网络数据包,并在电脑端使用 Wireshark 进行分析。适用于需要排查 App 网络请求、分析接口调用或调试网络问题的开发与测试场景。1. 手机要有 root 权限2. 下载 tcpdump3. adb push C:\Users\zhangkuixun\Downlo…

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战

2026/9/6 1:19:56

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战 在云原生基础设施中,容器镜像体积直接决定了服务的部署速度与弹性扩容敏捷度。对于传统的 Go / Java 微服务,镜像体积通常被严格控制在 50MB 到 200MB 以内,拉取镜像只…

基于YOLOv8和PyQt5的麦穗稻穗检测识别系统设计与实现

基于YOLOv8和PyQt5的麦穗稻穗检测识别系统设计与实现

2026/9/7 0:01:24

这次我们来看一个把目标检测算法和桌面端工具结合得很典型的项目:基于 YOLOv8 PyQt5 的麦穗稻穗检测识别系统。这个项目本身不是新概念,但它的价值在于落地形态很完整。YOLOv8 负责核心的麦穗稻穗目标检测,PyQt5 负责提供可视化的桌面交互界…

UL 1642锂电池安全标准全解析:测试项目、认证流程与避坑指南

UL 1642锂电池安全标准全解析:测试项目、认证流程与避坑指南

2026/9/7 0:01:24

简介:UL 1642是锂电池安全领域的重要规范,本中文版资源适合锂电池制造商、检测机构工程师及产品认证相关人员阅读,用于理解电池在设计与制造层面的安全要求、测试方法与合规要点。资源共1个PDF文件,压缩包大小834KB,便…

BS EN 13814-1-2019游乐设施安全标准:设计与制造核心要点解析

BS EN 13814-1-2019游乐设施安全标准:设计与制造核心要点解析

2026/9/7 0:01:24

简介:BS EN 13814-1:2019是英国采纳欧洲标准EN 13814-1:2019的正式版本,由BSI标准出版,重点规定游乐设施和游乐设备在设计与制造环节的安全准则,与BS EN 13814-2:2019、BS EN 13814-3:2019共同取代旧版BS EN 13814:2004。该标准面…

远程协作的工作台整理

远程协作的工作台整理

2026/9/3 6:56:24

远程协作的工作台整理远程协作的核心不是再加一个工具,而是让交接信息足够完整。异步任务要写明目标、输入位置、完成标准和需要决策的人。 工作台的最小配置 将日程、待办、代码和沟通入口收拢到少数固定位置;通知按紧急程度分层。工作台不需要模仿办公…

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

2026/9/4 7:42:10

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

2026/9/6 23:21:51

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…