当前位置:首页 > 文章列表 > Golang > Go教程 > Go Testify 怎样在 Run 回调中修改指针参数

Go Testify 怎样在 Run 回调中修改指针参数

来源:17golang原创 2026-10-29 11:09:53 0浏览 收藏

在测试解码器、填充器或查询适配器时,被模拟的方法经常不是直接返回一个对象,而是把结果写入调用方传入的指针。Go Testify 的 mock.Call.Run 正好适合表达这种“调用时修改参数”的行为:Run 接收本次调用的 mock.Arguments,通过 Get(0) 取出指针后赋值;Return 仍然只负责返回值。

官方地址:https://pkg.go.dev/github.com/stretchr/testify/mock

核心写法是 On(...).Run(...).Return(nil)。回调里必须取出真实的指针并修改它,不能把修改后的对象误写成 Return 的返回值。

先把“参数副作用”和“返回结果”分开

Run 是调用钩子,适合模拟被测依赖在参数上产生的副作用;Return 是方法调用结束时交给业务代码的结果。两者可以放在同一个期望链上,但职责不同。

下面的例子模拟一个把用户信息写入目标指针的解码器。示例只展示 Testify 的替身边界,真实解码过程由被测依赖负责。

package example

import (
    "testing"

    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/mock"
)

type User struct {
    ID   int
    Name string
}

type Decoder interface {
    Decode(dst *User) error
}

type DecoderMock struct {
    mock.Mock
}

func (m *DecoderMock) Decode(dst *User) error {
    // Called 记录本次调用,并把约定的错误值交回业务代码。
    return m.Called(dst).Error(0)
}

func TestDecodeWritesThroughPointer(t *testing.T) {
    decoder := new(DecoderMock)
    decoder.On("Decode", mock.AnythingOfType("*example.User")).
        Run(func(args mock.Arguments) {
            // Run 只处理调用期间的参数副作用,不代替 Return。
            dst, ok := args.Get(0).(*User)
            if !ok || dst == nil {
                // 类型不符或空指针时不要继续解引用。
                return
            }
            dst.ID = 7
            dst.Name = "Lin"
        }).
        Return(nil).
        Once()

    var got User
    err := decoder.Decode(&got)

    assert.NoError(t, err)
    assert.Equal(t, User{ID: 7, Name: "Lin"}, got)
    decoder.AssertExpectations(t)
}

这里真正被修改的是 got 的地址。Return(nil) 只让 Decode 返回空错误;如果把用户对象放进 Return,并不会自动写入 dst。

Testify Run 回调从 Arguments 取出 User 指针并写回调用方对象的静态结构图
图1:静态结构说明图,展示 On、Run、Arguments、User 指针和调用方对象之间的关系,不是运行截图。

回调里只做一次明确的指针绑定

这类测试的关键模式可以压缩成三段:先用 On 声明方法和参数约束,再用 Run 取得参数,最后用 Return 配置返回值。Arguments.Get 返回的是 interface{},因此类型断言应当紧挨着取值完成。

decoder.On("Decode", mock.AnythingOfType("*example.User")).
    Run(func(args mock.Arguments) {
        // 第 0 个参数就是 Decode 的目标地址。
        target, ok := args.Get(0).(*User)
        if !ok || target == nil {
            return
        }
        // 只填充本测试关心的字段,避免隐藏额外行为。
        target.ID = 42
    }).
    Return(nil)

如果方法有多个参数,按方法签名的顺序使用 Get(0)、Get(1)。不要依赖参数的具体变量名,也不要把 Arguments.String() 的文本表示当成可修改对象。

三个容易误判的边界

第一,值参数不能写回调用方。 如果方法签名是 Decode(dst User),回调拿到的是值副本,给它赋值不会改变调用方的变量。需要写回时,接口本身就应当表达 *User。

第二,参数匹配和回调类型是两层约束。 mock.AnythingOfType("*example.User") 只负责匹配调用参数的类型,回调里的类型断言仍然要保留。若测试本来就需要检查字段,可以用 mock.MatchedBy 做更窄的匹配,但不要为了让回调“看起来安全”而跳过 nil 检查。

第三,修改发生在调用过程内。 Run 不会把参数复制成新的返回对象,也不会替代业务代码对错误值的处理。若回调中途返回,仍应由 Return 提供明确的错误或结果;这能让“数据写入”和“调用是否成功”分别可断言。

Testify Run 回调中值参数、指针参数、nil 和 Return 返回值的边界关系结构图
图2:边界关系说明图,对比指针写回、值副本、nil 防护与 Return 结果的职责,不是运行截图。

什么时候值得用这个模式

当接口的自然契约就是“填充调用方提供的目标对象”时,Run 能让 mock 行为贴近真实依赖,尤其适合解码、扫描、读取配置和写入响应对象的测试。它的代价是测试知道了参数位置和可变字段,因此不适合把一大段业务逻辑塞进回调。

  • 接口确实通过指针输出数据,并且测试需要继续走调用方的后续逻辑。
  • 回调只设置当前用例需要的少量字段,复杂计算已经提取到生产代码或测试构造器。
  • 期望链同时保留 Return、Once 或 AssertExpectations,让调用次数和错误路径仍可观察。

反过来,如果依赖本来就返回完整对象,直接用 Return(value, nil) 更清楚;如果多个测试都需要同一组填充规则,可以抽成测试辅助函数,避免每个 Run 回调重复修改字段。

相关问题

Run 能不能修改普通值参数?

不能修改调用方持有的原变量。普通值进入 Arguments 后是值副本;只有参数本身指向可变对象,或者参数是包含可变对象的引用类型时,才有对应的写回效果。

Run 和 Return 可以同时使用吗?

可以。Run 负责调用时的参数副作用,Return 负责方法返回值,二者组合正适合模拟“填充指针并返回错误”的接口。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go Gock 怎样只模拟指定主机的 HTTP 请求Go Gock 怎样只模拟指定主机的 HTTP 请求
上一篇
Go Gock 怎样只模拟指定主机的 HTTP 请求
Go Gock 怎样为自定义 http.Client 安装拦截器
下一篇
Go Gock 怎样为自定义 http.Client 安装拦截器
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    773次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    770次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    852次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    821次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    651次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码