如何使用stretchr/testify正确断言decimal类型数值相等
问题原因
stretchr/testify的require.Equal默认基于反射做深度字段对比,不会主动调用类型自带的相等判断方法。常用的Go decimal库(shopspring/decimal)内部存储为「绝对值系数+10的指数」结构,你报错里的两个值:
- 期望值:系数为6,指数为3 → 6 * 10^3 = 6000
- 实际值:系数为6000,指数为0 → 6000 * 10^0 = 6000
二者数值完全相等,但内部字段值不同,反射对比自然会判定不通过。再加你混用了NewFromFloat和NewFromFloat32两种不同精度的构造方法,更容易触发这类内部表示不一致的问题。
正确断言方式
1. 直接配合decimal自带的Equal方法使用
调用decimal类型原生的Equal方法做数值相等判断,再用require.True断言结果为真即可:
func Test_example(t *testing.T) { t.Run("test if two decimals are equal", func(t *testing.T) { sum_amount := decimal.NewFromFloat(1000.00).Add(decimal.NewFromFloat(5000.00)) // 精确场景推荐统一用NewFromString/NewFromInt构造值,避免浮点数精度问题 expected := decimal.NewFromInt(6000) require.True(t, expected.Equal(sum_amount)) }) }
2. 封装通用断言helper(推荐项目中使用)
如果测试里大量用到decimal相等断言,可以封装一个和testify原生断言用法一致的辅助函数,减少重复代码:
// RequireDecimalEqual 断言两个decimal数值逻辑相等 func RequireDecimalEqual(t *testing.T, expected, actual decimal.Decimal, msgAndArgs ...interface{}) { t.Helper() // 标记为helper函数,断言失败时会定位到实际调用的代码行,不会定位到工具函数内部 require.True(t, expected.Equal(actual), append([]interface{}{"decimal values not equal"}, msgAndArgs...)...) }
使用方式和原生require.Equal完全一致:
RequireDecimalEqual(t, decimal.NewFromInt(6000), sum_amount)
注意事项
- 不要混用不同精度的浮点数构造函数(
NewFromFloat/NewFromFloat32/NewFromFloat64),浮点数本身存在二进制表示精度误差,涉及金额、计量等精确场景,优先用NewFromString或NewFromInt构造decimal值,从根源避免精度偏差。 - 不止decimal,所有自带自定义Equal判断逻辑的类型(比如部分时间类型、ORM自定义字段类型),都不建议直接用
require.Equal做反射对比,优先用类型自带的相等判断方法配合require.True/assert.True使用。
内容的提问来源于stack exchange,提问作者Barry Michael Doyle
相关产品推荐
相关产品推荐

