Go语言单元测试

这篇具有很好参考价值的文章主要介绍了Go语言单元测试。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1、Go语言单元测试

Go语言中的测试依赖 go test 命令,go test 命令是一个按照一定约定和组织的测试代码的驱动程序。在包目录

内,所有以 _test.go 为后缀名的源代码文件都是 go test 测试的一部分,不会被 go build 编译到最终的可执行

文件中。

*_test.go 文件中有三种类型的函数,单元测试函数基准测试函数示例函数

类型 格式 作用
单元测试函数 前缀为Test 测试程序的逻辑是否正确
基准测试函数 前缀为Benchmark 测试程序的性能
示例函数 前缀为Example 为程序提供示例

go test命令会遍历所有的 *_test.go 文件中符合上述命名规则的函数,然后生成一个临时的main包用于调用相

应的测试函数,然后构建并运行、报告测试结果,最后清理测试中生成的临时文件。

可以使用 go help testfunc 命令查看函数的写法。

$ go help testfunc
The 'go test' command expects to find test, benchmark, and example functions
in the "*_test.go" files corresponding to the package under test.

A test function is one named TestXxx (where Xxx does not start with a
lower case letter) and should have the signature,

        func TestXxx(t *testing.T) { ... }

A benchmark function is one named BenchmarkXxx and should have the signature,

        func BenchmarkXxx(b *testing.B) { ... }

A fuzz test is one named FuzzXxx and should have the signature,

        func FuzzXxx(f *testing.F) { ... }

An example function is similar to a test function but, instead of using
*testing.T to report success or failure, prints output to os.Stdout.
If the last comment in the function starts with "Output:" then the output
is compared exactly against the comment (see examples below). If the last
comment begins with "Unordered output:" then the output is compared to the
comment, however the order of the lines is ignored. An example with no such
comment is compiled but not executed. An example with no text after
"Output:" is compiled, executed, and expected to produce no output.

Godoc displays the body of ExampleXxx to demonstrate the use
of the function, constant, or variable Xxx. An example of a method M with
receiver type T or *T is named ExampleT_M. There may be multiple examples
for a given function, constant, or variable, distinguished by a trailing _xxx,
where xxx is a suffix not beginning with an upper case letter.

Here is an example of an example:

        func ExamplePrintln() {
                Println("The output of\nthis example.")
                // Output: The output of
                // this example.
        }

Here is another example where the ordering of the output is ignored:

        func ExamplePerm() {
                for _, value := range Perm(4) {
                        fmt.Println(value)
                }

                // Unordered output: 4
                // 2
                // 1
                // 3
                // 0
        }

The entire test file is presented as the example when it contains a single
example function, at least one other function, type, variable, or constant
declaration, and no tests, benchmarks, or fuzz tests.

See the documentation of the testing package for more information.

通过 go help test 可以看到go test的使用说明。

$ go help test
usage: 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
        ...
