在单元测试中,如何以编程方式关闭系统权限对话框?

问题描述 投票:4回答:2

我有一个单元测试,它调用CNContactStore()上的方法,例如CNContactStore().execute(saveRequest)。因此,会弹出联系人的权限对话框,例如推送通知警报,但联系人权限对话框不会自动被解除。我知道如何在使用addUIInterruptionMonitor()的UI测试中执行此操作,但不知道如何在单元测试中执行此操作。

ios swift xcode unit-testing permissions
2个回答
1
投票

我认为你将单元测试与UI测试混淆了。在单元测试中,你只想测试你的代码(例如函数和属性),然后你很可能需要“模拟”。

例如,您希望在验证输入字段后测试具有网络调用的登录按钮选择器。

以下应该是步骤:

  1. 测试验证逻辑。失败和后续案件。
  2. 测试API调用的完成块内的代码,但不使用REAL API数据。相反,请在此处使用您的模拟API。
  3. 等等...

现在,回到您的问题,您不需要处理系统生成的无法控制和“不可忽略”的警报控制器。相反,你想做的是通过系统触发该访问 - 联系人警报的委托功能来“模拟”(不再是)该弹出事件,“模拟”响应即“不允许”和“好”。当用户点击第一个按钮时,您期望发生什么?第二个按钮?设定期望/断言。

而已。点击您需要点击的所有功能,以增加代码的覆盖范围。如果这有帮助,请告诉我。


1
投票

我会在CNContactStore周围创建一个包装器,然后在测试时使用mock。

您对测试CNContactStore并不感兴趣,您是否有兴趣测试您的代码是否正确地与CNContactStore交互?

建立

我将开始创建协议和类,以从“正常”代码库中提取联系人的东西。

首先是一个Contact结构,用于保存稍后创建实际CNContact所需的属性

struct Contact {
   //holds whichever properties you need to create a CNContact
}

然后是一个协议来保存您想要执行的方法。这可以通过具有许多方法的协议来完成

protocol ContactsHolder {
    func save(contact: Contact)
    func add(contact: Contact)
    func delete(contact: Contact)
    func update(contact: Contact)
    //Maybe more methods, the important thing is that you abstract yourself away from CNContactStore and other Contact kit classes
}

或者你可以创建一个enum持有可能的选项,如此

enum ContactsUpdateMethod {
    case save(Contact)
    case add(Contact)
    case delete(Contact)
    case update(Contact)
}

protocol ContactsHolder {
    func execute(_ method: ContactsUpdateMethod)
}

在你的“真实”代码中

有了这些,您就可以创建实际的ContactsHolder,然后在内部使用CNContactStore以及与该框架相关的所有内容。

所以例如(如果您选择具有“纯”save函数的版本)

class CNContactsHolder: ContactsHolder {
    func save(contact: Contact) {
        //1. create a `CNContact` from your `Contact`
        //2. create a saveRequest
        //3. execute: CNContactStore().execute(saveRequest)
    }

    ....
}

然后你给需要与CNContactStore合作的班级提供你的新ContactsHolder协议的参考

所以在你的班上你有

let contactsHolder: ContactsHolder

然后你可以用你的init方法传入它

init(contactsHolder: ContactsHolder = CNContactsHolder()) {
    self.contactsHolder = contactsHolder
}

或者你可以将它声明为var然后给它一个默认值

所以代替:

let contactsHolder: ContactsHolder

你说:

var contactsHolder: ContactsHolder = CNContactsHolder()

重要的是,当你需要测试时,你可以将ContactsHolder从“真正的”CNContactsHolder变成模拟

在您的测试代码中

要测试这个,你创建一个模拟:

struct MockContactsHolder: ContactsHolder {
    var saveWasCalled = false
    func save(contact: Contact) {
        saveWasCalled = true
    }
}

然后你在你的班级而不是CNContactsHolder中使用它

现在您应该能够测试自己的代码,而不会被权限和与您的代码无关的东西打断,但这是使用CNContactStore的结果。

免责声明:)

我没有通过编译器运行上面的内容,因此可能存在拼写错误。

此外,可能会有一些零碎的东西使它适合CNContact(回调等),但我希望你能够了解如何将事情分开。

最后......它可能看起来很多工作,但我认为将“特定于框架”的代码放到一个单独的帮助程序类中,隐藏在协议之后是有意义的,这样您就可以在需要时将其交换出来例如,进行测试,或者......如果您决定稍后摆脱CNContact并使用不同的框架。

希望能帮助到你。

© www.soinside.com 2019 - 2024. All rights reserved.