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

如何在Gomock中匹配含动态值的[]interface{}及bson.D参数

处理Gomock中带动态参数的方法Mock问题

一、Mock含动态字段的InsertManyDocument方法

实际调用时传入的data里有自动生成的动态值(比如time.Now()生成的时间戳),固定值匹配肯定失效,这时候得自定义参数匹配器,只校验我们关心的固定字段,忽略动态字段。

实现方式

  1. 先实现gomock.Matcher接口,自定义匹配逻辑
    假设你要插入的结构体是User,示例如下:
    type User struct {
        Name string
        CreateTime time.Time // 动态生成的字段
    }
    
    // 自定义匹配器,只校验Name字段
    type userMatcher struct {
        expectedName string
    }
    
    func (m userMatcher) Matches(x interface{}) bool {
        user, ok := x.(User)
        if !ok {
            return false
        }
        // 只对比固定字段,动态的CreateTime直接忽略
        return user.Name == m.expectedName
    }
    
    func (m userMatcher) String() string {
        return fmt.Sprintf("匹配Name为%s的User", m.expectedName)
    }
    
  2. 对[]interface{}类型的data做匹配
    如果切片元素顺序固定,直接用自定义匹配器构造预期切片:
    oid1, _ := primitive.ObjectIDFromHex("60d21b4667d0d8992e610c85")
    oid2, _ := primitive.ObjectIDFromHex("60d21b4667d0d8992e610c86")
    
    expectedData := []interface{}{
        userMatcher{expectedName: "Alice"},
        userMatcher{expectedName: "Bob"},
    }
    
    // 录制Mock调用
    mockClient.EXPECT().InsertManyDocument(gomock.Eq(expectedData)).
        Return([]primitive.ObjectID{oid1, oid2}, nil)
    
    如果元素顺序不固定,用gomock.InAnyOrder()包裹预期切片:
    mockClient.EXPECT().InsertManyDocument(gomock.InAnyOrder(expectedData)).
        Return([]primitive.ObjectID{oid1, oid2}, nil)
    

二、处理UpdateOneDocumentWithBSON的动态参数校验

bson.D、options.ArrayFilters这类参数里有动态值时,没法直接用gomock.Any()(因为Mock方法要求传入具体类型,不是Matcher),同样用自定义校验逻辑解决,两种方式可选:

方式1:用gomock.MatchedBy()快速包装校验函数

直接写匿名函数或者单独函数来校验参数的关键字段,比如针对filter和update:

// 校验filter是否包含预期的_id
matchFilter := func(filter primitive.D) bool {
    expectedID, _ := primitive.ObjectIDFromHex("60d21b4667d0d8992e610c85")
    for _, elem := range filter {
        if elem.Key == "_id" {
            id, ok := elem.Value.(primitive.ObjectID)
            return ok && id == expectedID
        }
    }
    return false
}

// 校验update的$set是否设置了正确的status
matchUpdate := func(update bson.D) bool {
    for _, elem := range update {
        if elem.Key == "$set" {
            setData, ok := elem.Value.(bson.M)
            if ok && setData["status"] == "active" {
                return true
            }
        }
    }
    return false
}

// 录制Mock调用,用MatchedBy包装校验函数
mockClient.EXPECT().UpdateOneDocumentWithBSON(
    gomock.MatchedBy(matchFilter),
    gomock.Any(), // arrayFilter如果不关心就直接用Any()
    gomock.MatchedBy(matchUpdate),
)

方式2:自定义Matcher处理复杂校验

如果校验逻辑比较复杂,就单独实现gomock.Matcher接口:

// 针对filter的自定义匹配器
type filterMatcher struct {
    expectedID primitive.ObjectID
}

func (m filterMatcher) Matches(x interface{}) bool {
    filter, ok := x.(primitive.D)
    if !ok {
        return false
    }
    for _, elem := range filter {
        if elem.Key == "_id" {
            id, ok := elem.Value.(primitive.ObjectID)
            return ok && id == m.expectedID
        }
    }
    return false
}

func (m filterMatcher) String() string {
    return fmt.Sprintf("匹配_id为%s的filter", m.expectedID.Hex())
}

// 录制时直接传入自定义Matcher实例
expectedID, _ := primitive.ObjectIDFromHex("60d21b4667d0d8992e610c85")
mockClient.EXPECT().UpdateOneDocumentWithBSON(
    filterMatcher{expectedID: expectedID},
    gomock.Any(),
    // 这里可以再定义一个updateMatcher,逻辑和上面类似
    gomock.Any(),
)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 12:04:56