In addition to the build flags, the flags handled by 'go test' itself are:

        -args
            Pass the remainder of the command line (everything after -args)
            to the test binary, uninterpreted and unchanged.
            Because this flag consumes the remainder of the command line,
            the package list (if present) must appear before this flag.

        -c
            Compile the test binary to pkg.test but do not run it
            (where pkg is the last element of the package's import path).
            The file name can be changed with the -o flag.

        -exec xprog
            Run the test binary using xprog. The behavior is the same as
            in 'go run'. See 'go help run' for details.

        -i
            Install packages that are dependencies of the test.
            Do not run the test.
            The -i flag is deprecated. Compiled packages are cached automatically.

        -json
            Convert test output to JSON suitable for automated processing.
            See 'go doc test2json' for the encoding details.

        -o file
            Compile the test binary to the named file.
            The test still runs (unless -c or -i is specified).

格式形如: go test [build/test flags] [packages] [build/test flags & test binary flags]

参数解读:

  • -c :生成用于运行测试的可执行文件,但不执行它。这个可执行文件会被命名为 pkg.test,其中的 pkg 即

    为被测试代码包的导入路径的最后一个元素的名称。

  • -i :安装/重新安装运行测试所需的依赖包,但不编译和运行测试代码。

  • -o:指定用于运行测试的可执行文件的名称,追加该标记不会影响测试代码的运行,除非同时追加了标记

    -c 或 -i。

上述这几个标记可以搭配使用,搭配使用的目的可以是让 go test 命令既安装依赖包又编译测试代码,但不运行

测试。也就是说,让命令程序跑一遍运行测试之前的所有流程。这可以测试一下测试过程。注意,在加入 -c 标记

后,命令程序会把用于运行测试的可执行文件存放到当前目录下。

关于 build/test flags,调用 go help build,这些是编译运行过程中需要使用到的参数,一般设置为空。

关于 packages,调用 go help packages,这些是关于包的管理,一般设置为空。

关于 test binary flags,调用 go help testflag,这些是 go test 过程中经常使用到的参数:

  • -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阻塞的性能分析文件。

  • -test.memprofilerate n:内存性能分析的时候有一个分配了多少的时候才打点记录的问题。这个参数就

    是设置打点的内存分配间隔,也就是profile中一个sample代表的内存大小。默认是设置为512 * 1024的。如

    果你将它设置为1,则每分配一个内存块就会在profile中有个打点,那么生成的profile的sample就会非常多。

    如果你设置为0,那就是不做打点了。你可以通过设置memprofilerate=1和GOGC=off来关闭内存回收,并且

    对每个内存块的分配进行观察。

  • -test.blockprofilerate n:基本同上,控制的是goroutine阻塞时候打点的纳秒数。默认不设置就相当

    -test.blockprofilerate=1,每一纳秒都打点记录一下。

  • -test.parallel n:性能测试的程序并行cpu数,默认等于GOMAXPROCS。

  • -test.timeout t:如果测试用例运行时间超过t,则抛出panic。

  • -test.cpu 1,2,4:go test -cpu=1,2,4 将会执行 3 次,其中 GOMAXPROCS 值分别为 1,2,和 4。

  • -test.short:将那些运行时间较长的测试用例运行时间缩短。

  • -test.outputdir:输出目录。

  • -test.coverprofil:测试覆盖率参数,指定输出文件。

所有的 test 参数:

  -test.bench regexp
        run only benchmarks matching regexp
  -test.benchmem
        print memory allocations for benchmarks
  -test.benchtime d
        run each benchmark for duration d (default 1s)
  -test.blockprofile file
        write a goroutine blocking profile to file
  -test.blockprofilerate rate
        set blocking profile rate (see runtime.SetBlockProfileRate) (default 1)
  -test.count n
        run tests and benchmarks n times (default 1)
  -test.coverprofile file
        write a coverage profile to file
  -test.cpu list
        comma-separated list of cpu counts to run each test with
  -test.cpuprofile file
        write a cpu profile to file
  -test.failfast
        do not start new tests after the first test failure
  -test.fuzz regexp
        run the fuzz test matching regexp
  -test.fuzzcachedir string
        directory where interesting fuzzing inputs are stored (for use only by cmd/go)
  -test.fuzzminimizetime value
        time to spend minimizing a value after finding a failing input (default 1m0s)
  -test.fuzztime value
        time to spend fuzzing; default is to run indefinitely
  -test.fuzzworker
        coordinate with the parent process to fuzz random values (for use only by cmd/go)
  -test.list regexp
        list tests, examples, and benchmarks matching regexp then exit
  -test.memprofile file
        write an allocation profile to file
  -test.memprofilerate rate
        set memory allocation profiling rate (see runtime.MemProfileRate)
  -test.mutexprofile string
        write a mutex contention profile to the named file after execution
  -test.mutexprofilefraction int
        if >= 0, calls runtime.SetMutexProfileFraction() (default 1)
  -test.outputdir dir
        write profiles to dir
  -test.paniconexit0
        panic on call to os.Exit(0)
  -test.parallel n
        run at most n tests in parallel (default 20)
  -test.run regexp
        run only tests and examples matching regexp
  -test.short
        run smaller test suite to save time
  -test.shuffle string
        randomize the execution order of tests and benchmarks (default "off")
  -test.testlogfile file
        write test action log to file (for use only by cmd/go)
  -test.timeout d
        panic test binary after duration d (default 0, timeout disabled)
  -test.trace file
        write an execution trace to file
  -test.v
        verbose: print additional output
# 带参数的例子
# 其它的参数视情况决定是否添加
$ go test -v -parallel 2

1.1 Go test 注意事项

GO 语言里面的单元测试,是使用标准库 testing

注意事项如下:

  • 用来测试的代码必须以 _test.go 结尾。
  • 单元测试的函数名必须以 Test开头,并且只有一个参数,类型是 *testing.T
  • 单元测试函数名 Test 后必须紧跟着大写,比如:TestAdd
  • 基准测试或压力测试必须以 Benchmark 开头,并且只有参数 *testing.B

例如:

// Tests
func TestXxx(*testing.T)
package go_test

import (
	"testing"
	"math"
)

func TestAbs(t *testing.T) {
	got := math.Abs(-1)
	if got != 1 {
		t.Errorf("Abs(-1) = %f; want 1", got)
	}
}
$ go test -v 001_test.go
=== RUN   TestAbs
--- PASS: TestAbs (0.00s)
PASS
ok      command-line-arguments  0.258s
// Benchmarks
func BenchmarkXxx(*testing.B)
package go_test

import (
	"math/rand"
	"testing"
)

func BenchmarkRandInt(b *testing.B) {
	for i := 0; i < b.N; i++ {
		rand.Int()
	}
}
$ go test -bench BenchmarkRandInt -cpu=1 -count=5 002_test.go
goos: windows
goarch: amd64
cpu: 12th Gen Intel(R) Core(TM) i7-12700
BenchmarkRandInt        100000000               11.43 ns/op
BenchmarkRandInt        100000000               11.50 ns/op
BenchmarkRandInt        100000000               11.50 ns/op
BenchmarkRandInt        100000000               11.46 ns/op
BenchmarkRandInt        100000000               11.49 ns/op
PASS
ok      command-line-arguments  6.038s

基准函数必须运行目标代码 b.N 次,在基准测试执行期间,b.N 被调整,直到基准测试函数持续足够长的时间以

可靠地定时。

如果基准测试在运行前需要一些昂贵的设置,则可以重置计时器:b.ResetTimer()

1.2 Go test命令介绍

  • go test packageName:执行这个包下面的所有测试用例。
  • go test . :执行当前目录下的所有测试用例。
  • go test 测试源文件:执行这个测试源文件里的所有测试用例。
  • go test -run 选项:执行指定的测试用例。
  • go test -bench .:执行当前路径下的所有压力测试。
  • go test -bench BenchmarkAdd:对BenchmarkAdd这个函数执行压力测试。

1.3 利用单元测试来测试斐波那契数列的递归和非递归版本

斐波那契数列的计算:

package fb

// 斐波那契数列的递归版本
// 斐波那契数列:后一个数等于前面两个数的和
func FbRecursion(num int) int {
	if num <= 2 {
		return 1
	}
	return FbRecursion(num-1) + FbRecursion(num-2)
}

// 斐波那契数列的非递归版本
// 斐波那契数列:后一个数等于前面两个数的和
func FbNoRecursion(num int) int {
	l := make([]int, num+1)
	for i := 1; i <= num; i++ {
		if i <= 2 {
			l[i] = 1
		} else {
			l[i] = l[i-1] + l[i-2]
		}
	}
	return l[num]
}
package go_test

import (
	"proj/fb"
	"testing"
)

type FbTestCase struct {
	num    int
	expect int
}

var fbTestCases = map[string]FbTestCase{
	"1": {num: 1, expect: 1},
	"2": {num: 2, expect: 1},
	"3": {num: 3, expect: 2},
	"4": {num: 4, expect: 3},
	"5": {num: 5, expect: 5},
	"9": {num: 9, expect: 34},
}

// 基准测试
// 递归测试
func TestFbRecursion(t *testing.T) {
	for name, tc := range fbTestCases {
		// 使用t.Run执行子测试
		t.Run(name, func(tt *testing.T) {
			output := fb.FbRecursion(tc.num)
			if output != tc.expect {
				tt.Errorf("expect: %d, but output: %d", tc.expect, output)
			}
		})
	}
}

// 非递归测试
func TestFbNoRecursion(t *testing.T) {
	for name, tc := range fbTestCases {
		// 使用t.Run执行子测试
		t.Run(name, func(tt *testing.T) {
			output := fb.FbNoRecursion(tc.num)
			if output != tc.expect {
				tt.Errorf("expect: %d, but output: %d", tc.expect, output)
			}
		})
	}
}

使用如下命令进行测试,测试结果:

$ go test -run .*Fb.* -v
=== RUN   TestFbRecursion
=== RUN   TestFbRecursion/9
=== RUN   TestFbRecursion/1
=== RUN   TestFbRecursion/2
=== RUN   TestFbRecursion/3
=== RUN   TestFbRecursion/4
=== RUN   TestFbRecursion/5
--- PASS: TestFbRecursion (0.00s)
    --- PASS: TestFbRecursion/9 (0.00s)
    --- PASS: TestFbRecursion/1 (0.00s)
    --- PASS: TestFbRecursion/2 (0.00s)
    --- PASS: TestFbRecursion/3 (0.00s)
    --- PASS: TestFbRecursion/4 (0.00s)
    --- PASS: TestFbRecursion/5 (0.00s)
=== RUN   TestFbNoRecursion
=== RUN   TestFbNoRecursion/2
=== RUN   TestFbNoRecursion/3
=== RUN   TestFbNoRecursion/4
=== RUN   TestFbNoRecursion/5
=== RUN   TestFbNoRecursion/9
=== RUN   TestFbNoRecursion/1
--- PASS: TestFbNoRecursion (0.00s)
    --- PASS: TestFbNoRecursion/2 (0.00s)
    --- PASS: TestFbNoRecursion/3 (0.00s)
    --- PASS: TestFbNoRecursion/4 (0.00s)
    --- PASS: TestFbNoRecursion/5 (0.00s)
    --- PASS: TestFbNoRecursion/9 (0.00s)
    --- PASS: TestFbNoRecursion/1 (0.00s)
PASS
ok      proj    0.256s

生成测试的二进制文件:

$ go test -v -run="TestFbRecursion" -c
# 会生成proj.test.exe文件

1.4 测试支持的函数

使用 go doc testing.T 查询文档:

$ go doc testing.T
package testing // import "testing"

type T struct {
        // Has unexported fields.
}
    T is a type passed to Test functions to manage test state and support
    formatted test logs.

    A test ends when its Test function returns or calls any of the methods
    FailNow, Fatal, Fatalf, SkipNow, Skip, or Skipf. Those methods, as well as
    the Parallel method, must be called only from the goroutine running the Test
    function.

    The other reporting methods, such as the variations of Log and Error, may be
    called simultaneously from multiple goroutines.

func (c *T) Cleanup(f func())
func (t *T) Deadline() (deadline time.Time, ok bool)
func (c *T) Error(args ...any)
func (c *T) Errorf(format string, args ...any)
func (c *T) Fail()
func (c *T) FailNow()
func (c *T) Failed() bool
func (c *T) Fatal(args ...any)
func (c *T) Fatalf(format string, args ...any)
func (c *T) Helper()
func (c *T) Log(args ...any)
func (c *T) Logf(format string, args ...any)
func (c *T) Name() string
func (t *T) Parallel()
func (t *T) Run(name string, f func(t *T)) bool
func (t *T) Setenv(key, value string)
func (c *T) Skip(args ...any)
func (c *T) SkipNow()
func (c *T) Skipf(format string, args ...any)
func (c *T) Skipped() bool
func (c *T) TempDir() string

go doc testing testing.T.Cleanup 查询Cleanup的方法描述:

$ go doc testing testing.T.Cleanup
doc: too many periods in symbol specification
Usage of [go] doc:
        go doc
        go doc <pkg>
        go doc <sym>[.<methodOrField>]
        go doc [<pkg>.]<sym>[.<methodOrField>]
        go doc [<pkg>.][<sym>.]<methodOrField>
        go doc <pkg> <sym>[.<methodOrField>]
For more information run
        go help doc

Flags:
  -all
        show all documentation for package
  -c    symbol matching honors case (paths not affected)
  -cmd
        show symbols with package docs even if package is a command
  -short
        one-line representation for each symbol
  -src
        show source code for symbol
  -u    show unexported symbols as well as exported
exit status 2

1.5 跳过某些测试用例

通过调用 *T*B 的 Skip 方法,可以在运行时跳过测试或基准测试:

package go_test

import (
	"math"
	"testing"
)

func TestTimeConsuming(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping test in short mode.")
	}else{
		got := math.Abs(-10)
		if got != 10 {
			t.Errorf("Abs(-1) = %f; want 10", got)
		}
	}
}

