首页 > 解决方案 > 在 UiTests 或 UnitTests 中测试 UserDefaults 值

问题描述

我在 UserDefaults 中保存了一个 Int,通过单击一个按钮将其减少一个。我不知道这是否重要,但extension如果应用程序第一次启动,我已经添加到 UserDefaults 以加载初始值:

extension UserDefaults {

    public func optionalInt(forKey defaultName: String) -> Int? {
        let defaults = self
        if let value = defaults.value(forKey: defaultName) {
            return value as? Int
        }
        return nil
    }
}

UserDefaults 在应用程序中用作ObservableObject和访问,EnvironmentObject如下所示:

class Preferences: ObservableObject {
    @Published var counter: Int = UserDefaults.standard.optionalInt(forKey: COUNTER_KEY) ?? COUNTER_DEFAULT_VALUE {
        didSet {
            UserDefaults.standard.set(counter, forKey: COUNTER_KEY)
        }
    }
}

我现在正在尝试测试单击按钮时 UserDefaults 中的值是否减小。我正在尝试使用以下方法读取测试中的 UserDefaults:

XCTAssertEqual(UserDefaults.standard.integer(forKey: "COUNTER_KEY"), 9)// default is 10

我已经用普通的 UnitTests 尝试过,其中调用了 Button 背后的方法和 UITests 但两者都不起作用。在 UnitTestsCOUNTER_DEFAULT_VALUE中,我得到了 0,而在 UiTests 中,我得到了 0。

我试图在测试中直接访问 UserDefaults,而不是使用该Preferences对象,因为我还没有找到一种方法来访问它,因为它是一个ObservableObject.

我在模拟器中检查了用户默认值在使用应用程序时是否正确保存/加载。是否无法在测试中访问 UserDefaults 还是我做错了?

标签: xcodeswiftuixctestxcode-ui-testingxcuitest

解决方案


成功的关键是依赖注入。不要直接访问共享用户默认对象 ( UserDefaults.standard),而是在类中声明一个类型的对象UserDefaults

let userDefaults: UserDefaults

在声明模型的视图中,您可以自由使用共享对象:

@StateObject var model = Preferences(userDefaults: UserDefaults.standard)

但是在您的测试中,创建一个专用UserDefaults对象并将其传递给初始化程序,如下所示:

let userDefaults = UserDefaults(suiteName: #file)!
userDefaults.removePersistentDomain(forName: #file)
        
let model = Preferences(userDefaults: userDefaults)

好处很明显:您可以控制UserDefaults. 这意味着代码适用于各种环境。为了简单起见,我还没有合并你的扩展。但我相信你会设法让它工作。

TL;博士

请参阅下面的工作示例:

内容视图.swift

import SwiftUI
import Foundation

class Preferences: ObservableObject {
    let userDefaults: UserDefaults
    
    @Published var counter: Int {
        didSet {
            self.userDefaults.set(counter, forKey: "myKey")
        }
    }
    
    init(userDefaults: UserDefaults) {
        self.userDefaults = userDefaults
        self.counter = userDefaults.integer(forKey: "myKey")
    }
    
    func decreaseCounter() {
        self.counter -= 1
    }
}

struct ContentView: View {
    @StateObject var model = Preferences(userDefaults: UserDefaults.standard)
    
    var body: some View {
        HStack {
            Text("Value: \(self.model.counter)")
            Button("Decrease") {
                self.model.decreaseCounter()
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

InjectionTests.swift

import XCTest
@testable import Injection

class InjectionTests: XCTestCase {
    func testPreferences() throws {
        // arrange
        let userDefaults = UserDefaults(suiteName: #file)!
        userDefaults.removePersistentDomain(forName: #file)
        
        let model = Preferences(userDefaults: userDefaults)
        
        // act
        let valueBefore = userDefaults.integer(forKey: "myKey")
        model.decreaseCounter()
        let valueAfter = userDefaults.integer(forKey: "myKey")
        
        // assert
        XCTAssertEqual(valueBefore - 1, valueAfter)
    }
}

推荐阅读