Я бы создал обертку вокруг 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)
}
In Your "Real«Код
Имея это, вы готовы создать свой настоящий 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
на более позднем этапе и использовать другие фреймворки.
Надеюсь, это поможет.