IOS - Mocking UserDefault перед загрузкой контроллера представления

В настоящее время я работаю над тестированием своего приложения, и у меня возникла проблема с издевательством над пользовательскими настройками по умолчанию. Позвольте мне сначала показать вам мою установку:

вот как я издеваюсь над пользовательскими настройками по умолчанию:

class MockUserDefaults: UserDefaults {

    typealias FakeData = Dictionary<String, Any?>
    var data: FakeData

    convenience init() {
        self.init(suiteName: "mocking")!
    }

    override init?(suiteName suitename: String?) {
        data = FakeDefaults()
        UserDefaults().removePersistentDomain(forName: suitename!)
        super.init(suiteName: suitename)
    }

    override func object(forKey defaultName: String) -> Any? {
        if let data = data[defaultName] {
            return data
        }
        return nil
    }

    override func set(_ value: Any?, forKey defaultName: String) {
        if defaultName == "favs"{
            data[defaultName] = value
        }

    }

}

У меня есть переменная в моем контроллере представления, называемая: userDefaults, и я установил ее так:

var userDefaults : UserDefaults {
        if (NSClassFromString("XCTest") != nil) {
            return MockUserDefaults()
        }
        return UserDefaults.standard
    }

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

У меня также есть переменная в myViewcontroller под названием favouriteMovie, которую я установил следующим образом:

private var favoriteMovie: Favorite? {
    if let favoriteString = userDefaults.value(forKey: "favs") as? String {
        return favorites.first(where: {$0.name == favoriteString})
    }
    return nil   
}

Теперь вот где проблема, когда я иду и пытаюсь протестировать этот контроллер представления, мне нужно установить userDefault с объектом, например:

    myviewController.userDefaults.set("avengers", forKey: "favs")

перед запуском теста, но проблема в том, что переменная favouriteMovie всегда возвращает nil, и мне нужно, чтобы она возвращала объект до запуска теста. Любая помощь. Заранее спасибо.

ОБНОВИТЬ :

это протокол:

protocol Mockable: class {
    var userDefaults: UserDefaults { get }
}

это расширение:

extension UIViewController: Mockable {}
extension Mockable {
        var userDefaults : UserDefaults {
            if (NSClassFromString("XCTest") != nil) {
                return MockUserDefaults()
            }
            return UserDefaults.standard
        }
}
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
966
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вот два способа исправить это.

1) Выполняя DI. В вашем viewController объявляется userDefaults как невычисляемое свойство, как показано ниже.

var userDefaults : UserDefaults?

В вашем тестовом примере создайте объект MockUserDefaults, установите значения и назначьте его viewController при его запуске, как показано ниже.

let mockUD = MockUserDefaults()
mockUD.set("avengers", forKey: "favs")

myviewController.userDefaults = mockUD

Теперь вы получите объект avengers.

2) Поскольку вопрос обновляется, вот исправление для удержания объекта mockDefaults,

struct AssociatedMock {
    static var key: UInt8 = 0
}

extension Mockable {

    private (set) var _mockDefaults: MockUserDefaults? {
        get {
            guard let value = objc_getAssociatedObject(self, &AssociatedMock.key) as? MockUserDefaults else {
                return nil
            }
            return value
        }
        set(newValue) {
            objc_setAssociatedObject(self, &AssociatedMock.key, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }

    var userDefaults : UserDefaults {
        if (NSClassFromString("XCTest") != nil) {
            if self._mockDefaults == nil {
                self._mockDefaults = MockUserDefaults()
            }
            return self._mockDefaults!
        }
        return UserDefaults.standard
    }
}

спасибо за ответ, но я не знаю, видели ли вы мой отредактированный вопрос или нет, но, как я уже сказал, userDefaults - это расширение протокола, которому я сделал все контроллеры uiview, соответствующие ему, поэтому я могу получить доступ к этой переменной во всех их, но когда я перейду к вашему второму предложению и добавлю _mockDefaults к расширению, я получаю эту ошибку «Расширения не должны содержать сохраненные свойства», что вы думаете?

Kilom94l 07.05.2018 13:36

Хорошо, тогда обновите свой вопрос, указав протокол и расширение, чтобы лучше понять.

Kamran 07.05.2018 13:41

@ Kilom94l попробуйте сейчас, так как я обновил ответ в соответствии с кодом, которым вы поделились.

Kamran 07.05.2018 14:00

спасибо большое, это сработало. Если у вас есть время, не могли бы вы мне это объяснить?

Kilom94l 07.05.2018 15:17

Да, на самом деле в userDefaults вы всегда возвращали новый объект MockUserDefaults, что означает, что у него будут пустые данные. Объект MockUserDefaults отличался при сохранении и получении значения favs. В своем решении я создаю MockUserDefaults только один раз, когда он равен нулю, в противном случае я возвращаю тот же объект. Вот почему ваше значение favs извлекается правильно, поскольку это тот же объект, который вы использовали во время сохранения.

Kamran 07.05.2018 15:27

понятно. Спасибо за ваше время :)

Kilom94l 07.05.2018 15:40

Другие вопросы по теме