当前位置: 首页 > news >正文

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 回归测试
      • 2.4 Goland 直接运行单元测试
      • 2.5 Go Test 命令参数
      • 2.6 运行一个文件中的单个测试
      • 2.7 测试覆盖率
      • 2.8 公共的帮助函数(helpers)
    • 三、`testing.T`的拥有的方法
    • 四、Table Driven 模式
      • 4.1 介绍
      • 4.2 Go 组织测试的方式
      • 4.3 举个例子
      • 4.4 运行 Table Driven 下的单个测试
    • 五、`testify/assert` 断言工具包
      • 5.1 介绍
      • 5.2 安装
      • 5.3 使用
    • 六、单元测试代码模板
    • 七、参考文档

引入

正常的业务开发之后的测试流程,都是先单元测试,后集成测试。

  • 单元测试:针对每一个方法进行的测试,单独验证每一个方法的正确性。
  • 集成测试:多个组件合并在一起的测试,验证各个方法、组件之间配合无误。

所以一般项目都是开发人员要先搞单元测试,单元测试初步验证之后,再集成测试。

单元测试验证了各个方法的基本逻辑之后,集成测试就比较少问题了。

一、单元测试基本介绍

1.1 什么是单元测试?

单元测试(Unit Tests, UT) 是一个优秀项目不可或缺的一部分,是对软件中的最小可测试部分进行检查和验证。在面向对象编程中,最小测试单元通常是一个方法或函数。单元测试通常由开发者编写,用于验证代码的一个很小的、很具体的功能是否正确。单元测试是自动化测试的一部分,可以频繁地运行以检测代码的更改是否引入了新的错误。

特别是在一些频繁变动和多人合作开发的项目中尤为重要。你或多或少都会有因为自己的提交,导致应用挂掉或服务宕机的经历。如果这个时候你的修改导致测试用例失败,你再重新审视自己的修改,发现之前的修改还有一些特殊场景没有包含,恭喜你减少了一次上库失误。也会有这样的情况,项目很大,启动环境很复杂,你优化了一个函数的性能,或是添加了某个新的特性,如果部署在正式环境上之后再进行测试,成本太高。对于这种场景,几个小小的测试用例或许就能够覆盖大部分的测试场景。而且在开发过程中,效率最高的莫过于所见即所得了,单元测试也能够帮助你做到这一点,试想一下,假如你一口气写完一千行代码,debug 的过程也不会轻松,如果在这个过程中,对于一些逻辑较为复杂的函数,同时添加一些测试用例,即时确保正确性,最后集成的时候,会是另外一番体验。

1.2 如何写好单元测试

首先,学会写测试用例。比如如何测试单个函数/方法;比如如何做基准测试;比如如何写出简洁精炼的测试代码;再比如遇到数据库访问等的方法调用时,如何 mock

然后,写可测试的代码。高内聚,低耦合是软件工程的原则,同样,对测试而言,函数/方法写法不同,测试难度也是不一样的。职责单一,参数类型简单,与其他函数耦合度低的函数往往更容易测试。我们经常会说,“这种代码没法测试”,这种时候,就得思考函数的写法可不可以改得更好一些。为了代码可测试而重构是值得的。

1.3 单元测试的优点

单元测试讲究的是快速测试、快速修复。

  • 测试该环节中的业务问题,比如说在写测试的时候,发现业务流程设计得不合理。
  • 测试该环节中的技术问题,比如说nil之类的问题。

单元测试,从理论上来说,你不能依赖任何第三方组件。也就是说,你不能使用MySQL或者Redis

如图,要快速启动测试,快速发现BUG,快速修复,快速重测。

1.4 单元测试的设计原则

  1. 每个测试单元必须完全独立、能单独运行。
  2. 一个测试单元应只关注一个功能函数,证明它是正确的;
  3. 测试代码要能够快速执行。
  4. 不能为了单元测试而修改已完成的代码在编写代码后执行针对本次的单元测试,并执行之前的单元测试用例。
  5. 以保证你后来编写的代码不会破坏任何事情;
  6. 单元测试函数使用长的而且具有描述性的名字,例如都以test_开头,然后加上具体的函数名字或者功能描述;例如:func_test.go。
  7. 测试代码必须具有可读性。

二、Go语言测试

2.1 Go单元测试概要

Go 语言的单元测试默认采用官方自带的测试框架,通过引入 testing 包以及 执行 go test 命令来实现单元测试功能。

在源代码包目录内,所有以 _test.go 为后缀名的源文件会被 go test 认定为单元测试的文件,这些单元测试的文件不会包含在 go build 的源代码构建中,而是单独通过 go test 来编译并执行。

2.2 Go单元测试基本规范

