• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

青训营:进行单元测试 2

武飞扬头像
Blockchain
帮助5

简介

在青训营课程的基础上,总结了单元测试的基本用法,包括

  • 单元测试
  • 基准测试

单元测试

go test 命令

go test 命令只运行单元测试,如果添加了 -bench=. 的话,go test 将同时运行单元测试和基准测试;

也可以通过正则过滤的方式选择只运行基准测试,例如 -bench=^Benchmark,它会选择测试文件中以 Benchmark 开头的测试函数,即基准测试函数。

^ 表示以 xxx 开头,$ 表示以 xxx 结尾;

第 1 部分提到的,添加 --cover 参数可以展示测试覆盖率;

单元测试常用的参数

简单的执行单元测试

go test .

-v:测试时显示详细信息;

go test . -v

-run:指定要运行的单元测试函数(⭐️)

go test -run TestXXX 或者 BenckmarkXXX

-failfast:在第一个失败的测试处停止,立即退出测试

go test -failfast

-cpu:限制并行处理的 cpu 数量(相当于将参数值传递给 GOMAXPROCS)

go test -cpu=4

-race:检测静态条件(检测代码是否存在并发安全问题)(⭐️)

go test -race xxx.go

一般的并发冲突检测使用 go run -race main.go

学新通

并发冲突代码

package main

import "fmt"

func main() {
	c := make(chan bool)
	m := make(map[string]string)
	go func() {
        m["1"] = "a" //运行时,这里有个协程对m进行写操作
        c <- true
	}()

    m["2"] = "b" //主函数中也会对m进行写操作
    <-c          //根据通道先写后读的原则,这里会等待上面的go func执行完成
	for k, v := range m {
        fmt.Println(k, v)
	}
}

单元测试范围

  • 执行当前目录下所有的单元测试(包含子文件夹中的测试)

go test ./...

  • 执行当前目录下所有单元测试(不包含子文件夹)

go test ./dir

  • 执行指定文件进行单元测试,指定测试文件和被测试文件

go test xxx.go xxx_test.go

  • 运行指定单元测试用例

go test -run TestXXX

测试代码

编写 5 种字符串拼接方式的测试用例:

单元测试部分

  • splice.go
package splice

// SpliceWithPlus 使用   号拼接字符串
func SpliceWithPlus(s1 string, s2 string) string {
	return s1   s2
}

// SpliceWithSprintf 使用 Sprintf 进行字符串拼接
func SpliceWithSprintf(s1 string, s2 string) string {
	return fmt.Sprintf("%s%s", s1, s2)
}

// SpliceWithJoin 使用 strings.Join 函数拼接字符串
func SpliceWithJoin(s1 string, s2 string) string {
	return strings.Join([]string{s1, s2}, "")
}

// SpliceWithBuilder 使用 strings.Builder 拼接字符串
func SpliceWithBuilder(s1 string, s2 string) string {
	var builder strings.Builder
	builder.WriteString(s1)
	builder.WriteString(s2)

	return builder.String()
}

// SpliceWithBuffer 使用 bytes.Buffer 拼接字符串
func SpliceWithBuffer(s1 string, s2 string) string {
	var buffer bytes.Buffer

	buffer.WriteString(s1)
	buffer.WriteString(s2)
	return buffer.String()
}
  • splice_test.go
package main

import (
	"strings"
	"testing"
)

var (
	count = 10
        // 将子串 "1234567890" 重复 10 次,拼接成一个大的字符串
	s1    = strings.Repeat("1234567890", count)
	s2    = strings.Repeat("0987654321", count)

	want = s1   s2
)

func TestSpliceString(t *testing.T) {
	if got := SpliceWithPlus(s1, s2); got != want {
            t.Errorf("SpliceWithPlus() = %v, want %v", got, want)
	}

	if got := SpliceWithSprintf(s1, s2); got != want {
            t.Errorf("SpliceWithSprintf() = %v, want %v", got, want)
	}

	if got := SpliceWithJoin(s1, s2); got != want {
            t.Errorf("SpliceWithJoin() = %v, want %v", got, want)
	}

	if got := SpliceWithBuilder(s1, s2); got != want {
		t.Errorf("SpliceWithBuilder() = %v, want %v", got, want)
	}

	if got := SpliceWithBuffer(s1, s2); got != want {
		t.Errorf("SpliceWithBuffer() = %v, want %v", got, want)
	}
}

