我正在使用Go中的错误包装,并有一个返回包装的自定义错误类型的函数.我想要做的是迭代预期错误的列表,并测试函数的输出是否包装了这些预期错误.

我发现将定制错误放在[]error内意味着定制错误的类型将是*fmt.wrapError,这意味着errors.As()几乎总是返回TRUE.

作为示例,请考虑以下代码:

package main

import (
    "errors"
    "fmt"
)

type AnotherError struct {
}

func (e *AnotherError) Error() string {
    return "another error"
}

type MissingAttrError struct {
    missingAttr string
}

func (e *MissingAttrError) Error() string {
    return fmt.Sprintf("missing attribute: %s", e.missingAttr)
}

func DoSomething() error {
    e := &MissingAttrError{missingAttr: "Key"}
    return fmt.Errorf("DoSomething(): %w", e)
}

func main() {
    err := DoSomething()
    expectedErrOne := &MissingAttrError{}
    expectedErrTwo := &AnotherError{}
    expectedErrs := []error{expectedErrOne, expectedErrTwo}

    fmt.Printf("Is err '%v' type '%T'?: %t\n", err, expectedErrOne, errors.As(err, &expectedErrOne))
    fmt.Printf("Is err '%v' type '%T'?: %t\n", err, expectedErrTwo, errors.As(err, &expectedErrTwo))

    for i := range expectedErrs {
        fmt.Printf("Is err '%v' type '%T'?: %t\n", err, expectedErrs[i], errors.As(err, &expectedErrs[i]))

    }
}

其输出结果是

Is err 'DoSomething(): missing attribute: Key' type '*main.MissingAttrError'?: true
Is err 'DoSomething(): missing attribute: Key' type '*main.AnotherError'?: false
Is err 'DoSomething(): missing attribute: Key' type '*fmt.wrapError'?: true
Is err 'DoSomething(): missing attribute: Key' type '*fmt.wrapError'?: true

理想情况下,我希望输出为

Is err 'DoSomething(): missing attribute: Key' type '*main.MissingAttrError'?: true
Is err 'DoSomething(): missing attribute: Key' type '*main.AnotherError'?: false
Is err 'DoSomething(): missing attribute: Key' type '*main.MissingAttrError'?: true
Is err 'DoSomething(): missing attribute: Key' type '*main.AnotherError'?: false

拥有错误切片的原因是我希望能够定义每个测试用例条目的预期错误列表.假设我知道,为函数提供特定的输入将使它沿着一条应该返回包装特定错误的错误的路径前进.

如何将[]error切片中的*fmt.wrapError类型转换回原始类型,以便可以在error.As中使用它?

我知道我可以用.(AnotherError)将其强制为特定类型,但要使其在迭代切片时起作用,我必须对函数可能返回的每个可能的错误执行此操作,不是吗?)

推荐答案

你可以用下面这句话骗过errors.As:

func main() {
    err := DoSomething()
    m := &MissingAttrError{}
    a := &AnotherError{}
    expected := []any{&m, &a}

    for i := range expected {
        fmt.Printf("Is err '%v' type '%T'?: %t\n", err, expected[i], errors.As(err, expected[i]))
    }
}

打印的文字不是你所期望的,但errors.As的工作方式是应该的.

您的示例不起作用的原因是您传递给errors.As的是*error.因此,包络误差值(err)被直接分配给目标值.在我的示例中,传递给errors.As的值是**AnotherError,而err不能赋给*AnotherError.

Go相关问答推荐

在保留额外参数的同时解封YAML

一种基于时间的Golang函数节制器

在Go中根据命名空间/字符串生成UUID

Kafka架构注册表-Broker:Broker无法验证记录

如何在gofiber/websocket/v2中设置状态代码和原因

`docker system df` 与 `/system/df` (docker api 端点)

Golang Gorm Fiber - 如何将定义为别名的名称发送到索引模板?

Kafka golang 生产者在错误后更改分区计数

3 字节切片和有符号整数类型之间的转换

Caddy服务器try 打开端口80而不是8090.

Golang - POST 失败(NoSurf CSRF)

当图像是对象数组的元素时,如何显示存储为页面资源的图像?

如果值为 false,gRPC 不返回布尔值

没有任务角色的 AWS CDK ECS 任务定义

golang 中的可变参数函数

无法使用 Golang 扫描文件路径

Golang模板无法访问embedFS中的文件

Golang ACMEv2 HTTP-01 挑战不挑战服务器

go mod tidy 错误消息:但是 go 1.16 会 Select

Go/Golang:如何从 big.Float 中提取最低有效数字?