Examples of golang unit testing and coverage

Time:2021-2-25

1 Overview

C / C + + and Java (as well as most of the mainstream programming languages) have their own mature unit testing frameworks, the former such as check, the latter such as JUnit, but these programming frameworks are still third-party products in essence. In order to execute unit testing, we have to build a testing project from scratch, and we need to rely on third-party tools to generate unit test coverage.

In contrast, go language officially provides language level unit testing support, namely testing package, and it is convenient to generate coverage data only through go tool itself. That is to say, unit testing is a self-contained attribute of go language. In addition to designing your own trial of unit testing, developers do not need to worry about any details of project construction. Yes, that’s how willful golang is.

2 unit test

Next, let’s take the bit container in section 6.5 of the go programming language as an example to introduce how to conduct unit testing through the testing package and go toolset.

2.1 project catalogue

Isn’t it good to say that go language unit test doesn’t need to build a test project? In fact, there is only one sentence in golang’s testing project: Yes file.go New file_ test.go File, and write test cases in it. Therefore, our so-called project catalog is actually:

$ go env | grep GOPATH

GOPATH=”/home/pirlo/go”

$ tree /home/pirlo/go/src/github.com/pirlo-san/let-us-go

/home/pirlo/go/src/github.com/pirlo-san/let-us-go

├── bitvector
│ ├── bitvector.go
│ └── bitvector_test.go
├── LICENSE
└── README.md

/Home / Pirlo / go is my gopath github.com/pirlo -San / let us go is a git project, and bitdirector is a sub module of this project, namely bit container module, bitvector.go Is the module implementation file, bitdirector_ test.go Is the file used to test the bit container.

Implementation of 2.2 bit container

Golang has no container type. Most containers are implemented through map [type] bool. However, it is a waste of memory in some scenarios. For example, when the container elements are small non negative integers: 0 ~ 31, in fact, we only need a uint32 type of 4 bytes, but if we use map [uint32] bool, we need a u for each element Int32’s key and bool’s value. In C / C + + language, it is easy to save memory by bit field. Can golang be implemented in a similar way? Of course.

2.2.1 definition


type IntSet struct {
 words []uint
}

const (
 wordBitCount = (32 << (^uint(0) >> 63))
)

Intset is the type of bit container defined by us. It is a structure, in which the only member is a slice of uint type. Imagine that the elements of the slice are arranged in order into a “bit” array. If there is element n in the container, then the value of the nth element of the array is 1, otherwise it is 0

Wordbitcount is used to calculate the number of bits occupied by uint type, which is different on different operating systems or CPUs.

2.2.2 add an element to the container


// add x into set s
func (s *IntSet) Add(x int) {
 word, index := wordIndex(x)
 for word >= len(s.words) {
  s.words = append(s.words, 0)
 }
 s.words[word] |= (1 << index)
}

func wordIndex(x int) (int, uint) {
 return x / wordBitCount, uint(x) % wordBitCount
}

First, get the “word” of the element and the bit in the word. If the word slice is not long enough, add it until it can contain the element to be inserted. Finally, set the “bit” of the corresponding element position to 1

2.2.3 determine whether an element is in the container


// check wether x is in set s
func (s *IntSet) Has(x int) bool {
 word, index := wordIndex(x)
 if word >= len(s.words) {
  return false
 }

 return (s.words[word] & (1 << index)) != 0
}

The go programming language also implements other interfaces, including string, Unionwith, etc. see the link at the end of the article for the complete code.

2.3 unit test cases

Well, in order to test the bit container module, we just need to define the corresponding test file in the package directory and write the use case. This is the bitdirector_ test.go :


package bitvector

import (
 "testing"
)

func TestAdd(t *testing.T) {
 var s IntSet
 s.Add(1)
 s.Add(2)
 s.Add(3)
 s.Add(4)

 if s.Has(1) == false || s.Has(2) == false || s.Has(3) == false || s.Has(4) == false {
  t.Error("Failed")
 }

 if s.Has(0) == true || s.Has(5) == true || s.Has(100) == true {
  t.Error("Failed")
 }
}

Package declaration: test files also belong to the bitvector package. In this way, test files can freely access the exported and non exported types, functions, methods, etc. of the package. You can define different packages, such as package bitvector_ Test. In this way, the bitvector package_ The test package is an external library. The test package can only access the exported types, functions and methods. This is called external test;

