首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何模拟NSUbiquitousKeyValueStore进行单元测试?

如何模拟NSUbiquitousKeyValueStore进行单元测试?
EN

Stack Overflow用户
提问于 2020-05-01 05:35:43
回答 1查看 127关注 0票数 1

所以我有一个同时使用UserDefaults和NSUbiquitousKeyValueStore的类,我正在尝试对它进行单元测试。我可以很容易地使用UserDefaults(suiteName:# UserDefaults )模拟文件(例如),但是我不知道如何模拟NSUbiquitousKeyValueStore。我似乎在SO上找不到任何关于它的线索,而且我的google-fu也是缺乏的。

下面是我的测试类的开始部分,仅供参考:

代码语言:javascript
复制
class ReviewTests: XCTestCase {

    private var userDefaults: UserDefaults = UserDefaults(suiteName: #file)!
    private var ubiquitousKeyValueStore: NSUbiquitousKeyValueStore = // How do I mock this?
    private var reviewPromptController: ReviewPromptController!
EN

回答 1

Stack Overflow用户

发布于 2020-07-20 02:54:28

为了对我与NSUbiquitousKeyValueStore的交互进行单元测试,我创建了一个协议,让我围绕NSUbiquitousKeyValueStoreNSUbiquitousKeyValueStore的自定义云存储包装器符合这个协议。这些实现如下:

代码语言:javascript
复制
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中定义的方法,因此不需要为它做任何其他事情。

代码语言:javascript
复制
extension NSUbiquitousKeyValueStore: KeyValueStore { }

然后,我创建了一个类型,它是NSUbiquitousKeyValueStore的包装器。UserSettingsStorage接受任何符合KeyValueStore的类型。因为NSUbiquitousKeyValueStoreMockCloudKeyValueStore都符合KeyValueStore,所以我可以在生产中传递一个NSUbiquitousKeyValueStore实例,在测试中传递一个MockCloudKeyValueStore实例。

代码语言:javascript
复制
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)
    }

}
代码语言:javascript
复制
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的包装器是否按预期响应通知。

代码语言:javascript
复制
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)
    }

}
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61533139

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档