当执行 go test -short 时就不会执行上面的 TestTimeConsuming 测试用例。

$ go test 004_test.go -v -short
=== RUN   TestTimeConsuming
    004_test.go:10: skipping test in short mode.
--- SKIP: TestTimeConsuming (0.00s)
PASS
ok      command-line-arguments  0.264s
$ go test 004_test.go -v
=== RUN   TestTimeConsuming
--- PASS: TestTimeConsuming (0.00s)
PASS
ok      command-line-arguments  0.238s

1.6 子测试

Go1.7+中新增了子测试,支持在测试函数中使用 t.Run 执行一组测试用例,这样就不需要为不同的测试数据定义

多个测试函数了。

func TestFoo(t *testing.T) {
    // <setup code>
    t.Run("A=1", func(t *testing.T) { ... })
    t.Run("A=2", func(t *testing.T) { ... })
    t.Run("B=1", func(t *testing.T) { ... })
    // <tear-down code>
}
go test -run ''        # Run all tests.
go test -run Foo       # Run top-level tests matching "Foo", such as "TestFooBar".
go test -run Foo/A=    # For top-level tests matching "Foo", run subtests matching "A=".
go test -run /A=1      # For all top-level tests, run subtests matching "A=1".
go test -fuzz FuzzFoo  # Fuzz the target matching "FuzzFoo"

例子:

package go_test

import "testing"
import "fmt"

func myAdd(a int, b int) int {
	if a+b > 10 {
		return 10
	}
	return a + b
}

func mySub(one int, two int) int {
	if one-two < 0 {
		return 1
	}
	return one - two
}

func TestMyAdd(t *testing.T) {
	num := myAdd(4, 9)
	fmt.Println(num)
	num = myAdd(4, 2)
	fmt.Println(num)

}

func TestMySub(t *testing.T) {
	t.Run("one", func(t *testing.T) {
		if mySub(2, 3) != 1 {
			t.Fatal("cal error")
		}

	})
	t.Run("two", func(t *testing.T) {
		if mySub(3, 1) != 2 {
			t.Fatal(" error ")
		}
	})
}

