Невозможно привести к протоколу из каркаса во время тестовых случаев - PullRequest
0 голосов
/ 06 марта 2020

Итак, у меня есть класс из одной из наших внутренних структур. Он определяется следующим образом:

// This lives within a framework
class ExternalClass: ExternalClassProtocol {
    // implementation here
}

// This lives within my test target
class MockExternalClass: ExternalClassProtocol {
    // Mock implementation here
}

// This lives within the same external frame work as ExternalClass
protocol ExternalClassProtocol: AnyObject {
   // Protocol methods here  
}

Если я попытаюсь привести MockExternalClass в ходе моих тестовых случаев? ExternalClassProtocol, тестовый случай дает сбой.

Однако во время выполнения приложения в реальном времени нет проблем при приведении ExternalClass как? ExternalClassProtocol.

Это проблема при попытке реализовать протокол из внешнего модуля? Есть ли способ обойти это?

Доступ к классу осуществляется через внедрение зависимостей (см. Ниже реализацию внедрения зависимостей). Cra sh происходит в функции разрешения.

Если вы на самом деле отлаживаетесь до этой точки, вы можете видеть, что фиктивная зависимость ЕСТЬ в моей зависимости root (массив служб ниже).

Итак, я знаю, что его не удалось разыграть из-за отсутствия зависимости.

@propertyWrapper
struct Injected<Value> {
    var key: String

    var wrappedValue: Value {
        get { return Dependency.root.resolve(key: self.key) }
        set { Dependency.root.add(key: self.key, newValue) }
    }

    init(key: String) {
        self.key = key
    }
}


class Dependency {
    static let root = Dependency()

    var services: [String : Any] = [:]

    func add<T>(key: String, _ service: T) {
        services[key] = service
    }

    func resolve<T>(key: String) -> T {
        guard let component: T = services[key] as? T else {
            // The test crashes here. It works fine on other mocks that are internal to the project
            fatalError("Dependency '\(T.self)' not resolved!")
        }

        return component
    }

    func clearDependencies() {
        self.services.removeAll()
    }

    private init() {}
}

В моем тестовом примере:

@testable import MyProject
import ExternalDependency

class TestCase: XCTestCase {
    private var subject: ClassWithService!
    private var mockInternalClass: MockInternalClassProtocol!
    private var mockExternalClass: MockInternallClassProtocol!

    func setUp() {
        mockExternalClass = MockExternalClass() // This one crashes when trying to cast to its parent protocol
        mockInternalClass = MockInternalClass() // This one does not crash when casting to parent protocol.
        Dependency.root.add(key: "internal_class", mockInternalClass)
        Dependency.root.add(key: "external_class", mockExternalClass)
    }
}

Некоторые вещи, которые я ' мы пробовали:

Добавление AnyObject к протоколу (это исправило аналогичную проблему для внутренне определенных классов, которые я высмеиваю).

изменение типа mockExternalClass на протокол, изменяющий тип mockExternalClass на реализацию

Помимо одного протокола, определяемого в одном из наших модулей, нет никакой разницы между внешним протоколом и тем, который мы определили в нашем собственном проекте.

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

Есть идеи?

...