Golang UnitTest单元测试

本文详细介绍了Golang单元测试的基本概念、书写规范及如何利用VSCode进行高效开发。包括单元测试文件命名规则、方法命名及参数要求,以及如何在VSCode中自动生成单元测试文件和运行测试。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Golang UnitTest单元测试

单元测试是程序开发者适用一段代码来验证另外一段代码写的是否符合预期的一种相对高效的自我测试方法。

对于程序开发来说,经过长期的积累和方法总结,单元测试是一种比较好的开发程序验证方式,但是单元测试却是在正真的软件开发之外要写的额外的代码,而且编写和管理维护都有比较大的成本。但是单元测试确实能够提高程序开发的质量,所以基本上各种语言都有相应的单元测试框架来支持更为方便编写和管理单元测试。

golang也是一样,不过golang的测试框架的支持是自生就提供了,不像C/C++,php等语言的单元测试框架需要第三方提供和安装。golang你在安装了其开发编译环境之后就自然包含了单元测试的框架。今天这里就来简单说明一下golang的单元测试框架的适用。

golang UnitTest的书写规范和简单适用

规范

Golang单元测试对文件名和方法名,参数都有很严格的要求。

  例如:

1. 单元测试文件名必须以xxx_test.go命名
2. 方法必须是TestXxx开头
3. 方法参数必须 t *testing.T
4. 测试文件和被测试文件必须在一个包中

示例

这是我在我的小项目【设计模式golang实现】(https://github.com/helight/go-patterns)中的一个例子,另外这里说明一下:一般情况下单元测试文件和被测试文件是放到同一个目录的。

import (	
    "fmt"    "testing")

func TestFactory(t *testing.T) {	
    shapeFactory := ShapeFactory{}	
    rectangle := shapeFactory.Produce("rectangle")	
    
    if rectangle != nil {
		rectangle.Draw()
    }	
    errshape := shapeFactory.Produce("cricle")	
    if errshape == nil {
		fmt.Println("errshape")
    }
}

这样的来编写单元测试是不是很方便呀!

运行单元测试

这里也可以参考一下我的小项目设计模式golang实现中的Makefile 运行非常简单。

go test -v creational/01_factory/ 

这里加v参数是为了输出更多的信息, 可以把print的信息和一些测试信息都能比较详细的输出

golang单元测试进阶

go test参数帮助

这里给大家介绍与喜爱go test的帮助适用,golang这方面做的还是非常不错的,主要涉及两个命令:

命令1:go help test
$ go help testusage: go test [build/test flags] [packages] [build/test flags & test binary flags]'Go test' automates testing the packages named by the import paths.
It prints a summary of the test results in the format:

        ok   archive/tar   0.011s
        FAIL archive/zip   0.022s
        ok   compress/gzip 0.033s
        ...
...
命令2: go help packages

一般情况下整个命令用的比较少。

$ go help packages
Many commands apply to a set of packages:

        go action [packages]

Usually, [packages] is a list of import paths.
命令3:go help testflag
$ go help testflag
The 'go test' command takes both flags that apply to 'go test' itself
and flags that apply to the resulting test binary.
...

下面是常用的一些flags -test.v : 是否输出全部的单元测试用例(不管成功或者失败),默认没有加上,所以只输出失败的单元测试用例。

-test.run pattern: 只跑哪些单元测试用例

-test.bench patten: 只跑那些性能测试用例

-test.benchmem : 是否在性能测试的时候输出内存情况

-test.benchtime t : 性能测试运行的时间,默认是1s

-test.cpuprofile cpu.out : 是否输出cpu性能分析文件

-test.memprofile mem.out : 是否输出内存性能分析文件

-test.blockprofile block.out : 是否输出内部goroutine阻塞的性能分析文件

golang & vs code

使用过那么多编辑器之后发现只有vim和vs code最好用,vs code在图形化编码工具中目前我认为是最好的,vim是命令行编辑器中最好的。目前也是使用vs code来编写markdown文件的。这里不是重点,重点看下面的。

安装go插件

启动vscode选择插件->搜go选择Go for Visual Studio Code插件点击安装即可。如图: 

配置vscode

settins.json 基本上不需要配置,用默认值就可以了。如需调整 文件 -->首选项 -->设置 输入go即可查看go相关配置,以下是我的配置,不过一般情况下无需修改配置,也可以自己多设置一点

    "go.goroot": "C:\\Go",    
    "go.gopath": "${workspaceRoot};D:\\go4\\go_thirdpaty",    
    "go.buildFlags": [],    
    "go.lintFlags": [],    
    "go.coverOnSave": false,    
    "go.useCodeSnippetsOnFunctionSuggest": false,    
    "go.formatTool": "goreturns",    
    "go.gocodeAutoBuild": false,    
    "go.toolsGopath": "D:\\go4\\tools",

vscode中怎么开发单元测试

自动生成ut文件

// datacal.go

package main

func add_num(a int, b int) int {
	return a + b
}

例如有上面的文件,在vs安装了go工具之后,我们在add_num整个函数内点击右键,选择为该函数生成单元测试即可,如下图 

第一次适用的时候会安装相应的一个小组件,安装之后既可以适用了,上面的代码用该方式自动生成的单元测试文件如下:

// datacal.gopackage mainimport "testing"func Test_add_num(t *testing.T) {	type args struct {
		a int
		b int
	}	tests := []struct {
		name string
		args args
		want int
	}{                // TODO: Add test cases.
                {"1 + 1", args{1, 1}, 2}, // 自己添加的测试用例数据
		{"1 + 2", args{1, 2}, 3}, // 自己添加的测试用例数据
		{"1 + 3", args{1, 3}, 4}, // 自己添加的测试用例数据
	}	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {			if got := add_num(tt.args.a, tt.args.b); got != tt.want {
				t.Errorf("add_num() = %v, want %v", got, tt.want)
			}
		})
	}
}

运行测试

helightxu@helightxu-NB0 MINGW64 /d/code_dev/heliht_github/helight_code/go_code/go_test2 (master)
$ go test -v
=== RUN   Test_add_num
=== RUN   Test_add_num/1_+_1
=== RUN   Test_add_num/1_+_2
=== RUN   Test_add_num/1_+_3
--- PASS: Test_add_num (0.00s)
    --- PASS: Test_add_num/1_+_1 (0.00s)
    --- PASS: Test_add_num/1_+_2 (0.00s)
    --- PASS: Test_add_num/1_+_3 (0.00s)
PASS
ok      _/d_/code_dev/heliht_github/helight_code/go_code/go_test2       0.315s

这里我们也可以这样来跑:指定函数执行,很多时候测试用例多的时候修改了一个就想执行这一个,这个就很有用了。

$ go test -run add_num -v
=== RUN   Test_add_num
=== RUN   Test_add_num/1_+_1
=== RUN   Test_add_num/1_+_2
=== RUN   Test_add_num/1_+_3
--- PASS: Test_add_num (0.00s)
    --- PASS: Test_add_num/1_+_1 (0.00s)
    --- PASS: Test_add_num/1_+_2 (0.00s)
    --- PASS: Test_add_num/1_+_3 (0.00s)
PASS
ok      _/d_/code_dev/heliht_github/helight_code/go_code/go_test2       0.265s

单元测试关键点

测试数据构造

单元测试的一个重点就是测试数据的构造,在测试数据构造时要考虑这样几个方面:

  1. 正常输入,整个必不可少,至少验证函数的正常逻辑是否通过

  2. 边界输入,这个主要验证在极端情况下的输入,函数是否在有相应的容错处理

  3. 非法输入,对于一些非正常输入,我们要看函数是否处理,会不引起函数的奔溃和数据泄露等问题

  4. 白盒覆盖,白盒覆盖就要设计了,要设计一些用力,能够覆盖到函数的所有代码,这里主要考虑:语句覆盖、条件覆盖、分支覆盖、分支/条件覆盖、条件组合覆盖。

