无法将“MyEnum<T.Type>”类型的值转换为预期的参数类型“MyEnum<_>”

丹尼尔·费尔南德斯

我有一个使用泛型的网络层,我正在使用协议,所以我可以稍后对其进行测试。我已经按照本教程https://medium.com/thecocoapps/network-layer-in-swift-4-0-972bf2ea5033

这是我用于测试的 Mock:

import Foundation
@testable import TraktTest

class MockUrlSessionProvider: ProviderProtocol {

    enum Mode {
        case success
        case empty
        case fail
    }

    private var mode: Mode

    init(mode: Mode) {
        self.mode = mode
    }

    func request<T>(type: T.Type, service: ServiceProtocol, completion: @escaping (NetworkResponse<T>) -> Void) where T: Decodable {                
        switch mode {
        case .success: completion(NetworkResponse.success(T))
        case .empty: completion(.failure(.noData))
        case .fail: completion(.failure(.unknown("Error")))
        }

    }
}

我收到错误:Cannot convert value of type 'NetworkResponse<T.Type>' to expected argument type 'NetworkResponse<_>'在这一行:completion(NetworkResponse.success(T))

如果我将它发送到我的完成成功,它会编译:(try? JSONDecoder().decode(T.self, from: data!)我使用编码和我的模型创建的虚拟数据),但是在到达我的模型时崩溃,因为尽管我已经使用 JSONEncoder() 编码了正确的模型,但它仍然为零。

我认为它有效,因为我在我的类中使用的逻辑与在我的应用程序中实现 ProviderProtocol 的逻辑相同:

final class URLSessionProvider: ProviderProtocol {

    private var session: URLSessionProtocol

    init(session: URLSessionProtocol = URLSession.shared) {
        self.session = session
    }

    func request<T>(type: T.Type, service: ServiceProtocol, completion: @escaping (NetworkResponse<T>) -> Void) where T: Decodable {
        let request = URLRequest(service: service)
        session.dataTask(request: request) { [weak self]  data, response, error in
            let httpResponse = response as? HTTPURLResponse
            self?.handleDataResponse(data: data, response: httpResponse, error: error, completion: completion)
        }.resume()
    }

    private func handleDataResponse<T: Decodable>(data: Data?, response: HTTPURLResponse?, error: Error?, completion: (NetworkResponse<T>) -> Void) {
        guard error == nil else { return completion(.failure(.unknown(error?.localizedDescription ?? "Error"))) }
        guard let response = response else { return completion(.failure(.unknown("no_response".localized()))) }

        switch response.statusCode {
        case 200...299:
            guard let data = data, let model = try? JSONDecoder().decode(T.self, from: data) else { return completion(.failure(.noData)) }
            completion(.success(model))
        default: completion(.failure(.unknown("no_response".localized())))
        }
    }

}

URLSessionProtocol 只是一个协议,它的 dataTask 方法与 URLSession.shared 中的方法相同(接收 URLRequest 并在完成时返回数据、响应和错误)。

我的网络响应是几个枚举:

enum NetworkResponse<T> {
    case success(T)
    case failure(NetworkError)
}

enum NetworkError {
    case unknown(String)
    case noData
}

我的提供者协议只有一个使用泛型发出请求的功能:

protocol ProviderProtocol {
    func request<T>(type: T.Type, service: ServiceProtocol, completion: @escaping(NetworkResponse<T>) -> Void) where T: Decodable
}

我认为我不需要在我的测试中使用 ServiceProtocol 因为是使用端点、标头、正文、id 等设置请求。但这是我创建的协议:

typealias Headers = [String: String]
typealias Parameters = [String: Any]

protocol ServiceProtocol {
    func baseURL() -> URL
    var path: String? { get }
    var id: String? { get }
    var method: HTTPMethod { get }
    var task: Task { get }
    var headers: Headers? { get }
    var parametersEncoding: ParametersEncoding { get }
}

enum HTTPMethod: String {
    case get = "GET"
    case post = "POST"
}

enum Task {
    case requestPlain
    case requestParameters(Parameters)
}

enum ParametersEncoding {
    case url
    case json
}

