Go语言- 单元测试

发布于:2025-06-27 ⋅ 阅读:(19) ⋅ 点赞:(0)

实际开发中,需要保证单元功能正确。

传统方式:在main函数中直接调用,查看结合是否和预期一致。

缺点:1. 不方便 2. 不利于管理 

因此,单元测试具有必要性

testing测试框架

Go语言中自带testing轻量级测试框架和go test命令来实现单元测试和性能测试。可以解决以下问题:

1. 确保每个函数可运行并且结果正确

2. 确保单元代码的性能

3. 尽早发现错误

入门实例以及解析

需求:在service包下有一个函数addUpper需要测试。

解决方式:在service包下创建一个xxx_test.go文件,创建一个测试用例,代码如下:

package service

import(
	_ "fmt"
	"testing" //引入testing测试框架
)

// 编写一个测试用例,测试同包下addUpper函数是否正确
func TestAddUpper(t *testing.T){
	// 调用
	res := addUpper(10)
	if res != 55{
		t.Fatalf("AddUpper执行错误,期望值%v,实际值%v", 55, res)
	}
	// 如果正确,就输出日志
	t.Logf("执行正确...")
}

使用命令行调用testing框架进行测试。

go test:Go 的测试命令,会自动查找当前目录及其子目录中所有以 _test.go 结尾的文件,并运行其中的测试函数。

-v(verbose):启用“详细输出”模式。在该模式下,测试过程中会打印出每个测试函数的执行情况(包括测试通过与否、执行顺序等),而不是只显示最终结果。

在 Go 的测试中,使用 t.Log("...") 或 t.Logf("format", args...) 输出的日志信息,默认只输出到控制台(终端),并不会自动写入文件或其他地方。如果需要写入.log文件需要指定。

PASS表示运行成功,FAIL表示运行失败。

1. 测试文件命名必须是 xxx_test.go
2. 测试函数名必须以 Test 开头且Test后一位必须为大写,如 TestSomething(t *testing.T)
3. 只有在包目录中有 _test.go 文件时,go test 才会运行测试

4. 所有用于执行单元测试的函数(即以 Test 开头的函数)都必须使用 t *testing.T 作为唯一的参数

5. 一个xxx_test.go文件中可以有多个测试函数,以测试一个包中不同的单元。

在测试函数中直接调用被测试函数即可,对结果进行判断,使用t的方法进行输出。

运行一个_test.go文件

go test自动查找当前目录及其子目录中所有以 _test.go 结尾的文件,但是如果想指定 _test.go文件,就在命令行中指定:

go test -v cal_test.go cal.go

go test:Go 的测试命令,用于运行测试。
-v:表示“verbose(详细输出)”,会显示每个测试函数的执行情况。
cal_test.go:测试文件,里面包含以 TestXXX 开头的测试函数。
cal.go:普通 Go 源文件,通常是你想测试的代码逻辑所在文件。

运行一个测试用例

go test -v -run ^TestAddUpper$
或者
go test -v -run TestAddUpper

这样只会运行该包下指定的一个测试用例, ^ 和 $ 是正则表达式符号,表示完全匹配函数名。不加也可以,但建议加上更精确。

单元测试综合案例

需求

构建结构体Monster,结构体两个方法Store和ReStore进行序列化和反序列化。

编写测试用例测试两个方法。

Monster结构体和方法代码:

package main

import (
	"encoding/json"
	"fmt"
	"os"
)

type Monster struct {
	Name  string `json:"name"`
	Age   int    `json:"age"`
	Skill string `json:"skill"`
}

func (m *Monster) Store(filename string) error {
	// 序列化变量并保存到当前目录
	data, err := json.Marshal(m)
	if err != nil {
		return err
	}
	return os.WriteFile(filename, data, 0644)
}

func (m *Monster) ReStore(filename string) error {
	// 反序列化变量并保存到当前目录
	data, err := os.ReadFile(filename)
	if err != nil {
		return err
	}
	return json.Unmarshal(data, m)
}

func main() {
	// m := Monster{
	// 	Name:  "牛魔王",
	// 	Age:   800,
	// 	Skill: "魔王拳",
	// }
	// m.Store("MonsterJson.json")

	var m Monster
	m.ReStore("MonsterJson.json")
	fmt.Println(m)
}

_test.go文件代码:

package main

import (
	"os"
	"reflect"
	"testing"
)

const testFile string = "monster.json"

func TestStore(t *testing.T) {
	m := &Monster{
		Name:  "Dracula",
		Age:   500,
		Skill: "吸血",
	}

	err := m.Store(testFile)
	if err != nil {
		t.Fatalf("存储失败:%v", err)
	}
	// 检查文件是否存在
	if _, err := os.Stat(testFile); os.IsNotExist(err) {
		t.Fatal("文件未生成")
	}
	// 清理测试文件
	os.Remove(testFile)
}

// TestRestore 测试 Restore 方法
func TestRestore(t *testing.T) {
	// 准备一个测试文件内容
	expected := &Monster{
		Name:  "Frankenstein",
		Age:   200,
		Skill: "雷电之力",
	}

	// 先将预期对象存入文件
	err := expected.Store(testFile)
	if err != nil {
		t.Fatalf("准备测试文件失败:%v", err)
	}

	// 创建一个新的 Monster 实例并恢复数据
	var restored Monster
	err = restored.ReStore(testFile)
	if err != nil {
		t.Fatalf("恢复失败:%v", err)
	}

	// 使用reflect.DeepEqual判断两个值是否深度一致
	if !reflect.DeepEqual(expected, &restored) {
		t.Errorf("期望值 %v,实际值 %v", expected, restored)
	}

	// 清理测试文件
	os.Remove(testFile)
}

结果


网站公告

今日签到

点亮在社区的每一天
去签到