使用 Mock 和 Interface 进行 Golang 单测

在工作中我经常会发现很多工程师的 Golang 单测是写的有问题的,只是单纯的调用代码做输出,并且会包含各种 IO 操作,导致单测无法到处运行。

本文将介绍 Golang 中如何正确的做单测。

什么是单元测试?单元测试的特点

单元测试是质量保证十分重要的一环,好的单元测试不仅能及时地发现问题,更能够方便地调试,提高生产效率,所以很多人认为写单元测试是需要额外的时间,会降低生产效率,是对单元测试最大的偏见和误解。

单测会将对应测试的模块隔离出来进行测试,所以我们要尽可能把所有相关的外部依赖都移除,只对相关的模块进行单测。

所以大家看到的在业务代码仓库中的一些在 client 模块中调用 HTTP 的单测其实是不规范的,因为 HTTP 是外部依赖,你的目标服务器如果有故障,那么你的单测就会失败。

func Test_xxx(t *testing.T) {
    DemoClient := &demo.DemoClient{url: "http://localhost:8080"}
    DemoClient.Init()
    resp := DemoCliten.DoHTTPReq()
    fmt.Println(resp)
}

上面这个例子中 DemoClient 在做 DoHTTPReq 方法的时候,会调用 http.Get 方法,这个方法是中包含了外部依赖也就是说他会去请求本地的服务器,如果有一个新的同事刚拿到你的代码,并且本地是没有这个服务器的话,那么你的单测就会失败。

并且上面这个例子中,对于 DoHTTPReq 这个函数只是简单的做了一个输出,没有对返回值做任何检查,如果内部逻辑修改并且返回值修改了,虽然你的测试还是能够 pass 但是其实你的单测是不起作用的。

从上面的例子中我们可以总结出两个单测特点:

  • 没有外部依赖,尽量无副作用,能够到处运行
  • 对输出进行检查

此外我还想提及的一点就是对于单测的编写难度其实是有排序的:

UI > Service > Utils

所以对于单测的编写,我们会优先考虑对 Utils 的单测, 因为 Utils 不会有太多的依赖。其次是对于 Service 的单测,因为 Service 的单测主要是对上游服务和数据库的依赖,只需要分离出依赖就可以进行逻辑的测试。

那该如何分离出依赖呢?我们走到下一节,我们将会介绍如何分离出依赖。

什么是 Mock?

对于 IO 的依赖,我们可以使用 Mock 来模拟数据,这样我们就可以不用担心数据源不稳定的问题了。

那么什么是 Mock 呢?我们又该如何 Mock 呢? 可以想想一个场景,就是你和你的同事正在进行合作的项目开发,你这边的进展比较快,已经快完成你的开发了,但是你的同事进展稍慢,并且你还依赖他的服务。你要怎么不 bl

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
单元测试是一种软件测试方法,用于测试应用程序的各个部分是否按照预期进行工作。在 Golang 中,我们可以使用 Mock 对象来编写单元测试Mock 对象可以模拟应用程序的行为,以便进行测试。 以下是使用 Golang 编写 Mock 单元测试的步骤: 1. 安装 Mock 库 在 Golang 中,我们可以使用各种 Mock 库,例如 GoMockMockery 等。在本例中,我们将使用 GoMock 库。请使用以下命令安装 GoMock: ``` go get github.com/golang/mock/gomock ``` 2. 创建 Mock 对象 在 Golang 中,我们可以使用 Mock 对象来模拟应用程序的行为。我们可以使用 GoMock 库来创建 Mock 对象。以下是一个示例: ``` // mock_test.go package main import ( "testing" "github.com/golang/mock/gomock" ) type MockDatabase struct { mock.Mock } func (m *MockDatabase) GetUser(id int) (string, error) { args := m.Called(id) return args.String(0), args.Error(1) } ``` 在上面的示例中,我们创建了一个名为 `MockDatabase` 的结构体,该结构体包含一个 `mock.Mock` 字段。我们还定义了一个 `GetUser` 方法,该方法接受一个 `id` 参数,并返回一个字符串和一个错误。在该方法中,我们使用 `m.Called` 方法来模拟一个函数调用,并返回模拟的结果。 3. 编写测试用例 编写测试用例时,我们可以使用 `gomock.NewController` 方法来创建一个 Mock 控制器,并使用 `controller.Finish` 方法来释放资源。以下是一个示例: ``` // main_test.go package main import ( "testing" "github.com/golang/mock/gomock" ) func TestGetUser(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockDatabase := NewMockDatabase(ctrl) mockDatabase.EXPECT().GetUser(1).Return("Alice", nil) app := App{ Database: mockDatabase, } user, err := app.GetUser(1) if err != nil { t.Errorf("Unexpected error: %v", err) } if user != "Alice" { t.Errorf("Expected user to be Alice, but got %v", user) } } ``` 在上面的示例中,我们创建了一个名为 `TestGetUser` 的测试用例。在该测试用例中,我们使用 `gomock.NewController` 方法创建了一个 Mock 控制器,并使用 `ctrl.Finish` 方法释放资源。我们还创建了一个名为 `mockDatabase` 的 Mock 对象,并使用 `mockDatabase.EXPECT().GetUser(1).Return("Alice", nil)` 方法来模拟 `GetUser` 方法的行为。最后,我们使用模拟的 `Database` 对象创建了一个 `App` 对象,并调用 `GetUser` 方法进行测试。 总结 在 Golang 中,我们可以使用 Mock 对象来编写单元测试使用 Mock 对象可以模拟应用程序的行为,以便进行测试。在本文中,我们介绍了使用 GoMock 库编写 Mock 单元测试的步骤,并提供了示例代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值