一,安装需要用到的库

1,gorm官网:

2,安装gorm

liuhongdi@ku:~$ go get -u gorm.io/gorm

3,go-sqlmock的地址:

4,从命令行安装go-sqlmock:

liuhongdi@ku:~$ go get -u github.com/DATA-DOG/go-sqlmock

说明:刘宏缔的go森林是一个专注golang的博客,
网站:https://blog.imgtouch.com
原文: go语言单元测试之五:go语言用go-sqlmock和gorm做数据库查询mock – 架构森林

二,演示项目的相关信息

2,功能说明:演示了单元测试时用sqlmock摸拟数据库的数据记录

3,项目结构:如图:

三,go代码说明

1,global/db.go

package global

import (
	"gorm.io/gorm"
	"time"
	"gorm.io/driver/mysql"
)

var (
	DBLink *gorm.DB
)

//创建mysql链接
func SetupDBLink() (error) {
	var err error
	dsn:="root:password@tcp(127.0.0.1:3306)/business?charset=utf8&parseTime=True&loc=Local";
	DBLink, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
	//DBLink.Logger.LogMode(true)
	if err != nil {
		return err
	}
	sqlDB, err := DBLink.DB()
	// SetMaxIdleConns 设置空闲连接池中连接的最大数量
	sqlDB.SetMaxIdleConns(10)
	// SetMaxOpenConns 设置打开数据库连接的最大数量
	sqlDB.SetMaxOpenConns(30)
	// SetConnMaxLifetime 设置了连接可复用的最大时间
	sqlDB.SetConnMaxLifetime(time.Hour)
	return nil
}

2,controller/userController.go

package controller

import (
	"github.com/liuhongdi/unittest04/global"
	"github.com/liuhongdi/unittest04/model"
)

func GoodsOne(goodsId int) (*model.Goods, error) {
	goodsOne:=&model.Goods{}
	err := global.DBLink.Where("goodsId=?",goodsId).First(&goodsOne).Error
	//fmt.Println(err)
	if (err != nil) {
		return nil,err
	} else {
		return goodsOne,nil
	}
}

3,model/goods.go

package model

type Goods struct {
	GoodsId	int64  `gorm:"column:goodsId",json:"goodsid"` // 自增
	GoodsName string  `gorm:"column:goodsName",json:"goodsname"` //
	Subject string `gorm:"column:subject",json:"subject"`
	Price string `gorm:"column:price",json:"price"`
	Stock int `gorm:"column:stock",json:"stock"`
}

func (Goods) TableName() string {
	return "goods"
}

4,main.go

package main

import (
	"fmt"
	"github.com/liuhongdi/unittest04/controller"
	"github.com/liuhongdi/unittest04/global"
	"log"
)

//定义一个加法方法
func add(a, b int) int {
	return a + b
}

func init() {
	//mysql link
	err := global.SetupDBLink()
	if err != nil {
		log.Fatalf("init.setupDBEngine err: %v", err)
	}
}

func main() {
	goods,err := controller.GoodsOne(1)
	if (err != nil){
		log.Fatalf("err:%v",err)
	} else {
		fmt.Println(goods)
	}
}

5,main_test.go

package main

import (
	"database/sql"
	"errors"
	"fmt"
	"github.com/DATA-DOG/go-sqlmock"
	"github.com/liuhongdi/unittest04/controller"
	"github.com/liuhongdi/unittest04/global"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"log"
	"testing"
)

var mock sqlmock.Sqlmock

//初始化
func init() {
	//创建sqlmock
	var err error
	var db *sql.DB
	db, mock, err = sqlmock.New()
	if nil != err {
		log.Fatalf("Init sqlmock failed, err %v", err)
	}
	//结合gorm、sqlmock
	global.DBLink, err = gorm.Open(mysql.New(mysql.Config{
		SkipInitializeWithVersion: true,
		Conn: db,
	}), &gorm.Config{})
	if nil != err {
		log.Fatalf("Init DB with sqlmock failed, err %v", err)
	}
}