Import testing package: the testing package has everything necessary to execute golang unit tests;

Write test functions: all test functions start with test, and the input parameter is a pointer of type testing. T. call the function under test within the function, and call functions similar to error and fatal for the results that do not meet the expected results. The former will print error information after being called, and continue to execute subsequent cases, while the latter will terminate the test immediately after printing the information, generally only after the test results are finished Now there is a serious problem, and it is necessary to call the interface similar to fatal only when the subsequent use case test cannot be continued.

2.4 performing unit tests

The command to execute unit test in golang is go test. If you are in the directory of the package to be tested, you can directly execute go test


$ pwd
/home/pirlo/go/src/github.com/pirlo-san/let-us-go/bitvector
$ go test
PASS
ok  github.com/pirlo-san/let-us-go/bitvector 0.004s

Without any parameters, test only outputs the final test results. If you want to see the test process, you can specify the – V parameter:


$ go test -v
=== RUN TestAdd
--- PASS: TestAdd (0.00s)
PASS
ok  github.com/pirlo-san/let-us-go/bitvector 0.004s

The success or failure of each use case and the execution time will be displayed.

If it is not in the current directory, you need to specify the path of the module to be tested:


$ pwd
/home/pirlo/go
$ go test -v github.com/pirlo-san/let-us-go/bitvector/
=== RUN TestAdd
--- PASS: TestAdd (0.00s)
PASS
ok  github.com/pirlo-san/let-us-go/bitvector 0.004s

You can even perform tests on all modules by replacing the specific module path with three points:

$ go test -v …

3. Coverage generation

The generation of golang unit test coverage is also ridiculously simple. Two steps:

When go test is executed, specify the – coverprofile parameter to collect coverage data;

Execute go tool cover to generate coverage reports in visual formats such as text and HTML.

3.1 collection of coverage data


$ go test -v -coverprofile=cover.out github.com/pirlo-san/let-us-go/bitvector/
=== RUN TestAdd
--- PASS: TestAdd (0.00s)
PASS
coverage: 36.0% of statements
ok  github.com/pirlo-san/let-us-go/bitvector 0.009s
$ ll cover.out 
-rw-rw-r-- 1 pirlo pirlo 1330 Jan 12 23:11 cover.out

3.2 generate coverage report in HTML format


$ go tool cover -html=cover.out -o coverage.html
$ ll coverage.html 
-rw-rw-r-- 1 pirlo pirlo 4504 Jan 12 23:15 coverage.html

The results of the generated coverage report are as follows:

The drop-down list on the left side of the first line lists the coverage percentage of all files. The text uses blue-green font to identify the covered code lines (in this case, add and has have been tested), red font to identify the uncovered code lines (Union with has no corresponding test cases), and gray font to identify similar type definitions and function declarations, which do not need to be followed Select the line of code that you want.

4 Summary

The process of unit testing and coverage report generation of golang is very simple and fast, and it doesn’t need any third-party tools or libraries. In addition to the basic testing scenarios described in this article, golang also supports benchmark testing, internal function / method piling, etc. chat when you have time.

The complete code of this article is as follows:here

Supplementary knowledge:Golang test display output

By default, go test will not output the content of testing. T. log().

To display this, you need to add the switch – V

go test -v -timeout 30s xxx/xxx/package -run ^TestXXXFunction$

In the visual studio code ide environment, you can set workspace settings. Open. Vscode/ settings.json , add:

“go.testFlags”: [“-v”],

In this way, in the IDE editor, click Run Test above the function to run go test automatically, and the – V flag will be added. In the output window, you can see the output content of t.logf (“XXX% s”, “XXX”).

Before setting:

After adding settings:

The above example explanation of golang unit test and coverage is the whole content shared by Xiaobian. I hope it can give you a reference and support developer.

Recommended Today

Realization of promise source code

Promise 1、 Simple use of promise new Promise((resolve, reject) => { resolve(“ok”) }).then(data => { console.log(data) //OK }, error => { console.log(error) }) //Simplify let promise = new Promise(executor) promise.then(onFulfilled, onRejected) It should be noted that executorIs a callback function that will be executed immediately with two parameters,resolveand reject IfexecutorCalled inresolve(value)So thisvalueWill be passed on […]