We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
本文原创,著作权归WGrape所有,未经授权,严禁转载
在项目流程中占据重要位置的测试流程,通常是指在开发流程结束后进行的一次规范化、系统化的全面测试,这个测试工作一般是由专门的测试人员(QA)完成的,它是产品上线前的最后一道安全保障。
但随着技术的发展,人们越来越意识到一个问题,测试人员虽然可以使用接口测试、测试覆盖率、自动化测试等各种更先进的测试方式,但本质上都只能从用户的使用角度去测试,而用户的使用行为是无法完全枚举出来的。这就意味着只依赖测试人员的测试,一定是会存在有Bug风险的。
所以为了进一步提高测试质量,就产生了一种从代码角度去测试的方法,它以函数作为基本的测试单元,并在输入特定的Case后,通过比较输出是否符合预期,来表示此单元是否通过测试,这就是单元测试。
在一般的项目流程中,开发完成后,测试才会介入开始工作。这种流程下,经常会遇到开发质量较低,导致测试工作被阻塞甚至要求打回重新开发的问题。
由此产生了一种称为测试驱动开发(Test-Driven Development :TDD)的开发方式,它要求在开发项目的同时,也必须编写单元测试代码。单元测试作为整个测试驱动开发中的核心,旨在通过测试来提升代码质量,驱动开发过程。
在宏观上,AIR原则定义了单元测试的意义就像是空气一样,虽然在线上环境看不到它的存在,但它却是线上安全的必要保障
在微观上,BCDE原则规定了如何编写一个合格的单元测试
每一个Package下,都需要有一个TestMain函数,这个TestMain函数可以写在此Package下的任何 _test.go 文件中。通常在TestMain函数中会完成各种配置初始化相关的操作,并通过m.Run()自动执行所有Test*单元测试。
m.Run()
Test*
func TestMain(m *testing.M) { // config初始化相关 gopath := os.Getenv("GOPATH") if gopath == "" { gopath = build.Default.GOPATH } cnofigFile := gopath + "/src/project/config/config.toml" initConfig(cnofigFile) // 一定要执行这个,否则会提示找不到测试 m.Run() }
创建一个以某个具体go文件名为前缀,_test.go为后缀的文件即表示创建了一个单元测试文件。如现在有一个relation.go文件,再创建一个对应的relation_test.go文件即表示定义了一个relation.go的单元测试文件。
单元测试函数需要在单元测试文件中创建,单元测试函数的名称通常以Test为前缀,且必须使用大驼峰命名。
func TestCompare(t *testing.T){ }
每个单元的测试结果都是通过或失败,当检测到实际结果与预期值不符合的时候,可以使用t.Fail()来标识测试失败未通过。
t.Fail()
func TestCompare(t *testing.T){ if Compare(10, 20) != -1 { t.Fail() } }
在实际进行单元测试的时候,为了判断实际值与预期值是否一致,可能需要写很多丑陋的if条件语句,出现很多冗余代码。为了解决这个问题,可以使用断言。
断言是单元测试中用于判断结果是否符合预期的一种高效工具,Go语言本身不提供断言库,如果需要可以使用testify(https://github.com/stretchr/testify)库
func TestCompare(t *testing.T){ assert := assert.New(t) assert.NotEqual(-1, Compare(10, 20), "Compare error") }
虽然不建议测试有执行顺序的依赖,但有时候对于需要控制测试顺序的场景,一般可以定义TestAll函数(函数名任意即可),充当协调工作,通过控制 t.Run 的顺序来控制单元测试的顺序。
TestAll
t.Run
package match import ( "container/heap" "fmt" "testing" ) var workHeap = &WorkHeap{} func testLiveHeapPush(t *testing.T) { heap.Push(workHeap, WorkHeapElement{ WorkId: "id-1", Score: 10, }) heap.Push(workHeap, WorkHeapElement{ WorkId: "id-2", Score: 5, }) } func testLiveHeapLen(t *testing.T) { if workHeap.Len() != 2 { t.Fail() } } func testLiveHeapPop(t *testing.T) { fmt.Println(workHeap) if heap.Pop(workHeap).(WorkHeapElement).WorkId != "id-1" { t.Fail() return } } func TestAll(t *testing.T) { t.Run("testLiveHeapPush", testLiveHeapPush) t.Run("testLiveHeapLen", testLiveHeapLen) t.Run("testLiveHeapPop", testLiveHeapPop) }
一般可以在项目目录下创建test.sh脚本,在此脚本中完成对所有单元测试的运行,并将test.sh脚本集成在CI/CD环境中即可,可以参考ParseAOF项目。
test.sh
由于达到100%测试覆盖率是比较困难的,特别是对于大工程下的每一个单元测试,所以一般不会对单元测试中的测试覆盖率做强制要求。对于测试覆盖率的使用可以参考go test coverage
在单元测试中,测试的单元越小越精简,测试的准确度也就越高。所以要尽量做到使用最小测试单元,满足不可分割性
func TestCompare(t *testing.T){ var ( a = 1 b = 2 ) Swap(a, b) if Compare(a, b) != true { t.Fail() } }
func TestCompare(t *testing.T){ if Compare(1, 2) != true { t.Fail() } }
在函数中经常有读取文件、数据库、Redis等外部资源的场景,对于这种情况,一定要把数据读取和数据处理这两部分解耦,测试数据处理部分即可。
func TestIsOldUser(t *testing.T){ uid := 18726 user := GetUserFromDB(uid) if IsOldUser(user) != true { t.Fail() } }
func TestIsOldUser(t *testing.T){ user := User{ age: 23, } if IsOldUser(user) != true { t.Fail() } }
The text was updated successfully, but these errors were encountered:
No branches or pull requests
前言
本文原创,著作权归WGrape所有,未经授权,严禁转载
目录
一、什么是单元测试
在项目流程中占据重要位置的测试流程,通常是指在开发流程结束后进行的一次规范化、系统化的全面测试,这个测试工作一般是由专门的测试人员(QA)完成的,它是产品上线前的最后一道安全保障。
但随着技术的发展,人们越来越意识到一个问题,测试人员虽然可以使用接口测试、测试覆盖率、自动化测试等各种更先进的测试方式,但本质上都只能从用户的使用角度去测试,而用户的使用行为是无法完全枚举出来的。这就意味着只依赖测试人员的测试,一定是会存在有Bug风险的。
所以为了进一步提高测试质量,就产生了一种从代码角度去测试的方法,它以函数作为基本的测试单元,并在输入特定的Case后,通过比较输出是否符合预期,来表示此单元是否通过测试,这就是单元测试。
二、单元测试的重要性
在一般的项目流程中,开发完成后,测试才会介入开始工作。这种流程下,经常会遇到开发质量较低,导致测试工作被阻塞甚至要求打回重新开发的问题。
由此产生了一种称为测试驱动开发(Test-Driven Development :TDD)的开发方式,它要求在开发项目的同时,也必须编写单元测试代码。单元测试作为整个测试驱动开发中的核心,旨在通过测试来提升代码质量,驱动开发过程。
三、遵守首要原则
1、AIR原则
在宏观上,AIR原则定义了单元测试的意义就像是空气一样,虽然在线上环境看不到它的存在,但它却是线上安全的必要保障
2、BCDE原则
在微观上,BCDE原则规定了如何编写一个合格的单元测试
四、如何编写单元测试
1、创建TestMain
每一个Package下,都需要有一个TestMain函数,这个TestMain函数可以写在此Package下的任何 _test.go 文件中。通常在TestMain函数中会完成各种配置初始化相关的操作,并通过
m.Run()
自动执行所有Test*
单元测试。2、创建单元测试文件
创建一个以某个具体go文件名为前缀,_test.go为后缀的文件即表示创建了一个单元测试文件。如现在有一个relation.go文件,再创建一个对应的relation_test.go文件即表示定义了一个relation.go的单元测试文件。
3、创建单元测试函数
单元测试函数需要在单元测试文件中创建,单元测试函数的名称通常以Test为前缀,且必须使用大驼峰命名。
4、单元测试结果
每个单元的测试结果都是通过或失败,当检测到实际结果与预期值不符合的时候,可以使用
t.Fail()
来标识测试失败未通过。5、使用断言
在实际进行单元测试的时候,为了判断实际值与预期值是否一致,可能需要写很多丑陋的if条件语句,出现很多冗余代码。为了解决这个问题,可以使用断言。
断言是单元测试中用于判断结果是否符合预期的一种高效工具,Go语言本身不提供断言库,如果需要可以使用testify(https://github.com/stretchr/testify)库
五、场景需求
1、测试顺序控制
虽然不建议测试有执行顺序的依赖,但有时候对于需要控制测试顺序的场景,一般可以定义
TestAll
函数(函数名任意即可),充当协调工作,通过控制t.Run
的顺序来控制单元测试的顺序。2、CI/CD
一般可以在项目目录下创建
test.sh
脚本,在此脚本中完成对所有单元测试的运行,并将test.sh
脚本集成在CI/CD环境中即可,可以参考ParseAOF项目。3、测试覆盖率
由于达到100%测试覆盖率是比较困难的,特别是对于大工程下的每一个单元测试,所以一般不会对单元测试中的测试覆盖率做强制要求。对于测试覆盖率的使用可以参考go test coverage
六、通用规范
1、最小测试单元
在单元测试中,测试的单元越小越精简,测试的准确度也就越高。所以要尽量做到使用最小测试单元,满足不可分割性
2、不依赖外部资源
在函数中经常有读取文件、数据库、Redis等外部资源的场景,对于这种情况,一定要把数据读取和数据处理这两部分解耦,测试数据处理部分即可。
The text was updated successfully, but these errors were encountered: