📸 SnapshotPreviews

一个构建在 Xcode 预览之上的全功能快照测试解决方案。自动生成可浏览的预览图库,并且无需编写代码即可使用 XCTest 生成快照。支持使用 PreviewProvider#Preview 的 SwiftUI 和 UIKit 预览,并适用于所有 Apple 平台 (iOS/macOS/watchOS/tvOS/visionOS)。

特性

预览图库

PreviewGallery 是一个构建在快照提取之上的交互式 UI。它将你的 Xcode 预览变成一个组件和功能图库,你可以从你的应用程序中访问,例如在内部设置屏幕中。无需 Xcode 即可查看预览。 你可以使用它来预览单个组件(按钮/行/图标等),甚至整个交互式功能。

PreviewGallery 的公共 API 是一个名为 PreviewGallery 的 SwiftUI View。显示此视图可让你访问完整的图库。例如,你可以添加一个按钮来打开图库,如下所示:

import SwiftUI
import PreviewGallery

struct InternalSettingsView: View {
  var body: some View {
    NavigationStack {
      Form {
        Section("Previews") {
          NavigationLink("Open Gallery") { PreviewGallery() }
        }
      }
    }
    .navigationTitle("Internal Settings")
  }
}

本地快照生成

在 XCTest 中,无需编写任何代码即可为每个 Xcode 预览生成 PNG。将你的 XCTest 目标链接到 SnapshottingTests,并创建一个继承自 SnapshotTest 的测试,如下所示:

import SnapshottingTests

class DemoAppPreviewTest: SnapshotTest {

  // Return the type names of previews like "MyApp.MyView._Previews" to selectively render only some previews
  override class func snapshotPreviews() -> [String]? {
    return nil
  }

  // Use this to exclude some previews from generating
  override class func excludedSnapshotPreviews() -> [String]? {
    return nil
  }
}

请注意,没有测试函数;它们在运行时由 SnapshotTest 自动添加。你可以从 snapshotPreviews() 函数返回预览列表,基于你想要在本地验证的预览。快照将被添加为 Xcode 测试结果中的附件。

注意

当你使用 Preview 宏 (#Preview("Display Name")) 时,快照的名称使用文件路径和名称,例如:"MyModule/MyFile.swift:Display Name"

Screenshot of Xcode test output

EmergeTools 快照测试服务 在云端生成快照并进行差异比较,以控制不稳定因素的来源,将图像存储在 git 之外,并优化测试性能。SnapshotTest 用于在本地调试这些快照测试。你还可以使用 PreviewLayoutTest 来获取单元测试中所有预览的代码覆盖率,而无需生成 PNG。这将验证预览不会崩溃(例如缺少 @EnvironmentObject),但运行速度更快,因为它不会将视图渲染为图像。

可访问性审核

Xcode 可访问性审核 也可以在本地对任何预览运行。它们在 UI 测试(而不是单元测试)中运行。要启用这些,请继承自 AccessibilityPreviewTest。要自定义行为,你可以在你的测试中覆盖以下函数:

import SnapshottingTests
import Snapshotting

class DemoAppAccessibilityPreviewTest: AccessibilityPreviewTest {

  override func auditType() -> XCUIAccessibilityAuditType {
    return .all
  }

  override func handle(issue: XCUIAccessibilityAuditIssue) -> Bool {
    return false
  }
}

有关完整的示例,请参阅演示应用程序。

它是如何工作的?

XCTest 通过使用 Objective-C 运行时创建函数并覆盖 XCTest 的 testInvocations 函数来动态插入测试函数。

通过解析 __swift5_proto Mach-O 部分来发现二进制文件中的预览,以查看哪些类型符合 PreviewProvider(以及由 #Preview 宏生成的类似协议)。有关这在 Swift 运行时中如何工作的详细信息,请参阅我们的博客文章

安装

使用此存储库的 URL (https://github.com/EmergeTools/SnapshotPreviews) 将包依赖项添加到你的 Xcode 项目。

将你的应用程序链接到 PreviewGallery 和(可选)SnapshotPreferences,以自定义快照生成行为。将你的 XCTest 目标链接到 SnapshottingTests

提示

唯一的名称

强烈建议为每个预览使用显示名称,例如:

struct MyView_Previews: PreviewProvider {
  var previews: some View {
    MyView().previewDisplayName("My Display Name")
    // Note if you had more than one view here they should all have different display names.
  }
}

#Preview("My Display Name") {
  MyView()
}

显示名称将显示在 XCTest 结果和 EmergeTools UI 中。显示名称在每个 PreviewProvider 中或在预览宏的文件中应该是唯一的。

环境变量

建议在你的单元测试 scheme 中将环境变量 EMERGE_IS_RUNNING_FOR_SNAPSHOTS 设置为 1。当从 EmergeTools 快照测试服务生成快照时,也会设置此变量。像这样将其与 Xcode 预览变量结合使用:

extension ProcessInfo {
  var isRunningPreviews: Bool {
    environment["EMERGE_IS_RUNNING_FOR_SNAPSHOTS"] == "1" || environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1"
  }
}

检查 ProcessInfo.isRunningPeviews 以禁用你不希望在预览中出现的行为,例如发出日志数据。

变体

提示

使用 PreviewVariants 可以大大简化快照测试,方法是确保一组一致的变体,并确保为每个视图提供一个名称。

使用同一视图的多个变体可以确保测试覆盖用户与你的 UI 交互的所有方式。 大部分由 SwiftUI 提供,例如:.dynamicTypeSize(.xxxLarge)。 包中内置了一个:.emergeAccessibility(true)。 此函数将旁白元素的 可视化添加到您的快照。 您可以使用示例应用程序中演示的PreviewVariants视图自动添加变体。它添加了RTL、横向、辅助功能、深色模式和大文本变体。您可以这样使用它

struct MyView_Previews: PreviewProvider {
  static var previews: some View {
    PreviewVariants(layout: .sizeThatFits) {
      MyView(mode: .loaded)
        // PreviewVariants requires that every view has a name, so you can’t create one without a display name
        .previewVariant(named: "My View - Loaded")
      
      MyView(mode: .loading)
        .previewVariant(named: "My View - Loading")
      
      MyView(mode: .error)
        .previewVariant(named: "My View - Error")
    }
  }
}

Star History

Star History Chart

相关阅读