大多数浏览器和
Developer App 均支持流媒体播放。
-
使用 XCTIssue 分类测试失效
让你的测试失效正常运行:了解如何在 Xcode 中使用最新测试 API 对 app 中未监测到的问题进行分类和诊断。我们将向你展示如何简化测试工作流程,并结合故障原因帮助你推送优质产品。 若想了解如何设计你的测试,从而改进分类,请观看 “编写失效测试”。 若想了解 Xcode 测试工作流程的最新改进,请观看“更快地获取测试结果”、“处理 UI 测试中的中断和警报问题”以及“XCTSkip 你的测试”。
资源
相关视频
WWDC20
-
下载
(你好 WWDC 2020) 大家好 欢迎参加 WWDC (使用 XCTISSUE 分类测试失效) 大家好 我叫 Wil 从事测试、自动化和 XCode 工作 本次讲座中 我们将了解一下 API 和其他改进 以及如何调查项目中的测试失效 测试失效的调查 是维护任何活动测试套件的最重要的部分 难以诊断的失效会耗费你的时间 耗费太多时间 在发布计划的错误时刻 它们甚至会导致产品出现漏洞 对于任何不断成长的项目 代码更改有时都会导致测试失效 或者在本地 或者在持续集成过程中 当它们真的失败时 你将会考虑以下问题 (读懂测试失效) 什么失效了? 是如何失效的? 原因是什么? 也许 最重要的是 在我的源代码中 失效发生在哪里? 在 Xcode 12 中 我们添加了新的 API 增强了测试失效报告的用户界面 以便更有效地回答这些问题
值得注意的是 这些问题的答案是如此重要 以至于需要举办一次专门讲座 来讨论 可以用来进一步改进这个过程的编码模式 有关这个问题的更多信息 参见《为失效编写测试》
我们把本次讲座的内容 组织安排为四个部分 Swift 测试错误、富文本失效对象、 调用测试失效堆栈和高级工作流 我们将一个一个依次探讨这些主题 但首先 让我们先来认识一下 在 Xcode 12 中失效是如何呈现的
我这里有一个小项目 叫 PlayGarden 这个项目我和我三岁的女儿一直在玩 PlayGarden 帮助我们 跟踪后院所有的植物、玩具和家具 现在 即使只有三岁 我女儿已经完全接受了测试驱动开发 所以 我们对表示花园中元素的 所有视图类进行测试也就不足为奇了 我们最近注意到 在这些测试中有很多重复代码 所以我们把它们重构成了一些实用工具 现在我要做一个测试 我已经引入了一个人为的失效 我们可以在 Xcode 12 中 看到它是如何呈现的 (测试失败) 你可能马上就会注意到 测试被标记为失败 但我们唯一能看到的注释是灰色的 这是在提示我们失效 发生在注释行下面的调用中 而不是该行本身 我们可以切换到问题导航器 来进一步研究这个问题
问题导航器显示在这里出现了测试失败 但它显示的不止于此 失效的下面是测试代码中的调用堆栈 如果我点击一个帧 源编辑器会把我带到那个位置 这里的注释是红色的 因为这才是实际的失效点 现在 如果我浏览剩下的帧 问题导航器和源代码编辑器 将带我浏览我的代码 从失效点返回 到测试中触发失效的地方 这有助于我 迅速读懂测试失效的所有情境 减少修复问题所需的时间 我们还有另一种方法来研究这些数据 我们切换到测试报告 这是研究测试失效的一种很好的方法 特别是 当你使用来自持续集成系统的结果包时
在我们最近一次测试运行的报告中 我们用红色标出了失效的测试 如果我们把它展开 我们就会看到失效消息以及 记录该信息的文件和行 我们再往下滚动一点
现在 我们可以看到 与问题导航器中相同的调用堆栈 这为我们提供了另一种研究代码的方法 你会注意到 当将鼠标悬停在调用堆栈中的一个帧上时 这两个按钮出现在了帧的右边 第一个是跳转按钮 它可以导航到源代码位置
我将返回到报告 以便我们可以研究第二个按钮 这是 Xcode 12 中的一个新功能 助理按钮会打开 测试报告旁边的一个辅助编辑器 该编辑器会显示参考源的位置 这让我们可以并排查看测试报告和源代码 同时我们可以使用与问题导航器 相同的方法来研究调用堆栈的失效
这就是我们在 Xcode 12 中 显示测试失效的情况 现在我想谈谈 在测试中使用 Swift 错误的问题
XCTest 支持 Swift 中 惯用编码模式的方法之一 是实现测试函数抛出 当测试确实抛出时 该错误就会被用来以公式表示失效消息 这意味着 不用像使用处理此类错误的样板文件 你的测试可以这样编写 更加清晰 但是 直到最近 这些失效还不能提供源代码位置 文件和行 而这通常是 XCTAssert 记录的测试失效的一部分 由于这种限制 一些开发者仍在使用空气处理样板文件 令人高兴的是 对Swift运行时间 iOS、tvOS 13.4 和 macOS 10.15.4 的改进 使得 XCTest 可以开始报告 抛出错误和测试的源代码位置了 这意味着 你可以为这些错误获得非常好的情境 而无需任何其他额外的处理代码 在改进源代码位置的同时 我们还添加了 API 这样在测试的设置和 清除中也可以获得同等级别的便利了 (从设置和清除中抛出) 这些新的 API 是 这里所示的原始设置和清除方法的变体
新的 setUpWithError 将在原始设置方法之前运行 而新的 tearDownWithError 将在原始的清除方法之后被调用 你将在为新测试文件提供的模板中 看到这两种方法
在同一个测试用例中 可能使用这两个 API 的变体 但通常 我们鼓励你 将测试切换到新的方法 除非你因为继承而需要保留旧方法 现在我想转换话题 谈谈富文本失效对象 (核心测试失效数据) XCTest 总是将失效记录为四个离散值 失效信息、文件路径 记录失效的行号 以及指示是否预料到失效的标志
预期失效是由 XCTAssert 记录的失效 意外失效通常表示 XCTest 捕获了 测试代码抛出的未处理异常
这些值由 XCTAssert 传递 到记录失效 API 中 确保记录了失效 并将失效泄露给 Xcode 进行显示 (富文本失效对象) 在 Xcode 12中 这些不同的值 由一个新的对象 XCTIssue 封装 此外 还有各种新的失效数据类型 显式计数方法、详细说明 潜在错误和附件
XCTAttachment 是一种 用于测试运行中捕获任意数据的 API 附件既可以添加到测试本身 也可以添加到 XCTContext 创建的活动中 还可以将附件添加到 XCTIssue 从而使自定义诊断 与测试失败相关联成为可能 (记录 XCTISSUE) XCTestCase 添加了 新的 API 用于记录测试失效 所有 XCTAssert 都可以 使用这个 API record(_issue:) 它可以直接被调用 甚至被覆盖 我们在之前的几张幻灯片上展示的 记录失效的 API 已被弃用 如果你要直接调用 recordFailure 或覆盖它来自定义失效记录 我们鼓励你 尽早更新到 record(_issue:)
使用 record(_issue:) 时 可能需要知道如何修改 XCTissue (修改问题) 在 Swift 中 使用 let 声明的问题是不可变的 使用 var 声明的问题允许你进行修改 在 Objective-C 中 XCTIssue 有一个不可变的子类 同时也符合 NS mutableCopy
XCTIssue 在许多方面增强了失效分类 但其调用堆栈可能是最具变革性的 本讲座开始时 我曾提出回答关于测试失效的 一个最重要的问题是“在哪里”? 这就是为什么核心测试失效数据 总是包含文件路径和行号的原因 在bill时捕获 使用编译器标记 如井号文件路径
单个源代码位置对于简单测试非常好 但是当测试代码被分解成由多个测试 共享的函数时 就没有那么有用了 请考虑一下这个例子
下面的两个测试都调用相同的共享函数 当出现失效时 注释将会出现在断言的旁边 测试方法本身就变得令人困惑 因为它被标记为失效 但并没有给出进一步的信息 来帮助开发者回答“在哪里”的问题
如果辅助函数可以捕获调用它的源位置 则可以缓解这种情况 并且明确地 在其 XCTAssert 调用中使用它 这样就改进了展示和测试方法 但是如果辅助函数有多个断言 那么二义性就会被简单地转移
幸运的是 XCTIssue 会捕获 并符号化表示调用堆栈 这意味着 在复杂的测试代码中有更多的失效情境
当我们捕获调用堆栈时 同样的失效是这样的 和我们之前在演示中看到的很像 回答“在哪里”的问题 是一种完全不同的体验 测试方法中的灰色注释表示 故障发生的行 帮助方法中的红色注释 突出显示失效本身 传递一个位置不需要额外的代码 所以你也不需要选择注释哪个位置 不需要额外的努力你就能做到两全其美
最后 我给大家展示一下由这些新 API 实现的一些更高级的工作流 首先 大家可以实现自定义断言 直接创建 XCTIssue 实例 并调用 record(_issue:) 下面是一个断言示例 它将验证一些数据 并将其作为附件 包含到它所记录的问题之中
在问题的初始创建中 我使用了 var 因为代码的其余部分 对结构进行了一些修改 我们还可以将所有信息 预先传递给更长的初始化器 但我认为这样读取更容易一些
接下来 我将把数据作为附件添加到问题中 这意味着数据将在 Xcode 测试报告中 与失效一起出现 使我能够在分类期间对其检查 并确定其验证失败的原因
在这里 我将捕获调用自定义断言的位置 这并非必需 但是可以得到清晰的展示 因为你从这里看到的代码不可能 对理解故障本身有所帮助
最后 我们将调用 record(_issue:) 它会记录问题并将其发送到 Xcode
我想向大家展示的另一个高级工作流 是如何覆盖 record(_issue:) 以观察、抑制 或修改测试类中记录的失效 这个方法是每个失效通过的漏斗点 覆盖可以完全控制测试类的输出
我们的第一个示例 覆盖 record(_issue:)来进行观察 重要的是调用 super 以确保问题沿着记录链继续 您还可以使用 XCTestObservationCenter 来观察问题 但是 如果你只想观察一个类中的失效 那么这里的方法是有用的
如果你的覆盖没有调用 super 你将抑制这个问题 您将不会沿着记录链继续 这将不会进行任何记录 也不会报告给 Xcode
修改是覆盖 record(_issue:) 的 最常见理由 这种模式使得添加附件成为可能 这可能成为很好的诊断助手 这里的示例显示 只添加一个简单的字符串附件 但是 API 却可以处理广泛的类型
我们的讲座到此结束 希望大家能记住几个要点 分类测试失效 是爱护你的测试套件最重要的部分之一 使用调用堆栈可以很容易地 跟踪代码中与失效关系最大的位置
反过来 这又在你的测试代码中支持更自然的模式 使你能够集中精力于 代码复用和其他好的做法 XCTIssue 还支持附件 允许你添加自定义诊断数据 帮助你回答测试失效的方式和原因 谢谢收看
(你好 WWDC 2020)
-
-
9:52 - Implement a custom test assertion using XCTIssue
func assertSomething(about data: Data, file: StaticString = #filePath, line: UInt = #line) { // Call out to custom validation function. if !isValid(data) { // Create issue, declare with var for mutability. var issue = XCTIssue(type: .assertionFailure, compactDescription: "Invalid data") // Attach the invalid data. issue.add(XCTAttachment(data: data)) // Capture the call site location as the point of failure. let location = XCTSourceCodeLocation(filePath: file, lineNumber: line) issue.sourceCodeContext = XCTSourceCodeContext(location: location) // Record the issue. self.record(issue) } }
-
11:12 - Override record(_ issue:) for observation
override func record(_ issue: XCTIssue) { // Observe, introspect, log, etc.: if shouldLog(issue) { print("I just observed an issue!") } // Don't forget to call super! super.record(issue) }
-
11:30 - Override record(_ issue:) to suppress failures
override func record(_ issue: XCTIssue) { // If you don't want to record it, just return. if shouldSuppress(issue) { return } // Otherwise pass it to super. super.record(issue) }
-
11:39 - Override record(_ issue:) to add an attachment
override func record(_ issue: XCTIssue) { // Redeclare using var to enable mutation. var issue = issue // Add a simple attachment. issue.add(XCTAttachment(string: "hello")) // Pass it to super. super.record(issue) }
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。