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单元测试入门

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

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

相关文章

easyexcel升级3.3.4失败的经历

原本想通过easyexcel从2.2.6升级到3.3.3解决一部分问题,结果之前的可以用的代码,却无端的出现bug 1 Sheet index (1) is out of range (0…0) 什么都没有改,就出了问题,那么问题肯定出现在easyexcel版本自身.使用模板填充的方式进…

conda新建环境报错An HTTP error occurred when trying to retrieve this URL.

conda新建环境报错如下 cat .condarc #将 .condarc文件中的内容删除,改成下面的内容 vi .condarc channels:- defaults show_channel_urls: true default_channels:- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main- https://mirrors.tuna.tsinghua.…

权限管理Ranger详解

文章目录 一、Ranger概述与安装1、Ranger概述1.1 Ranger介绍1.2 Ranger的目标1.3 Ranger支持的框架1.4 Ranger的架构1.5 Ranger的工作原理 2、Ranger安装2.1 创建系统用户和Kerberos主体2.2 数据库环境准备2.3 安装RangerAdmin2.4 启动RangerAdmin 二、Ranger简单使用1、安装 R…

【Java NIO】那NIO为什么速度快?

Java IO在工作中其实不常用到,更别提NIO了。但NIO却是高效操作I/O流的必备技能,如顶级开源项目Kafka、Netty、RocketMQ等都采用了NIO技术,NIO也是大多数面试官必考的体系知识。虽然骨头有点难啃,但还是要慢慢消耗知识、学以致用哈…

# RAG | Langchain # Langchain RAG:打造Markdown文件的结构化分割解决方案

【文章简介】 在信息技术的现代背景下,高效地处理和分析文本数据对于知识获取和决策支持至关重要。Markdown文件因其易读性和高效性,在文档编写和知识共享中占据了重要地位。然而,传统的文本处理方法往往忽视了Markdown的结构化特性&#xff…

KNIME 国际化支持投票

你的投票也许能让 KNIME 中文化快一点点。 i18n 是个很搞笑的单词,它是英文 internationalization 国际化的缩写。18 指的是首字母i和末字母n中间有18个字母。另外还有什么 K8s 也是一样,中间省去了8个字母 ... 真是懒的可以。指北君还想起一个类似的笑话…

数字革命的先锋:Web3对社会的影响

引言 在信息技术飞速发展的当下,Web3作为一个新兴的互联网模式,正在逐渐改变我们的生活方式、商业模式和社会结构。本文将深入探讨Web3的核心特点、它在各个领域中的应用以及对社会产生的深远影响。 1. Web3的核心特点 1.1 去中心化 Web3强调去中心化…

记【k8s】:访问 Prometheus UI界面:kubernetes-etcd (0/1 up) Error : out of bounds

记【k8s】:访问 Prometheus UI界面:kubernetes-etcd (0/1 up) Error : out of bounds 1、报错详情2、解决方法 💖The Begin💖点点关注,收藏不迷路💖 出现 “out of bound…

Synchronized锁详解(全网最细)

目录 以下知识基于HotSpot虚拟机实现 1.前置知识 1.1 锁的作用 1.2 Java中常见的锁类型 1.3 锁的重入 2.使用场景 2.1 修饰实例方法 2.1.1 用法 2.1.2 原理 2.1.3 特点 2.2 修饰静态方法 2.2.1 用法 2.2.2 原理 2.3 修饰代码块 2.3.1 用法 3.原理 3.1 对象锁 …

数字电路(四,五章总结)

四.组合逻辑电路设计 由波形图列真值表,之 后画出卡诺图,写出最简逻辑表达式。 卡诺图化简的时候圈住的部分如果某个字母有0又有1的话这个字母删掉,写出其他两个字母。 如下图中黄圈A有0又有1则删除A,这样黄圈代表BC;同理绿圈代…

SpringBoot项目基于java的教学辅助平台

采用技术 SpringBoot项目基于java的教学辅助平台的设计与实现~ 开发语言:Java 数据库:MySQL 技术:SpringBootMyBatis 工具:IDEA/Ecilpse、Navicat、Maven 页面展示效果 学生信息管理 教师信息管理 课程信息管理 科目分类管…

Pytorch入门实战 P06-调用vgg16模型,进行人脸预测

目录 1、本文内容: 1、内容: 2、简单介绍下VGG16: 3、相关其他模型也可以调用: 2、代码展示: 3、训练结果: 1、不同优化器: ①【使用SGD优化器】 ②【使用Adam优化器】 ③Adam 动态学…

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

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

深度学习 Lecture 7 迁移学习、精确率、召回率和F1评分

一、迁移学习(Transfer learning) 用来自不同任务的数据来帮助我解决当前任务。 场景:比如现在我想要识别从0到9度手写数字,但是我没有那么多手写数字的带标签数据。我可以找到一个很大的数据集,比如有一百万张图片的猫、狗、汽…

论文笔记:(INTHE)WILDCHAT:570K CHATGPT INTERACTION LOGS IN THE WILD

iclr 2024 spotlight reviewer 评分 5668 1 intro 由大型语言模型驱动的对话代理(ChatGPT,Claude 2,Bard,Bing Chat) 他们的开发流程通常包括三个主要阶段 预训练语言模型在被称为“指令调优”数据集上进行微调&…

JDK5.0新特性

目录 1、JDK5特性 1.1、静态导入 1.2 增强for循环 1.3 可变参数 1.4 自动装箱/拆箱 1.4.1 基本数据类型包装类 1.5 枚举类 1.6 泛型 1.6.1 泛型方法 1.6.2 泛型类 1.6.3 泛型接口 1.6.4 泛型通配符 1、JDK5特性 JDK5中新增了很多新的java特性,利用这些新…

v-for中涉及的key

一、为什么要用key? key可以标识列表中每个元素的唯一性,方便Vue高效地更新虚拟DOM;key主要用于dom diff算法,diff算法是同级比较,比较当前标签上的key和标签名,如果都一样,就只移动元素&#…

【刷题笔记】第七天

文章目录 [924. 尽量减少恶意软件的传播](https://leetcode.cn/problems/minimize-malware-spread/)方法一,并查集方法二,dfs [GCD and LCM ](https://vjudge.net.cn/problem/HDU-4497#authorKING_LRL) 924. 尽量减少恶意软件的传播 如果移除一个感染节…

上海计算机学会 2023年10月月赛 乙组T4 树的覆盖(树、最小点覆盖、树形dp)

第四题:T4树的覆盖 标签:树、最小点覆盖、树形 d p dp dp题意:求树的最小点覆盖集的大小和对应的数量,数量对 1 , 000 , 000 , 007 1,000,000,007 1,000,000,007取余数。所谓覆盖集,是该树的点构成的集合,…

docker 环境变量设置实现方式

1、前言 docker在当前运用的越来广泛,很多应用或者很多中间软件都有很多docker镜像资源,运行docker run 启动镜像资源即可应用。但是很多应用或者中间件有很多配置参数。这些参数在运用过程怎么设置给docker 容器呢?下面介绍几种方式 2 、do…