Swift XCTest - 如何测试 TabView 中 tabItems 的属性?

问题描述 投票:0回答:2

考虑以下结构

struct MainView: View {
    // The `Model` the   `User` shall be read from
    @EnvironmentObject private var model: Model
    
    var body: some View {
        TabView {
            AccountsOverview()
            .tabItem {
                Image(systemName: "rectangle.stack")
                Text("Accounts")
            }
            TransactionOverview()
            .tabItem {
                Image(systemName: "list.dash")
                Text("Transactions")
            }
        }
    }
}

如何编写一个 XCTest 来测试第二个选项卡项

  • 有一个带有 systemName list.dash 的图像
  • 有文字“交易”

感谢您的意见!

swift testing swiftui xctest
2个回答
0
投票

您要测试/验证的东西是

View
并且要测试
View
应该使用
SnapshotTests
UITests
。在这里,我建议从
SnapshotTests
开始验证您的视图组件。

考虑以下是您要测试的观点

class Model: ObservableObject {}

struct MainView: View {
    @EnvironmentObject private var model: Model

    var body: some View {
        TabView {
            AccountsOverview()
                .tabItem {
                    Image(systemName: "rectangle.stack")
                    Text("Accounts")
                }
            TransactionOverview()
                .tabItem {
                    Image(systemName: "list.dash")
                    Text("Transactions")
                }
        }
    }
}

struct AccountsOverview: View {
    var body: some View {
        Text("AccountsOverview")
    }
}

struct TransactionOverview: View {
    var body: some View {
        Text("TransactionOverview")
    }
}

现在有很多框架可以实现快照测试,但是你可以使用 PointFree 的 SnapshotTesting 库。参考Kodeco的这篇文章开始SnapshotTesting。

以下是截图测试给大家查看:

import XCTest
import SnapshotTesting
@testable import <your project target>

class MainViewSnapshotTests: XCTestCase {
    func testExample() throws {
        let view = MainView()
        assertSnapshot(matching: view, as: .image)
    }
}

以下是使用上述快照测试生成的参考快照。


0
投票

ViewInspector 是用于单元测试 SwiftUI 的第三方库。我们可以找到第二个选项卡项

let tabItem = try MainView().inspect().tabView().view(TransactionOverview.self, 1).tabItem()

这证实了

MainView
包含一个
TabView
。它寻找第二个孩子(即索引 1)并确认它是一个
TransactionOverview
。然后它返回
TabItem
.

的可检查表示

然后我们可以在这个

TabItem
中搜索一个
Text
并得到它的字符串:

let text = try tabItem.find(ViewType.Text.self).string()

最后,我们可以检查此文本是否匹配:

XCTAssertEqual(text, "Transactions")

检查图像有点棘手,因为没有办法问一个标准的

Image
,“你是用系统名称创建的吗?它是什么?”就其本身而言,
Image
不可检查。

但是俗话说,我们可以通过引入一个额外的层来解决问题。让我们围绕

Image
定义我们自己的包装器。它会完全一样,但它会让我们检查系统名称:

struct ExaminableImage: View {
    let systemName: String

    var body: some View {
        Image(systemName: systemName)
    }
}

用它代替

Image
使其可测试。现在我们可以编写第二个测试。它以相同的方式开始,找到第二个选项卡项。由于这是重复的代码,让我们使用 Extract Method 重构来创建一个 helper:

private func secondTabItem() throws -> InspectableView<ViewType.ClassifiedView> {
    try MainView().inspect().tabView().view(TransactionOverview.self, 1).tabItem()
}

现在我们的新测试在第二个选项卡项中寻找

ExaminableImage
。我们使用
find
就像我们在
Text
测试中所做的那样,因为我们不关心图像在内部指定的位置。然后我们使用
actualView()
将这个可检查的表示转换回实际视图。现在我们可以查询它的系统名称:

func test_secondTabItem_image() throws {
    let image = try secondTabItem().find(ExaminableImage.self).actualView()

    XCTAssertEqual(image.systemName, "list.dash")
}

为什么要这样做而不是快照测试?快照是确认外观与先前批准的图像匹配的好方法。但是为了使快照测试足够快以进行重构,它们必须逐像素匹配。如果出现以下情况,图像将不匹配:

  • 一台机器使用不同的芯片(特别是如果你使用的是 Apple Silicon 但构建系统运行的是英特尔)
  • 一台机器使用不同的iOS版本
  • 机器使用不同的模拟器(即使快照可以指定模拟器大小,实际模拟器很重要)

这些差异通常很小。有一种方法可以对快照进行模糊匹配以忽略微小差异。但这会使快照测试慢得多。

我想要测试的原因是为了在经过验证的小步骤中启用重构。为此,测试必须尽可能快。在他们最快的逐像素匹配中,快照测试比常规单元测试慢一个数量级。它们通常仍可用于以该速度进行重构。但是模糊匹配将它们的速度降低了另一个数量级。

UITests 太慢了,它们是不可能的。它们也不允许您用假货来隔离系统的各个部分以便于测试。

ViewInspector 满足我的需求:测试快速稳定

© www.soinside.com 2019 - 2024. All rights reserved.