在我的应用程序中,我有一个实现 ProviderProtocol 的类,并在某些 viewModel 使用适当的模型调用请求时使用 URLSession.shared 来创建 dataTask。

我习惯于使用协议和特定模型进行测试,但使用泛型向我展示了该错误。如何使用泛型实现模拟提供程序,以便我可以测试任何使用不同类型模型(存根)调用网络的视图模型。

克里斯提克

发生错误是因为NetworkResponse需要 的实例T,而模拟尝试提供实际的T.

所以,你需要以某种方式提供一个实例,但是这不能由模拟生成,因为它没有关于如何构造实例的足够信息。

我建议在创建模拟时从外部注入成功值。您可以通过使模拟类通用或通过使Mode枚举通用来做到这一点下面是后者的示例实现:

class MockUrlSessionProvider: ProviderProtocol {

    // making the enum generic, to support injecting the success value
    enum Mode<T> {
        case success(T)
        case empty
        case fail
    }

    // need to have this as `Any` to cover all possible T generic arguments
    private var mode: Any

    // however the initializer can be very specific
    init<T>(mode: Mode<T>) {
        self.mode = mode
    }

    func request<T>(type: T.Type, service: ServiceProtocol, completion: @escaping (NetworkResponse<T>) -> Void) where T: Decodable {
        // if the mock was not properly configured, do nothing
        guard let mode = mode as? Mode<T> else { return }
        // alternatively you force cast and have the unit test crash, this should help catching early configuration issues
        // let mode = mode as! Mode<T>

        switch mode {
        case let .success(value): completion(NetworkResponse.success(value))
        case .empty: completion(.failure(.noData))
        case .fail: completion(.failure(.unknown("Error")))
        }

    }
}

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

Swift泛型错误:无法将类型'Type <T>'的值转换为预期的参数类型'Type <_>'

Realm iOS:无法将类型“ Dogs.Type”的值转换为预期的参数类型“ T.Type”

无法将类型“ [T]”的值转换为预期的参数类型“ [_]”

无法将类型“ A <T>”的值转换为预期的参数类型“ A <_>”

无法将类型“ NSImageInterpolation.Type”的值转换为预期的参数类型“ [NSImageRep.HintKey:任何]?”

swift无法将类型“ Plugin.Type”的值转换为预期的参数类型“ MyClass?”

无法将类型'UIAlertControllerStyle.Type'的值转换为预期的参数类型'UIAlertControllerStyle'

无法将“ViewController.Type”类型的值转换为预期的参数类型“UIViewController”

无法将“UIViewController.Type”类型的值转换为预期的参数类型“UIViewController”

无法将 [Type] 类型的值转换为预期的参数类型“some View”

无法将类型“ ViewController.Type”的值转换为预期的参数类型“ UIViewController”

无法将类型“ String.Type”的值转换为预期的参数类型“ String!”

无法将“Object.Type”类型的值转换为预期的参数类型“Object”

无法将“UserInfoEntity!.Type”类型的值转换为预期的参数类型“NSEntityDescription”

“无法将 'AVAudioPlayer.Type' 类型的值转换为预期的参数类型”

无法将“(SwipeableTabBarController).Type”类型的值转换为预期的参数类型“UIView”

无法将类型“(Store).Type”的值转换为预期的参数类型“Binding<C>”

无法将“UnsafePointer<T>”类型的值转换为预期的参数类型“UnsafePointer<Int16>”

无法转换'UILabel!'类型的值!预期参数'type inout String'

无法将 '(ViewController) -> () -> ()' 类型的值转换为预期的参数类型 '() -> ()'

无法将类型的值转换为预期的参数类型NSURLSessionDelegate

无法将类型的值转换为预期的参数类型“布尔”

无法将类型'()'的值转换为预期的参数类型'()->()'

无法将类型“ Int”的值转换为预期的参数类型“ _?”

无法将类型'(_)->()'的值转换为预期的参数类型'(()-> Void)?

无法将“()”类型的值转换为预期的参数类型“(() -> Void)?”

无法将类型[()]的值转换为预期的参数类型()

swift 无法将类型“in_addr_t”(又名“UInt32”)的值转换为预期的参数类型“UnsafeMutablePointer<in_addr_t>!”

无法将“UserFile”类型的值转换为预期的参数“User”