编写原则

单元测试是要写额外的代码的,这对开发同学的也是一个不小的工作负担,在一些项目中,我们合理的评估单元测试的编写,我认为我们不能走极端,当然理论上来说全写肯定时好的,但是从成本,效率上来说我们必须做出权衡。所以这里给出一些衡量的原则

  1. 优先编写核心组件和逻辑模块的测试用例

  2. 逻辑类似的组件如果存在多个,优先编写其中一种逻辑组件的测试用例

  3. 发现Bug时一定先编写测试用例进行Debug

  4. 关键util工具类要编写测试用例,这些util工具适用的很频繁,所以这个原则也叫做热点原则,和第1点相呼应。

  5. 测试用户应该独立,一个文件对应一个,而且不同的测试用例之间不要互相依赖。

  6. 测试用例的保持更新。

Benchmark测试

介绍

最后这里简单也说以下go中benchmark的测试,这个先对比较简单,主要针对cpu类型或者内存类型的计算进行性能测试的一种方式,有时也用在io上,但是io在单元测试中是比较敏感的,一般磁盘io和远程网络io很少做benchmark测试。

benchmark的测试函数形式时这样的:

  1. 函数是以Benchmark开头

  2. 参数是*testing.B和普通单元测试的不一样哈(t *testing.T))。

func BenchmarkXxx(b *testing.B)

测试用例示例

在上面的例子基础上增加一个benchmark的测试:

func Benchmark_add_num(b *testing.B) {
	sum := 0
	for i := 0; i < b.N; i++ {
        sum = add_num(1343, 434)
	}
	b.Log(sum)
}

这里可以看到b.N这个参数,这个数据是go自己预测的一个值,目标是有足够的测试来观察。

运行benchmark测试

$ go test -v -run="none" -bench=. -benchmem
goos: windows
goarch: 386
Benchmark_add_num-4     2000000000               0.90 ns/op            0 B/op          0 allocs/op
--- BENCH: Benchmark_add_num-4
    datacal_test.go:37: 1777
    datacal_test.go:37: 1777
    datacal_test.go:37: 1777
    datacal_test.go:37: 1777
    datacal_test.go:37: 1777
    datacal_test.go:37: 1777
PASS
ok      _/d_/code_dev/heliht_github/helight_code/go_code/go_test2       3.870s

这不允许单元测试,运行所有的基准测试,-bench可以指定函数名,支持正则。

-benchmem 表示分配内存的次数和字节数

其它

这里没有介绍golang的cover,内存,cpu分析,还有mock等测试方式,留作以后再介绍。想在下一篇中写写mock测试,尤其golang开发中的。

参考链接

go testing: http://docs.studygolang.com/pkg/testing/ https://github.com/golang/go/wiki