Go 单元测试的基本规范如下:

  • 每个测试函数都必须导入 testing 包。测试函数的命名类似func TestName(t *testing.T),入参必须是 *testing.T
  • 测试函数的函数名必须以大写的 Test 开头,后面紧跟的函数名,要么是大写开关,要么就是下划线,比如 func TestName(t *testing.T) 或者 func Test_name(t *testing.T) 都是 ok 的, 但是 func Testname(t *testing.T)不会被检测到
  • 通常情况下,需要将测试文件和源代码放在同一个包内。一般测试文件的命名,都是 {source_filename}_test.go,比如我们的源代码文件是allen.go ,那么就会在 allen.go 的相同目录下,再建立一个 allen_test.go 的单元测试文件去测试 allen.go 文件里的相关方法。

当运行 go test 命令时,go test 会遍历所有的 *_test.go 中符合上述命名规则的函数,然后生成一个临时的 main 包用于调用相应的测试函数,然后构建并运行、报告测试结果,最后清理测试中生成的临时文件。

2.3 一个简单例子

2.3.1 使用Goland 生成测试文件

我们来创建一个示例,创建名为 add.go的文件

package mainfunc Add(a int, b int) int {return a + b
}
func Mul(a int, b int) int {return a * b
}

这里借助Goland给 ADD 函数生成并且编写测试用例,只需要右键点击函数,转到Generate -> Test for file function(生成函数测试)。

Goland 为我们生成了add_test.go单测文件

package mainimport "testing"func TestAdd(t *testing.T) {type args struct {a intb int}tests := []struct {name stringargs argswant int}{// TODO: Add test cases.}for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {if got := Add(tt.args.a, tt.args.b); got != tt.want {t.Errorf("Add() = %v, want %v", got, tt.want)}})}
}
2.3.2 运行单元测试

运行 go test,该 package 下所有的测试用例都会被执行。

go test .                                                
ok      gotest  1.060s

go test -v-v 参数会显示每个用例的测试结果,另外 -cover 参数可以查看覆盖率。

go test -v                                               
=== RUN   TestAdd
--- PASS: TestAdd (0.00s)
PASS
ok      gotest  1.208s
2.3.3 完善测试用例

接着我们来完善上面的测试用例,代码如下:

package mainimport "testing"func TestAdd(t *testing.T) {type args struct {a intb int}tests := []struct {name stringargs argswant int}{{name: "Adding positive numbers",args: args{a: 2, b: 3},want: 5,},{name: "Adding negative numbers",args: args{a: -2, b: -3},want: -5,},{name: "Adding positive and negative numbers",args: args{a: 2, b: -3},want: -1,},{name: "Adding zero",args: args{a: 2, b: 0},want: 2,},}for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {if got := Add(tt.args.a, tt.args.b); got != tt.want {t.Errorf("Add() = %v, want %v", got, tt.want)}})}
}
2.3.5 回归测试

我们修改了代码之后仅仅执行那些失败的测试用例或新引入的测试用例是错误且危险的,正确的做法应该是完整运行所有的测试用例,保证不会因为修改代码而引入新的问题。

go test -v
=== RUN   TestAdd
=== RUN   TestAdd/Adding_positive_numbers
=== RUN   TestAdd/Adding_negative_numbers
=== RUN   TestAdd/Adding_positive_and_negative_numbers
=== RUN   TestAdd/Adding_zero
--- PASS: TestAdd (0.00s)--- PASS: TestAdd/Adding_positive_numbers (0.00s)--- PASS: TestAdd/Adding_negative_numbers (0.00s)--- PASS: TestAdd/Adding_positive_and_negative_numbers (0.00s)--- PASS: TestAdd/Adding_zero (0.00s)
=== RUN   TestMul
=== RUN   TestMul/结果为0
=== RUN   TestMul/结果为-1
=== RUN   TestMul/结果为1
--- PASS: TestMul (0.00s)--- PASS: TestMul/结果为0 (0.00s)--- PASS: TestMul/结果为-1 (0.00s)--- PASS: TestMul/结果为1 (0.00s)
PASS
ok      gotest  0.912s

测试结果表明我们的单元测试全部通过。

2.4 Goland 直接运行单元测试

如果你的测试方法签名没错的话,就能看到这个绿色图标,点击就能看到很多选项。

最主要的是:

  • Run:运行模式,直接运行整个测试。
  • Debug:Debug模式,你可以打断点。
  • Run xxx with Coverage:运行并且输出测试覆盖率。
  • 其它Profile都是性能分析,很少用。

除非你要看测试覆盖率,不然都用Debug

2.5 Go Test 命令参数

go test 是 Go 语言的测试工具,你可以使用它来运行 Go 程序的测试函数。

你可以在命令行中使用以下参数来调用 go test 命令:

  • -run:指定要运行的测试函数的名称的正则表达式。例如,使用 go test -run TestAdd 可以运行名称为 TestSum 的测试函数。
  • -bench:指定要运行的基准测试的名称的正则表达式。例如,使用 go test -bench . 可以运行所有基准测试。
  • -count:指定要运行测试函数或基准测试的次数。例如,使用 go test -count 2 可以运行测试函数或基准测试两次。
  • -v:输出测试函数或基准测试的详细输出。
  • -timeout:设置测试函数或基准测试的超时时间。例如,使用 go test -timeout 1s 可以将超时时间设置为 1 秒。

