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的回答,但您也可能会发现这很有帮助,它正好解决了这个问题: