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

时间:2019-04-19 08:40:02

标签: ios swift xcode unit-testing permissions

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

contact permissions alert

2 个答案:

答案 0 :(得分:1)

我认为您将单元测试 UI测试混淆了。在单元测试中,您只想测试代码(例如,函数和属性),并因此可能需要“模拟”。

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

以下应为步骤:

  1. 测试您的验证逻辑。失败和成功的案例。
  2. 在您的API调用的完成块中测试代码,但不要使用REAL API数据。相反,请在此处使用模拟的API。
  3. 依此类推...

现在,回到您的问题,您无需处理系统生成的不可控制且“不可禁用”的警报控制器。相反,您要执行的操作是通过点击系统的该访问联系人警报的委托功能来“模拟”(不是再次)该弹出事件,“模拟”响应即“不允许” “ “确定” 。当用户点击第一个按钮时,您期望发生什么?第二个按钮?设定期望/主张。

就是这样。点击您需要点击的每个功能以增加代码的覆盖范围。让我知道是否有帮助。

答案 1 :(得分:1)

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

您对测试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并使用其他框架,就可以这样做。

希望有帮助。

相关问题