В модульных тестах, как я могу программно закрыть диалоговое окно системных разрешений? - PullRequest
4 голосов
/ 19 апреля 2019

У меня есть модульный тест , который вызывает методы на CNContactStore(), например. CNContactStore().execute(saveRequest). Таким образом, всплывающее диалоговое окно разрешений для контактов, например, оповещение о push-уведомлениях, но диалоговое окно разрешений контактов не закрывается автоматически. Я знаю, как сделать это в тестах пользовательского интерфейса с addUIInterruptionMonitor(), но не знаю, как это сделать в модульном тесте .

contact permissions alert

Ответы [ 2 ]

1 голос
/ 19 апреля 2019

Я бы создал обертку вокруг 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 на более позднем этапе и использовать другие фреймворки.

Надеюсь, это поможет.

1 голос
/ 19 апреля 2019

Я думаю, вы путаете Модульное тестирование с UI Testing . В модульном тестировании вы просто хотите протестировать свои коды (например, функции и свойства), а с этим вам, скорее всего, понадобится «макет».

Например, вы хотите проверить селектор кнопки входа в систему, который вызывает сеть после проверки полей ввода.

Следующие шаги должны быть:

  1. Проверьте вашу логику проверки. И неудачные и последующие дела.
  2. Протестируйте код внутри блока завершения вашего вызова API, НО не используйте РЕАЛЬНЫЕ данные API. Вместо этого используйте ваш смоделированный API здесь.
  3. и так далее ...

Теперь, возвращаясь к вашему вопросу, вам не нужно обрабатывать тот неконтролируемый и «недопустимый» контроллер предупреждений, генерируемый системой. Вместо этого вам нужно «смоделировать» (не снова) это всплывающее событие, нажав функцию делегата для этого оповещения о контактах доступа системой, «смоделировать» ответ, а именно «Не разрешать». " и " ОК ". Что вы ожидаете, когда пользователь нажмет первую кнопку? Вторая кнопка? Установить ожидания / утверждать.

Вот и все. Нажмите на каждую функцию, которая вам нужна, чтобы увеличить охват вашего кода. Дайте мне знать, если это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...