Два класса, обратный вызов и модульное тестирование

У меня есть один класс со статическими методами: этот класс переносит вызовы в API Twitter

Во втором классе у меня есть бизнес-логика.

Из-за поведения асинхронности некоторых методов в классе-оболочке мне сложно сконструировать связь. Вот что я сделал:

APIManager.swift

public class APIManager {
    class func getPermission(callback : () -> Void) {

        let accountStore = ACAccountStore()
        let accountType = 
        ACAccountStore().accountTypeWithAccountTypeIdentifier(ACAccountTypeIdentifierTwitter)

        let callbackRequestAccess = { (granted: Bool, error: NSError!) -> Void in
            ...
            if(granted) {
                callback()
            }

        }

        accountStore.requestAccessToAccountsWithType(setAccountType, 
                     options: nil, completion: callbackRequestAccess)

    }
}

Welcome.swift

public class Welcome {

    public func checkPermission() {
        APIManager.getPermission(getTweet)
    } 
    public func getTweet() {
        ...
    }        
}

Я не уверен, что этот дизайн в порядке или нет. Я не хочу иметь прочную связь между этими классами, поэтому я использую обратный вызов.

Это классический дизайн? Более того, я не чувствую, что это поведение будет легко протестировать?

Ответ 1

Вы значительно улучшите тестируемость, не используя методы класса здесь. Создайте протокол TwitterConnection. Создайте SystemTwitterConnection, который соответствует ему и управляет вещами через ACAccountStore. Создайте TestTwitterConnection, который возвращает заданные ответы, которые вы можете настроить для тестирования. Вы даже можете создать KeychainTwitterConnection для управления входами в Twitter с помощью ACAccountStore или другой реализации, если Apple выпустит еще один способ хранения этих учетных записей.

Передайте соответствующее соединение Welcome при его создании.

Если протокол TwitterConnection становится большим, вам следует рассмотреть возможность его разделения на более мелкие протоколы, такие как TwitterAuthenticator и TweetFetcher, которые обрабатывают меньшее количество вещей (даже если один тип фактически реализует все эти протоколы). Это может значительно облегчить тестирование, позволяя вашим тестовым типам реализовать всего несколько функций, а не десятки.

Использование закрытий, вероятно, прекрасное, но вы должны более внимательно относиться к соглашениям об именах Cocoa. То, что вы называете callback, традиционно называется completion. Я также следовал примеру Cocoa о том, как назвать методы. Вместо getPermission() это будет requestAccessWithCompletionHandler(). Это поможет вызывающему абоненту понять, что он имеет очень похожее поведение с requestAccessToAccountsWithType(options:completion:). Не создавайте новый словарь для вызывающего.

Ответ 2

https://en.wikipedia.org/wiki/Observer_pattern

Это поможет вам разделить издателя событий (Observable) и пользователя (Observer).
Также вы можете иметь специальную реализацию Observable
который не подключается нигде, но уведомляет наблюдателей со статическим содержимым.
Поэтому вы напрямую вызываете метод notifyObservers для тестирования поведения наблюдателей.