I have an unit test which calls methods on CNContactStore()
e.g. CNContactStore().execute(saveRequest)
. So the permission dialog for contacts pops up, like the Push notifications alert but the contacts permission dialog doesn't get dismissed automatically. I know how to do this in UI tests with addUIInterruptionMonitor()
but have no idea how to do this in unit test.

- 437
- 1
- 6
- 10
-
Hey Dali! Do you have any updates or did you solve this? Thanks – Alexander Khitev Sep 18 '22 at 18:46
-
@AlexanderKhitev Unfortunately not. I just repeated the UITests until they were green :| – Dali Sep 19 '22 at 09:13
2 Answers
I would create a wrapper around CNContactStore
and then use a mock when testing.
You're not really interested in testing CNContactStore
, you are interested in testing that your code interacts with CNContactStore
properly right?
Setup
I would start out creating protocols and classes to extract the contact stuff out of your "normal" code base.
First a Contact
struct to hold the properties you need later to create an actual CNContact
struct Contact {
//holds whichever properties you need to create a CNContact
}
Then a protocol to hold the methods you would like to execute. This could be done with a protocol with a lot of methods like so
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
}
Or you could create an enum
holding the possible options like so
enum ContactsUpdateMethod {
case save(Contact)
case add(Contact)
case delete(Contact)
case update(Contact)
}
protocol ContactsHolder {
func execute(_ method: ContactsUpdateMethod)
}
In Your "Real" Code
With that in place, you are ready to create your actual ContactsHolder, which then internally uses CNContactStore
and everything related to that framework.
So for instance (if you chose the version with a "pure" save
function)
class CNContactsHolder: ContactsHolder {
func save(contact: Contact) {
//1. create a `CNContact` from your `Contact`
//2. create a saveRequest
//3. execute: CNContactStore().execute(saveRequest)
}
....
}
And then you give the class(es) who needs to work with CNContactStore
a reference to your new ContactsHolder
protocol
So in your class you have
let contactsHolder: ContactsHolder
And then you can either pass it in, in your init
method
init(contactsHolder: ContactsHolder = CNContactsHolder()) {
self.contactsHolder = contactsHolder
}
Or you can declare it as a var
and then give it a default value
So instead of:
let contactsHolder: ContactsHolder
You say:
var contactsHolder: ContactsHolder = CNContactsHolder()
The important thing is that you can change the ContactsHolder
from being a "real" CNContactsHolder
into a mock when you need to test
In Your Test Code
To test this, you create a mock:
struct MockContactsHolder: ContactsHolder {
var saveWasCalled = false
func save(contact: Contact) {
saveWasCalled = true
}
}
And then you use that in your class instead of the CNContactsHolder
Now you should be able to test your own code, without getting interrupted with permissions and stuff that is not relevant to your code, but is a consequence of using CNContactStore
.
Disclaimer :)
I haven't run the above by a compiler, so there may be typos.
Also, there might be bits and pieces missing to make it fit to CNContact
(callbacks and so on), but I hope you get the idea about how to split things apart.
And finally...it may seem like a lot of work, but I think it makes sense to get the "framework specific" code out into a separate helper class, hid behind a protocol, so that you can swap it out whenever you need to do testing for instance, or...if you decide to get rid of CNContact
at a later point and use a different frameworks.
Hope it helps.

- 6,787
- 3
- 21
- 51
-
3While I think this is a very good answer and this is definitely the approach I would recommend to test stuff that asks for permissions, I'd like to point out that the question was about dismissing alerts specifically. Any ideas here? – Xavier Lowmiller Apr 24 '19 at 12:13
-
1@XavierLowmiller Thank you for your kind words. As for your question. Unfortunately no. I can't see how you can dismiss the alert programatically from a test target that is not "UI aware" (which is why I suggested a different approach). I'm just thinking out loud now...Do you have access to the `ViewController` in your testcode and can you from there get access to the `UIWindow` or `UIApplication` and from there disable the alert? – pbodsk Apr 24 '19 at 13:03
I think you're confusing Unit Testing with UI Testing. In Unit Testing, you just want to test, your codes (e.g. functions and properties) and with that, you'll most probably need to have "mock-up".
For instance, you want to test your login button selector that has a network calls after validation of the input fields.
The following should be the steps:
- Test your validation logic. Both failing and succeeding cases.
- Test the code inside the completion block of your API call, BUT not using the REAL API data. Instead, use your mocked API here.
- and so on...
Now, back to your question, you don't need to handle that uncontrollable and "un-dismissable" alert controller generated by the system. Instead, what you wanna do is to "mock" (ughh not again) that pop-up event by hitting the delegate function for that access-contacts alert by the system, "mock" a response namely "Don't Allow" and "OK". What do you expect to happen when user taps on the first button? The second button? Set expectations/assert.
That's it. Hit every function you need to hit to increase the coverage of your code. Let me know if this helps.

- 12,555
- 6
- 54
- 95