当前位置:网站首页>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 !
边栏推荐
- [foundation of deep learning] back propagation method (1)
- Find the median of two ordered arrays (leetcode 4)
- Autolock solves the problem of forgetting to unlock after locking
- 服务端渲染与客户端渲染的区别(优缺点)
- mysql复习
- 关于报文
- TinyMCE series (III) introduction to common TinyMCE APIs
- 传统的DOM渲染方式?
- JS to load and display Excel files
- How to operate the newly revised Taobao merchants and what should be paid attention to
猜你喜欢

Chaîne la plus longue sans caractères dupliqués (leetcode 3)

5g NR protocol learning -- ts38.211 downlink channel

Beyondcompare 4 uses PJ

Dom+js+ carousel map + no time

Create servlet project

Cookie和Session

Visio 2019 uses PJ

5g NR Protocol Learning - - ts38.211 downlink channel

B. Wall painting (C language)

Reprint --win10 open the task manager to solve the blue screen problem
随机推荐
[译] Go语言测试进阶版建议与技巧
鸡尾酒排序
LeetCode 890. Find and replace mode (analog + double hash table)
作物模型的区域模拟实现过程初探
Traditional DOM rendering?
QT添加QObject类(想使用信号和槽)遇到的问题汇总,亲测解决有效error: undefined reference to `vtable for xxxxxx(你的类名)‘
ARP protocol data processing process of neighbor subsystem
The difference between bind, call and apply, and the encapsulation of bind()
Conversion between ROS map picture pixels and map coordinate system coordinates
Neighbor item status update of neighbor subsystem
LeetCode 1037. Effective boomerang (vector cross product)
Common debugging tools and commands for ROS
Jump instruction of arm instruction set
Channel shuffle class
AutoLock 解决加锁后忘记解锁问题
Tpage design
TinyMCE series (IV) introduction to common built-in UI components of TinyMCE
IP address management
Ficusjs series (I) introduction to ficusjs
Load/store instruction addressing mode of arm instruction set (2)