Go语言单元测试

news2024/11/16 1:54:58

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 内容:

在这里插入图片描述

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/696754.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Python爬虫】利用爬虫抓取双色球开奖号码,获取完整数据,简洁45行代码实现,更新时间2023-06-28

链接&#xff1a;https://pan.baidu.com/s/18oE308_NVNPaCOACw_H5Hw?pwdabc1 利用爬虫抓取双色球开奖号码&#xff0c;获取完整数据&#xff0c;简洁45行代码实现&#xff0c;更新时间2023-06-28 这是网上的数据&#xff0c;怎么将它爬取下来 它将只爬取最近30期的双色球开…

星辰秘典:解开Python项目的神秘面纱——迷宫之星(迷宫探索与求解)

✨博主&#xff1a;命运之光 &#x1f338;专栏&#xff1a;Python星辰秘典 &#x1f433;专栏&#xff1a;web开发&#xff08;html css js&#xff09; ❤️专栏&#xff1a;Java经典程序设计 ☀️博主的其他文章&#xff1a;点击进入博主的主页 前言&#xff1a;你好&#x…

文件系统理解——磁盘文件

磁盘文件 1. 问题提出2. 了解磁盘的物理结构2.1 磁盘大体结构2.2 磁盘的具体物理结构 3.对磁盘结构逻辑抽象3.1 OS是通过CSH方法进行扇区的管理的吗&#xff1f;3.2 磁盘逻辑过程 4.文件系统4.1 管理思想4.2 区结构Boot BlockSuper BlockGroup Descriptor Tableinode Table &am…

共享办公室在国内外的发展史以及现状介绍

共享办公室&#xff0c;这个曾经陌生的概念&#xff0c;如今已成为全球范围内炙手可热的话题。在这个时代&#xff0c;越来越多的人开始关注灵活性和协作性&#xff0c;而共享办公室正是在这种需求下应运而生的。本文将带您一探共享办公室的国内外发展史、国内当前发展现状以及…

记录一次nginx占用cpu饱满解决

一天下午&#xff0c;突然就爆满了。刚开始服务器都登不上&#xff0c;后来服务器可以登录上&#xff0c;宝塔进不去&#xff0c;网站访问不了&#xff0c;top后发现nginx爆满 执行下列代码&#xff0c;停止nginx服务 systemctl stop nginx 虽然停止了 可以上宝塔 再次手动…

硬件工程师JD

不知道这么做合不合适&#xff0c;我先发一个试试&#xff0c;团队在招人&#xff0c; 岗位是 航天航空电子工程师1&#xff0c;混合电推和起发电方向&#xff1b; 电机控制硬件工程师1&#xff0c;新能源汽车电机控制器硬件开发方向。 工作内容&#xff1a; 1、负责硬件系…

美国首只杠杆比特币期货ETF开盘,成交量近550万美元!

纽约时间6月27日&#xff0c;美国首只杠杆比特币期货ETF&#xff08;股票代码&#xff1a;BITX&#xff09;在CBOE BZX交易所开盘&#xff0c;成交量近550万美元&#xff0c;成为今年推出的ETF中首日交易量最大的产品。 BITX的推出&#xff0c;正值贝莱德(BlackRock)向SEC递交现…

【教学类-36-04】Midjounery 各种图片切割(通用公式)

作品展示&#xff1a; 6*6切割36图 5*4图&#xff1a;20张 问题产生&#xff1a; 之前的代码是&#xff1a;一张图的四个坐标&#xff0c;有4张就要写4行。 后续发现&#xff0c;Midjounery生成的图片矩阵X Y数量不等&#xff0c;不仅有3*3&#xff0c;还有4*3、5*4的结构&am…

基于appnium+python+夜神模拟器的自动化

目录 1、安装夜神模拟器 2、定位元素 3、开始编码 首先搭好appnium环境&#xff01;参考https://www.cnblogs.com/testlearn/p/11419797.html 1、安装夜神模拟器 下载安装夜神模拟器后&#xff0c;在cmd命令输入adb connect 127.0.0.1:62001&#xff0c;显示出设备则表示…

虚拟稀疏卷积在多模态 3D 目标检测中的应用

