首页/新闻资讯/正文详情

用 Sourcery 的 Diffable 模板生成精确到属性级别的测试差异输出

发布时间:2026/9/26 8:05:41 来源:云帆数科 栏目:资讯中心
用 Sourcery 的 Diffable 模板生成精确到属性级别的测试差异输出
代码生成开发工具【免费下载链接】SourceryMeta-programming for Swift, stop writing boilerplate code.项目地址https://gitcode.com/gh_mirrors/so/Sourcery点击查看免费下载Sourcery 是 Swift 的元编程工具用于自动生成样板代码。本文围绕仓库 guides/Diffable.md 中讲解的 Diffable 模板展开说明如何让测试断言失败时的输出从一整面墙的文本变成精确到属性级别的差异报告。读完本文你将掌握 Diffable 模板的作用原理、AutoDiffable协议的使用方式、skipEquality注解的用法以及差异结果的输出格式与底层实现。Diffable 模板解决的问题很具体在测试中做相等性比较时一旦失败默认的断言输出往往是整块对象的文本描述难以快速定位到底是哪个属性不一致。Diffable 模板基于 SourceryRuntime/Sources/Common/Diffable.swift 中提供的Diffable协议与DiffableResult容器为遵循AutoDiffable协议的类型自动生成diffAgainst(_:)方法从而输出按属性逐项对比的精确差异。一、模板要解决的痛点在测试中对比两个对象是否相等是常见操作但默认输出对排查问题并不友好。例如一个包含多个属性的模型对象断言失败时只会输出整个对象的一整段文本开发者需要在一大段文字里人工比对属性值。Diffable 模板的目标就是把这种体验从读一整面墙的文本before改造成精确到属性级别的差异after失败信息会逐条列出哪个属性、期望值是什么、实际值是什么甚至能定位到数组中的下标或字典中的键。二、Diffable 模板与 AutoDiffable 协议模板的完整实现位于 Sourcery/Templates/Diffable.stencil它遍历所有实现AutoDiffable协议的类型为每个类型生成一个diffAgainst(_ object: Any?) - DiffableResult扩展方法。模板核心逻辑如下{% for type in types.implementing.AutoDiffable|!protocol|!annotated:skipDiffing %} extension {{ type.name }}: Diffable { public func diffAgainst(_ object: Any?) - DiffableResult { let results DiffableResult() guard let castObject object as? {{ type.name }} else { results.append(Incorrect type expected: {{ type.name }}, received: \(Swift.type(of: object))) return results } {% for variable in type.storedVariables|!annotated:skipEquality %} results.append(contentsOf: DiffableResult(identifier: {{ variable.name }}) .trackDifference(actual: self.{{ variable.name }}, expected: castObject.{{ variable.name }})) {% endfor %} return results } } {% endfor %}生成逻辑可以拆解为四步筛选类型从types.implementing.AutoDiffable中取出所有实现AutoDiffable协议的类型并通过!protocol排除协议本身、通过!annotated:skipDiffing排除打了skipDiffing注解的类型。类型校验先把待比较对象强转为目标类型类型不匹配时直接记录Incorrect type expected: 类型名, received: 实际类型并返回保证后续属性访问安全。逐属性对比遍历该类型的所有storedVariables存储属性排除带skipEquality注解的属性对每个属性用DiffableResult(identifier: 属性名)包裹再调用trackDifference(actual:expected:)记录差异。汇总返回把每个属性的差异结果拼接进总的DiffableResult返回给调用方。其中AutoDiffable是一个幽灵协议phantom protocol只起标记作用实现位于 SourceryRuntime/Sources/Common/AST/PhantomProtocols.swiftprotocol AutoDiffable {}它不声明任何方法纯粹作为模板筛选的标签。只要让你的类型遵守它模板就会为该类型生成Diffable实现。仓库自身的运行时类型也大量使用这一约定例如 Modifier.swift 和 Attribute.swift 都同时声明了AutoDiffable与Diffable。从源码结构看运行时自身的模型也通过同一个模板机制生成差异代码只是生成产物已预编译进框架。三、可用的注解模板暴露了两个注解来控制生成行为skipEqualityskipEquality用于跳过某个属性的比较。模板在遍历storedVariables时用!annotated:skipEquality将其过滤掉注解直接写在属性声明的上方注释里struct User: AutoDiffable { let id: Int // sourcery: skipEquality let lastLoginDate: Date? }生成后的diffAgainst只比较idlastLoginDate被排除在外适合忽略时间戳、缓存、内部状态等不稳定属性。该注解的解析逻辑由 SourceryFramework/Sources/Utils/AnnotationsParser.swift 负责相关解析用例可参见 SourceryTests/Parsing/Helpers/AnnotationsParserSpec.swift。skipDiffingskipDiffing用于跳过整个类型的生成。模板在第一行的类型筛选中通过!annotated:skipDiffing排除带此注解的类型适合不想让某个遵守AutoDiffable的类型生成代码的场景例如由其他模板接管。四、DiffableResult 差异容器与输出格式生成的方法返回DiffableResult这是 SourceryRuntime/Sources/Common/Diffable.swift 中定义的一个容器类负责收集并格式化差异信息。核心输出格式由其description决定Diffable.swiftpublic override var description: String { guard !results.isEmpty else { return } var description \(identifier.flatMap { \($0) } ?? ) description.append(results.joined(separator: \n)) return description }即先输出identifier属性名作为前缀再把该属性下收集到的所有差异条目用换行符连接。聚合后的整体输出形如localName expected: Bar, received: Foo其中localName是属性标识expected: Bar, received: Foo是差异条目。整个DiffableResult遵守AutoEquatable且自带hash、isEqual实现并声明了skipEquality、skipJSExport注解说明它自身同样由 Sourcery 的自动代码生成体系维护Diffable.swift。trackDifference系列方法是差异对比的核心针对不同数据结构有不同重载Diffable.swift标量值直接比较Equatable值不等时记录expected: 期望值, received: 实际值可空值把nil显示为字面量nil再按标量规则比较嵌套 Diffable 对象调用对象的diffAgainst递归展开让差异深入对象内部数组先比较数量数量一致时逐下标比较差异条目带idx N:前缀便于定位元素位置字典先比较数量再逐键比较缺失的键会被汇总为Missing keys:列表差异条目带key K:前缀NSObject 字典针对NSObjectProtocol值提供基于isEqual的字典比较重载。这些重载的行为都有对应的单元测试覆盖位于 SourceryTests/Models/DiffableSpec.swift测试断言了以下典型输出标量不等expected: 5, received: 3嵌套对象属性不等localName expected: Bar, received: Foo数组数量不等Different count, expected: 2, received: 1数组元素不等idx 1: localName expected: Foo2, received: Foo字典键缺失Different count, expected: 2, received: 1\nMissing keys: Something字典值不等key Something: localName expected: Bar, received: FooBar。五、在测试中的接入方式接入流程分三步让模型遵守AutoDiffable在源码类型声明处加上协议例如struct User: AutoDiffable并按需用skipEquality注解排除不稳定属性。运行 Sourcery 生成代码把Sourcery/Templates/Diffable.stencil加入模板路径通过命令行或.sourcery.yml配置生成到测试目标。命令行为例./bin/sourcery --sources 源码目录 --templates Diffable.stencil 所在目录 --output 生成目录也可以改用.sourcery.yml配置文件指定sources、templates、output三组路径详见 README.md。在测试断言中使用diffAgainst断言失败时调用actual.diffAgainst(expected)把返回的DiffableResult的description写入失败信息即可得到属性级别的精确差异。生成的代码是普通扩展可以放进测试 target 的生成目录不影响运行时行为。由于模板只遍历storedVariables计算属性不会参与对比同时比较的是实际值 vs 期望值actual在前、expected在后阅读输出时注意方向。六、小结Diffable 模板把测试中的对象对比从整块文本墙升级为逐属性差异报告以AutoDiffable幽灵协议作为生成开关用skipEquality和skipDiffing两个注解控制粒度由DiffableResult统一收集格式化差异并对标量、可空值、嵌套对象、数组、字典提供了递归对比能力。模板文件在 Sourcery/Templates/Diffable.stencil运行时支持在 SourceryRuntime/Sources/Common/Diffable.swift行为验证在 SourceryTests/Models/DiffableSpec.swift这三处共同构成了从模板到运行时的完整链路。赞分享代码生成开发工具【免费下载链接】SourceryMeta-programming for Swift, stop writing boilerplate code.项目地址https://gitcode.com/gh_mirrors/so/Sourcery点击查看免费下载相关推荐Android-Next核心组件完全解析高效Android开发的秘密武器Android Next核心组件完全解析高效Android开发的秘密武器 Android Next是一套功能强大的Android公共组件库专为提升开发效率和开发工具Java-AES-Crypto密码学基础避免Android加密的7个常见错误Java AES Crypto密码学基础避免Android加密的7个常见错误 在Android应用开发中数据安全是至关重要的环节。Java AES CrypSourcery自动化测试策略确保生成代码的可靠性Sourcery自动化测试策略确保生成代码的可靠性 Meta Programming元编程技术能显著减少重复性代码工作但生成代码的可靠性验证一直是开发中代码生成开发工具上一篇GHelper华硕笔记本终极轻量控制工具告别Armoury Crate臃肿体验下一篇gitness 许可证头批量回填insert-license-headers.sh 用法与实现原理深度解析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关推荐