单独调用子测试函数,执行:

$ go test -run TestMySub/one -v
=== RUN   TestMySub
=== RUN   TestMySub/one
--- PASS: TestMySub (0.00s)
    --- PASS: TestMySub/one (0.00s)
PASS
ok      proj    0.217s

$ go test -run TestMySub/two -v
=== RUN   TestMySub
=== RUN   TestMySub/two
--- PASS: TestMySub (0.00s)
    --- PASS: TestMySub/two (0.00s)
PASS
ok      proj    0.261s

1.7 Fuzzing

go test 和测试包支持 fuzzing,这是一种测试技术,通过随机生成的输入调用函数来发现单元测试没有预料到的错

误。

func FuzzXxx(*testing.F)
package go_test

import (
	"bytes"
	"encoding/hex"
	"testing"
)

func FuzzHex(f *testing.F) {
	for _, seed := range [][]byte{{}, {0}, {9}, {0xa}, {0xf}, {1, 2, 3, 4}} {
		f.Add(seed)
	}
	f.Fuzz(func(t *testing.T, in []byte) {
		enc := hex.EncodeToString(in)
		out, err := hex.DecodeString(enc)
		if err != nil {
			t.Fatalf("%v: decode: %v", in, err)
		}
		if !bytes.Equal(in, out) {
			t.Fatalf("%v: not equal after round trip: %v", in, out)
		}
	})
}
$ go test -v -fuzz FuzzHex 006_test.go
=== FUZZ  FuzzHex
fuzz: elapsed: 0s, gathering baseline coverage: 0/28 completed
fuzz: elapsed: 0s, gathering baseline coverage: 28/28 completed, now fuzzing with 20 workers
fuzz: elapsed: 3s, execs: 2269065 (754894/sec), new interesting: 0 (total: 28)
fuzz: elapsed: 6s, execs: 4247710 (660281/sec), new interesting: 0 (total: 28)
fuzz: elapsed: 9s, execs: 6091725 (614495/sec), new interesting: 0 (total: 28)
fuzz: elapsed: 12s, execs: 7923134 (609359/sec), new interesting: 0 (total: 28)
fuzz: elapsed: 15s, execs: 9752940 (610984/sec), new interesting: 0 (total: 28)
fuzz: elapsed: 18s, execs: 11603037 (612920/sec), new interesting: 0 (total: 28)
fuzz: elapsed: 21s, execs: 13436307 (615324/sec), new interesting: 0 (total: 28)
fuzz: elapsed: 24s, execs: 15295542 (615446/sec), new interesting: 0 (total: 28)
fuzz: elapsed: 27s, execs: 16936635 (587500/sec), new interesting: 0 (total: 28)
--- PASS: FuzzHex (26.82s)
PASS
ok      command-line-arguments  27.178s

可以设置 -fuzz 标志,以便模糊目标,跳过所有其他测试的执行。

1.8 表格驱动测试

测试讲究 case 覆盖,按上面的方式,要覆盖更多 case 时,显然通过修改代码的方式很笨拙。这时可以采用

Table-Driven 的方式写测试,标准库中有很多测试是使用这种方式写的。

表格驱动测试的步骤通常是定义一个测试用例表格,然后遍历表格,并使用t.Run对每个条目执行必要的测试。

package go_test

import (
	"proj/fb"
	"testing"
)

func TestFib(t *testing.T) {
	var fibTests = []struct {
		in       int // input
		expected int // expected result
	}{
		{1, 1},
		{2, 1},
		{3, 2},
		{4, 3},
		{5, 5},
		{6, 8},
		{7, 13},
	}

	for _, tt := range fibTests {
		actual := fb.FbRecursion(tt.in)
		if actual != tt.expected {
			t.Errorf("Fib(%d) = %d; expected %d", tt.in, actual, tt.expected)
		}
	}
}
$ go test 007_test.go -v
=== RUN   TestFib
--- PASS: TestFib (0.00s)
PASS
ok      command-line-arguments  0.248s

1.9 并行测试

表格驱动测试中通常会定义比较多的测试用例,而Go语言又天生支持并发,所以很容易发挥自身并发优势将表格

驱动测试并行化。 想要在单元测试过程中使用并行测试,可以像下面的代码示例中那样通过添加t.Parallel()来实

现。

package go_test

import (
	"reflect"
	"testing"
	"strings"
)

func TestSplitAll(t *testing.T) {
	// 将TLog标记为能够与其他测试并行运行
	t.Parallel()
	// 定义测试表格
	// 为每个测试用例设置了一个名称
	tests := []struct {
		name  string
		input string
		sep   string
		want  []string
	}{
		{"base case", "a:b:c", ":", []string{"a", "b", "c"}},
		{"wrong sep", "a:b:c", ",", []string{"a:b:c"}},
		{"more sep", "abcd", "bc", []string{"a", "d"}},
		{"leading sep", "沙河有沙又有河", "沙", []string{"", "河有", "又有河"}},
	}
	// 遍历测试用例
	for _, tt := range tests {
		// 注意这里重新声明tt变量(避免多个goroutine中使用了相同的变量)
		tt := tt
		// 使用t.Run()执行子测试
		t.Run(tt.name, func(t *testing.T) {
			// 将每个测试用例标记为能够彼此并行运行
			t.Parallel()
			got := strings.Split(tt.input, tt.sep)
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("expected:%#v, got:%#v", tt.want, got)
			}
		})
	}
}
$ go test 008_test.go -v
=== RUN   TestSplitAll
=== PAUSE TestSplitAll
=== CONT  TestSplitAll
=== RUN   TestSplitAll/base_case
=== PAUSE TestSplitAll/base_case
=== RUN   TestSplitAll/wrong_sep
=== PAUSE TestSplitAll/wrong_sep
=== RUN   TestSplitAll/more_sep
=== PAUSE TestSplitAll/more_sep
=== RUN   TestSplitAll/leading_sep
=== PAUSE TestSplitAll/leading_sep
=== CONT  TestSplitAll/base_case
=== CONT  TestSplitAll/wrong_sep
=== CONT  TestSplitAll/leading_sep
=== CONT  TestSplitAll/more_sep
--- PASS: TestSplitAll (0.00s)
    --- PASS: TestSplitAll/base_case (0.00s)
    --- PASS: TestSplitAll/wrong_sep (0.00s)
    --- PASS: TestSplitAll/leading_sep (0.00s)
    --- PASS: TestSplitAll/more_sep (0.00s)
PASS
ok      command-line-arguments  0.237s

1.10 Main

测试或基准测试程序有时需要在执行之前或之后进行额外的设置和拆卸,有时还需要控制哪些代码在主线程上运

行,为了支持这些和其他情况,如果测试文件包含以下函数:

func TestMain(m *testing.M)

那么生成的测试将调用 TestMain,而不是直接运行测试或基准测试。TestMain 在主 goroutine 中运行,可以围绕

对 m.Run 的调用进行任何必要的设置和拆卸。m.Run 将返回一个可能传递给 os.Exit 的退出代码。如果 TestMain

返回,测试包装器将把 m.Run 的结果传递给 os.Exit 本身。

当调用 TestMain 时,flag.Parse 尚未运行。如果 TestMain 依赖于命令行标志,包括测试包的标志,它应该显式

调用 flag.Parse。命令行标志总是在测试或基准函数运行时解析。

TestMain的一个简单实现是:

package go_test

import (
	"fmt"
	"math"
	"os"
	"testing"
)

func TestMain(m *testing.M) {
	// 测试之前的做一些设置
	fmt.Println("write setup code here...")
	// 如果 TestMain 使用了 flags,这里应该加上flag.Parse()
	// 执行测试
	retCode := m.Run()
	// 测试之后做一些拆卸工作
	fmt.Println("write teardown code here...")
	// 退出测试
	os.Exit(retCode)
}

func TestAbs(t *testing.T) {
	got := math.Abs(-1)
	if got != 1 {
		t.Errorf("Abs(-1) = %f; want 1", got)
	}
}
$ go test 009_test.go -v
write setup code here...
=== RUN   TestAbs
--- PASS: TestAbs (0.00s)
PASS
write teardown code here...
ok      command-line-arguments  0.248s

1.11 Setup与Teardown

有时候可能需要为每个测试集设置Setup与Teardown,也有可能需要为每个子测试设置Setup与Teardown。

package  go_test

import (
	"reflect"
	"strings"
	"testing"
)

// 测试集的Setup与Teardown
func setupTestCase(t *testing.T) func(t *testing.T) {
	t.Log("如有需要在此执行:测试之前的setup")
	// 返回Teardown
	return func(t *testing.T) {
		t.Log("如有需要在此执行:测试之后的teardown")
	}
}

// 子测试的Setup与Teardown
func setupSubTest(t *testing.T) func(t *testing.T) {
	t.Log("如有需要在此执行:子测试之前的setup")
	// 返回Teardown
	return func(t *testing.T) {
		t.Log("如有需要在此执行:子测试之后的teardown")
	}
}

func TestSplit(t *testing.T) {
	// 定义test结构体
	type test struct {
		input string
		sep   string
		want  []string
	}
	// 测试用例使用map存储
	tests := map[string]test{
		"simple":      {input: "a:b:c", sep: ":", want: []string{"a", "b", "c"}},
		"wrong sep":   {input: "a:b:c", sep: ",", want: []string{"a:b:c"}},
		"more sep":    {input: "abcd", sep: "bc", want: []string{"a", "d"}},
		"leading sep": {input: "沙河有沙又有河", sep: "沙", want: []string{"", "河有", "又有河"}},
	}
	// 测试之前执行setup操作
	teardownTestCase := setupTestCase(t)
	// 测试之后执行testdoen操作
	defer teardownTestCase(t)
	for name, tc := range tests {
		// 使用t.Run()执行子测试
		t.Run(name, func(t *testing.T) {
			// 子测试之前执行setup操作
			teardownSubTest := setupSubTest(t)
			// 测试之后执行testdoen操作
			defer teardownSubTest(t)
			got := strings.Split(tc.input, tc.sep)
			if !reflect.DeepEqual(got, tc.want) {
				t.Errorf("expected:%#v, got:%#v", tc.want, got)
			}
		})
	}
}
$ go test 017_test.go -v
=== RUN   TestSplit
    017_test.go:11: 如有需要在此执行:测试之前的setup
=== RUN   TestSplit/simple
    017_test.go:20: 如有需要在此执行:子测试之前的setup
    017_test.go:23: 如有需要在此执行:子测试之后的teardown
=== RUN   TestSplit/wrong_sep
    017_test.go:20: 如有需要在此执行:子测试之前的setup
    017_test.go:23: 如有需要在此执行:子测试之后的teardown
=== RUN   TestSplit/more_sep
    017_test.go:20: 如有需要在此执行:子测试之前的setup
    017_test.go:23: 如有需要在此执行:子测试之后的teardown
=== RUN   TestSplit/leading_sep
    017_test.go:20: 如有需要在此执行:子测试之前的setup
    017_test.go:23: 如有需要在此执行:子测试之后的teardown
=== CONT  TestSplit
    017_test.go:14: 如有需要在此执行:测试之后的teardown
--- PASS: TestSplit (0.00s)
    --- PASS: TestSplit/simple (0.00s)
    --- PASS: TestSplit/wrong_sep (0.00s)
    --- PASS: TestSplit/more_sep (0.00s)
    --- PASS: TestSplit/leading_sep (0.00s)
PASS
ok      command-line-arguments  0.228s

1.12 覆盖率测试

测试覆盖率是指代码被测试套件覆盖的百分比,也就是在测试中至少被运行一次的代码占总代码的比例,在公司内

部一般会要求测试覆盖率达到80%左右。

查看测试覆盖率:

$ go test -cover -run .*Fb.* -coverprofile='c.out' -coverpkg=proj/fb
  • -cover:代表进行覆盖率测试

  • -coverprofile:代表将结果存储到c.out

  • -coverpkg:用于指定覆盖率测试的目标包,测试代码所依赖的源文件所在包。这就是说,不是目录下的.go

    文件,而是直接是包就可以了

$ go test -cover -run .*Fb.* -coverprofile='c.out' -coverpkg=proj/fb
PASS
coverage: 100.0% of statements in proj/fb
ok      proj    0.286s

生成的测试结果,可使用 go tool cover -html='c.out' -o coverage.html 来转换成可视化的html:

$ go tool cover -html='c.out' -o coverage.html

查看生成的 html 内容:

Go语言单元测试

