Golang单元测试需遵循文件名以_test.go结尾、测试函数以Test开头并接收*testing.T参数的约定,通过go test命令自动执行,利用t.Errorf/t.Fatalf报告失败,t.Run实现子测试与数据驱动测试,提升测试可读性与维护性。

Golang中的单元测试,说白了,就是确保你写的每一小段代码,在各种预期输入下,都能给出正确的输出。它主要通过Go语言自带的
testing
go test
在Golang中编写单元测试,首先要理解它的哲学:简单、直接。不像某些语言需要引入复杂的测试框架,Go把基础测试能力内建在语言和工具链中。具体操作上,你需要创建一个与被测试文件同目录,但以
_test.go
math.go
Add
math_test.go
测试函数本身的命名也有讲究,必须以
Test
TestAdd
*testing.T
在测试函数内部,我们通常会调用被测试的函数,然后将实际结果与预期结果进行比较。如果两者不符,就使用
t.Errorf()
t.Fatalf()
t.Errorf()
t.Fatalf()
立即学习“go语言免费学习笔记(深入)”;
// math.go
package mymath
func Add(a, b int) int {
return a + b
}
// math_test.go
package mymath
import "testing"
func TestAdd(t *testing.T) {
result := Add(1, 2)
expected := 3
if result != expected {
t.Errorf("Add(1, 2) 期望得到 %d, 实际得到 %d", expected, result)
}
// 尝试一个负数的情况
result = Add(-1, 1)
expected = 0
if result != expected {
t.Errorf("Add(-1, 1) 期望得到 %d, 实际得到 %d", expected, result)
}
}运行测试只需在终端中进入项目根目录或
mymath
go test
Go语言的单元测试有其一套清晰的约定,这使得
go test
首先是文件命名。任何包含测试代码的文件,其文件名都必须以
_test.go
user.go
user_test.go
go test
其次是测试函数的结构。每个单元测试函数都必须以
Test
TestGetUserByID
TestCalculateDiscount
func TestXxx(t *testing.T)
t *testing.T
testing.T
go test
例如,一个典型的测试函数看起来是这样的:
// user.go
package user
type User struct {
ID int
Name string
}
func GetUserByID(id int) (*User, error) {
if id <= 0 {
return nil, &Error{Msg: "Invalid ID"}
}
// 假设从数据库获取
if id == 1 {
return &User{ID: 1, Name: "Alice"}, nil
}
return nil, nil // 没有找到
}
type Error struct {
Msg string
}
func (e *Error) Error() string {
return e.Msg
}
// user_test.go
package user
import "testing"
func TestGetUserByID(t *testing.T) {
// 测试有效ID
u, err := GetUserByID(1)
if err != nil {
t.Fatalf("GetUserByID(1) 不应返回错误: %v", err)
}
if u == nil || u.Name != "Alice" {
t.Errorf("GetUserByID(1) 期望用户 Alice, 实际得到 %v", u)
}
// 测试无效ID
u, err = GetUserByID(0)
if err == nil {
t.Error("GetUserByID(0) 期望返回错误, 实际没有")
}
if _, ok := err.(*Error); !ok {
t.Errorf("GetUserByID(0) 期望返回 *user.Error 类型错误, 实际得到 %T", err)
}
}这些约定不仅让
go test
*testing.T
*testing.T
最常用的方法是报告测试失败:
t.Error(args ...interface{})t.Errorf(format string, args ...interface{}) if got != expected {
t.Errorf("期望 %v, 实际 %v", expected, got)
}t.Fatal(args ...interface{})t.Fatalf(format string, args ...interface{})t.Error/Errorf
t.Fatal/Fatalf
if err != nil {
t.Fatalf("初始化失败: %v", err) // 停止当前测试
}除了报告失败,
*testing.T
t.Log(args ...interface{})t.Logf(format string, args ...interface{})go test -v
t.Logf("正在测试输入: %v", input)t.Skip(args ...interface{})t.Skipf(format string, args ...interface{}) if !isDatabaseAvailable() {
t.Skip("跳过数据库相关测试,因为数据库不可用")
} t.Run("PositiveCase", func(t *testing.T) {
// ... 子测试逻辑
})
t.Run("NegativeCase", func(t *testing.T) {
// ... 另一个子测试逻辑
})t.Cleanup(f func())
tempFile := createTempFile(t)
t.Cleanup(func() {
os.Remove(tempFile) // 测试结束后自动删除临时文件
})
// ... 使用 tempFile 进行测试合理运用这些方法,可以让你的测试代码更具表达力,更容易调试,也更健壮。
在Go语言的单元测试中,数据驱动测试(也常称为表驱动测试,Table-Driven Tests)是一种非常强大且常见的模式。它允许你用一组预定义的数据来反复测试同一个函数或方法,大大减少了重复代码,提高了测试的覆盖率和可读性。结合
t.Run
我个人在编写复杂逻辑的测试时,几乎都会优先考虑表驱动测试。它能让我一眼看到各种边界条件、正常情况和异常情况,而且添加新的测试用例也变得异常简单。
实现数据驱动测试的基本步骤是:
t.Run
t.Run
t.Run
go test
下面是一个具体的例子,假设我们有一个
CalculateArea
// geometry.go
package geometry
import "fmt"
type Shape interface {
Area() float64
}
type Rectangle struct {
Width, Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return 3.14159 * c.Radius * c.Radius // 简化π
}
// geometry_test.go
package geometry
import "testing"
func TestShapeArea(t *testing.T) {
tests := []struct {
name string // 测试用例的名称
shape Shape // 输入的形状
expected float64 // 期望的面积
}{
{
name: "Rectangle_3x4",
shape: Rectangle{Width: 3, Height: 4},
expected: 12.0,
},
{
name: "Rectangle_0x5", // 边界情况
shape: Rectangle{Width: 0, Height: 5},
expected: 0.0,
},
{
name: "Circle_Radius2",
shape: Circle{Radius: 2},
expected: 3.14159 * 2 * 2, // 12.56636
},
{
name: "Circle_Radius0", // 边界情况
shape: Circle{Radius: 0},
expected: 0.0,
},
}
for _, tt := range tests {
// 使用 t.Run 为每个测试用例创建一个子测试
t.Run(tt.name, func(t *testing.T) {
actual := tt.shape.Area()
// 浮点数比较需要注意精度,这里简化处理
if actual != tt.expected {
t.Errorf("测试 %s: 期望面积 %f, 实际 %f", tt.name, tt.expected, actual)
}
})
}
}在这个例子中,
TestShapeArea
tests
for
tests
tt
t.Run(tt.name, func(t *testing.T){...})当运行
go test
--- RUN TestShapeArea --- RUN TestShapeArea/Rectangle_3x4 --- PASS: TestShapeArea/Rectangle_3x4 (0.00s) --- RUN TestShapeArea/Rectangle_0x5 --- PASS: TestShapeArea/Rectangle_0x5 (0.00s) --- RUN TestShapeArea/Circle_Radius2 --- PASS: TestShapeArea/Circle_Radius2 (0.00s) --- RUN TestShapeArea/Circle_Radius0 --- PASS: TestShapeArea/Circle_Radius0 (0.00s) --- PASS: TestShapeArea (0.00s) PASS ok geometry 0.004s
如果其中一个子测试失败,比如
Circle_Radius2
TestShapeArea/Circle_Radius2
以上就是Golang测试编写方式 单元测试基础的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号