Go 语言中对 Redis 和 SQL 操作进行单元测试

news/2024/12/13 22:53:46/文章来源:https://www.cnblogs.com/tangxianmeng/p/18605969

在这篇博客文章中,我们将探讨如何在 Go 语言中使用 RedisMock 库和 SQLMock 库进行单元测试。本文将通过一个简单的示例项目展示如何为数据库操作编写单元测试,并确保我们的代码按预期工作。

项目结构

我们的项目包含以下文件:

  • sql.go:包含数据库操作的实现。
  • sql_test.go:包含数据库操作的单元测试。
  • redis.go:包含 Redis 操作的实现。
  • redis_test.go:包含 Redis 操作的单元测试。

数据库操作实现 sql.go

首先,我们定义了一个 User 结构体和一个 Data 结构体,用于封装数据库操作。

package mainimport ("context""time""gorm.io/gorm"
)type User struct {ID       int       `gorm:"primaryKey column:id"`Birthday time.Time `gorm:"column:birthday"`Username string    `gorm:"column:username"`
}func (u User) TableName() string {return "users"
}type Data struct {DB *gorm.DB
}func (d *Data) Create(ctx context.Context, user *User) error {return d.DB.Create(user).Error
}func (d *Data) Get(ctx context.Context, id int) (*User, error) {var user Usererr := d.DB.Where("`id` = ?", id).First(&user).Errorreturn &user, err
}func (d *Data) Update(ctx context.Context, user *User) error {return d.DB.Save(user).Error
}func (d *Data) Delete(ctx context.Context, id int) error {return d.DB.Where(`id = ?`, id).Delete(User{}).Error
}

数据库操作单元测试 sql_test.go

接下来,我们使用 SQLMock 库为数据库操作编写单元测试。

package mainimport ("context""testing""time""github.com/DATA-DOG/go-sqlmock""github.com/stretchr/testify/assert""gorm.io/driver/mysql""gorm.io/gorm"
)func initTestDB() (*gorm.DB, sqlmock.Sqlmock, error) {db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))if err != nil {return nil, nil, err}mock.ExpectQuery("SELECT VERSION()").WillReturnRows(sqlmock.NewRows([]string{"version"}).AddRow("8.0.23"))gormDB, err := gorm.Open(mysql.New(mysql.Config{Conn: db,}))if err != nil {return nil, nil, err}return gormDB, mock, nil
}func TestData_Create(t *testing.T) {gormDB, mock, err := initTestDB()assert.NoError(t, err)birthday, err := time.Parse("2006-01-02", "2021-01-01")assert.NoError(t, err)user := &User{ID:       1,Birthday: birthday,Username: "test",}mock.ExpectBegin()mock.ExpectExec("INSERT INTO `users` (`birthday`,`username`,`id`) VALUES (?,?,?)").WithArgs(user.Birthday, user.Username, user.ID).WillReturnResult(sqlmock.NewResult(1, 1))mock.ExpectCommit()data := &Data{DB: gormDB}err = data.Create(context.Background(), user)assert.NoError(t, err)assert.NoError(t, mock.ExpectationsWereMet())
}func TestData_Get(t *testing.T) {gormDB, mock, err := initTestDB()assert.NoError(t, err)userID := 1birthday, err := time.Parse("2006-01-02", "2021-01-01")assert.NoError(t, err)rows := sqlmock.NewRows([]string{"id", "birthday", "username"}).AddRow(userID, birthday, "test")mock.ExpectQuery("SELECT * FROM `users` WHERE `id` = ? ORDER BY `users`.`id` LIMIT ?").WithArgs(userID, 1).WillReturnRows(rows)data := &Data{DB: gormDB}user, err := data.Get(context.Background(), userID)assert.NoError(t, err)assert.Equal(t, userID, user.ID)assert.Equal(t, birthday, user.Birthday)assert.Equal(t, "test", user.Username)assert.NoError(t, mock.ExpectationsWereMet())
}func TestData_Update(t *testing.T) {gormDB, mock, err := initTestDB()assert.NoError(t, err)birthday, err := time.Parse("2006-01-02", "2021-01-01")assert.NoError(t, err)user := &User{ID:       1,Birthday: birthday,Username: "test",}mock.ExpectBegin()mock.ExpectExec("UPDATE `users` SET `birthday`=?,`username`=? WHERE `id` = ?").WithArgs(user.Birthday, user.Username, user.ID).WillReturnResult(sqlmock.NewResult(1, 1))mock.ExpectCommit()data := &Data{DB: gormDB}err = data.Update(context.Background(), user)assert.NoError(t, err)assert.NoError(t, mock.ExpectationsWereMet())
}func TestData_Delete(t *testing.T) {gormDB, mock, err := initTestDB()assert.NoError(t, err)userID := 1mock.ExpectBegin()mock.ExpectExec("DELETE FROM `users` WHERE id = ?").WithArgs(userID).WillReturnResult(sqlmock.NewResult(1, 1))mock.ExpectCommit()data := &Data{DB: gormDB}err = data.Delete(context.Background(), userID)assert.NoError(t, err)assert.NoError(t, mock.ExpectationsWereMet())
}