论文背景 虚拟点云生成时&#xff0c;从图像中生成的虚拟点非常密集&#xff0c;在检测过程中引入了大量的冗馀计算量。同时&#xff0c;不准确的深度补全所带来的噪声会显著降低检测精度。 早期利用图像特征扩展LiDAR点的特征的方法&#xff0c;如语义掩膜和二维CNN特征。他…

机器学习11:逻辑回归-Logistic Regression

目录 1.计算概率 2.损失和正则化 2.1 逻辑回归的损失函数 2.2 逻辑回归中的正则化 3.参考文献 1.计算概率 许多问题需要概率估计作为输出。逻辑回归是一种极其有效的概率计算机制。实际上&#xff0c;我们可以通过以下两种方式使用返回的概率&#xff1a; 原始概率&…

Appium安装部署

目录 一、检查Java环境 二、安装android SDK 一、检查Java环境 Android SDK依赖ava环境&#xff0c;因此需要先安装jdk。在CMD中输入java -version 出现下图的结果&#xff0c;说明当前环境已安装jdk 如果提示java命令无效&#xff0c;请安装后进行下一步。 二、安装androi…

360手机打开USB调试+文件传输

360手机USB调试文件传输[用户版] 参考&#xff1a;360手机-360刷机360刷机包twrp、root 360刷机包360手机刷机&#xff1a;360rom.github.io 【前言】 *360手机USB调试 &#xff0c;必须提前打开“开发者模式USB调试”进行 【操作流程】 打开&#xff1a;开发者模式、USB调…

vue3进阶-----单文件组件

目录 三.vue3进阶 1.单文件组件 1-1组件定义-重塑经脉&#xff0c;断了&#xff1f; 1-2单文件组件(SFC)-独立日 1-3Vue-CLI创建项目-锅灶升级 1-4 vuecli选项介绍 1-5 VueCLI创建项目-风云再起 index.html main.js 作用 ES6 导入 导出 解决跨域之“Vue-Cli配置跨域…

Vue 自定义ColorPicker

Vue 自定义ColorPicker 创建自定义组件ColorPicker.vue <template><div class"box"><div class"picker-box"><div class"color" :style"{ background: color }"></div><div class"el-icon-arr…

使用元类实现ORM

使用元类实现ORM ORM 是 python编程语言后端web框架 Django的核心思想&#xff0c;“Object Relational Mapping”&#xff0c;即对象-关系映射&#xff0c;简称ORM。 我们想实现的目标是: 创建一个实例对象&#xff0c;用创建它的类名当做数据表名&#xff0c;用创建它的类属…

十分钟实现 Android Camera2 相机拍照

1. 前言 因为工作中要使用Android Camera2 API&#xff0c;但因为Camera2比较复杂&#xff0c;网上资料也比较乱&#xff0c;有一定入门门槛&#xff0c;所以花了几天时间系统研究了下&#xff0c;并在CSDN上记录了下&#xff0c;希望能帮助到更多的小伙伴。 上篇文章 我们使用…

【数据分享】1929-2022年全球站点的逐月平均压力数据(Shp\Excel\12000个站点)

气象数据是在各项研究中都经常使用的数据&#xff0c;气象指标包括气温、风速、降水、能见度等指标&#xff0c;说到气象数据&#xff0c;最详细的气象数据是具体到气象监测站点的数据&#xff01; 对于具体到监测站点的气象数据&#xff0c;之前我们分享过1929-2022年全球气象…

paddlespeech 声纹识别embedding向量提取;TTS文本合成语音

1、声纹识别embedding向量提取 参考&#xff1a; https://aistudio.baidu.com/aistudio/projectdetail/4353348 https://github.com/PaddlePaddle/PaddleSpeech/blob/develop/demos/speaker_verification/README_cn.md https://aistudio.baidu.com/aistudio/projectdetail/4…

Apache Zeppelin 番外篇——参与开源的得与失

背景 经常在公司做一些业务开发&#xff0c;公司里面由于各种人员流动等问题&#xff0c;导致代码质量也是参差不齐&#xff0c;最终问题就是很难维护&#xff0c;前期还想着能够优化代码&#xff0c;但是大部分时间都是需求都是倒排期&#xff0c;所以也导致不再想进行代码优…