以下是一个go Test命令表格:

参数说明
-bench regexp仅运行与正则表达式匹配的基准测试。默认不运行任何基准测试。使用 -bench .-bench= 来运行所有基准测试。
-benchtime t运行每个基准测试足够多的迭代,以达到指定的时间 t(例如 -benchtime 1h30s)。默认为1秒(1s)。特殊语法 Nx 表示运行基准测试 N 次(例如 -benchtime 100x)。
-count n运行每个测试、基准测试和模糊测试 n 次(默认为1次)。如果设置了 -cpu,则为每个 GOMAXPROCS 值运行 n 次。示例总是运行一次。-count 不适用于通过 -fuzz 匹配的模糊测试。
-cover启用覆盖率分析。
-covermode set,count,atomic设置覆盖率分析的 mode。默认为 “set”,如果启用了 -race,则为 “atomic”。
-coverpkg pattern1,pattern2,pattern3对匹配模式的包应用覆盖率分析。默认情况下,每个测试仅分析正在测试的包。
-cpu 1,2,4指定一系列的 GOMAXPROCS 值,在这些值上执行测试、基准测试或模糊测试。默认为当前的 GOMAXPROCS 值。-cpu 不适用于通过 -fuzz 匹配的模糊测试。
-failfast在第一个测试失败后不启动新的测试。
-fullpath在错误消息中显示完整的文件名。
-fuzz regexp运行与正则表达式匹配的模糊测试。当指定时,命令行参数必须精确匹配主模块中的一个包,并且正则表达式必须精确匹配该包中的一个模糊测试。
-fuzztime t在模糊测试期间运行足够多的模糊目标迭代,以达到指定的时间 t(例如 -fuzztime 1h30s)。默认为永远运行。特殊语法 Nx 表示运行模糊目标 N 次(例如 -fuzztime 1000x)。
-fuzzminimizetime t在每次最小化尝试期间运行足够多的模糊目标迭代,以达到指定的时间 t(例如 -fuzzminimizetime 30s)。默认为60秒。特殊语法 Nx 表示运行模糊目标 N 次(例如 -fuzzminimizetime 100x)。
-json以 JSON 格式记录详细输出和测试结果。这以机器可读的格式呈现 -v 标志的相同信息。
-list regexp列出与正则表达式匹配的测试、基准测试、模糊测试或示例。不会运行任何测试、基准测试、模糊测试或示例。
-parallel n允许并行执行调用 t.Parallel 的测试函数,以及运行种子语料库时的模糊目标。此标志的值是同时运行的最大测试数。
-run regexp仅运行与正则表达式匹配的测试、示例和模糊测试。
-short告诉长时间运行的测试缩短其运行时间。默认情况下是关闭的,但在 all.bash 中设置,以便在安装 Go 树时可以运行健全性检查,但不花费时间运行详尽的测试。
-shuffle off,on,N随机化测试和基准测试的执行顺序。默认情况下是关闭的。如果 -shuffle 设置为 on,则使用系统时钟种子随机化器。如果 -shuffle 设置为整数 N,则 N 将用作种子值。在这两种情况下,种子将报告以便复现。
-skip regexp仅运行与正则表达式不匹配的测试、示例、模糊测试和基准测试。
-timeout d如果测试二进制文件运行时间超过持续时间 d,则发生 panic。如果 d 为0,则禁用超时。默认为10分钟(10m)。
-v详细输出:记录所有运行的测试。即使测试成功,也打印所有来自 LogLogf 调用的文本。
-vet list配置在 “go test” 期间对 “go vet” 的调用,以使用由逗号分隔的 vet 检查列表。如果列表为空,“go test” 使用被认为总是值得解决的精选检查列表运行 “go vet”。如果列表为

更多可以参考 Go 语言的官方文档或使用 go help test 命令查看帮助信息

2.6 运行一个文件中的单个测试

如果只想运行其中的一个用例,例如 TestAdd,可以用 -run 参数指定,该参数支持通配符 *,和部分正则表达式,例如 ^$

go test -run TestAdd -v
=== RUN   TestAdd
=== RUN   TestAdd/Adding_positive_numbers
=== RUN   TestAdd/Adding_negative_numbers
=== RUN   TestAdd/Adding_positive_and_negative_numbers
=== RUN   TestAdd/Adding_zero
--- PASS: TestAdd (0.00s)--- PASS: TestAdd/Adding_positive_numbers (0.00s)--- PASS: TestAdd/Adding_negative_numbers (0.00s)--- PASS: TestAdd/Adding_positive_and_negative_numbers (0.00s)--- PASS: TestAdd/Adding_zero (0.00s)
PASS
ok      gotest  1.008s

2.7 测试覆盖率

