当前位置:网站首页>Suggestions and skills for advanced version of go language test
Suggestions and skills for advanced version of go language test
2022-06-12 12:10:00 【Just want to call Yoko】
Before reading this article , You'd better already know how to write basic unit tests . This article contains 3 A little suggestion , as well as 7 A little trick .
Recommendation 1 , Don't use frames
Go The language itself already has a great testing framework , It allows you to use Go Write test code , There is no need to learn additional libraries or test engines . Help functions for assertions , You can look at this testing, Or this assert.go ?
Recommendation two , Use "_test" Package name
Instead of using the package name of the tested code directly , Use *_test The package name allows the test code to access only the exposed interfaces in the package . This makes you write tests from the perspective of package users , This allows you to think about whether the package interface is properly designed .
Recommendation three , Avoid global constant configuration items
Avoid using global constant configuration items , Because the test code cannot modify constants . Here are three examples for comparison :
// 1. Not good. , The test code cannot modify it
const port = 8080
// 2. Better , The test code can modify it
var port = 8080
// 3. A better way , The test code can pass struct To configure Port
const defaultPort = 8080
type AppConfig {
Port int // The constructor is initialized to defaultPort
}
Skill 1 , Load test data
Go It provides very good support for loading test data from files . First ,Go Will be ignored at compile time testdata Catalog . then , When the test code runs ,Go The current directory will be used as the directory of the package . This allows you to use relative paths to access testdata Catalog . Look at examples :
func helperLoadBytes(t *testing.T, name string) []byte {
path := filepath.Join("testdata", name) // relative path
bytes, err := ioutil.ReadFile(path)
if err != nil {
t.Fatal(err)
}
return bytes
}
Tip two , Save the expected results of the test to .golden In file
Save the expected results of the test to .golden In file . And provide a flag To decide whether to update it . Using this technique can avoid hard coding the expected output in the test code . Look at examples :
var update = flag.Bool("update", false, "update .golden files")
func TestSomething(t *testing.T) {
actual := doSomething()
golden := filepath.Join(“testdata”, tc.Name+”.golden”)
if *update {
ioutil.WriteFile(golden, actual, 0644)
}
expected, _ := ioutil.ReadFile(golden)
if !bytes.Equal(actual, expected) {
// FAIL!
}
}
Tip three , Initialization during test 、 Clean up the code
Sometimes the test code is complicated , Running test case You need to initialize the environment before , This may involve a lot of unrelated error checking , For example, test whether the file is loaded successfully , Test whether the data can be pressed json Format parsing and so on . This makes the test code not purely elegant .
To solve this problem , You can put irrelevant code into the help function . These functions never return error, But in *testing.T, When an error occurs, it is directly asserted that an error is reported .
alike , If the help function needs to clean up after the completion , The help function should return a function to clean up . Look at examples :
func testChdir(t *testing.T, dir string) func() {
old, err := os.Getwd()
if err != nil {
t.Fatalf("err: %s", err)
}
if err := os.Chdir(dir); err != nil {
t.Fatalf("err: %s", err)
}
return func() { // Return cleanup function , It is called for external cleaning
if err := os.Chdir(old); err != nil {
t.Fatalf("err: %s", err)
}
}
}
func TestThing(t *testing.T) {
defer testChdir(t, "/other")()
// ...
}
The above example contains another example about defer Using very cool techniques .defer testChdir(t, "/other")() Will execute first testChdir Code in , And in TestThing Execute at the end testChdir The code in the returned cleanup function .
Tip four , When relying on third-party executable programs
Sometimes test code relies on third-party executables , We can check whether the program exists by the following methods , If it exists, execute the test , If not, skip the test .
var testHasGit bool
func init() {
if _, err := exec.LookPath("git"); err == nil {
testHasGit = true
}
}
func TestGitGetter(t *testing.T) {
if !testHasGit {
t.Log("git not found, skipping")
t.Skip()
}
// ...
}
Tip five , The test contains os.Exit Code for
This method starts the child process , Avoid tests that include os.Exit Your code caused the test program to exit prematurely . Look at examples :
func CrashingGit() {
os.Exit(1)
}
func TestFailingGit(t *testing.T) {
if os.Getenv("BE_CRASHING_GIT") == "1" { // The child process enters this logical branch
CrashingGit()
return
}
// By go test perform ,
// Set the environment variables , Start the subprocess to execute again TestFailingGit
cmd := exec.Command(os.Args[0], "-test.run=TestFailingGit")
cmd.Env = append(os.Environ(), "BE_CRASHING_GIT=1")
err := cmd.Run()
if e, ok := err.(*exec.ExitError); ok && !e.Success() {
return
}
t.Fatalf("Process ran with err %v, want os.Exit(1)", err)
}
The idea of the above example is , When Go The test framework runs TestFailingGit when , Start a subprocess (os.Args[0] That is generated Go The test program ). The subprocess runs the test program again , And only perform TestFailingGit( Through parameters -test.run=TestFailingGit Realization ), And set the environment variable BE_CRASHING_GIT=1, So the subprocess will execute CrashingGit().
Tip six , take mocks、helpers Put in testing.go In file
testing.go The file will be treated as a normal source file , Instead of testing code files . In this way, these can be used in other packages or test code of other packages mocks、helpers.
Tip seven , Handle time-consuming tests alone
When there are some time-consuming tests , Waiting for all the tests to finish is annoying . The solution is to put these time-consuming tests into _integration_test.go In file , And add compilation in the header of the file tag. Look at examples :
// +build integration
such Go These test code will not be run by default when testing .
If you want to run all the test code , You can do this :
go test -tags=integration
Here is my personal use alias A simple command to do , Can run the current directory and subdirectories except vendoer All tests outside the directory :
alias gtest="go test \$(go list ./… | grep -v /vendor/) -tags=integration"
This command can be used in conjunction with -v Parameters use :
$ gtest
…
$ gtest -v
…
Thank you for reading , Original English address :Go advanced testing tips & tricks (https://medium.com/@povilasve/go-advanced-tips-tricks-a872503ac859)
The author of this article : yoko
Link to this article : http://www.pengrl.com/p/32101/
Copyright notice : All articles in this blog except special statement , All adopt CC BY-NC-SA 3.0 license agreement . Reprint please indicate the source !
边栏推荐
- Who moved my package lock
- 宏编译 预处理头 WIN32_LEAN_AND_MEAN
- Multiplication instruction of arm instruction set
- Pytoch notes
- PDSCH 相关
- LeetCode 497. Random points in non overlapping rectangles (prefix and + bisection)
- ARP protocol data processing process of neighbor subsystem
- Pseudo instruction of arm instruction set
- 什么是模块化?模块化的好处
- 点击产生4位随机数,验证码设置
猜你喜欢
随机推荐
Common debugging tools and commands for ROS
无重复字符的最长字符串(LeetCode 3)
Dom+js+ carousel map + no time
LeetCode_字符串_简单_344.反转字符串
Linear model of machine learning
Pseudo instruction of arm instruction set
How to determine the relationship between home page and search? Relationship between homepage and search
【Leetcode】199. Right view of binary tree
创建Servlet项目
Autolock solves the problem of forgetting to unlock after locking
PDSCH related
安装canvas遇到的问题和运行项目遇到的报错
Pytoch notes
机器学习基础概念
Traditional DOM rendering?
Promise understanding has used promise to realize picture preloading (sequential loading)
[译] QUIC Wire Layout Specification - Packet Types and Formats | QUIC协议标准中文翻译(2) 包类型和格式
Automatic generation of folder directory structure
用cloneNode 克隆,解决id问题/方法 深复制和浅复制修改id的方法
服务端渲染与客户端渲染的区别(优缺点)