Claude Code /powerup 教程:18 个官方互动课程全解析与 TaoToken 配置骨架
Claude Code /powerup 教程:18 个官方互动课程全解析与 TaoToken 配置骨架

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 8:05:35

电动汽车充电负荷仿真:蒙特卡洛模拟与Matlab实现全解析
电动汽车充电负荷仿真:蒙特卡洛模拟与Matlab实现全解析

做电动汽车充电负荷仿真的活儿看着简单,里头的门道其实不少。很多人手里拿到一片区域的EV规模数据后,第一反应是用“平均功率车辆数”直接画一条直线,但真实充电行为完全是随机事件——几点插枪、充多久、用快充还是慢充,每辆车都… · 2026/9/26 8:05:23

yolo26 语义分割特征融合:全网首发--使用 PSFM 模块改进 Neck 多尺度特征融合能力 ✨
yolo26 语义分割特征融合:全网首发--使用 PSFM 模块改进 Neck 多尺度特征融合能力 ✨

1. 工程简介 🚀 本工程基于 Ultralytics 框架扩展,面向语义分割与 YOLO 系列模型改进实验。核心特点是通过切换 yaml 配置文件,即可快速完成不同网络结构的训练、对比与验证,无需为每个模型单独编写训练脚本。 当前已支持的主要模型家族 🧩 语义分割模型:UNet、UNet+… · 2026/9/26 8:05:23