测试执行

  • go test
  • go test .
  • go test -v
  • go test -run TestSpliceString
  • go test -v --cover

基准测试

借此机会,测试 5 种字符串拼接方式的效果,这也是面试中会涉及的问题;

只需要在 splice_test.go 文件中添加基准测试部分的代码

基准测试代码

  • splice_test.go
func BenchmarkSpliceWithPlus(b *testing.B) {
	for i := 0; i < b.N; i   {
		SpliceWithPlus(s1, s2)
	}
}

func BenchmarkSpliceWithSprintf(b *testing.B) {
	for i := 0; i < b.N; i   {
		SpliceWithSprintf(s1, s2)
	}
}

func BenchmarkSpliceWithJoin(b *testing.B) {
	for i := 0; i < b.N; i   {
		SpliceWithJoin(s1, s2)
	}
}

func BenchmarkSpliceWithBuilder(b *testing.B) {
	for i := 0; i < b.N; i   {
		SpliceWithBuilder(s1, s2)
	}
}

func BenchmarkSpliceWithBuffer(b *testing.B) {
	for i := 0; i < b.N; i   {
		SpliceWithBuffer(s1, s2)
	}
}

基准测试基本介绍

基准测试框架对一个测试用例的默认测试时间为 1s,开始测试时,当以 Benckmark 开头的基准测试用例函数执行返回时间还不到 1s,那么 testing.B 中的 N 值将按 1、2、5、10、20、50 递增,同时以递增后的值重新调用执行测试用例函数。

基准测试常用参数

  • -bench :基准测试必填参数,参数值支持正则表达式;

    • -bench=. 表示同时运行单元测试和基准测试
    • -bench=^Benckmark 表示执行当前目录下所有的基准测试,也可以具体到某个函数的基准测试;
  • -benchtime=3s:自定义测试时间

go test -bench=. -benchtime=3s

  • -benchmem:显示内存分配统计信息

go test -bench=. -benchmem

  • -cpu:限制线程数量,把参数值传递给 GOMAXPROCS

go test -bench=. -cpu=4

测试

修改 splice_test.go 文件的 count 变量值,分别为 1、10、100,分别进行测试,测试命令如下:

go test -bench=. -benchmem

  • count = 1 时的测试结果

学新通

  • count = 10 时的测试结果

学新通

  • count = 100 时的测试结果

学新通

基准压测函数后面数字 8 ,表示执行基准测试使用的线程数量,可以通过-cpu=4参数来修改。

测试结果和我之前了解的有些出入,以前我总认为:

strings.Join ≈ strings.Builder > bytes.Buffer > fmt.Sprintf >

但从基准测试结果来看,无论是每秒执行次数、每次操作使用时间、每次操作内存分配量以及每次调用内存分配次数,都是 ' ' 拼接性能最高;根据测试结果得出的结果是:

WithPlus > WithJoin ≈ WithBuilder > WithSprintf > WithBuffer

分析

  • 字符串在 go 中是不可变类型,占用的内存大小是固定的,之前使用 ' ' 号拼接两个字符串时会生成一个新的字符串,这就需要重新申请一个更大的内存空间,新空间的大小是原来两个字符串的大小之和;(测试结果是 ' ' 的拼接性能最好,应该是对字符串拼接做了优化,性能现在是最好的);
  • strings.Builder 和 bytes.Buffer 底层都是字节数组;
    • bytes.Buffer 将字节数组转换为字符串时重新申请了一块空间来存放生成的字符串变量;
    • strings.Builder 直接将底层的字节数组转换成字符串类型返回,因此性能比较好。

测试覆盖率

go test 命令还支持展示测试覆盖率信息

常用参数

  • -cover:测试覆盖率必填参数

go test -cover splice.go splice_test.go

  • -coverprofile:导出单元测试覆盖率统计信息到指定文件中

go test -coverprofile=cover.out

  • -html:在浏览器中(可视化方式)查看覆盖了哪些代码

go tool cover -html=cover.out

也可以两者合并到一起执行

go test -coverprofile=cover.out && go tool cover -html=cover.out

学新通

以可视化方式展示哪些代码已经被测试覆盖,哪些还未被覆盖,这对完善测试用例非常有帮助。

ok,这是之前看的一篇帖子,现在找不到引用了,无法贴出原出处,见谅。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhfhbhkk
系列文章
更多 icon
同类精品
更多 icon
继续加载