测试覆盖率是指代码被测试套件覆盖的百分比。通常我们使用的都是语句的覆盖率,也就是在测试中至少被运行一次的代码占总代码的比例。在公司内部一般会要求测试覆盖率达到80%左右。

Go提供内置功能来检查你的代码覆盖率,即使用go test -cover来查看测试覆盖率。

go test -cover
PASS
coverage: 100.0% of statements
ok      gotest  1.381s

还可以使用 -coverprofile 标志将覆盖率数据输出到一个文件中,然后使用 go tool cover 命令来查看更详细的覆盖率报告。

2.8 公共的帮助函数(helpers)

对一些重复的逻辑,抽取出来作为公共的帮助函数(helpers),可以增加测试代码的可读性和可维护性。 借助帮助函数,可以让测试用例的主逻辑看起来更清晰。

例如,我们可以将创建多次使用的逻辑抽取出来:

type addCase struct{ A, B, want int }func createAddTestCase(t *testing.T, c *addCase) {// t.Helper()if ans := Add(c.A, c.B); ans != c.want {t.Fatalf("%d * %d expected %d, but %d got",c.A, c.B, c.want, ans)}}func TestAdd2(t *testing.T) {createAddTestCase(t, &addCase{1, 1, 2})createAddTestCase(t, &addCase{2, -3, -1})createAddTestCase(t, &addCase{0, -1, 0}) // wrong case
}

在这里,我们故意创建了一个错误的测试用例,运行 go test,用例失败,会报告错误发生的文件和行号信息:

go test
--- FAIL: TestAdd2 (0.00s)add_test.go:109: 0 * -1 expected 0, but -1 got
FAIL
exit status 1
FAIL    gotest  1.090s

可以看到,错误发生在第11行,也就是帮助函数 createAddTestCase 内部。116, 117, 118行都调用了该方法,我们第一时间并不能够确定是哪一行发生了错误。有些帮助函数还可能在不同的函数中被调用,报错信息都在同一处,不方便问题定位。因此,Go 语言在 1.9 版本中引入了 t.Helper(),用于标注该函数是帮助函数,报错时将输出帮助函数调用者的信息,而不是帮助函数的内部信息。

修改 createAddTestCaseV1,调用 t.Helper()

type addCaseV1 struct {name stringA, B intwant int
}func createAddTestCaseV1(c *addCaseV1, t *testing.T) {t.Helper()t.Run(c.name, func(t *testing.T) {if ans := Add(c.A, c.B); ans != c.want {t.Fatalf("%s: %d + %d expected %d, but %d got",c.name, c.A, c.B, c.want, ans)}})
}func TestAddV1(t *testing.T) {createAddTestCaseV1(&addCaseV1{"case 1", 1, 1, 2}, t)createAddTestCaseV1(&addCaseV1{"case 2", 2, -3, -1}, t)createAddTestCaseV1(&addCaseV1{"case 3", 0, -1, 0}, t)
}

运行 go test,报错信息如下,可以非常清晰地知道,错误发生在第 131 行。

go test
--- FAIL: TestAddV1 (0.00s)--- FAIL: TestAddV1/case_3 (0.00s)add_test.go:131: case 3: 0 + -1 expected 0, but -1 got
FAIL
exit status 1
FAIL    gotest  0.434s

关于 helper 函数的 2 个建议:

  • 不要返回错误, 帮助函数内部直接使用 t.Errort.Fatal 即可,在用例主逻辑中不会因为太多的错误处理代码,影响可读性。
  • 调用 t.Helper() 让报错信息更准确,有助于定位。

当然,如果你是用Goland 编辑器的话,可以不使用t.Helper(),自动会帮你打印出错误详细信息

三、testing.T的拥有的方法

以下是提供的 *testing.T 类型的方法及其用途的注释:

// T 是 Go 语言测试框架中的一个结构体类型,它提供了用于编写测试的方法。
// 它通常通过测试函数的参数传递给测试函数。// Cleanup 注册一个函数,该函数将在测试结束时执行,用于清理测试过程中创建的资源。
func (c *T) Cleanup(func())// Error 记录一个错误信息,但不会立即停止测试的执行。
func (c *T) Error(args ...interface{})// Errorf 根据 format 和 args 记录一个格式化的错误信息,但不会立即停止测试的执行。
func (c *T) Errorf(format string, args ...interface{})// Fail 标记测试函数为失败,但不会停止当前测试的执行。
func (c *T) Fail()// FailNow 标记测试函数为失败,并立即停止当前测试的执行。
func (c *T) FailNow()// Failed 检查测试是否失败。
func (c *T) Failed() bool// Fatal 记录一个错误信息,并立即停止测试的执行。
func (c *T) Fatal(args ...interface{})// Fatalf 记录一个格式化的错误信息,并立即停止测试的执行。
func (c *T) Fatalf(format string, args ...interface{})// Helper 标记当前函数为辅助函数,当测试失败时,辅助函数的文件名和行号将不会显示在错误消息中。
func (c *T) Helper()// Log 记录一些信息,这些信息只有在启用详细日志(-v标志)时才会显示。
func (c *T) Log(args ...interface{})// Logf 记录一些格式化的信息,这些信息只有在启用详细日志(-v标志)时才会显示。
func (c *T) Logf(format string, args ...interface{})// Name 返回当前测试或基准测试的名称。
func (c *T) Name() string// Skip 标记测试为跳过,并记录一个错误信息。
func (c *T) Skip(args ...interface{})// SkipNow 标记测试为跳过,并立即停止当前测试的执行。
func (c *T) SkipNow()// Skipf 标记测试为跳过,并记录一个格式化的错误信息。
func (c *T) Skipf(format string, args ...interface{})// Skipped 检查测试是否被跳过。
func (c *T) Skipped() bool// TempDir 返回一个临时目录的路径,该目录在测试结束时会被自动删除。
func (c *T) TempDir() string

