0


Go基础学习11-测试工具gomock和monkey的使用

文章目录

基础回顾

测试:
在这里插入图片描述在这里插入图片描述
● 建立完善的回归测试,如果采用微服务,可以限制测试范围,只需要保证独立的微服务内部功能正常,因为微服务对外暴露的是接口。
● 为什么要将测试文件和源文件写到一个包中:便于测试包级别的示例,方法。

  • go test 命令是一个按照一定约定和组织的测试代码驱动测试,在包目录下 所有以_test.go结尾的文件都会被视为测试文件。并且 _test.go结尾的文件不会被go build 编译到可执行文件中。

Mock

Mock是什么

Mock是单元测试中常见的一种技术,就是在测试过程中,对于一些不容易构造或者获取的对象,创建一个Mock对象来模拟对象的行为,从而把测试与测试边界以外的对象隔离开。
优点:
团队并行工作
测试驱动开发 TDD (Test-Driven Development)
测试覆盖率
隔离系统
缺点

  • Mock不是万能的,使用Mock也存在着风险,需要根据项目实际情况和具体需要来确定是否选用Mock。
  • 测试过程中如果大量使用Mock,mock测试的场景失去了真实性,可能会导致在后续的系统性测试时才发现bug,使得缺陷发现的较晚,可能会造成后续修复成本更大

Mock广泛应用于接口类的方法的生成。 针对接口可以使用mock,针对不是接口的函数或者变量使用下面的monkey。

安装gomock

go get github.com/golang/mock/gomock
go get github.com/golang/mock/mockgen
go install github.com/golang/mock/[email protected]

安装完成后执行mockgen命令查看是否生效

Mock使用

1. 创建user.go源文件

// Package mock  ------------------------------------------------------------// @file      : user.go// @author    : WeiTao// @contact   : [email protected]// @time      : 2024/10/4 13:16// ------------------------------------------------------------package mock

import"context"type User struct{
    Mobile   string
    Password string
    NickName string}type UserServer struct{
    Db UserData
}func(us *UserServer)GetUserByMobile(ctx context.Context, mobile string)(User,error){
    user, err := us.Db.GetUserByMobile(ctx, mobile)if err !=nil{return User{}, err
    }if user.NickName =="TestUser"{
        user.NickName ="TestUserModified"}return user,nil}type UserData interface{GetUserByMobile(ctx context.Context, mobile string)(User,error)}

上述代码中的UserData是一个Interface{}类型,后面使用Mock生成的对象就是此接口对象,生成后可以将UserData接口中的方法GetUserByMoblie方法实现黑盒,从而无需关注具体实现细节,直接可以设置此函数对应的返回值。

2. 使用mockgen生成对应的Mock文件

mockgen使用:

// 源码模式
  mockgen -source 需要mock的文件名 -destination 生成的mock文件名 -package 生成mock文件的包名
// 参考示例:
mockgen -source user.go-destination=./mock/user_mock.go-package=mock

3. 使用mockgen命令生成后在对应包mock下可以查看生成的mock文件

// Code generated by MockGen. DO NOT EDIT.// Source: user.go// Package mock is a generated GoMock package.package mock

import(
    context "context"
    reflect "reflect"

    gomock "github.com/golang/mock/gomock")// MockUserData is a mock of UserData interface.type MockUserData struct{
    ctrl     *gomock.Controller
    recorder *MockUserDataMockRecorder
}// MockUserDataMockRecorder is the mock recorder for MockUserData.type MockUserDataMockRecorder struct{
    mock *MockUserData
}// NewMockUserData creates a new mock instance.funcNewMockUserData(ctrl *gomock.Controller)*MockUserData {
    mock :=&MockUserData{ctrl: ctrl}
    mock.recorder =&MockUserDataMockRecorder{mock}return mock
}// EXPECT returns an object that allows the caller to indicate expected use.func(m *MockUserData)EXPECT()*MockUserDataMockRecorder {return m.recorder
}// GetUserByMobile mocks base method.func(m *MockUserData)GetUserByMobile(ctx context.Context, mobile string)(User,error){
    m.ctrl.T.Helper()
    ret := m.ctrl.Call(m,"GetUserByMobile", ctx, mobile)
    ret0,_:= ret[0].(User)
    ret1,_:= ret[1].(error)return ret0, ret1
}// GetUserByMobile indicates an expected call of GetUserByMobile.func(mr *MockUserDataMockRecorder)GetUserByMobile(ctx, mobile interface{})*gomock.Call {
    mr.mock.ctrl.T.Helper()return mr.mock.ctrl.RecordCallWithMethodType(mr.mock,"GetUserByMobile", reflect.TypeOf((*MockUserData)(nil).GetUserByMobile), ctx, mobile)}

观察上述Mock文件可以看到实现了GetUserByMobile方法等。

4. 编写测试代码

// ------------------------------------------------------------// package test// @file      : user_test.go// @author    : WeiTao// @contact   : [email protected]// @time      : 2024/10/4 13:23// ------------------------------------------------------------package mock