以沟通为核心线索的CRM设计:从理念到落地实践
以沟通为核心线索的CRM设计:从理念到落地实践

1. 整体设计:为什么要把"沟通"作为CRM的核心线索1.1 名字背后的产品思路DeskcommCRM,拆开看就是Desk(桌面)、Comm(Communication,沟通)和CRM的组合。这个名字不是拍脑袋起的&#xff… · 2026/9/26 8:45:55

从“形式审查”到“开放协作”:代码审查流程改造实践
从“形式审查”到“开放协作”:代码审查流程改造实践

1. 为什么我最终把代码审查做成了"开放式"的 先说一个我自己踩出来的结论: 代码审查这件事,开放程度决定了它到底是质量保障手段,还是团队内耗源头。 早年我在一家小团队带项目,代码审查基本靠"领导抽检后端互看… · 2026/9/26 8:45:55

Open Code Review 流程设计:从评审对象到反馈闭环的落地实践
Open Code Review 流程设计:从评审对象到反馈闭环的落地实践

代码审查这件事,我在不同团队里见过完全相反的两种状态。有的团队每次合并代码前,reviewer 都要在群里被 好几轮,最后丢一句“看着没问题,合吧”;也有的团队能把一次 MR 讨论出十几个高质量评论,顺带把新人… · 2026/9/26 8:45:55

基于OpenReview数据的ICLR投稿分析:从API抓取到词云可视化
基于OpenReview数据的ICLR投稿分析:从API抓取到词云可视化

1. 从投稿数据里挖出会议的真实偏好 ICLR 这几年的投稿量涨得有点离谱。2023 年还不到五千篇,2024 年直接冲到七千多,2025 年据说又创新高。我连续三年盯着 OpenReview 上的公开数据做了一些统计,本来只是想看看自己投的领域竞争有多激烈&… · 2026/9/26 8:45:55

Atlas 300V 24G是推理加速卡吗?昇腾NPU部署YOLO全解析
Atlas 300V 24G是推理加速卡吗?昇腾NPU部署YOLO全解析

早上刷到一条热搜,问“atlas 300v 24g 是运算加速卡吗”。底下回答五花八门,有人说它是显卡,有人说不能玩游戏,还有人说买回来装不上Windows。这些说法都沾点边,但都没说到点上。 这个问题背后,其实是一个… · 2026/9/26 8:45:55

OCR识别与条码预处理技术实践指南
OCR识别与条码预处理技术实践指南

我无法根据您提供的输入内容生成符合要求的博文。 原因如下: 项目正文为空,关键词为空,摘要描述为空; 所有可用信息仅剩标题“清雨剑挑码助手2015资源下载:提升工作效率的强大助手”及热搜词“清雨剑挑码助手2015”… · 2026/9/26 8:45:49

数据库课后习题答案别硬背:当测试用例集刷,效率翻倍
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍

简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21

OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40

向下兼容与向上兼容:接口设计中的兼容性策略与工程实践
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践

一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码