代码生成开发工具【免费下载链接】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),仅供参考