图中绿色的部分是已覆盖,红色的部分是未覆盖,咱们的例子已经全部覆盖具体的函数功能。

1.13 多个文件同时进行测试

$ go test -o 001_test.go 002_test.go -v
# 或者
$ go test 001_test.go 002_test.go -v
=== RUN   TestAbs
--- PASS: TestAbs (0.00s)
PASS
ok      command-line-arguments  0.236s

go test 如果不指定文件,则测试的是当前目录下的所有文件。

1.14 报告方法

遇到一个断言错误的时候,标识这个测试失败:

Fail() : 测试失败,测试继续
FailNow() : 测试失败,测试中断

遇到一个断言错误,只希望跳过这个错误,但是不希望标识测试失败:

SkipNow() : 跳过测试,测试中断

只希望打印信息:

Log : 输出信息
Logf : 输出格式化的信息

希望跳过这个测试,并且打印出信息:

Skip : 相当于 Log + SkipNow
Skipf : 相当于 Logf + SkipNow

希望断言失败的时候,标识测试失败,并打印出必要的信息,但是测试继续:

Error : 相当于 Log + Fail
Errorf : 相当于 Logf + Fail

希望断言失败的时候,标识测试失败,打印出必要的信息,但中断测试:

Fatal : 相当于 Log + FailNow
Fatalf : 相当于 Logf + FailNow

2、示例函数

实例函数的编写可以使用 go help testfunc 查看:

$ go help testfunc
The 'go test' command expects to find test, benchmark, and example functions
in the "*_test.go" files corresponding to the package under test.

A test function is one named TestXxx (where Xxx does not start with a
lower case letter) and should have the signature,

        func TestXxx(t *testing.T) { ... }

A benchmark function is one named BenchmarkXxx and should have the signature,

        func BenchmarkXxx(b *testing.B) { ... }

A fuzz test is one named FuzzXxx and should have the signature,

        func FuzzXxx(f *testing.F) { ... }

An example function is similar to a test function but, instead of using
*testing.T to report success or failure, prints output to os.Stdout.
If the last comment in the function starts with "Output:" then the output
is compared exactly against the comment (see examples below). If the last
comment begins with "Unordered output:" then the output is compared to the
comment, however the order of the lines is ignored. An example with no such
comment is compiled but not executed. An example with no text after
"Output:" is compiled, executed, and expected to produce no output.

Godoc displays the body of ExampleXxx to demonstrate the use
of the function, constant, or variable Xxx. An example of a method M with
receiver type T or *T is named ExampleT_M. There may be multiple examples
for a given function, constant, or variable, distinguished by a trailing _xxx,
where xxx is a suffix not beginning with an upper case letter.

Here is an example of an example:

        func ExamplePrintln() {
                Println("The output of\nthis example.")
                // Output: The output of
                // this example.
        }

Here is another example where the ordering of the output is ignored:

        func ExamplePerm() {
                for _, value := range Perm(4) {
                        fmt.Println(value)
                }

                // Unordered output: 4
                // 2
                // 1
                // 3
                // 0
        }

The entire test file is presented as the example when it contains a single
example function, at least one other function, type, variable, or constant
declaration, and no tests, benchmarks, or fuzz tests.

See the documentation of the testing package for more information.

实例:

package go_test

import (
	"fmt"
	"proj/fb"
)

func Example_fb_norecursion() {
	fmt.Println(fb.FbRecursion(1))
	fmt.Println(fb.FbNoRecursion(3))
	// Output:
	// 1
	// 2
}

示例函数也可以当作测试函数进行运行,运行结果需要与Output一致:

$ go test 010_test.go -v
=== RUN   Example_fb_norecursion
--- PASS: Example_fb_norecursion (0.00s)
PASS
ok      command-line-arguments  0.238s

如果修改为:

package go_test

import (
	"fmt"
	"proj/fb"
)

func Example_fb_norecursion() {
	fmt.Println(fb.FbRecursion(1))
	fmt.Println(fb.FbNoRecursion(3))
	// Output:
	// 1
	// 4
}
$ go test 010_test.go -v
=== RUN   Example_fb_norecursion
--- FAIL: Example_fb_norecursion (0.00s)
got:
1
2
want:
1
4
FAIL
FAIL    command-line-arguments  0.245s
FAIL

示例函数只要包含了// Output:也是可以通过 go test 运行的可执行测试。

$ go test -run Example
PASS
ok      proj    0.272s
// 示例
package go_test

import "fmt"

func ExampleHello() {
	fmt.Println("hello")
	// Output: hello
}

func ExampleSalutations() {
	fmt.Println("hello, and")
	fmt.Println("goodbye")
	// Output:
	// hello, and
	// goodbye
}
$ go test 011_test.go -v
=== RUN   ExampleHello
--- PASS: ExampleHello (0.00s)
=== RUN   ExampleSalutations
--- PASS: ExampleSalutations (0.00s)
PASS
ok      command-line-arguments  0.223s

前缀 Unordered output:Output: 相似,但是它不按照顺序匹配。

package go_test

import "fmt"

func ExamplePerm() {
	list := []int{0,1,2,3,4}
	for _, value := range list {
		fmt.Println(value)
	}
	// Unordered output: 4
	// 2
	// 1
	// 3
	// 0
}
$ go test 012_test.go -v
=== RUN   ExamplePerm
--- PASS: ExamplePerm (0.00s)
PASS
ok      command-line-arguments  0.220s

没有 Outout 注释的示例函数被编译但不执行。

一个包/类型/函数/方法的多个示例函数可以通过在名称后面附加一个不同的后缀来提供,后缀必须以小写字母开

头。

// package
func Example() { ... }
// function
func ExampleF() { ... }
// type
func ExampleT() { ... }
// method
func ExampleT_M() { ... }
func Example_suffix() { ... }
func ExampleF_suffix() { ... }
func ExampleT_suffix() { ... }
func ExampleT_M_suffix() { ... }

3、性能测试

性能测试就是在一定的工作负载之下检测程序性能的一种方法。

func BenchmarkName(b *testing.B){
    // ...
}

基准测试以 Benchmark 为前缀,需要一个 testing.B 类型的参数 b,基准测试必须要执行 b.N 次,这样的测试才

有对照性,b.N 的值是系统根据实际情况去调整的,从而保证测试的稳定性。

3.1 支持的函数

使用 go doc testing.B 查询文档:

