在Golang开发中,函数通常通过返回error类型来传递错误信息。为了让错误处理逻辑真实可靠,我们需要为这些分支编写单元测试,验证在异常情况下程序的行为是否符合预期。

为什么需要测试错误处理
不少项目的测试只覆盖了正常流程,忽略了错误分支。一旦依赖服务超时、参数非法或文件不存在,未经验证的错误处理逻辑可能直接panic或返回模糊信息。对错误处理做单元测试,能提前暴露这些问题。
使用表驱动测试覆盖错误场景
Golang社区习惯用表驱动测试来同时验证多组数据,包括正常与错误输入。下面示例展示如何测试一个除法函数对除数为零的处理:
package mathutil
import (
"errors"
"testing"
)
func Divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
func TestDivide(t *testing.T) {
tests := []struct {
name string
a, b float64
want float64
wantErr bool
}{
{"正常除法", 10, 2, 5, false},
{"除数为零", 1, 0, 0, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := Divide(tt.a, tt.b)
if (err != nil) != tt.wantErr {
t.Errorf("Divide() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !tt.wantErr && got != tt.want {
t.Errorf("Divide() = %v, want %v", got, tt.want)
}
})
}
}
错误类型断言与错误包装
当使用fmt.Errorf配合%w包装底层错误时,测试中可用errors.Is与errors.As做判断:
package service
import (
"errors"
"fmt"
)
var ErrNotFound = errors.New("not found")
func GetUser(id int) (string, error) {
if id <= 0 {
return "", fmt.Errorf("get user: %w", ErrNotFound)
}
return "tom", nil
}
对应测试代码如下:
func TestGetUser(t *testing.T) {
_, err := GetUser(0)
if !errors.Is(err, ErrNotFound) {
t.Errorf("expected ErrNotFound, got %v", err)
}
}
模拟依赖返回错误
对于依赖外部接口的函数,可定义接口并通过伪实现返回错误,从而在不连真实服务的情况下测试处理路径:
type Store interface {
Save(data string) error
}
type failStore struct{}
func (failStore) Save(string) error {
return errors.New("disk full")
}
func Process(s Store) error {
return s.Save("log")
}
func TestProcessError(t *testing.T) {
if err := Process(failStore{}); err == nil {
t.Error("expected error from Process, got nil")
}
}
小结
测试Golang错误处理逻辑并不复杂,核心思路是构造错误输入或伪依赖,再用标准testing工具验证error返回。坚持为错误分支写测试,能让项目在异常场景下更可控。