ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

深入解析 mockery 多模板渲染:同一 Go 接口生成多套 Mock 的机制与实践

深入解析 mockery 多模板渲染:同一 Go 接口生成多套 Mock 的机制与实践 开发工具代码生成测试【免费下载链接】mockeryA mock code autogenerator for Go项目地址https://gitcode.com/gh_mirrors/moc/mockery点击查看免费下载Mockery 的本质是一个 Go 接口代码生成框架其核心能力之一是针对同一个接口使用不同模板渲染出不同风格的 Mock 实现。仓库中的 internal/fixtures/multi_template/ 目录正是为验证这一能力而设计的测试夹具它用同一个Foo接口同时生成了matryer风格与testify风格两套 Mock并在测试中混用它们。读完本文你将掌握 mockery 内置模板的切换配置、两种模板产物的结构差异、模板选择背后的源码调用链以及如何通过template配置参数扩展出自定义风格的生成代码。1. multi_template 测试夹具的整体设计该夹具位于 internal/fixtures/multi_template/其 README.md 用一句话点明了存在意义This package tests that mockery can render different templates for the same interface.即验证 mockery 能够为同一个接口渲染出不同的模板实现。目录内的文件分工非常清晰文件角色interface.go被 mock 的目标接口定义mocks_matryer_multitemplate_test.gomatryer模板生成的 Mock生成结果已提交入库mocks_testify_multitemplate_test.gotestify模板生成的 Mock生成结果已提交入库interface_test.go同时使用两套 Mock 的测试用例被 mock 的接口非常简单只有一个方法方便对比两套模板在同样输入下的输出差异package multitemplate type Foo interface { Bar() string }2. 两种内置模板的产物对比对同一个Foo接口mockery 分别生成了MockMatryerFoo与MockTestifyFoo两个结构截然不同的 Mock。这正是template配置参数决定生成形态的直观证据。2.1 matryer 模板基于函数字段的 Mockmocks_matryer_multitemplate_test.go 的头部注释明确标注了生成来源// github.com/vektra/mockery与// template: matryer。该风格借鉴了 matryer/moq 项目该项目已并入 mockery其核心设计是用函数字段承载方法实现type MockMatryerFoo struct { // BarFunc mocks the Bar method. BarFunc func() string // calls tracks calls to the methods. calls struct { // Bar holds details about calls to the Bar method. Bar []struct { } } lockBar sync.RWMutex }关键特征直接注入函数使用方通过给BarFunc字段赋函数来定制行为无需任何断言机制调用追踪每次调用Bar()时方法先把调用信息追加进calls.Bar再执行BarFunc并使用sync.RWMutexlockBar保护并发安全调用记录查询BarCalls()返回历史调用列表可供断言使用。生成的Bar()实现体现了先记录、后执行的调用链func (mock *MockMatryerFoo) Bar() string { if mock.BarFunc nil { panic(MockMatryerFoo.BarFunc: method is nil but Foo.Bar was just called) } callInfo : struct{}{} mock.lockBar.Lock() mock.calls.Bar append(mock.calls.Bar, callInfo) mock.lockBar.Unlock() return mock.BarFunc() }2.2 testify 模板基于 Expecter 的 Mockmocks_testify_multitemplate_test.go 则基于 testify 生态头部同样标注// template: testify。其核心设计是通过期望匹配expectation matching驱动行为type MockTestifyFoo struct { mock.Mock } type MockTestifyFoo_Expecter struct { mock *mock.Mock }关键特征构造器NewMockTestifyFoo(t)接收*testing.T自动注册mock.Mock.Test(t)并通过t.Cleanup在测试结束时自动执行AssertExpectations校验所有期望是否被满足Expecter 链式 APIEXPECT().Bar().Return(bar)声明期望Run/Return/RunAndReturn提供类型安全的链式设置返回值断言Bar()通过_mock.Called()收集调用参数与返回值若未配置期望会panic(no return value specified for Bar)。2.3 同一测试中混用两套 Mockinterface_test.go 演示了两套 Mock 可以在同一个测试函数中并存、各司其职func TestFoo(t *testing.T) { testifyMock : NewMockTestifyFoo(t) testifyMock.EXPECT().Bar().Return(bar) assert.Equal(t, bar, testifyMock.Bar()) matryerMock : MockMatryerFoo{ BarFunc: func() string { return bar }, } assert.Equal(t, bar, matryerMock.Bar()) }这段测试同时验证了两种风格的使用范式testify 风格声明期望→调用→自动断言matryer 风格注入函数→调用→人工校验返回值。这也证明 mockery 生成的两套 Mock 互不干扰可以出现在同一包内注意结构体名不同因此不存在命名冲突。3. 模板选择背后的源码实现为什么同一个接口能渲染出两套代码答案在模板生成器的实现中。3.1 内置模板的注册表internal/template_generator.go 通过//go:embed将两个模板及其 JSON Schema 编译进二进制并以 map 形式注册L34-L55var styleTemplates map[string]string{ matryer: templateMatryer, testify: templateTestify, } var jsonSchemas map[string]string{ matryer: templateMatryerJSONSchema, testify: templateTestifyJSONSchema, }模板源文件分别是 internal/mock_matryer.templ 与 internal/mock_testify.templ两者都用 Gotext/template语法书写开头是boilerplate-file、mock-build-tags等可选片段随后遍历.Interfaces与每个接口的.Methods输出对应代码结构。两套模板对同一份template.Data做不同渲染就得到了风格迥异的 Mock。3.2 getTemplate 的解析优先级TemplateGenerator.getTemplateinternal/template_generator.go按以下顺序解析template配置以file://、https://、http://开头的值走远程/文件模板加载NewRemoteTemplate负责下载带缓存否则在内置styleTemplatesmap 中按名字查找找不到即报错template name does not exist。也就是说template: testify命中的是嵌入的templateTestify而template: file://./my.tmpl则加载自定义模板文件。3.3 生成主流程Generate方法internal/template_generator.go完整展示了多模板渲染的流水线从template.Registry中LookupInterface查找接口对每个方法调用methodData提取参数、返回值与类型信息含GetReplacement类型替换支持组装template.NewInterface连同包级配置一起构造template.NewDatagetTemplate取出目标模板字符串与 JSON SchemavalidateSchema校验模板数据是否符合 Schematempl.Execute执行模板渲染g.format按formatter配置gofmt/goimports/noop对内存中的产物做格式化后输出。这套流程对任何模板一视同仁正是同一接口、不同模板得以成立的基础。4. 如何配置与切换模板模板选择完全由配置参数驱动。在 docs/configuration.md 的参数表中template选择要渲染的模板。内置可选值为testify默认与matryer也支持file://、https://、http://前缀指向自定义模板见 docs/template/index.mdtemplate-datamap[string]any向模板传入任意选项不同模板接受的键集合不同template-schema模板数据 JSON Schema 的 URL默认值为{{.Template}}.schema.json即自动在模板路径后追加.schema.json来发现 Schemarequire-template-schema-exists默认true若 Schema 下载失败则 mockery 报错设为false则跳过 Schema 校验formatter生成产物的格式化方式取值gofmt/goimports/noop。例如在配置文件中声明template: matryer或在配置中同时使用template-data为模板提供附加信息。这些参数的默认值定义在 config/config.go 的DefaultConfig中Template: addr(testify)、RequireTemplateSchemaExists: addr(true)、TemplateData: map[string]any{}并通过ParseTemplates支持模板变量递归展开如{{.InterfaceFile | base}}、{{.Template}}等模板数据来自config.TemplateData结构体参见 config/config.go。5. 如何复现与验证该夹具的测试以*_test.go形式与生成产物同目录存放可直接运行验证cd internal/fixtures/multi_template go test ./...TestFoo会分别构造MockTestifyFoo与MockMatryerFoo并断言两者行为一致。若想亲身体验同一接口、两套模板的生成过程可以在项目中用 mockery 对Foo分别指定template: testify与template: matryer生成到不同文件或直接参考本夹具中被提交的两份生成产物作为对照基线——它们本身就是多模板渲染能力的可运行证据。6. 小结multi_template 夹具虽小却浓缩了 mockery 最核心的设计理念mockery 是模板渲染引擎而非单一风格的 mock 工具。通过 internal/template_generator.go 中嵌入模板注册表与统一的Generate流水线template配置参数可以自由切换内置的matryer、testify风格甚至接入file://、https://自定义模板同一接口因此可以产出函数注入式、期望匹配式乃至任意自定义形态的代码。无论是日常测试还是基于接口的代码生成框架二次开发理解这套模板即生成策略的机制都是掌握 mockery 的关键一步。赞分享开发工具代码生成测试【免费下载链接】mockeryA mock code autogenerator for Go项目地址https://gitcode.com/gh_mirrors/moc/mockery点击查看免费下载相关推荐LeetCode-Go 的 README 自动生成机制template.markdown 模板与 Go 渲染链路深度解析LeetCode Go 的 README 自动生成机制template.markdown 模板与 Go 渲染链路深度解析 本文以 ctl/template/t示例工程深入理解 Mockery 的 testify 模板从配置到生成的 Mock 代码全解析深入理解 Mockery 的 testify 模板从配置到生成的 Mock 代码全解析 导读 testify 模板是 mockery 内置的、最常用的 moc开发工具代码生成测试go-echarts 渲染机制深度解析Renderer 接口、RenderSnippet 与自定义渲染器实战go echarts 渲染机制深度解析Renderer 接口、RenderSnippet 与自定义渲染器实战 本篇技术指南以 go echarts 官方渲染文数据可视化后端上一篇gpui-kit Accordion 组件完全指南折叠面板的构建、交互与源码剖析下一篇Wasp 应用自托管部署到 Caprover 完整指南GitHub Actions GHCR 全自动 CI/CD创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表