Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/18.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Swift 您将如何测试这个通用API?_Swift - Fatal编程技术网

Swift 您将如何测试这个通用API?

Swift 您将如何测试这个通用API?,swift,Swift,我有以下apirest协议: protocol APIRequest { associatedtype Result var httpMethod: HTTPMethod { get } var pathComponents: [String] { get } func handle(responseData: Data) throws -> Result } protocol APIClientProtocol { func perform&l

我有以下
apirest
协议:

protocol APIRequest {
    associatedtype Result

    var httpMethod: HTTPMethod { get }
    var pathComponents: [String] { get }
    func handle(responseData: Data) throws -> Result
}
protocol APIClientProtocol {
    func perform<T : APIRequest>(_ request: T,
                                 completion: @escaping ((Result<T.Result, APIError>) -> Void))
}
此外,我还有以下
APIClient
协议:

protocol APIRequest {
    associatedtype Result

    var httpMethod: HTTPMethod { get }
    var pathComponents: [String] { get }
    func handle(responseData: Data) throws -> Result
}
protocol APIClientProtocol {
    func perform<T : APIRequest>(_ request: T,
                                 completion: @escaping ((Result<T.Result, APIError>) -> Void))
}

现在,我想为
DataSource
编写一个测试,但我需要模拟
APIClientProtocol
来实现这一点。我怎样才能模拟它呢?

使用
URLProtocol
的模拟思考得太深了

模拟API有两种基本方法

  • 创建一个完整的模拟服务器并连接到它,而不是真正的服务器。这样您就可以创建集成测试

  • 直接模仿对方的反应

  • 类APIClientProtocolMock:APIClientProtocol{
    函数执行(
    _请求:T,
    完成:@escaping((结果)->Void)
    ) {
    //替换为后台队列,具体取决于您的实现
    DispatchQueue.main.async{
    完成(.success(/*在此处放置模拟响应*/))
    }
    }
    }
    
    老实说,我不是FE单元测试和大量模拟的好朋友,因为e2e测试(UI测试)做得更好,你不必创建数百个不必要的协议来模拟工作

    一个真实的例子:

    protocol APIRequest {
        associatedtype Result
    
        var httpMethod: String { get }
        var pathComponents: [String] { get }
        func handle(responseData: Data) throws -> Result
    }
    
    protocol APIClientProtocol {
        func perform<T : APIRequest>(
            _ request: T,
            completion: @escaping ((Result<T.Result, Error>) -> Void)
        )
    }
    
    
    struct MockAPIRequest: APIRequest {
        typealias Result = String
    
        let httpMethod: String = "POST"
        let pathComponents: [String] = ["url"]
    
        func handle(responseData: Data) throws -> Result {
            return "success"
        }
    }
    
    struct SecondMockAPIRequest: APIRequest {
        typealias Result = String
    
        let httpMethod: String = "POST"
        let pathComponents: [String] = ["url"]
    
        func handle(responseData: Data) throws -> Result {
            return "failed"
        }
    }
    
    class MockAPIClientProtocol: APIClientProtocol {
        var mockData: [String: Data] = [:]
    
        // you can load mock data dynamically
        func perform<T : APIRequest>(_ request: T, completion: @escaping ((Result<T.Result, Error>) -> Void)) {
            let data = mockData[request.pathComponents.joined(separator: "/")] ?? Data()
            completion(.success(try! request.handle(responseData: data)))
        }
    
        // you can add implementation for a specific type
        func perform(_ request: SecondMockAPIRequest, completion: @escaping ((Result<SecondMockAPIRequest.Result, Error>) -> Void)) {
            let data = mockData[request.pathComponents.joined(separator: "/")] ?? Data()
            completion(.success(try! request.handle(responseData: data)))
        }
    }
    
    let client = MockAPIClientProtocol()
    client.mockData["url"] = Data()
    client.perform(MockAPIRequest()) { result in
        print(result) // success
    }
    client.perform(SecondMockAPIRequest()) { result in
        print(result) // failed
    }
    
    协议请求{
    关联类型结果
    var httpMethod:字符串{get}
    var pathComponents:[字符串]{get}
    func句柄(responseData:Data)抛出->结果
    }
    协议APIClientProtocol{
    函数执行(
    _请求:T,
    完成:@escaping((结果)->Void)
    )
    }
    结构MockAPIRequest:APIRequest{
    typealias结果=字符串
    让httpMethod:String=“POST”
    让pathComponents:[字符串]=[“url”]
    func句柄(responseData:Data)抛出->结果{
    返回“成功”
    }
    }
    结构SecondMockAPIRequest:APIRequest{
    typealias结果=字符串
    让httpMethod:String=“POST”
    让pathComponents:[字符串]=[“url”]
    func句柄(responseData:Data)抛出->结果{
    返回“失败”
    }
    }
    类MockAPIClientProtocol:APIClientProtocol{
    var mockData:[字符串:数据]=[:]
    //您可以动态加载模拟数据
    函数执行(uu请求:T,完成:@escaping((结果)->Void)){
    let data=mockData[request.pathComponents.joined(分隔符:“/”)
    完成(.success(try!request.handle(responseData:data)))
    }
    //可以为特定类型添加实现
    func perform(uu请求:secondmockapirect,完成:@escaping((结果)->Void)){
    let data=mockData[request.pathComponents.joined(分隔符:“/”)
    完成(.success(try!request.handle(responseData:data)))
    }
    }
    let client=MockAPIClientProtocol()
    client.mockData[“url”]=Data()
    client.perform(mockapirest()){result in
    打印(结果)//成功
    }
    client.perform(secondmockapirest()){结果为
    打印(结果)//失败
    }
    
    Re:2,这是我要走的路,但是
    .succes()
    中的部分很有趣,我不知道如何让它工作。结果是一个
    T
    函数,它是
    apirest
    ,具有一个
    关联类型
    。如何用适当的存根值实例化mock?我认为您缺少一个抽象级别。您的方法适用于网络层请求,但通常您应该有一个抽象通信层,其中包含
    func loadData(对于object:object,onCompletion:(Result)->Void)
    等方法,这些方法不应该是通用的。这个层应该是创建请求和解析响应的层。但是,mock仍然可以是通用的,这将解决问题。如果你试图对多个请求使用同一个模拟,那是行不通的。我已经更新了我的问题。我有另一个层,它不是通用的,并且与通用协议接口。现在我想测试该层,但我需要注入通用协议,我不确定如何注入。@PeterFoti我添加了一个如何测试该协议的示例。我同意Sulthan的回答,但您也可能会发现这很有帮助,它正好解决了这个问题: