You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在GitHub Actions中编写预期错误的Go单元测试并通过检查?

在Go中验证预期错误并让测试通过的实现方法

要实现「验证代码返回预期错误,同时让测试通过」的需求,核心是在测试逻辑里主动判断错误是否符合预期,而非让错误直接导致测试失败。以下是具体实现方式:

1. 直接判断特定错误

针对单个错误场景,先检查是否返回错误,再验证错误是否匹配预期值:

import (
    "errors"
    "testing"
)

var ErrInvalidInput = errors.New("invalid input")

func SomeFunction(input string) (string, error) {
    if input == "" {
        return "", ErrInvalidInput
    }
    return "success", nil
}

func TestSomeFunction_InvalidInput(t *testing.T) {
    _, err := SomeFunction("")
    // 首先确保确实返回了错误
    if err == nil {
        t.Fatal("预期返回错误,但实际未返回任何错误")
    }
    // 验证错误是否为预期的特定错误
    if !errors.Is(err, ErrInvalidInput) {
        t.Errorf("错误不匹配:预期%v,实际%v", ErrInvalidInput, err)
    }
}

这种方式下,只有当「应该报错却没报」或者「报错不是预期的类型/内容」时,测试才会失败;如果错误符合预期,测试会正常通过。

2. 使用表驱动测试(多场景覆盖)

如果需要同时测试成功和失败场景,表驱动测试会更清晰,能统一管理所有测试用例:

func TestSomeFunction(t *testing.T) {
    testCases := []struct {
        name        string
        input       string
        expectErr   bool
        expectedErr error
    }{
        {
            name:      "有效输入-正常返回",
            input:     "valid",
            expectErr: false,
        },
        {
            name:        "无效输入-返回指定错误",
            input:       "",
            expectErr:   true,
            expectedErr: ErrInvalidInput,
        },
    }

    for _, tc := range testCases {
        t.Run(tc.name, func(t *testing.T) {
            result, err := SomeFunction(tc.input)
            if tc.expectErr {
                if err == nil {
                    t.Fatalf("测试用例[%s]:预期返回错误,但实际未返回", tc.name)
                }
                if !errors.Is(err, tc.expectedErr) {
                    t.Errorf("测试用例[%s]:错误不匹配,预期%v,实际%v", tc.name, tc.expectedErr, err)
                }
            } else {
                if err != nil {
                    t.Fatalf("测试用例[%s]:预期无错误,实际返回错误%v", tc.name, err)
                }
                if result != "success" {
                    t.Errorf("测试用例[%s]:返回结果不匹配,预期%s,实际%s", tc.name, "success", result)
                }
            }
        })
    }
}

3. 处理自定义错误类型

如果代码返回带额外字段的自定义错误类型,可以用errors.As断言错误类型,并验证字段值:

type CustomError struct {
    Code int
    Msg  string
}

func (e *CustomError) Error() string {
    return e.Msg
}

func AnotherFunction(input int) (int, error) {
    if input < 0 {
        return 0, &CustomError{Code: 400, Msg: "input cannot be negative"}
    }
    return input * 2, nil
}

func TestAnotherFunction_NegativeInput(t *testing.T) {
    _, err := AnotherFunction(-1)
    if err == nil {
        t.Fatal("预期返回错误,但实际未返回")
    }
    // 断言错误类型为CustomError
    var customErr *CustomError
    if !errors.As(err, &customErr) {
        t.Errorf("错误类型不匹配:预期%T,实际%T", customErr, err)
    }
    // 验证错误字段
    if customErr.Code != 400 {
        t.Errorf("错误码不匹配:预期%d,实际%d", 400, customErr.Code)
    }
    if customErr.Msg != "input cannot be negative" {
        t.Errorf("错误信息不匹配:预期%s,实际%s", "input cannot be negative", customErr.Msg)
    }
}

关键注意事项

  • 不要直接用if err != nil { t.Fatal(err) }处理预期的错误,这会让测试直接失败,违背验证错误的初衷。
  • 优先使用errors.Is和errors.As,而非直接比较错误字符串,这能更好地处理错误包装(fmt.Errorf("wrap: %w", err))的场景。
  • 所有测试用例的预期逻辑要明确:无论是成功还是失败场景,只要符合预期,测试就会通过;只有不符合预期时才触发失败。

内容的提问来源于stack exchange,提问作者delos777

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 18:54:20