如何测试Golang错误处理逻辑_Golang单元测试错误验证方法

答案:Go语言中通过t.Error、errors.Is/As及testify库验证错误。首先检查err是否为nil,再用errors.Is判断是否包含目标错误,errors.As断言具体错误类型,或使用testify简化断言流程,确保错误处理正确。

在Go语言开发中,错误处理是程序健壮性的关键部分。为了确保代码在异常情况下行为正确,必须对错误处理逻辑进行充分的单元测试。下面介绍几种常用的Golang单元测试中验证错误的方法。

使用 t.Error 或 t.Errorf 检查错误是否符合预期

最基础的方式是在测试函数中调用目标函数,然后判断返回的 error 是否为 nil 或是否匹配预期类型/消息。

例如,假设有一个除法函数:

func Divide(a, b float64) (float64, error) {
  if b == 0 {
    return 0, fmt.Errorf("cannot divide by zero")
  }
  return a / b, nil
}

对应的测试可以这样写:

func TestDivide_ByZero(t *testing.T) {
  _, err := Divide(10, 0)
  if err == nil {
    t.Errorf("expected an error when dividing by zero, but got nil")
  }
  if err.Error() != "cannot divide by zero" {
    t.Errorf("expected 'cannot divide by zero', got '%s'", err.Error())
  }
}

使用 errors.Is 和 errors.As 进行语义化错误比较

从 Go 1.13 开始,标准库提供了 errors.Iserrors.As 来更安全地比较和解包错误。

如果你使用的是自定义错误类型或 wrapped 错误,应优先使用这些方法。

var ErrDivideByZero = errors.New("cannot divide by zero")

func Divide(a, b float64) (float64, error) {
  if b == 0 {
    return 0, fmt.Errorf("calculation failed: %w", ErrDivideByZero)
  }
  return a / b, nil
}

测试时使用 errors.Is 判断是否包含目标错误:

func TestDivide_WithErrorIs(t *testing.T) {
  _, err := Divide(5, 0)
  if !errors.Is(err, ErrDivideByZero) {
    t.Errorf("expected error to be wrapped with ErrDivideByZero")
  }
}

断言错误类型(使用 errors.As)

当你的函数返回特定类型的错误(如自定义结构体),可以用 errors.As 提取并验证字段。

type ValidationError struct {
  Field string
  Msg   string
}

func (e *ValidationError) Error() string {
  return fmt.Sprintf("validation error on %s: %s", e.Field, e.Msg)
}

测试时判断错误是否为该类型:

func TestSomeValidation(t *testing.T) {
  err := ValidateInput("")
  var ve *ValidationError
  if !errors.As(err, &ve) {
    t.Fatal("expected ValidationError, got another type")
  }
  if ve.Field != "Input" {
    t.Errorf("expected Field=Input, got %s", ve.Field)
  }
}

使用 testify/assert 等第三方库简化断言

虽然标准 testing 包足够用,但使用像 testify 这样的库可以让错误验证更简洁。

import "github.com/stretchr/testify/assert"

func TestDivide_WithTestify(t *testing.T) {
  _, err := Divide(10, 0)
  assert.Error(t, err)
  assert.Contains(t, err.Error(), "cannot divide by zero")
  assert.ErrorContains(t, err, "divide by zero") // Go 1.14+ 推荐方式
}

注意:Go 1.14 起,testing.T 提供了 t.ErrorContains 方法,也可用于检查错误信息子串。

基本上就这些。关键是根据错误的设计方式选择合适的验证策略:简单字符串比较适用于调试信息,errors.Is/As 更适合生产级错误处理,而测试库能提升可读性。不复杂但容易忽略的是对 wrapped 错误的正确解包和类型断言。