所以我有一个同时使用UserDefaults和NSUbiquitousKeyValueStore的类,我正在尝试对它进行单元测试。我可以很容易地使用UserDefaults(suiteName:# UserDefaults )模拟文件(例如),但是我不知道如何模拟NSUbiquitousKeyValueStore。我似乎在SO上找不到任何关于它的线索,而且我的google-fu也是缺乏的。
下面是我的测试类的开始部分,仅供参考:
class ReviewTests: XCTestCase {
private var userDefaults: UserDefaults = UserDefaults(suiteName: #file)!
private var ubiquitousKeyValueStore: NSUbiquitousKeyValueStore = // How do I mock this?
private var reviewPromptController: ReviewPromptController!发布于 2020-07-20 02:54:28
为了对我与NSUbiquitousKeyValueStore的交互进行单元测试,我创建了一个协议,让我围绕NSUbiquitousKeyValueStore和NSUbiquitousKeyValueStore的自定义云存储包装器符合这个协议。这些实现如下:
protocol KeyValueStore: class {
// MARK: Properties
static var didChangeExternallyNotification: Notification.Name { get }
// MARK: Reading
func object(forKey aKey: String) -> Any?
func string(forKey aKey: String) -> String?
func array(forKey aKey: String) -> [Any]?
func dictionary(forKey aKey: String) -> [String : Any]?
func data(forKey aKey: String) -> Data?
func longLong(forKey aKey: String) -> Int64
func double(forKey aKey: String) -> Double
func bool(forKey aKey: String) -> Bool
// MARK: Writing
func set(_ anObject: Any?, forKey aKey: String)
func set(_ aString: String?, forKey aKey: String)
func set(_ aData: Data?, forKey aKey: String)
func set(_ anArray: [Any]?, forKey aKey: String)
func set(_ aDictionary: [String : Any]?, forKey aKey: String)
func set(_ value: Int64, forKey aKey: String)
func set(_ value: Double, forKey aKey: String)
func set(_ value: Bool, forKey aKey: String)
func removeObject(forKey aKey: String)
}KeyValueStore是一种协议,它包含NSUbiquitousKeyValueStore拥有的方法签名。通过这样做,我可以创建一个可以在可以使用NSUbiquitousKeyValueStore实例的测试中使用的类型。
接下来,我使NSUbiquitousKeyValueStore符合KeyValueStore。由于NSUbiquitousKeyValueStore已经实现了KeyValueStore中定义的方法,因此不需要为它做任何其他事情。
extension NSUbiquitousKeyValueStore: KeyValueStore { }然后,我创建了一个类型,它是NSUbiquitousKeyValueStore的包装器。UserSettingsStorage接受任何符合KeyValueStore的类型。因为NSUbiquitousKeyValueStore和MockCloudKeyValueStore都符合KeyValueStore,所以我可以在生产中传递一个NSUbiquitousKeyValueStore实例,在测试中传递一个MockCloudKeyValueStore实例。
class MockCloudKeyValueStore: KeyValueStore {
// MARK: Properties
// KeyValueStore
static var didChangeExternallyNotification: Notification.Name = Notification.Name(NSUbiquitousKeyValueStore.didChangeExternallyNotification.rawValue)
// Helpers
private var arrays: [String: [Any]] = [:]
private var bools: [String: Bool] = [:]
private var datas: [String: Data] = [:]
private var dictionaries: [String: [String: Any]] = [:]
private var doubles: [String: Double] = [:]
private var longLongs: [String: Int64] = [:]
private var objects: [String: Any] = [:]
private var strings: [String: String] = [:]
// MARK: Reading
func object(forKey aKey: String) -> Any? {
objects[aKey]
}
func string(forKey aKey: String) -> String? {
strings[aKey]
}
func array(forKey aKey: String) -> [Any]? {
arrays[aKey]
}
func dictionary(forKey aKey: String) -> [String : Any]? {
dictionaries[aKey]
}
func data(forKey aKey: String) -> Data? {
datas[aKey]
}
func longLong(forKey aKey: String) -> Int64 {
longLongs[aKey] ?? 0
}
func double(forKey aKey: String) -> Double {
doubles[aKey] ?? 0
}
func bool(forKey aKey: String) -> Bool {
bools[aKey] ?? false
}
// MARK: Writing
func set(_ anObject: Any?, forKey aKey: String) {
objects[aKey] = anObject
postServerDidChangeNotification()
}
func set(_ aString: String?, forKey aKey: String) {
strings[aKey] = aString
postServerDidChangeNotification()
}
func set(_ aData: Data?, forKey aKey: String) {
datas[aKey] = aData
postServerDidChangeNotification()
}
func set(_ anArray: [Any]?, forKey aKey: String) {
arrays[aKey] = anArray
postServerDidChangeNotification()
}
func set(_ aDictionary: [String : Any]?, forKey aKey: String) {
dictionaries[aKey] = aDictionary
postServerDidChangeNotification()
}
func set(_ value: Int64, forKey aKey: String) {
longLongs[aKey] = value
postServerDidChangeNotification()
}
func set(_ value: Double, forKey aKey: String) {
doubles[aKey] = value
postServerDidChangeNotification()
}
func set(_ value: Bool, forKey aKey: String) {
bools[aKey] = value
postServerDidChangeNotification()
}
func removeObject(forKey aKey: String) {
arrays[aKey] = nil
bools[aKey] = nil
datas[aKey] = nil
dictionaries[aKey] = nil
doubles[aKey] = nil
longLongs[aKey] = nil
objects[aKey] = nil
strings[aKey] = nil
postServerDidChangeNotification()
}
// MARK: Helpers
private func postServerDidChangeNotification() {
var userInfo = [AnyHashable : Any]()
let changeReason = NSUbiquitousKeyValueStoreServerChange
userInfo[NSUbiquitousKeyValueStoreChangeReasonKey] = changeReason
NotificationCenter.default.post(name: Self.didChangeExternallyNotification, object: nil, userInfo: userInfo)
}
}class UserSettingsStorage: ObservableObject {
// MARK: Properties
private let cloudKeyValueStore: KeyValueStore
private let notificationCenter: NotificationCenter
@Published var selectedListName = ""
// MARK: Initialization
init(cloudKeyValueStore: KeyValueStore,
notificationCenter: NotificationCenter = .default) {
self.cloudKeyValueStore = cloudKeyValueStore
self.notificationCenter = notificationCenter
observeUbiquitousKeyValueStoreDidChangeExternallyNotification()
}
// MARK: Reading
private func getSelectedListIndex() {
selectedListName = cloudKeyValueStore.string(forKey: "selectedListIndexKey") ?? ""
}
// MARK: Writing
// Cloud Key-Value Store
func setSelectedList(name: String) {
selectedListName = name
cloudKeyValueStore.set(name, forKey: "selectedListIndexKey")
}
// MARK: Notification Observation
private func observeUbiquitousKeyValueStoreDidChangeExternallyNotification(in center: NotificationCenter = .default) {
center.addObserver(self,
selector: #selector(handleUbiquitousKeyValueStoreDidChangeExternallyNotification(notification:)),
name: NSUbiquitousKeyValueStore.didChangeExternallyNotification,
object: nil)
}
// MARK: Notification Selectors
@objc private func handleUbiquitousKeyValueStoreDidChangeExternallyNotification(notification: Notification) {
// Check for the reasons listed at https://developer.apple.com/documentation/foundation/nsubiquitouskeyvaluestore/1433687-change_reason_values to handle errors/blockers
if let userInfo = notification.userInfo, let changeReason = userInfo[NSUbiquitousKeyValueStoreChangeReasonKey] as? NSNumber {
switch changeReason.intValue {
case NSUbiquitousKeyValueStoreServerChange:
getSelectedListIndex()
case NSUbiquitousKeyValueStoreInitialSyncChange:
print("initial sync change")
case NSUbiquitousKeyValueStoreQuotaViolationChange:
print("quota violation change")
case NSUbiquitousKeyValueStoreAccountChange:
print("account change")
default:
print("unknown change")
}
} else {
print("userInfo not available for Notification", notification)
}
}
}最后,我可以在单元测试中使用我的模拟,确保从我的模拟中调用NSUbiquiousKeyValueStore.didChangeExternallyNotification,然后验证NSUbiquitousKeyValueStore的包装器是否按预期响应通知。
class UnitTests: XCTestCase {
// MARK: Properties
var storage: UserSettingsStorage!
// MARK: Lifecycle
override func setUp() {
super.setUp()
storage = UserSettingsStorage(cloudKeyValueStore: MockCloudKeyValueStore())
}
override func tearDown() {
storage = nil
super.tearDown()
}
// MARK: Selected List Name
func test_UserSettingsStorage_PublishesSelectedListName_WhenListNameIsSet() {
let listName = "list name"
let publishedExpectation = expectation(forNotification: NSUbiquitousKeyValueStore.didChangeExternallyNotification, object: nil) { notification in
return self.storage.selectedListName == listName
}
storage.setSelectedList(name: listName)
wait(for: [publishedExpectation], timeout: 2)
}
}https://stackoverflow.com/questions/61533139
复制相似问题