Golang中如何编写高效的单元测试

Golang中如何编写高效的单元测试 你好,

我必须承认,在 Go 语言中运行测试方面,我完全是个新手。举例来说,假设我在一个文件中有以下这段代码:

func getJSON(body []byte) (models.Jsonrepo, error) {
	var s = make(models.Jsonrepo, 0)
	err := json.Unmarshal(body, &s)
	if err != nil {
		log.Fatal(err)
	}
	return s, err
}

应该如何为其编写单元测试,以确保它返回了应有的结果(在本例中是一个结构体切片和一个错误,jsonrepo 是结构体切片类型)?


更多关于Golang中如何编写高效的单元测试的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

通常来说,编写单元测试时需要验证:对于给定的正确输入,能否返回正确有效的输出。同时测试函数在接收到错误输入时,能否正确返回、记录日志或报错也同样重要。

在Go语言中编写测试时,需要创建名为somefile_test.go的文件。假设你的函数位于json.go中,那么测试文件应命名为json_test.go。当运行go test命令时,会自动执行该测试文件中的测试用例。

我的首要建议是在if err != nil {检查之后直接使用return nil, err,而不是记录日志。这种设计更合理,也能提升测试效果。

import "testing"

func TestGetJSON(t *testing.T) {
    goodInput := SomeCorrectInput()
    expectedOutput := models.JsonRepo{/*expected values from above input*/}

    out, err := getJSON(goodInput)
    if err != nil {
        t.Fatal("Failure message", err)
    }

    if !reflect.DeepEqual(expectedOutput, out) {
        t.Fatal("Actual output doesn't match expected")
    }
}

通过匹配这种函数签名模式,你就可以编写类似的简单测试。虽然这里没有涵盖错误用例处理、真实输入数据或完善的错误信息,但这个结构应该能满足你的需求。你还可以使用断言框架来简化部分代码,例如testify。断言框架的优势在于比较两个本应相等的对象(预期输出与实际输出)时,会自动打印参数差异,直观显示不同之处。

更多关于Golang中如何编写高效的单元测试的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go语言中编写高效的单元测试需要遵循几个关键原则:使用标准库的testing包、创建测试用例表、使用子测试,以及正确处理错误。以下是为你的getJSON函数编写单元测试的示例:

package yourpackage

import (
	"encoding/json"
	"testing"
	"yourproject/models"
)

func TestGetJSON(t *testing.T) {
	tests := []struct {
		name        string
		input       []byte
		wantResult  models.Jsonrepo
		wantErr     bool
	}{
		{
			name:       "valid json",
			input:      []byte(`[{"id": 1, "name": "test"}]`),
			wantResult: models.Jsonrepo{{ID: 1, Name: "test"}},
			wantErr:    false,
		},
		{
			name:       "invalid json",
			input:      []byte(`invalid json`),
			wantResult: nil,
			wantErr:    true,
		},
		{
			name:       "empty json array",
			input:      []byte(`[]`),
			wantResult: models.Jsonrepo{},
			wantErr:    false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := getJSON(tt.input)
			
			if (err != nil) != tt.wantErr {
				t.Errorf("getJSON() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			
			if !tt.wantErr {
				if len(got) != len(tt.wantResult) {
					t.Errorf("getJSON() result length = %d, want %d", len(got), len(tt.wantResult))
					return
				}
				
				// 比较每个字段,这里假设Jsonrepo是[]YourStruct类型
				for i, item := range got {
					if item != tt.wantResult[i] {
						t.Errorf("getJSON() result[%d] = %v, want %v", i, item, tt.wantResult[i])
					}
				}
			}
		})
	}
}

关于你的原始代码,有一个重要问题需要修正:log.Fatal在测试中会导致整个测试程序退出,应该改为返回错误:

func getJSON(body []byte) (models.Jsonrepo, error) {
	var s = make(models.Jsonrepo, 0)
	err := json.Unmarshal(body, &s)
	if err != nil {
		return nil, err  // 不要使用log.Fatal
	}
	return s, nil
}

测试文件应该命名为*_test.go(例如yourfile_test.go),并与被测试的代码放在同一个包中。运行测试使用go test命令:

go test -v

这种测试方法提供了清晰的测试用例组织、详细的错误报告,并且能够并行运行测试用例(如果需要可以使用t.Parallel())。

回到顶部