四、Table Driven 模式

4.1 介绍

Table Driven 模式是一种软件设计模式,它通过将测试数据存储在一个表格(通常是结构化的数据结构,如数组、切片、映射或结构体)中,然后在一个单独的函数或方法中遍历这个表格来执行测试。这种模式使得测试代码更加模块化、可读性和可维护性更高。

在 Go 语言中,Table Driven 模式通常通过定义一个结构体来组织测试数据,然后使用一个循环来遍历这个结构体,为每个测试用例执行相同的测试逻辑。这种方法可以很容易地添加新的测试用例,并且可以使测试代码更加简洁和易于维护。

4.2 Go 组织测试的方式

Go里面,惯常的组织测试的方式,都是用Table Driven

Table Driven的形式如下图。主要分成三个部分:

  • 测试用例的定义:即每一个测试用例需要有什么。
  • 具体的测试用例:你设计的每一个测试用例都在这里。
  • 执行测试用例:这里面还包括了对测试结果进行断言。

注意,你要优先使用Table Driven,但是不用强求

你把测试用例定义看做是列名,每一个测试用例就是一行数据,就能理解Table Driven这个含义了。

4.3 举个例子

func TestMul(t *testing.T) {type args struct {a intb int}tests := []struct {name stringargs argswant int}{{name: "结果为0",args: args{a: 2, b: 0},want: 0,},{name: "结果为-1",args: args{a: -1, b: 1},want: -1,},{name: "结果为1",args: args{a: -1, b: -1},want: 1,},}for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {if got := Mul(tt.args.a, tt.args.b); got != tt.want {t.Errorf("Mul() = %v, want %v", got, tt.want)}})}
}

4.4 运行 Table Driven 下的单个测试

当你使用前面 Table Driven 的模式时,可以单个运行测试用例

五、testify/assert 断言工具包

5.1 介绍

testify/assert 是一个流行的Go语言断言库,它提供了一组丰富的断言函数,用于简化测试代码的编写。这个库提供了一种更声明式的方式来编写测试,使得测试意图更加明确,代码更加简洁。

使用 testify/assert 时,您不再需要编写大量的 if 语句和 Error 方法调用来检查条件和记录错误。相反,您可以使用像 assert.Equalassert.Nilassert.True 这样的断言函数来验证测试的期望结果。

5.2 安装

go get github.com/stretchr/testify

5.3 使用

断言包提供了一些有用的方法,可以帮助您在Go语言中编写更好的测试代码。

  • 打印友好、易于阅读的失败描述
  • 允许编写可读性强的代码
  • 可以为每个断言添加可选的注释信息
    看看它的实际应用:
package yours
import ("testing""github.com/stretchr/testify/assert"
)
func TestSomething(t *testing.T) {// 断言相等assert.Equal(t, 123, 123, "它们应该相等")// 断言不等assert.NotEqual(t, 123, 456, "它们不应该相等")// 断言为nil(适用于错误处理)assert.Nil(t, object)// 断言不为nil(当你期望得到某个结果时使用)if assert.NotNil(t, object) {// 现在我们知道object不是nil,我们可以安全地进行// 进一步的断言而不会引起任何错误assert.Equal(t, "Something", object.Value)}
}

每个断言函数都接受 testing.T 对象作为第一个参数。这就是它如何通过正常的Go测试能力输出错误信息的方式。

每个断言函数都返回一个布尔值,指示断言是否成功。这对于在特定条件下继续进行进一步的断言非常有用。

当我们有多个断言语句时,还可以使用assert := assert.New(t)创建一个assert对象,它拥有前面所有的断言方法,只是不需要再传入Testing.T参数了。

package yours
import ("testing""github.com/stretchr/testify/assert"
)
func TestSomething(t *testing.T) {assert := assert.New(t)// 断言相等assert.Equal(123, 123, "它们应该相等")// 断言不等assert.NotEqual(123, 456, "它们不应该相等")// 断言为nil(适用于错误处理)assert.Nil(object)// 断言不为nil(当你期望得到某个结果时使用)if assert.NotNil(object) {// 现在我们知道object不是nil,我们可以安全地进行// 进一步的断言而不会引起任何错误assert.Equal("Something", object.Value)}
}

