当前位置: 首页 > news >正文

Go 单元测试之Mysql数据库集成测试

文章目录

    • 一、 sqlmock介绍
    • 二、安装
    • 三、基本用法
    • 四、一个小案例
    • 五、Gorm 初始化注意点

一、 sqlmock介绍

sqlmock 是一个用于测试数据库交互的 Go 模拟库。它可以模拟 SQL 查询、插入、更新等操作,并且可以验证 SQL 语句的执行情况,非常适合用于单元测试中。

二、安装

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

三、基本用法

使用 sqlmock 进行 MySQL 数据库集成测试的基本步骤如下:

  1. 创建模拟 DB 连接:
import ("database/sql""testing""github.com/DATA-DOG/go-sqlmock"
)func TestMyDBFunction(t *testing.T) {db, mock, err := sqlmock.New()if err != nil {t.Fatalf("Error creating mock database: %v", err)}defer db.Close()// 使用 mock 来替代真实的数据库连接// db 可以传递给被测试的函数进行测试
}
  1. 设置模拟 SQL 查询和预期结果:
// 模拟 SQL 查询并设置预期结果
rows := sqlmock.NewRows([]string{"id", "name"}).AddRow(1, "Alice").AddRow(2, "Bob")
mock.ExpectQuery("SELECT id, name FROM users").WillReturnRows(rows)
  1. 调用被测试的函数,并传入模拟的数据库连接:
// 调用被测试的函数,传入模拟的数据库连接
result := MyDBFunction(db)// 验证结果是否符合预期
if result != expected {t.Errorf("Expected %d, got %d", expected, result)
}

四、一个小案例

这里我们定义了一个 GORMUserDAO 结构体,它实现了 UserDAO 接口,用于与用户表进行交互。这个结构体通过 gorm.DB 实例与数据库进行通信。

具体来说,GORMUserDAO 提供了 Insert 方法,用于在数据库中创建新用户。这个方法接受一个 User 类型的结构体作为参数,该结构体定义了用户的基本信息,包括 ID、邮箱、密码、手机号、生日、昵称、自我介绍、微信 UnionID 和 OpenID 等字段。

Insert 方法中,首先获取当前时间戳(以毫秒为单位),并设置用户的创建时间和更新时间。然后,使用 gorm.DBCreate 方法将用户信息插入到数据库中。如果插入操作遇到唯一性约束错误(例如邮箱或手机号已存在),方法会返回一个特定的错误 ErrUserDuplicate

User 结构体定义了数据库表的结构,其中包含了一些列的定义,如 EmailPhone 被设置为唯一索引。此外,还定义了一些列的类型和约束,如 AboutMe 字段被设置为最大长度为 1024 的字符串类型。

提供了一个使用 GORM 进行数据库操作的 DAO 层,用于处理用户数据的创建。

// internal/user/dao/user.go
package daoimport ("context""database/sql""errors""github.com/go-sql-driver/mysql""gorm.io/gorm""time"
)var (ErrUserDuplicate = errors.New("邮箱冲突")
)type UserDAO interface {Insert(ctx context.Context, u User) error
}type GORMUserDAO struct {db *gorm.DB
}func NewUserDAO(db *gorm.DB) UserDAO {return &GORMUserDAO{db: db,}
}func (dao *GORMUserDAO) Insert(ctx context.Context, u User) error {// 存毫秒数now := time.Now().UnixMilli()u.Utime = nowu.Ctime = nowerr := dao.db.WithContext(ctx).Create(&u).Errorif mysqlErr, ok := err.(*mysql.MySQLError); ok {const uniqueConflictsErrNo uint16 = 1062if mysqlErr.Number == uniqueConflictsErrNo {// 邮箱冲突 or 手机号码冲突return ErrUserDuplicate}}return err
}// User 直接对应数据库表结构
// 有些人叫做 entity,有些人叫做 model,有些人叫做 PO(persistent object)
type User struct {Id int64 `gorm:"primaryKey,autoIncrement"`// 设置为唯一索引Email    sql.NullString `gorm:"unique"`Password string//Phone *stringPhone sql.NullString `gorm:"unique"`Birthday sql.NullInt64// 昵称Nickname sql.NullString// 自我介绍AboutMe       sql.NullString `gorm:"type=varchar(1024)"`WechatUnionID sql.NullStringWechatOpenID  sql.NullString `gorm:"unique"`// 创建时间Ctime int64// 更新时间Utime int64
}

接着我们用编写测试用例

package daoimport ("context""database/sql""errors""github.com/DATA-DOG/go-sqlmock""github.com/go-sql-driver/mysql""github.com/stretchr/testify/assert""github.com/stretchr/testify/require"gormMysql "gorm.io/driver/mysql""gorm.io/gorm""testing"
)func TestGORMUserDAO_Insert(t *testing.T) {//testCases := []struct {name string// 为什么不用 ctrl ?// 因为你这里是 sqlmock,不是 gomockmock func(t *testing.T) *sql.DBctx  context.Contextuser UserwantErr error}{{name: "插入成功",mock: func(t *testing.T) *sql.DB {mockDB, mock, err := sqlmock.New()res := sqlmock.NewResult(3, 1)// 这边预期的是正则表达式// 这个写法的意思就是,只要是 INSERT 到 users 的语句mock.ExpectExec("INSERT INTO `users` .*").WillReturnResult(res)require.NoError(t, err)return mockDB},user: User{Email: sql.NullString{String: "123@qq.com",Valid:  true,},},},{name: "邮箱冲突",mock: func(t *testing.T) *sql.DB {mockDB, mock, err := sqlmock.New()// 这边预期的是正则表达式// 这个写法的意思就是,只要是 INSERT 到 users 的语句mock.ExpectExec("INSERT INTO `users` .*").WillReturnError(&mysql.MySQLError{Number: 1062,})require.NoError(t, err)return mockDB},user:    User{},wantErr: ErrUserDuplicate,},{name: "数据库错误",mock: func(t *testing.T) *sql.DB {mockDB, mock, err := sqlmock.New()// 这边预期的是正则表达式// 这个写法的意思就是,只要是 INSERT 到 users 的语句mock.ExpectExec("INSERT INTO `users` .*").WillReturnError(errors.New("数据库错误"))require.NoError(t, err)return mockDB},user:    User{},wantErr: errors.New("数据库错误"),},}for _, tc := range testCases {t.Run(tc.name, func(t *testing.T) {db, err := gorm.Open(gormMysql.New(gormMysql.Config{Conn: tc.mock(t),// SELECT VERSION;SkipInitializeWithVersion: true,}), &gorm.Config{// 你 mock DB 不需要 pingDisableAutomaticPing: true,// 这个是什么呢?SkipDefaultTransaction: true,})d := NewUserDAO(db)u := tc.usererr = d.Insert(tc.ctx, u)assert.Equal(t, tc.wantErr, err)})}
}

五、Gorm 初始化注意点

这里运行测试的代码也有点与众不同,在初始化 GORM 的时候需要额外设置三个参数。

  • SkipInitializeWithVersion:如果为 false,那么 GORM 在初始化的时候,会先调用 show version
  • DisableAutomiticPing:为 true 不允许 Ping 数据库。
  • SkipDefaultTransaction:为 false 的时候,即便是一个单一增删改语句,GORM 也会开启事务。

这三个选项禁用之后,就可以确保 GORM 不会在初始化的过程中发起额外的调用。

相关文章:

Go 单元测试之Mysql数据库集成测试

文章目录 一、 sqlmock介绍二、安装三、基本用法四、一个小案例五、Gorm 初始化注意点 一、 sqlmock介绍 sqlmock 是一个用于测试数据库交互的 Go 模拟库。它可以模拟 SQL 查询、插入、更新等操作,并且可以验证 SQL 语句的执行情况,非常适合用于单元测试…...

Prometheus + Grafana 搭建监控仪表盘

目标要求 1、需要展现的仪表盘: SpringBoot或JVM仪表盘 Centos物理机服务器(实际为物理分割的虚拟服务器)仪表盘 2、展现要求: 探索Prometheus Grafana搭建起来的展示效果,尽可能展示能展示的部分。 一、下载软件包 监控系统核心…...

机器人管理系统的增删查改(Python)

#交互模式 robot ["机器人1","机器人2","机器人3","机器人4"] name input("请输入您的姓名:") print("%s您好欢迎使用机器人管理系统"%(name))while True:print("您可以进行 1.查找 2.修改 3.增…...

【.Net动态Web API】背景与实现原理

🚀前言 本文是《.Net Core进阶编程课程》教程专栏的导航站(点击链接,跳转到专栏主页,欢迎订阅,持续更新…) 专栏介绍:通过源码实例来讲解Asp.Net Core进阶知识点,让大家完全掌握每一…...

JS-43-Node.js02-安装Node.js和npm

Node.js是一个基于Chrome V8引擎的JavaScript运行时环境,可以让JavaScript实现后端开发,所以,首先在本机安装Node.js环境。 一、安装Node.js 官网:下载 Node.js 默认两个版本的下载: 64位windows系统的LTS(Long Tim…...

设计模式(分类)

目录 设计模式(分类) 设计模式(六大原则) 设计模式是软件工程中一种经过验证的、用于解决特定设计问题的通用解决方案。它们是面向对象编程(Object-Oriented Programming, OOP)实践中提炼出的最佳实…...

请陪伴Kimi和GPT成长

经验的闪光汤圆 但是我想要写实的 你有吗? 岁数大了,希望如何学习新知识呢?又觉得自己哪些能力亟需补强呢? 看论文自然得用Kimi,主要是肝不动了,眼睛也顶不住了。 正好昨天跟专业人士学会了用工作流的办法跟…...

优思学院|ISO45001职业健康安全管理体系是什么?

ISO45001:2018是新公布的国际标准规范,全球备受期待的职业健康与安全国际标准(OH&S)于2018年公布,并将在全球范围内改变工作场所实践。ISO45001将取代OHSAS18001,成为全球工作场所健康与安全的参考。 ISO45001:201…...

抖去推短视频矩阵系统----源头开发

为什么一直说让企业去做短视频矩阵?而好处就是有更多的流量入口,不同平台或账号之间可以进行资源互换,最终目的就是获客留咨,提单转化。你去看一些做得大的账号,你会发现他们在许多大的平台上,都有自己的账…...

Golang函数重试机制实现

前言 在编写应用程序时,有时候会遇到一些短暂的错误,例如网络请求、服务链接终端失败等,这些错误可能导致函数执行失败。 但是如果稍后执行可能会成功,那么在一些业务场景下就需要重试了,重试的概念很简单&#xff0c…...

工业电脑在ESOP工作站行业应用

ESOP工作站行业应用 项目背景 E-SOP是实现作业指导书电子化,并统一管理和集中控制的一套管理信息平台。信迈科技的ESOP终端是一款体积小巧功能齐全的高性价比工业电脑,上层通过网络与MES系统连接,下层连接显示器展示作业指导书。ESOP控制终…...

java项目实战之图书管理系统(1)

✅作者简介:大家好,我是再无B~U~G,一个想要与大家共同进步的男人😉😉 🍎个人主页:再无B~U~G-CSDN博客 1.背景 图书管理系统是一种用于管理图书…...

3DGS渐进式渲染 - 离线生成渲染视频

总览 输入:环绕Object拍摄的RGB视频 输出:自定义相机路径的渲染视频(包含渐变效果) 实现过程 首先,编译3DGS的C代码,并跑通convert.py、train.py和render.py。教程如下: github网址&#xf…...

chromium 协议栈 cronet ios 踩坑案例

1、请求未携带 Accept-Language http header 出现图片加载失败 现象: 访问 https://www.huawei.com/cn/?ic_mediumdirect&ic_sourcesurlent 时出现图片加载失败的问题 预期结果: 原因: 网络库删除了添加 Accept-Language header 的逻…...

Java快速排序知识点(含面试大厂题和源码)

快速排序(Quick Sort)是一种高效的排序算法,采用分治法(Divide and Conquer)的策略来对一个数组进行排序。快速排序的平均时间复杂度为 O(n log n),在最坏的情况下为 O(n^2),但这种情况很少发生…...

SpringBoot整合Swagger2

SpringBoot整合Swagger2 1.什么是Swagger2?(应用场景)2.项目中如何使用2.1 导入依赖2.2 编写配置类2.3 注解使用2.3.1 controller注解:2.3.2 方法注解2.3.3 实体类注解2.3.4 方法返回值注解2.3.5 忽略的方法 3.UI界面 1.什么是Swa…...

C++算法题 - 矩阵

目录 36. 有效的数独54. 螺旋矩阵48. 旋转图像73. 矩阵置零289. 生命游戏 36. 有效的数独 LeetCode_link 请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。 数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现…...

记录一个没测出来,有点严重的Bug

前提: 人物:若干个 部门:若干个 部门有一个人物选择框,可以选择所有的人物,且为非必填字段 bug现象: 部门中 的人物选择框每次都少一个人物 代码分析: F12接口后端没问题,定位为前端的问题。 前…...

科学突破可能开创6G通信新时代

格拉斯哥大学开发的火柴盒大小的天线可以为全息通话、改进自动驾驶和更好的医疗保健的世界铺平道路。 格拉斯哥大学表示,这种创新的无线通信天线将超材料的独特特性与复杂的信号处理相结合,有助于构建未来的 6G 网络。 数字编码动态超表面天线&#xf…...

游戏、app抓包

文章目录 协议app抓包游戏抓包 协议 在抓包之前,首先我们要对每个程序使用什么协议有个大致的了解,比如网页这种就是走的http协议。 在一些app中我们通过发送一个请求,然后服务器接受,响应,返回一个数据包&#xff0c…...

51c自动驾驶~合集58

我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留,CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制(CCA-Attention),…...

Cesium1.95中高性能加载1500个点

一、基本方式&#xff1a; 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...

mongodb源码分析session执行handleRequest命令find过程

mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程&#xff0c;并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令&#xff0c;把数据流转换成Message&#xff0c;状态转变流程是&#xff1a;State::Created 》 St…...

嵌入式常见 CPU 架构

架构类型架构厂商芯片厂商典型芯片特点与应用场景PICRISC (8/16 位)MicrochipMicrochipPIC16F877A、PIC18F4550简化指令集&#xff0c;单周期执行&#xff1b;低功耗、CIP 独立外设&#xff1b;用于家电、小电机控制、安防面板等嵌入式场景8051CISC (8 位)Intel&#xff08;原始…...

路由基础-路由表

本篇将会向读者介绍路由的基本概念。 前言 在一个典型的数据通信网络中&#xff0c;往往存在多个不同的IP网段&#xff0c;数据在不同的IP网段之间交互是需要借助三层设备的&#xff0c;这些设备具备路由能力&#xff0c;能够实现数据的跨网段转发。 路由是数据通信网络中最基…...

Win系统权限提升篇UAC绕过DLL劫持未引号路径可控服务全检项目

应用场景&#xff1a; 1、常规某个机器被钓鱼后门攻击后&#xff0c;我们需要做更高权限操作或权限维持等。 2、内网域中某个机器被钓鱼后门攻击后&#xff0c;我们需要对后续内网域做安全测试。 #Win10&11-BypassUAC自动提权-MSF&UACME 为了远程执行目标的exe或者b…...

代理服务器-LVS的3种模式与调度算法

作者介绍&#xff1a;简历上没有一个精通的运维工程师。请点击上方的蓝色《运维小路》关注我&#xff0c;下面的思维导图也是预计更新的内容和当前进度(不定时更新)。 我们上一章介绍了Web服务器&#xff0c;其中以Nginx为主&#xff0c;本章我们来讲解几个代理软件&#xff1a…...

多模态学习路线(2)——DL基础系列

目录 前言 一、归一化 1. Layer Normalization (LN) 2. Batch Normalization (BN) 3. Instance Normalization (IN) 4. Group Normalization (GN) 5. Root Mean Square Normalization&#xff08;RMSNorm&#xff09; 二、激活函数 1. Sigmoid激活函数&#xff08;二分类&…...

React 进阶特性

1. ref ref 是 React 提供的一种机制,用于访问和操作 DOM 元素或 React 组件的实例。它可以用于获取某个 DOM 元素的引用,从而执行一些需要直接操作 DOM 的任务,例如手动设置焦点、选择文本或触发动画。 1.1. 使用 ref 的步骤 1. 创建一个 ref:使用 React.createRef 或 …...

vb监测Excel两个单元格变化,达到阈值响铃

需求 在Excel中实现监控两个单元格之间的变化范围&#xff0c;当达到某个设定的值的范围内时&#xff0c;实现自动响铃提示。 实现&#xff1a; 首先设置Excel&#xff0c;开启宏、打开开发者工具&#xff0c;点击visual Basic按钮&#xff0c;然后在左侧双击需要监测的shee…...