//测试得到一件商品的信息,启用sqlmock,放回结果集
func TestOneMockRes(t *testing.T) {
	goodsId:=1
	//创建数据库记录
	rows := sqlmock.NewRows([]string{"goodsId", "goodsName", "subject", "price", "stock"}).
		AddRow(2, "Moonii陶瓷月相灯", "这是一件测试商品", "5.32", "33")
	mock.ExpectQuery("^SELECT \\* FROM `goods` WHERE goodsId=\\? ORDER BY `goods`.`goodsId` LIMIT 1").
		WithArgs(goodsId).WillReturnRows(rows)
	//执行
	goods,err:=controller.GoodsOne(goodsId)
	if (err != nil) {
		t.Fatalf("goodsId: %d, err:%v", goodsId, err)
	}else {
			fmt.Println(goods)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("there were unfulfilled expectations: %s", err)
	}
}

//测试得到一件商品的信息,启用sqlmock,无结果集
func TestOneMockNoRes(t *testing.T) {
	goodsId:=1

	//创建数据库记录
	rows := sqlmock.NewRows([]string{"goodsId", "goodsName", "subject", "price", "stock"})
		//AddRow(2, "Moonii陶瓷月相灯", "这是一件测试商品", "5.32", "33")
	mock.ExpectQuery("^SELECT \\* FROM `goods` WHERE goodsId=\\? ORDER BY `goods`.`goodsId` LIMIT 1").
		WithArgs(goodsId).WillReturnRows(rows)
	//执行
	goods,err:=controller.GoodsOne(goodsId)
	if (err != nil) {
		t.Fatalf("goodsId: %d, err:%v", goodsId, err)
	}else {
			fmt.Println(goods)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("there were unfulfilled expectations: %s", err)
	}
}

//测试得到一件商品的信息,启用sqlmock,返回数据库错误
func TestOneMockError(t *testing.T) {
	goodsId:=1
	//传递sql
	mock.ExpectQuery("^SELECT \\* FROM `goods` WHERE goodsId=\\? ORDER BY `goods`.`goodsId` LIMIT 1").
		WithArgs(goodsId).WillReturnError(errors.New("some error"))
	//执行
	goods,err:=controller.GoodsOne(goodsId)
	if (err != nil) {
		t.Fatalf("goodsId: %d, err:%v", goodsId, err)
	}else {
		fmt.Println(goods)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("there were unfulfilled expectations: %s", err)
	}
}

四,测试效果

执行测试命令:

root@ku:/data/go/unittest04# go test -v

返回:

root@ku:/data/go/unittest04# go test -v
=== RUN   TestOneMockRes
&{2 Moonii陶瓷月相灯 这是一件测试商品 5.32 33}
--- PASS: TestOneMockRes (0.00s)
=== RUN   TestOneMockNoRes

2021/02/01 10:20:56 /data/go/unittest04/controller/userController.go:10 record not found
[0.075ms] [rows:0] SELECT * FROM `goods` WHERE goodsId=1 ORDER BY `goods`.`goodsId` LIMIT 1
    main_test.go:69: goodsId: 1, err:record not found
--- FAIL: TestOneMockNoRes (0.00s)
=== RUN   TestOneMockError

2021/02/01 10:20:56 /data/go/unittest04/controller/userController.go:10 some error
[0.038ms] [rows:0] SELECT * FROM `goods` WHERE goodsId=1 ORDER BY `goods`.`goodsId` LIMIT 1
    main_test.go:87: goodsId: 1, err:some error
--- FAIL: TestOneMockError (0.00s)
FAIL
exit status 1
FAIL	github.com/liuhongdi/unittest04	0.012s

五,查看库的版本:

module github.com/liuhongdi/unittest04

go 1.15

require (
	gorm.io/driver/mysql v1.0.1
	gorm.io/gorm v1.20.6
	github.com/DATA-DOG/go-sqlmock v1.5.0
)