$ go doc testing.B
package testing // import "testing"

type B struct {
        N int

        // Has unexported fields.
}
    B is a type passed to Benchmark functions to manage benchmark timing and to
    specify the number of iterations to run.

    A benchmark ends when its Benchmark function returns or calls any of the
    methods FailNow, Fatal, Fatalf, SkipNow, Skip, or Skipf. Those methods must
    be called only from the goroutine running the Benchmark function. The other
    reporting methods, such as the variations of Log and Error, may be called
    simultaneously from multiple goroutines.

    Like in tests, benchmark logs are accumulated during execution and dumped to
    standard output when done. Unlike in tests, benchmark logs are always
    printed, so as not to hide output whose existence may be affecting benchmark
    results.

func (c *B) Cleanup(f func())
func (c *B) Error(args ...any)
func (c *B) Errorf(format string, args ...any)
func (c *B) Fail()
func (c *B) FailNow()
func (c *B) Failed() bool
func (c *B) Fatal(args ...any)
func (c *B) Fatalf(format string, args ...any)
func (c *B) Helper()
func (c *B) Log(args ...any)
func (c *B) Logf(format string, args ...any)
func (c *B) Name() string
func (b *B) ReportAllocs()
func (b *B) ReportMetric(n float64, unit string)
func (b *B) ResetTimer()
func (b *B) Run(name string, f func(b *B)) bool
func (b *B) RunParallel(body func(*PB))
func (b *B) SetBytes(n int64)
func (b *B) SetParallelism(p int)
func (c *B) Setenv(key, value string)
func (c *B) Skip(args ...any)
func (c *B) SkipNow()
func (c *B) Skipf(format string, args ...any)
func (c *B) Skipped() bool
func (b *B) StartTimer()
func (b *B) StopTimer()
func (c *B) TempDir() string

3.2 简单例子

package go_test

import (
	"fmt"
	"testing"
)

func BenchmarkHello(b *testing.B) {
	for i := 0; i < b.N; i++ {
		fmt.Println("hello world!")
	}
}
# 通过go test命令,加上-bench标志来执行
$ go test -bench BenchmarkHello
......
hello world!
hello world!
hello world!
BenchmarkHello-20          66876             16921 ns/op
PASS
ok      proj    1.586s

3.3 性能比较函数

通常需要对两个不同算法的实现使用相同的输入来进行基准比较测试。

默认情况下,每个基准测试至少运行1秒。如果在Benchmark函数返回时没有到1秒,则b.N的值会按

1,2,5,10,20,50,…增加,并且函数再次运行。

可以使用 -benchtime 标志增加最小基准时间,以产生更准确的结果。

package go_test

import (
	"proj/fb"
	"testing"
)


func BenchmarkFib40(b *testing.B) {
	for i := 0; i < b.N; i++ {
		fb.FbRecursion(40)
	}
}

func BenchmarkFib40No(b *testing.B) {
	for i := 0; i < b.N; i++ {
		fb.FbNoRecursion(40)
	}
}
$ go test -bench=Fib40 -benchtime=20s
goos: windows
goarch: amd64
pkg: proj
cpu: 12th Gen Intel(R) Core(TM) i7-12700
BenchmarkFib40-20            100         257579890 ns/op
BenchmarkFib40No-20     154547244              163.6 ns/op
PASS
ok      proj    67.233s

3.4 计时方法

有三个方法用于计时:

StartTimer:开始对测试进行计时。该方法会在基准测试开始时自动被调用,也可以在调用 StopTimer 之后恢复

计时;

StopTimer:停止对测试进行计时。当需要执行一些复杂的初始化操作,并且不想对这些操作进行测量时,就可以

使用这个方法来暂时地停止计时;

ResetTimer:对已经逝去的基准测试时间以及内存分配计数器进行清零。对于正在运行中的计时器,这个方法

不会产生任何效果。

package go_test

import (
	"fmt"
	"testing"
	"time"
)

func BenchmarkPrint(b *testing.B) {
	// 假设需要做一些耗时的无关操作
	time.Sleep(5 * time.Second)
	// 重置计时器
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		fmt.Println("hello world!")
	}
}
$ go test -bench=Print -v
......
hello world!
hello world!
hello world!
hello world!
BenchmarkPrintParallel-20          62869             18777 ns/op
PASS
ok      proj    22.963s

3.5 并行测试

func (b *B) RunParallel(body func(*PB)) 会以并行的方式执行给定的基准测试。

通过 RunParallel 方法能够并行地执行给定的基准测试,RunParallel 通常会与 -cpu 标志一同使用。

b.SetParallelism() 可以设置使用的CPU数。

body 函数将在每个 goroutine 中执行,这个函数需要设置所有 goroutine 本地的状态,并迭代直到 pb.Next 返回

false 值为止。因为 StartTimer、StopTime 和 ResetTimer 这三个方法都带有全局作用,所以 body 函数不应该调

用这些方法; 除此之外,body 函数也不应该调用 Run 方法。

package go_test

import (
	"fmt"
	"testing"
)

func BenchmarkPrintParallel(b *testing.B) {
	// 设置使用的CPU数
	// b.SetParallelism(1)
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			fmt.Println("hello world!")
		}
	})
}
$ go test -bench=PrintParallel -v
......
hello world!
hello world!
hello world!
hello world!
BenchmarkPrintParallel-20          61087             18990 ns/op
PASS
ok      proj    1.646s

3.6 子测试

package go_test

import (
	"fmt"
	"testing"
)

type identifier interface {
	idInline() int32
	idNoInline() int32
}

type id32 struct{ id int32 }

func (id *id32) idInline() int32 { return id.id }

func (id *id32) idNoInline() int32 { return id.id }

var escapeMePlease *id32

func escapeToHeap(id *id32) identifier {
	escapeMePlease = id
	return escapeMePlease
}

func BenchmarkMethodCall_direct(b *testing.B) {
	var myID int32
	b.Run("single/noinline", func(b *testing.B) {
		m := escapeToHeap(&id32{id: 6754}).(*id32)
		b.ResetTimer()
		for i := 0; i < b.N; i++ {
			myID = m.idNoInline()
			fmt.Print(myID)
		}
	})

	b.Run("single/inline", func(b *testing.B) {
		m := escapeToHeap(&id32{id: 6754}).(*id32)
		b.ResetTimer()
		for i := 0; i < b.N; i++ {
			myID = m.idInline()
			fmt.Print(myID)
		}
	})
}