在上面的示例中,assert.New(t) 创建了一个新的 assert 实例,然后您可以使用这个实例的方法来进行断言。如果断言失败,testify/assert 会自动标记测试为失败,并记录一个详细的错误消息。

六、单元测试代码模板

func Test_Function(t *testing.T) {testCases := []struct {name string //测试用例的名称args any    //测试用例的输入参数want string //期望的返回值}{// 测试用例,测试用例表格{},{},}for _, tc := range testCases {t.Run(tc.name, func(t *testing.T) {//具体的测试代码})}
}

七、参考文档

  • Go Test 单元测试简明教程
  • Go单元测试入门

相关文章:

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 完善测试用…...

uniapp 上传视频到阿里云之后回显视频获取视频封面

uniapp 上传视频到阿里云之后回显视频获取视频封面 官网的解决方案 1.initial-time Number 指定视频初始播放位置&#xff0c;单位为秒&#xff08;s&#xff09;。 没什么卵用 2.使用 uni.createVideoContext(“myVideo”, this).seek(number)。 没什么卵用 <video :id&quo…...

使用undetected-chromedriver遇到的问题及解决方法,以及它使用SOCKS代理的问题

环境&#xff1a;python3.8.10 uc的安装方法&#xff1a; pip38 install undetected-chromedriver 上测试代码&#xff1a; import undetected_chromedriver as uc driver uc.Chrome() driver.get(https://www.baidu.com) driver.save_screenshot(baidu.png)报错&#xff…...

Hadoop入门学习路线

目录 一、基础理论学习 二、安装与配置 三、Hadoop安装与部署 四、实践操作与项目练习 五、进阶学习 六、学习资源推荐 一、基础理论学习 了解Hadoop的起源、发展历程及其在大数据领域的重要性。 掌握Hadoop的核心组件及其作用&#xff0c;包括HDFS&#xff08;分布式文件…...

Python中的设计模式与最佳实践【第166篇—设计模式】

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 Python中的设计模式与最佳实践 在软件开发中&#xff0c;设计模式是一种解决常见问题的经过…...

Python赋能AI数据分析开启人工智能新时代

文章目录 一、Python是办公自动化的重要工具二、Python是提升职场竞争力的利器三、Python是企业数字化的重要平台四、Python是AI发展的重要通道之一《编程菜鸟学Python数据分析》编辑推荐内容简介作者简介目录前言为什么要写这本书读者对象如何阅读本书 随着我国企业数字化和信…...

TP5使用group报错:1055 Expression #1 of SELECT list is not in GROUP

使用group报错 Mysql环境是5.7的, 使用了View进行了表连接, 进行了表连接 搬迁到本地后, 查询报错 Syntax error or access violation: 1055 Expression 解决方法1 配置 my.cnf(linux)文件 win下面是 mysql.ini文件 在 mysqld 里加上 sql_modeNO_ENGINE_SUBSTITUTION,STR…...

SQL-DML数据操纵语言(Oracle)

文章目录 DML数据操纵语言常见的字段属性字符型字段属性char(n)varchar2(n)/varchar(n) 数值型字段属性number([p],[s]int 日期型字段属性DATEtimestamp 如何查看字段属性增加数据INSERT快捷插入 删除数据DELETE修改数据UPDATE DML数据操纵语言 定义 是针对数据做处理&#xf…...

springboot+axios传参问题

目录 get请求方式: 不携带参数: 携带参数 第一种方式: 第二种传参方式: post方式: 携带参数: 第一种方式: 第二种方式:...

(BERT蒸馏)TinyBERT: Distilling BERT for Natural Language Understanding

文章链接&#xff1a;https://arxiv.org/abs/1909.10351 背景 在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;预训练语言模型&#xff08;如BERT&#xff09;通过大规模的数据训练&#xff0c;已在多种NLP任务中取得了卓越的性能。尽管BERT模型在语言理解和生成…...

【数据结构|C语言版】双向链表

前言1. 初步认识双向链表1.1 定义1.2 结构1.3 储存 2. 双向链表的方法&#xff08;接口函数&#xff09;2.1 动态申请空间2.2 创建哨兵位2.3 查找指定数据2.4 指定位置插入2.5 指定位置删除2.6 头部插入2.7 头部删除2.8 尾部插入2.9 尾部删除2.10 计算链表大小2.11 销毁链表 3.…...

适用于 Windows 的 10 个顶级 PDF 编辑器 [免费和付费]

曾经打开PDF文件&#xff0c;感觉自己被困在数字迷宫中吗&#xff1f;无法编辑的文本、无法调整大小的图像以及签署感觉像是一件苦差事的文档&#xff1f;好吧&#xff0c;不用再担心了&#xff01;本指南解开了在 Windows 上掌握 PDF 的秘密&#xff0c;其中包含 10 款适用于 …...

久菜盒子|留学|推荐信|活动类|改性伽马-三氧化二铝催化剂上甲醇制备二甲醚的研究项目

尊敬的录取委员会: 我是华东理工大学化工学院的刘殿华。非常荣幸在此推荐我校优秀学生 XXX 进入贵校学习。 我认识 XXX是在一年前&#xff0c;当时&#xff0c;我正计划做一个有关改性伽马-三氧化二铝催化剂上甲醇制备二甲醚的研究项目。XXX 找到了我&#xff0c;表示希望能够加…...

Java项目如何使用EasyExcel插件对Excel数据进行导入导出

文章目录 一、EasyExcel的示例导入依赖创建实体类数据导入和导出 二、EasyExcel的作用三、EasyExcel的注解 EasyExcel是一个阿里巴巴开源的excel处理框架&#xff0c;它以使用简单、节省内存著称。在解析Excel时&#xff0c;EasyExcel没有将文件数据一次性全部加载到内存中&…...

python标准库常用方法集合

前段时间准备第十五届蓝桥杯python a组&#xff0c;因为赛中不允许导包&#xff0c;因此对py中的标准库进行了笔记和总结&#xff0c;即不导包即可使用的常用方法。包含了内置函数、math、random、datetime、os、sys、re、queue、collections、itertools库的常用方法&#xff0…...

智谱AI通用大模型:官方开放API开发基础

目录 一、模型介绍 1.1主要模型 1.2 计费单价 二、前置条件 2.1 申请API Key 三、基于SDK开发 3.1 Maven引入SDK 3.2 代码实现 3.3 运行代码 一、模型介绍 GLM-4是智谱AI发布的新一代基座大模型&#xff0c;整体性能相比GLM3提升60%&#xff0c;支持128K上下文&#x…...

单片机家电产品--OC门电路

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 单片机家电产品–OC门电路 前言 记录学习单片机家电产品内容 已转载记录为主 一、知识点 1OC门电路和OD门电路的区别 OC门电路和OD门电路的区别 OC门&#xff1a;三极管…...

gcc常用命令指南(更新中...)

笔记为gcc常用命令指南&#xff08;自用&#xff09;&#xff0c;用到啥方法就具体研究一下&#xff0c;更新进去... 编译过程的分布执行 64位系统生成32位汇编代码 gcc -m32 test.c -o test -m32用于生成32位汇编语言...

【深度学习】【机器学习】用神经网络进行入侵检测,NSL-KDD数据集,基于机器学习(深度学习)判断网络入侵,网络攻击,流量异常【3】

之前用NSL-KDD数据集做入侵检测的项目是&#xff1a; 【1】https://qq742971636.blog.csdn.net/article/details/137082925 【2】https://qq742971636.blog.csdn.net/article/details/137170933 有人问我是不是可以改代码&#xff0c;我说可以。 训练 我将NSL_KDD_Final_1.i…...

两步解决 Flutter Your project requires a newer version of the Kotlin Gradle plugin

在开发Flutter项目的时候,遇到这个问题Flutter Your project requires a newer version of the Kotlin Gradle plugin 解决方案分两步: 1、在android/build.gradle里配置最新版本的kotlin 根据提示的kotlin官方网站搜到了Kotlin的最新版本是1.9.23,如下图所示: 同时在Ko…...

ArcGIS加载的各类地图怎么去除服务署名水印

昨天介绍的&#xff1a; 一套图源搞定&#xff01;清新规划底图、影像图、境界、海洋、地形阴影图、导航图-CSDN博客文章浏览阅读373次&#xff0c;点赞7次&#xff0c;收藏11次。一体化集成在一起的各类型图源&#xff0c;比如包括影像、清新的出图底图、地形、地图阴影、道路…...

AttributeError: module ‘cv2.face’ has no attribute ‘LBPHFaceRecognizer_create’

问题描述&#xff1a; 报错如下&#xff1a; recognizer cv2.face.LBPHFaceRecognizer_create() AttributeError: module ‘cv2.face’ has no attribute ‘LBPHFaceRecognizer_create’ 解决方案&#xff1a; 把opencv-python卸载了&#xff0c;然后安装ope…...

配置路由器实现互通

1.实验环境 实验用具包括两台路由器(或交换机)&#xff0c;一根双绞线缆&#xff0c;一台PC&#xff0c;一条Console 线缆。 2.需求描述 如图6.14 所示&#xff0c;将两台路由器的F0/0 接口相连&#xff0c;通过一台PC 连接设备的 Console 端口并配置P地址&#xff08;192.1…...

Google Guava第五讲:本地缓存实战及踩坑

本地缓存实战及踩坑 本文是Google Guava第五讲,先介绍为什么使用本地缓存;然后结合实际业务,讲解如何使用本地缓存、清理本地缓存,以及使用过程中踩过的坑。 文章目录 本地缓存实战及踩坑1、缓存系统概述2、缓存架构演变2.1、无缓存架构2.2、引入分布式缓存问题1:为什么选…...

一个文生视频MoneyPrinterTurbo项目解析

最近抖音剪映发布了图文生成视频功能,同时百家号也有这个功能,这个可以看做是一个开源的实现,一起看看它的原理吧~ 一句话提示词 大模型生成文案 百家号生成视频效果 MoneyPrinterTurbo生成视频效果 天空为什么是蓝色的? 天空之所以呈现蓝色,是因为大气中的分子和小粒子会…...

智能商品计划系统如何提升鞋服零售品牌的竞争力

国内鞋服零售企业经过多年的发展&#xff0c;已经形成了众多知名品牌&#xff0c;然而近年来一些企业频频受到库存问题的困扰&#xff0c;这一问题不仅影响了品牌商自身&#xff0c;也给长期合作的经销商带来了困扰。订货会制度在初期曾经有效地解决了盲目生产的问题&#xff0…...

OpenHarmony开发案例:【分布式遥控器】

1.概述 目前家庭电视机主要通过其自带的遥控器进行操控&#xff0c;实现的功能较为单一。例如&#xff0c;当我们要在TV端搜索节目时&#xff0c;电视机在遥控器的操控下往往只能完成一些字母或数字的输入&#xff0c;而无法输入其他复杂的内容。分布式遥控器将手机的输入能力…...

如何将Oracle 中的部分不兼容对象迁移到 OceanBase

本文总结分析了 Oracle 迁移至 OceanBase 时&#xff0c;在出现三种不兼容对象的情况时的处理策略以及迁移前的预检方式&#xff0c;通过提前发现并处理这些问题&#xff0c;可以有效规避迁移过程中的报错风险。 作者&#xff1a;余振兴&#xff0c;爱可生 DBA 团队成员&#x…...

Python也可以合并和拆分PDF,批量高效!

PDF是最方便的文档格式&#xff0c;可以在任何设备原样且无损的打开&#xff0c;但因为PDF不可编辑&#xff0c;所以很难去拆分合并。 知乎上也有人问&#xff0c;如何对PDF进行合并和拆分&#xff1f; 看很多回答推荐了各种PDF编辑器或者网站&#xff0c;确实方法比较多。 …...

python笔记(14)迭代器和生成器

迭代器的优势 延迟计算&#xff1a;迭代器按需提供数据&#xff0c;无需一次性加载整个数据集到内存中&#xff0c;特别适合处理大规模或无限数据流。资源效率&#xff1a;减少内存占用&#xff0c;尤其在处理大量数据时&#xff0c;避免一次性构建完整数据结构带来的开销。统…...

做淘客网站怎么建要购买数据库吗/服装市场调研报告

新接手的项目采用的是Maven作为编译管理工具&#xff0c;项目内容则是按子模块Module来划分。由于模块太多&#xff0c;且都比较大(基本上都能独立运行并负责处理一方面的业务)&#xff0c;并采用Socket来进行模块之间的数据通信&#xff0c;结果就导致一个略显臃肿的系统产生了…...

深圳做二维码网站建设/semicircle

0&#xff09;基础知识——线性可分支持向量机定义&#xff1b;函数间隔和集合间隔定义 1&#xff09;线性硬间隔支持向量机 2&#xff09;凸二次规划最优解求法——对偶方法 3&#xff09;线性硬间隔支持向量机学习算法及简单实例 4&#xff09;线性软间隔支持向量机 5&…...

单位网站 方案/百度网页版登录

现在我们对可视化信息的需求越来越高&#xff0c;可视化信息比传统的文本信息更吸引眼球&#xff0c;方便阅读&#xff0c;加深记忆&#xff0c;因此也可以更快地被人们传播出去。近年来涌现出了许多数据可视化工具。有哪些值得使用呢&#xff1f;下面与大家分享九大数据可视化…...

wordpress 调试模式/app优化网站

Javascript有许多内建的方法来产生对话框&#xff0c;如&#xff1a;window.alert(), window.confirm(),window.prompt().等。 然而IE提供更多的方法支持对话框。如&#xff1a; showModalDialog() (IE 4 支持)   showModelessDialog() (IE 5 支持) window.showModalDialog()方…...

web网站开发+语言/创建网站的基本步骤

定义注释。定义文档类型。定义超链接。定义缩写。HTML 5 中不支持。定义首字母缩写。定义地址元素。 HTML 5 中不支持。定义 applet。 定义图像映射中的区域。 定义 article。 定义页面内容之外的内容。 定义声音内容。 定义粗体文本。 定义页面中所有链接的基准 URL。 HTML 5 …...

苏州网站建设企业网站制作/蜜雪冰城网络营销案例分析

千里之行始于足下 题目&#xff1a;打印一个数的每一位 要求&#xff1a;递归方式实现打印一个整数的每一位 思路&#xff1a; 首先要了解C语言中函数递归的概念&#xff0c;一个调用自身&#xff08;不管是直接地还是间接地调用&#xff09;的函数被称为函数的递归。&…...