Redis 操作实现 redis.go

我们还定义了一个 SessionStore 结构体,用于封装 Redis 操作。

package mainimport ("context""time""github.com/redis/go-redis/v9"
)type SessionStore struct {redisClient redis.UniversalClient
}func NewSessionStore(redisClient redis.UniversalClient) *SessionStore {return &SessionStore{redisClient: redisClient}
}func (s *SessionStore) GetSession(ctx context.Context, key string) (string, error) {return s.redisClient.Get(ctx, key).Result()
}func (s *SessionStore) SetSession(ctx context.Context, key, value string, expiration time.Duration) error {return s.redisClient.Set(ctx, key, value, expiration).Err()
}

Redis 操作单元测试 redis_test.go

最后,我们使用 Redismock 库为 Redis 操作编写单元测试。

import ("context""testing""time""github.com/go-redis/redismock/v9""github.com/stretchr/testify/assert"
)func TestSessionStore_GetSession(t *testing.T) {db, mock := redismock.NewClientMock()store := NewSessionStore(db)key := "test-key"value := "test-value"mock.ExpectGet(key).SetVal(value)result, err := store.GetSession(context.Background(), key)assert.Nil(t, err)assert.Equal(t, value, result)assert.NoError(t, mock.ExpectationsWereMet())
}func TestSessionStore_SetSession(t *testing.T) {db, mock := redismock.NewClientMock()store := NewSessionStore(db)key := "test-key"value := "test-value"expiration := 10 * time.Minutemock.ExpectSet(key, value, expiration).SetVal("OK")err := store.SetSession(context.Background(), key, value, expiration)assert.NoError(t, err)assert.NoError(t, mock.ExpectationsWereMet())
}

执行单元测试

通过go test -v ./...对当前项目进行单元测试。结果如下:

总结

通过本文,我们展示了如何使用 SQLMock 和 Redismock 库为 Go 语言中的数据库和 Redis 操作编写单元测试。希望这篇文章能帮助你更好地理解和应用这些工具,提高代码的可靠性和可维护性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/852141.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MYSQL安装和版本选择

下载 mysql下载地址:https://dev.mysql.com/downloads/mysql/ 系统版本选择 进入后如图,需要选择版本与系统版本selectVersion:选择版本mysql官网只支持部分版本,如果需要其他版本,需要在其他地方下载select Operating System:选择系统平台如windows,linux,macOs等需要安…

《Django 5 By Example》阅读笔记:p521-p542

《Django 5 By Example》学习第 18 天,p521-p542 总结,总计 22 页。 一、技术总结 1.django-parler django-parler 用于 model 的 translate。这里要注意下,使用 django-parler 之后会生成新的表,对原来的表数据有影响。本人未在实际项目中做过国际化开发,但个人觉得这种做…

反光衣穿戴识别摄像机

反光衣穿戴识别摄像机利用高清摄像头捕捉道路上的实时图像,并通过图像处理算法进行人体检测和识别,识别出穿戴反光衣的行人和工作者。其特点包括高精度的人体识别算法、多视角的监测能力和实时的自动识别与报警功能。通过这些特点,反光衣穿戴识别摄像机能够有效地提高识别的…

Scala中常量val和变量var的区别

一 val和var的区别 Scala的变量分为两种 val是常量,一旦定义值后就不能修改 var是变量,可以被重新赋值 常量: 如图所示,先给常量val赋值为100,再去修改val的值会导致报错 这里定义常量时,没有写类型,scala会自动推导类型完整的常量写法 变量:(值可变) 下方,先将name变…

linux web终端wetty食用方法

学校有些机房电脑性能贼垃但又不得不去那些机房上课 我也不想带电脑,于是弄台廉价的服务器 本来想拿个公网ip配frp连我电脑完事 想到vim是在终端中运行的编辑器 于是想弄个web终端然后就找到了wetty 别问为什么不用面板的终端 面板终端没一个好用 官网:https://github.com/bu…

转载:【AI系统】MobileVit 系列

自 Vision Transformer 出现之后,人们发现 Transformer 也可以应用在计算机视觉领域,并且效果还是非常不错的。但是基于 Transformer 的网络模型通常具有数十亿或数百亿个参数,这使得它们的模型文件非常大,不仅占用大量存储空间,而且在训练和部署过程中也需要更多的计算资…

转载: 【AI系统】GhostNet 系列

本文主要会介绍 GhostNet 系列网络,在本文中会给大家带来卷积结构的改进方面的轻量化,以及与注意力(self-attention)模块的进行结合,部署更高效,更适合移动计算的 GhostNetV2。让读者更清楚的区别 V2 与 V1 之间的区别。 GhostNet V1 模型 GhostNet V1:提供了一个全新的 Gh…

转发:【AI系统】指令和存储优化

除了应用极广的循环优化,在 AI 编译器底层还存在指令和存储这两种不同优化。 指令优化 指令优化依赖于硬件提供的特殊加速计算指令。这些指令,如向量化和张量化,能够显著提高计算密度和执行效率。向量化允许我们并行处理数据,而张量化则进一步扩展了这一概念,通过将数据组…

转发:【AI系统】算子循环优化

在具体硬件执行计算的时候,实际会大量地使用 for 等循环指令不断地去读取不同的数据执行重复的指令(SIMT/SIMD),因此循环优化主要是为了提升数据的局部性或者计算的并行性,从而提升整体算子性能,当然这二者都需要 AI 芯片硬件的支持。 循环优化挑战 数据局部性 数据的局部…

《计算机基础与程序设计》第12周学习总结

学期(如2024-2025-15) 学号(如:20241404) 《计算机基础与程序设计》第12周学习总结 作业信息这个作业属于哪个课程 https://edu.cnblogs.com/campus/besti/2024-2025-1-CFAP/这个作业要求在哪里 https://msg.cnblogs.com/item/4306363这个作业的目标作业正文 https://www.…

7、C语言-标识符、键盘录入scanf

变量名命名规则由数字、字母、下划线_ 组成 不能以数字开头 不能是关键字(如 if等) 严格区分大小写建议变量名尽量都小写 用英文单词,做到见名知意 代码文件名 尽量都小写键盘录入scanfscanf,是scanner format的缩写、是C语言提供的一个函数 作用:获取用户在键盘上输入的数…

智能行为防错识别系统

智能行为防错识别系统的核心在于实时监控和分析工人的操作行为,智能行为防错识别系统依托高清摄像头捕捉工作场景,智能行为防错识别系统通过计算机视觉算法对视频中的人体关键点进行识别,如手腕、肩膀、膝盖等,从而提取人体动作的细节特征。这些关键点的识别和特征提取,使…