<think>首先,用户的问题是关于单元测试中mock技术的用法和相关工具。用户提到“我想了解如何在单元测试中使用mock技术,或寻找相关的工具和实现方法”,并具体询问“单元测试中mock的用法及工具”。根据系统级指令:-所有行内数学表达式必须使用$...$格式。-独立公式使用$$...$$格式并单独成段。-LaTeX语法正确。-使用中文回答。-生成相关问题。-在回答中引用的段落末尾自然地添加引用标识。-回答结构清晰,帮助用户逐步解决问题,保证真实可靠。参考提供的引用:-[^1]介绍了Go语言的gomock框架。-[^2]介绍了Java中的单元测试和Mockito框架。-[^3]详细解释了Java中的Mock技术。-[^4]讨论了Python中使用unittest.mock进行mock测试。-[^5]解释了mock在单元测试中的优点和使用场景。用户的问题没有指定编程语言,所以我需要覆盖多个常见语言。引用中提到了Go、Java和Python,我应该基于这些来回答。回答结构:-先解释什么是mock技术及其核心价值。-然后分语言介绍用法和工具:包括Java(Mockito)、Gogomock)、Python(unittest.mock)。-每个部分提供简单代码示例。-最后生成相关问题。-在引用内容后添加引用标识。关键点:-Mock的核心价值:隔离测试环境、模拟复杂场景、验证交互行为、提升测试速度。-工具:Java的Mockito,Gogomock,Python的unittest.mock。-用法:创建mock对象,设置期望行为,验证调用。代码示例:-使用代码块,如示例中的Python代码。-确保语言正确。生成相关问题:-在结尾添加相关问题部分,如示例所示。添加引用:-当引用信息时,在段落末尾添加引用标识,如[^1]。完整回答结构:1.**引言**:解释mock技术在单元测试中的作用。2.**核心概念**:简述mock、stub、spy的区别。3.**分语言介绍**:-Java:使用Mockito。-Go:使用gomock。-Python:使用unittest.mock。4.**总结**:强调mock的好处。5.**相关问题**:生成几个相关问题。确保中文回答。</think>### 单元测试中 Mock 技术的用法及工具 在单元测试中,Mock(模拟)技术用于创建虚拟对象替代真实依赖项,从而隔离外部环境、模拟复杂场景(如异常或特定返回值),并验证代码交互行为。它解决了依赖项未实现或不可控的问题,提升测试效率和可靠性[^5]。Mock 的核心价值包括: - **隔离测试环境**:消除数据库、网络服务等外部依赖的影响。 - **模拟复杂场景**:预设依赖对象的行为,例如模拟错误响应或特定数据。 - **验证交互行为**:检查被测代码是否按预期调用了依赖对象的方法。 - **提升测试速度**:避免真实 I/O 操作,测试执行更快[^3]。 Mock 与类似技术(如 Stub 和 Spy)的区别: - **Mock**:完全虚拟对象,可验证交互行为(如方法调用次数)。 - **Stub**:仅提供预设响应,不验证交互。 - **Spy**:部分真实对象,可监视真实调用并覆盖特定方法[^3]。 下面,我将分编程语言介绍 Mock 的用法和常用工具,并提供简单代码示例。工具选择基于流行度和社区支持。 #### 1. **Java 中的 Mock 用法与工具** 在 Java 中,Mockito 是最流行的 Mock 框架,API 简洁且功能强大。它支持创建 Mock 对象、设置期望行为并验证调用。 **核心用法步骤**: 1. 添加依赖:在 Maven 或 Gradle 中引入 Mockito。 2. 创建 Mock 对象:使用 `Mockito.mock()` 模拟接口或类。 3. 设置期望行为:定义方法调用的返回值或异常。 4. 注入 Mock:将 Mock 对象注入被测类。 5. 验证交互:检查方法是否被正确调用。 **示例代码**:模拟一个用户服务接口,测试用户登录逻辑。 ```java import static org.mockito.Mockito.*; import org.junit.jupiter.api.Test; // 定义接口 interface UserService { boolean login(String username, String password); } // 被测类 class LoginController { private UserService userService; public LoginController(UserService userService) { this.userService = userService; } public String authenticate(String user, String pass) { if (userService.login(user, pass)) { return "登录成功"; } return "登录失败"; } } // 单元测试类 public class LoginTest { @Test public void testLoginSuccess() { // 创建 UserService 的 Mock 对象 UserService mockService = mock(UserService.class); // 设置期望:当调用 login("admin", "123") 时返回 true when(mockService.login("admin", "123")).thenReturn(true); // 注入 Mock 到被测对象 LoginController controller = new LoginController(mockService); String result = controller.authenticate("admin", "123"); // 验证结果和交互 assertEquals("登录成功", result); verify(mockService, times(1)).login("admin", "123"); // 验证 login 方法被调用一次 } } ``` **工具**: - **Mockito**:主流框架,易于上手,支持注解如 `@Mock`。 - **PowerMock**:扩展 Mockito,可模拟静态方法或私有方法[^3]。 - **依赖管理**:通过 Maven 添加 `mockito-core` 依赖。 Mockito 的优势在于其流畅的 API 和强大的验证功能,适合大多数 Java 项目[^2]。 #### 2. **Go 中的 Mock 用法与工具** 在 Go 中,gomock 是官方推荐的 Mock 框架,通过代码生成创建 Mock 对象,特别适合模拟接口。 **核心用法步骤**: 1. 安装工具:运行 `go get github.com/golang/mock/gomock` 安装 gomock。 2. 生成 Mock 代码:使用 `mockgen` 命令为接口生成 Mock 实现。 3. 设置期望:在测试中定义 Mock 方法的返回值。 4. 验证调用:使用 gomock 的控制器检查交互。 **示例代码**:模拟一个数据库服务,测试用户查询逻辑。 ```go package main import ( "testing" "github.com/golang/mock/gomock" // 假设已生成 Mock 代码:mock_db 包 ) // 定义接口 type DBService interface { GetUser(id int) (string, error) } // 被测函数 func FetchUserName(db DBService, id int) (string, error) { return db.GetUser(id) } // 单元测试 func TestFetchUserName(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() // 创建 Mock 对象 mockDB := mock_db.NewMockDBService(ctrl) // 设置期望:当 GetUser(1) 被调用时返回 "Alice", nil mockDB.EXPECT().GetUser(1).Return("Alice", nil) // 调用被测函数 name, err := FetchUserName(mockDB, 1) if err != nil || name != "Alice" { t.Errorf("预期 'Alice',实际 %s", name) } } ``` **工具**: - **gomock**:Go 标准库的扩展,支持严格的接口模拟[^1]。 - 使用 `mockgen` 自动生成 Mock 代码,减少手写工作量。 gomock 强调类型安全和编译时检查,适合 Go 的强类型特性[^1]。 #### 3. **Python 中的 Mock 用法与工具** Python 内置 `unittest.mock` 模块,无需额外安装,可直接用于模拟对象和函数。 **核心用法步骤**: 1. 导入模块:使用 `from unittest.mock import Mock, patch`。 2. 创建 Mock 对象:通过 `Mock()` 或 `patch` 装饰器模拟依赖。 3. 设置行为:定义返回值或副作用。 4. 验证调用:使用 `assert_called_with` 等方法检查交互。 **示例代码**:模拟一个外部 API 调用,测试数据处理逻辑。 ```python from unittest.mock import Mock, patch import unittest # 被测函数 def process_data(api_client): response = api_client.fetch_data() return response.upper() if response else "Error" # 单元测试类 class TestDataProcessing(unittest.TestCase): @patch('__main__.APIClient') # 模拟 APIClient 类 def test_process_data_success(self, mock_api_client): # 创建 Mock 对象并设置返回值 mock_instance = mock_api_client.return_value mock_instance.fetch_data.return_value = "hello" # 调用被测函数 result = process_data(mock_instance) # 验证结果和调用 self.assertEqual(result, "HELLO") mock_instance.fetch_data.assert_called_once() # 验证 fetch_data 被调用一次 def test_process_data_failure(self): # 直接使用 Mock 对象 mock_client = Mock() mock_client.fetch_data.return_value = None result = process_data(mock_client) self.assertEqual(result, "Error") ``` **工具**: - **unittest.mock**:Python 标准库的一部分,简单易用[^4]。 - **pytest-mock**:pytest 插件,提供更简洁的 API。 Python 的 mock 模块灵活,适合快速模拟函数或类[^4]。 ### 总结 Mock 技术在单元测试中至关重要,它通过隔离依赖项提升测试的可靠性和速度。核心步骤包括创建 Mock 对象、设置期望行为、注入依赖和验证交互。选择工具时: - Java 推荐 Mockito,语法流畅。 - Go 使用 gomock,适合接口模拟。 - Python 内置 unittest.mock,无需额外依赖。 实际项目中,Mock 可应用于数据库访问、网络请求或第三方服务模拟[^5]。通过 mock,单元测试能专注于当前逻辑,避免环境依赖问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值