8.2 彩排与监视器:测试调试与风格


8.2 彩排与监视器:测试、调试与风格

本节摘要:本节是全剧最后一场:用 XCTest 给核心逻辑写单元测试(含错误分支与内存行为),用断点与内存图做工程化排障,最后用一张按界面生命周期组织的终检清单复盘全册。结论先行:测试锁行为、调试省猜测、清单防回归——三件事都是给"下一次修改"买的保险。

工作台(8.1 节)搭好,最后开两间房:彩排场(测试)与监控室(调试),再发一张巡演手册(风格与清单)。

彩排场:XCTest 单元测试

测试类的骨架是 setUp 准备、断言验证、tearDown 收拾。给第3章的排片模型写第一组测试:

import XCTest // 测试目标来自 8.1 节的模型包(示意引用) final class SessionTests: XCTestCase { var session: Session! // 隐式解包:setUp 里必赋值(1.2 节说过的"编译器已证明"场景) override func setUp() { // 每个测试方法前都会跑一遍,彼此隔离 super.setUp() session = Session(filmName: "雾中灯塔", hall: .imax, minutes: 118, seatsLeft: 4) } func testIsAlmostFull_TrueWhenSeatsLow() { XCTAssertTrue(session.isAlmostFull, "余票 4 座应判定为告急") // 断言 + 失败说明 } func testSummary_ContainsHallName() { XCTAssertTrue(session.summary().contains("IMAX"), "摘要应包含厅名") } }

断言家族按用途选:XCTAssertTrue 验布尔、XCTAssertEqual 验相等(带精度参数验浮点)、XCTAssertNil 验可选、XCTAssertThrowsError 验抛错。错误分支必须测——2.3 节的每条失败路径都值得一个测试:

final class BoardErrorTests: XCTestCase { func testLoadBoard_ThrowsUnauthorized_WhenTokenEmpty() { XCTAssertThrowsError(try fetchBoard(token: "")) { error in guard case BoardError.unauthorized = error else { // 模式匹配验证错误类型 return XCTFail("应为未登录错误,实际 \(error)") } } } func testDecode_MapsFieldError() { // 2.3 节的 decodeFailed 分支:坏数据进来要抛带字段名的错误 XCTAssertThrowsError(try JSONDecoder().decode(SessionDTO.self, from: Data("{}".utf8))) } }

内存行为也能测——第4.4 节的泄漏实验升级成自动断言(XCTestExpectation 等待 deinit 回调,或在 addTeardownBlock 里检查弱引用已置 nil)。测试跑法:Cmd+U 跑当前 Scheme 的全部测试,报告逐条列出绿红。

⚠️ 常见坑:只测成功路径。失败分支、边界值(0 座、空数组、超长字符串)才是回归的重灾区——错误处理写了不测,等于安全气囊没装在车上。

监控室:断点、变量与内存图

调试三件套从轻到重:

print 大法适合临时看一眼,但断点更专业:在行号点一下出现蓝色标记,运行到该处暂停,底部调试区直接查看每个变量的当前值;po 命令(print object)能在控制台对当前上下文求任意表达式。条件断点(右键断点 → 编辑条件)适合"循环第 38 次才出错"的场景——只有条件成立才停,不用手按 37 次继续。

视图层级调试:运行时点调试栏的"视图层级"按钮,界面以三维爆炸图展开, misplaced 的约束、被遮挡的按钮一眼现形——UIKit 排版问题的定位效率提升一个数量级。

内存图:调试栏的内存图按钮打开对象关系图,按类名搜"排片页",能看到有几个实例、各自被谁引用。第4.2 节的循环引用实验在这里工程化:反复进出页面后内存图里若出现两个 BoardViewController 实例,点开它的引用链,环就画在图上。配合 Instruments(模板选 Leaks)可长时间观测泄漏曲线。

排障路径速查: 界面不动了 → 疑死锁:暂停按钮看主线程栈,是否停在 sync 派发(5.1 节事故) 对象不释放 → 内存图搜类名,看引用链成环位置(4.2 节) 界面数据不对 → 断点停在状态写入处,逐变量核对(7.1 节链路) 解析失败 → 断点在 do-catch 的 catch,po error 看详情(2.3 节)

巡演手册:代码风格与终检清单

风格约定不用从零发明:官方 API 设计准则与社区流行的格式约定(缩进、逗号尾缀、访问级别顺序)足够起步,关键是团队一致。三条对教程读者立刻有用的约定:优先 let(1.2 节)、类型当文档用(可选值与 throws 写进签名)、方法不超过一屏(超了就拆,第2.1 节的嵌套函数是拆法之一)。

最后的终检清单,按界面的一生把全册要点归位——这张表本身就是教程的复习地图:

生命周期时刻 必查项 出处
开演前(编译期) 类型标注完整、let 优先、无强制解包散落 第1章
出生(init/加载) 可选值建模数据未到位、错误枚举覆盖失败分支 第1、2章
登台(appear) 异步任务持柄、回调 weak 守卫 第5、4章
演出中(交互) 状态经单一通道修改、private(set) 收窄写权限 第3章
同台(并发) 后台干活主线程刷新、QoS 已标注 第5章
进后台 轮询已停、通知已撤 第7章
退场(disappear) 任务已 cancel、定时器已 invalidate 第5、7章
谢幕(deinit) 无循环引用、deinit 打印可观测 第4章
复盘(工程) 测试覆盖错误分支、依赖走 SPM、风格一致 第8章

💡 关键直觉:测试、调试、清单的共同身份是"把知识固化成可重复执行的检查"。人的记性会衰减,清单不会——终幕交给你的不是三条技巧,而是对抗遗忘的机制。

全剧终

  • 测试:setUp 隔离、断言家族按用途选、错误分支必测、内存行为可自动验证;
  • 调试:断点与 po 做现场勘查、条件断点对付偶发、视图层级解排版、内存图抓环;
  • 风格:let 优先、签名即文档、方法一屏内、团队一致高于个人偏好;
  • 终检清单按生命周期组织,既是质检表也是全册复习地图。

从序幕的第一句 print 到终幕的测试报告,排片界面的一生演完了。剧场的灯亮起,观众散场,而你已知道这座剧场每一块地板下藏着什么——下一次大幕拉开,导演是你。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U