func BenchmarkMethodCall_interface(b *testing.B) {
	var myID int32
	b.Run("single/noinline", func(b *testing.B) {
		m := escapeToHeap(&id32{id: 6754})
		b.ResetTimer()
		for i := 0; i < b.N; i++ {
			myID = m.idNoInline()
			fmt.Print(myID)
		}
	})
	b.Run("single/inline", func(b *testing.B) {
		m := escapeToHeap(&id32{id: 6754})
		b.ResetTimer()
		for i := 0; i < b.N; i++ {
			myID = m.idInline()
			fmt.Print(myID)
		}
	})

}

测试命令:

$ go test -bench BenchmarkMethodCall_direct/single/inline -cpu=1 -count=5 018_test.go
......       20631 ns/op
PASS
ok      command-line-arguments  14.152s
$ go test -bench BenchmarkMethodCall_direct/single/noinline -cpu=1 -count=5 018_test.go
......           20869 ns/op
PASS
ok      command-line-arguments  7.133s
$ go test -bench BenchmarkMethodCall_interface/single/inline -cpu=1 -count=5 018_test.go
......            20198 ns/op
PASS
ok      command-line-arguments  13.932s
$ go test -bench BenchmarkMethodCall_interface/single/noinline -cpu=1 -count=5 018_test.go
......           21178 ns/op
PASS
ok      command-line-arguments  7.070s
$ go test -bench . -cpu=1 -count=5 018_test.go
......             18983 ns/op
PASS
ok      command-line-arguments  28.031s

ns/op 的意思是:ns纳秒/op操作。

4、参考

官方文档:https://pkg.go.dev/testing文章来源地址https://www.toymoban.com/news/detail-509435.html

到了这里,关于Go语言单元测试的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 7 文件操作、单元测试、goroutine【Go语言教程】

    1.1 介绍 os.File 封装所有文件相关操作,File 是一个结构体 常用方法: 打开文件 关闭文件 1.2 应用实例 ①读文件 常用方法: ①bufio.NewReader(), reader.ReadString【带缓冲】 ②io/ioutil【一次性读取,适用于小文件】 读取文件的内容并显示在终端(带缓冲区的方式),使用 os.Open, file.

    2024年02月04日
    浏览(54)
  • 36-代码测试(上):如何编写Go语言单元测试和性能测试用例?

    每种语言通常都有自己的测试包/模块,Go语言也不例外。在Go中,我们可以通过 testing 包对代码进行单元测试和性能测试。  Go语言有自带的测试框架 testing ,可以用来实现单元测试(T类型)和性能测试(B类型),通过 go test 命令来执行单元测试和性能测试。 go test 执行测试

    2024年04月11日
    浏览(47)
  • 【GO语言依赖】Go语言依赖管理简述

    在运行环境中,遭遇报错,显示找不到函数 经过研究后发现需要进行依赖管理,进行如下操作后解决: 最早的时候,Go所依赖的所有的第三方库都放在GOPATH这个目录下面。这就导致了同一个库只能保存一个版本的代码。如果不同的项目依赖同一个第三方的库的不同版本,就需

    2024年01月16日
    浏览(46)
  • 单元测试系列 | 如何更好地测试依赖外部接口的方法

    在现在这个微服务时代,我们项目中经常都会遇到很多业务逻辑是依赖其他服务或者第三方接口。工作中各位同学对于这类型场景的测试方式也是五花八门,有些是直接构建一个外部 mock 服务,返回一些固定的 response ;有些是单元测试都不写,直接利用IDE工具,通过 debug 模式

    2024年02月04日
    浏览(34)
  • Go语言之依赖管理

    go module是Go1.11版本之后官方推出的版本管理工具,并且从Go1.13版本开始,go module将是Go语言默认的依赖管理工具。 GO111MODULE 要启用go module支持首先要设置环境变量GO111MODULE 通过它可以开启或关闭模块支持,它有三个可选值:off、on、auto,默认值是auto。 GO111MODULE=off禁用模块支

    2024年02月12日
    浏览(42)
  • go 语言实现依赖注入

    依赖注入和控制反转恰恰相反,它是一种具体的编码技巧。我们不通过 new 的方式在类内部创建依赖类的对象,而是将依赖的类对象在外部创建好之后,通过构造函数、函数参数等方式传递(或注入)给类来使用。 DI(依赖性注入)是一种技术,当你的模块间接地接受依赖性

    2024年01月17日
    浏览(56)
  • Go单元测试与集成测试:编写可靠的Go测试用例

    Go语言是一种现代编程语言,它具有简洁的语法、强大的性能和易于使用的并发特性。随着Go语言的发展和广泛应用,编写高质量的测试用例变得越来越重要。在Go语言中,我们可以使用内置的testing包来编写单元测试和集成测试。本文将涵盖Go单元测试与集成测试的核心概念、

    2024年02月19日
    浏览(39)
  • Go 单元测试之mock接口测试

    目录 一、gomock 工具介绍 二、安装 三、使用 3.1 指定三个参数 3.2 使用命令为接口生成 mock 实现 3.3 使用make 命令封装处理mock 四、接口单元测试步骤 三、小黄书Service层单元测试 四、flags 五、打桩(stub) 参数 六、总结 6.1 测试用例定义 6.2 设计测试用例 6.3 执行测试用例代码

    2024年04月22日
    浏览(36)
  • Go单元测试入门

    测试文件以 _test.go 结尾 假如 calc.go 的代码如下: 那么 calc_test.go 中的测试用例可以这么写: 测试用例名称一般 命名为 Test 加上待测试的方法名。 测试用的参数有且只有一个,在这里是 t *testing.T。 基准测试(benchmark)的参数是 *testing.B,TestMain 的参数是 *testing.M 类型。 运行

    2024年02月09日
    浏览(40)
  • Go 单元测试基本介绍

    目录 一、单元测试基本介绍 1.1 什么是单元测试? 1.2 如何写好单元测试 1.3 单元测试的优点 1.4 单元测试的设计原则 二、Go语言测试 2.1 Go单元测试概要 2.2 Go单元测试基本规范 2.3 一个简单例子 2.3.1 使用Goland 生成测试文件 2.3.2 运行单元测试 2.3.3 完善测试用例 2.3.5 回归测试

    2024年04月16日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包