import("context""github.com/golang/mock/gomock""testing")funcTestGetUserByMobile(t *testing.T){// mock准备工作
    ctl := gomock.NewController(t)defer ctl.Finish()
    userData :=NewMockUserData(ctl)
    userData.EXPECT().GetUserByMobile(gomock.Any(),"15023076751").Return(
        User{
            Mobile:"15023076751",
            Password:"123456",
            NickName:"TestUser",},nil,)// 实际调用过程
    userServer := UserServer{
        Db: userData,}
    user, err := userServer.GetUserByMobile(context.Background(),"15023076751")// 判断过程if err !=nil{
        t.Error("GetUserByMobile error:", err)}if user.Mobile !="15023076751"|| user.Password !="123456"|| user.NickName !="TestUserModified"{
        t.Error("GetUserByMobile result is not expected.")}}

5. 运行代码并查看输出

GOROOT=/usr/local/go #gosetup
GOPATH=/home/wt/Backend/go/goprojects #gosetup
/usr/local/go/bin/go test -c -o /home/wt/.cache/JetBrains/GoLand2024.2/tmp/GoLand/___TestGetUserByMobile_in_golearndetail_test_mock.test golearndetail/test/mock #gosetup
/usr/local/go/bin/go tool test2json -t /home/wt/.cache/JetBrains/GoLand2024.2/tmp/GoLand/___TestGetUserByMobile_in_golearndetail_test_mock.test -test.v=test2json -test.paniconexit0 -test.run ^\QTestGetUserByMobile\E$
=== RUN   TestGetUserByMobile
--- PASS: TestGetUserByMobile (0.00s)
PASS

Process finished with the exit code 0

Gomonkey

Gomonkey优势

上面使用mockgen生成对应的mock文件缺点非常明显,只能对于接口类的函数进行mock,然而实际项目并非所有函数都是接口类函数,大部分是内部使用的临时函数以及变量等,此时对这些函数以及变量无法使用mockgen生成对应的mock文件,此时可以使用另一个工具gomonkey
链接:https://github.com/agiledragon/gomonkey

安装

参考官网安装说明:

$ go get github.com/agiledragon/gomonkey/[email protected]

使用

对函数进行monkey

  1. 编写函数
package monkey

funcnetworkCompute(a, b int)(int,error){
    c := a + b
    return c,nil}funccompute(a, b int)(int,error){
    c, err :=networkCompute(a, b)return c, err
}
  1. 编写测试用例
// 对函数进行mockfuncTest_compute(t *testing.T){
    patches := gomonkey.ApplyFunc(networkCompute,func(a, b int)(int,error){return8,nil})defer patches.Reset()
    sum, err :=compute(1,2)if err !=nil{
        t.Error("Error occurred:", err)}
    fmt.Printf("sum : %d\n", sum)if sum !=8{
        t.Error("Error occurred in sum:", err)}}

在使用gomonkey运行测试用例的时候,直接run会报内联错误,解决方法有两个:

  • 在终端执行命令go test时加上参数:go test -gcflags=all=-l
  • 在Goland编辑器添加对应go运行变量:-gcflags=all=-l
  • 在这里插入图片描述 加上 "all=-N -l"和”=all=-l"效果相同。
  1. 运行结果
/usr/local/go/bin/go tool test2json -t /home/wt/.cache/JetBrains/GoLand2024.2/tmp/GoLand/___7Test_compute_in_golearndetail_test_monkey.test -test.v=test2json -test.paniconexit0 -test.run ^\QTest_compute\E$
=== RUN   Test_compute
sum :8--- PASS: Test_compute (0.00s)
PASS

Process finished with the exit code 0

对结构体中方法进行monkey

  1. 编写代码
type Compute struct{}func(c *Compute)NetworkCompute(a, b int)(int,error){
    sum := a + b
    return sum,nil}func(c *Compute)Compute(a, b int)(int,error){
    sum, err := c.NetworkCompute(a, b)return sum, err
}
  1. 编写测试用例
// 对结构体中的方法进行mockfuncTest_Compute_NetworkCompute(t *testing.T){var compute *Compute
    patches := gomonkey.ApplyMethod(reflect.TypeOf(compute),"NetworkCompute",func(_*Compute, a, b int)(int,error){return10,nil})defer patches.Reset()
    compute =&Compute{}
    sum, err := compute.Compute(3,2)if err !=nil{
        t.Error("Error occurred:", err)}
    fmt.Printf("sum : %d\n", sum)if sum !=10{
        t.Error("Error occurred in sum:", err)}}
  1. 运行结果
/usr/local/go/bin/go tool test2json -t /home/wt/.cache/JetBrains/GoLand2024.2/tmp/GoLand/___2Test_Compute_NetworkCompute_in_golearndetail_test_monkey.test -test.v=test2json -test.paniconexit0 -test.run ^\QTest_Compute_NetworkCompute\E$
=== RUN   Test_Compute_NetworkCompute
sum :10--- PASS: Test_Compute_NetworkCompute (0.00s)
PASS

Process finished with the exit code 0

对全局变量进行monkey

代码展示:

var num =5// 对变量进行mockfuncTest_GlobalVal(t *testing.T){
    patches := gomonkey.ApplyGlobalVar(&num,10)defer patches.Reset()if num !=10{
        t.Error("Error occurred in num:mock failure", num)}}

本文转载自: https://blog.csdn.net/weixin_45863010/article/details/142704374
版权归原作者 快进到失业 所有, 如有侵权,请联系我们删除。

“Go基础学习11-测试工具gomock和monkey的使用”的评论:

还没有评论