xcode - 在 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 还是我做错了?
解决方案
成功的关键是依赖注入。不要直接访问共享用户默认对象 ( 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)
}
}
推荐阅读
- reactjs - 在 Apollo Mutation 中调用 Meteor 方法
- google-cloud-platform - 通过 Dataflow 管道写入 Cloud SQL 非常慢
- if-statement - rpm 有条件的子字符串?
- c++ - 如何在 Visual C++ 中使用仅标头库?
- javascript - 为什么 Jquery onclick 监听器不起作用?
- html - 图像宽度被其他东西覆盖
- spring-boot - Jackson Object Mapper 在提供扩展配置时不工作,但在 Spring Boot 中提供类级别/字段级别注释时工作
- ansible - 使用 Ansible 将启动磁盘(如果存在)附加到 Gcloud 实例
- python - Kivy 数字时钟问题
- c# - PostSharp 将调用堆栈显示为“方面代码”并降低调用堆栈的可用性