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

Go使用assert.Equal比对含time.Time字段的interface对象测试失败如何解决

问题描述

我想要校验返回数据是否符合预期。

我的函数代码如下:

func extractData(payload string) (interface{}, time.Time, error) {
    eventTime := gjson.Get(payload, "data.eventDateTime").String()
    dateTime, err := time.Parse("2006-01-02T15:04:05-07:00", eventTime)
    if err != nil {
        return nil, time.Time{}, fmt.Errorf("Date Time Error: %w", err)
    }
    body := data.Body{
        EventTime: dateTime,
    }
    return body, dateTime, nil
}

我为其编写的单元测试代码如下:

func TestExtractData(t *testing.T) {
    tests := []struct {
        Name           string
        Payload        string
        ExpectedOutput interface{}
    }{
        {
            Name:    "test-2",
            Payload: `{"data":"2020-11-02T10:44:48+01:00"}`,
            ExpectedOutput: data.Body{
                EventTime: time.Date(2020, 11, 2, 10, 44, 48, 0, time.FixedZone("CET", 3600)),
            },
        },
    }
    for _, tt := range tests {
        t.Run(tt.Name, func(t *testing.T) {
            data, _, _ := extractData(tt.Payload)
            assert.Equal(t, tt.ExpectedOutput, data)
        })
    }
}

测试运行失败,输出如下:

{ 2020-11-02 10:44:48 +0100 CET} does not equal { 2020-11-02 10:44:48 +0100 CET}

我怀疑问题出在时区上,但不知道如何编写测试代码。我不想单独使用time.Equal做校验,因为我的data.Body并不总是包含eventTime字段。
我临时强转为具体类型使用如下代码测试,可以正常通过:

if !tt.ExpectedOutput.EventTime.Equal(data.EventTime) {
    //throw error
}

我还尝试过如下写法,但是测试失败:

if !reflect.DeepEqual(tt.ExpectedOutput.EventTime, data.EventTime) {
    t.Errorf("extractData() output = %v, want = %v",data,tt.ExpectedOutput)
}

请问该如何解决这个问题?


解决方案

根本原因

Go标准库的reflect.DeepEqual比较time.Time类型时,除了比较时间点和时区偏移,还会比较内部*time.Location指针是否指向同一个实例。你测试用例中手动用time.FixedZone生成的时区实例,和time.Parse从字符串解析出来的时区实例是两个不同的内存对象,哪怕偏移和名称完全一致,也会被判为不等,testify/assert的Equal方法底层默认用reflect.DeepEqual,所以会出现打印内容一致但测试失败的情况。

可选方案

方案1:使用自定义比较器适配testify断言

如果已经在使用testify库,可以直接给assert.Equal传入time.Time类型的自定义比较规则,自动适配所有包含time.Time字段的结构体,不需要单独处理EventTime字段:

assert.Equal(t, tt.ExpectedOutput, data, assert.Comparer(func(a, b time.Time) bool {
    return a.Equal(b)
}))

该比较器会自动识别所有time.Time类型的字段,用官方推荐的time.Equal判断相等,其他类型字段保持默认比较逻辑。

方案2:统一预期值的时间构造方式

构造测试预期值时,直接用和业务代码相同的time.Parse方法生成EventTime,保证时区实例完全一致:

eventTime, _ := time.Parse("2006-01-02T15:04:05-07:00", "2020-11-02T10:44:48+01:00")
ExpectedOutput: data.Body{
    EventTime: eventTime,
},

这种方式不需要修改原有断言逻辑,用默认的assert.Equal即可通过测试。

方案3:使用go-cmp库做结构化比较

如果可以引入新依赖,go-cmp库默认支持time.Time的等值判断,会自动调用time.Equal,不需要额外配置:

if diff := cmp.Diff(tt.ExpectedOutput, data); diff != "" {
    t.Errorf("结果不匹配 (-预期值 +实际值):\n%s", diff)
}

该方式会输出详细的差异字段,适合复杂结构体的比较场景。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 06:39:00