gpt4 book ai didi

unit-testing - 单元测试 SwiftUI/Combine @Published bool 值

转载 作者:行者123 更新时间:2023-12-05 04:36:33 30 4
gpt4 key购买 nike

我正在尝试熟悉在 SwiftUI 中对某些 View 模型进行单元测试。 View 模型当前有两个 @Published bool 值,它们在底层 UserDefaults 属性更改时发布更改。对于我的单元测试,我遵循了 this guide关于如何设置 UserDefaults 进行测试,这样我的生产值就不会被修改。我可以这样测试默认值:

func testDefaultValue() {
XCTAssertFalse(viewModel.canDoThing)
}

我将如何着手切换 @Published 值,然后确保我的 View 模型已收到更改?因此,例如,我在我的 XCTestCase 中引用了我的模拟用户默认值。我试图以零成功执行以下操作:

func testValueTogglesToTrue() {
defaults.canDoThing = true
XCTAssertTrue(viewModel.canDoThing)

更新底层用户默认值的想法是将更改发布到 View 模型中的已发布值将通知我们的 View 模型。以上对 View 模型变量没有任何作用。我是否需要订阅发布者并使用接收器来完成此操作?

最佳答案

假设您在 UserDefaults 中存储了一个标志以了解用户是否已完成入职:

extension UserDefaults {

@objc dynamic public var completedOnboarding: Bool {
bool(forKey: "completedOnboarding")
}
}

您有一个 ViewModel,它告诉您的 View 是否显示入职,并且有一个方法将入职标记为已完成:

class ViewModel: ObservableObject {

@Published private(set) var showOnboarding: Bool = true

private let userDefaults: UserDefaults

public init(userDefaults: UserDefaults) {
self.userDefaults = userDefaults
self.showOnboarding = !userDefaults.completedOnboarding
userDefaults
.publisher(for: \.completedOnboarding)
.map { !$0 }
.receive(on: RunLoop.main)
.assign(to: &$showOnboarding)
}

public func completedOnboarding() {
userDefaults.set(true, forKey: "completedOnboarding")
}
}

要测试这个类,您有一个 XCTestCase:

class MyTestCase: XCTestCase {

private var userDefaults: UserDefaults!
private var cancellables = Set<AnyCancellable>()

override func setUpWithError() throws {
try super.setUpWithError()
userDefaults = try XCTUnwrap(UserDefaults(suiteName: #file))
userDefaults.removePersistentDomain(forName: #file)
}

// ...
}

一些测试用例是同步的,例如您可以轻松地测试 showOnboarding 依赖于 UserDefaults completedOnboarding 属性:

func test_whenCompletedOnboardingFalse_thenShowOnboardingTrue() {
userDefaults.set(false, forKey: "completedOnboarding")
let subject = ViewModel(userDefaults: userDefaults)
XCTAssert(subject.showOnboarding)
}

func test_whenCompletedOnboardingTrue_thenShowOnboardingFalse() {
userDefaults.set(true, forKey: "completedOnboarding")
let subject = ViewModel(userDefaults: userDefaults)
XCTAssertFalse(subject.showOnboarding)
}

有些测试是异步的,这意味着您需要使用 XCTExpectation 来等待 @Published 值发生变化:

func test_whenCompleteOnboardingCalled_thenShowOnboardingFalse() {
let subject = ViewModel(userDefaults: userDefaults)
// first define the expectation that showOnboarding will change to false (1)
let showOnboardingFalse = expectation(
description: "when completedOnboarding called then show onboarding is false")

// subscribe to showOnboarding publisher to know when the value changes (2)
subject
.$showOnboarding
.filter { !$0 }
.sink { _ in
// when false received fulfill the expectation (5)
showOnboardingFalse.fulfill()
}
.store(in: &cancellables)

// trigger the function that changes the value (3)
subject.completedOnboarding()
// tell the tests to wait for your expectation (4)
waitForExpectations(timeout: 0.1)
}

关于unit-testing - 单元测试 SwiftUI/Combine @Published bool 值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70809030/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com