提问人:Tharsan E. 提问时间:9/10/2023 更新时间:9/10/2023 访问量:54
XCTestCase 在 Swift 中使用 URLProtocolStub 的 Alamofire 多部分参数
XCTestCase Multipart params with Alamofire using URLProtocolStub in Swift
问:
我想知道如何使用 Alamofire(网络堆栈)测试多部分发送参数。例如:发送带有图像的字符串(数据类型)。
我的问题是,当我收到响应时,我会得到 URLRequest,并检查 httpBody 以获取我的参数。不幸的是,它是零,我不知道另一种获取 multipartData 的方法。
(在;)问这里之前,我已经做了一些搜索)
为此,我创建了一个存根 URLProtocol(称为 URLProtocolStub)
final class URLProtocolStub: URLProtocol {
private struct Stub {
let data: Data?
let response: URLResponse?
let error: Error?
let requestObserver: ((URLRequest) -> Void)?
}
private static var _stub: Stub?
private static var stub: Stub? {
get { return queue.sync { _stub } }
set { queue.sync { _stub = newValue } }
}
private static let queue = DispatchQueue(label: "URLProtocolStub.queue")
static func stub(data: Data?, response: URLResponse?, error: Error?) {
stub = Stub(data: data, response: response, error: error, requestObserver: nil)
}
static func observeRequests(observer: @escaping (URLRequest) -> Void) {
stub = Stub(data: nil, response: nil, error: nil, requestObserver: observer)
}
override class func canInit(with request: URLRequest) -> Bool {
return true
}
override class func canonicalRequest(for request: URLRequest) -> URLRequest {
return request
}
override func startLoading() {
guard let stub = URLProtocolStub.stub else { return }
if let data = stub.data {
client?.urlProtocol(self, didLoad: data)
}
if let response = stub.response {
client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
}
if let error = stub.error {
client?.urlProtocol(self, didFailWithError: error)
} else {
client?.urlProtocolDidFinishLoading(self)
}
stub.requestObserver?(request)
}
}
我将其设置在 URLConfigurationSession 上,以便在 Alamaofire 上使用假会话。
let configuration = URLSessionConfiguration.af.ephemeral
configuration.protocolClasses = [URLProtocolStub.self]
最后,我的测试函数用于使用多部分数据测试HTTP请求。
func test_uploadMultipart_with_params() async {
// 1
let httpURL = HTTPURLResponse(statusCode: 204)
let bodyParams = ["firstname": "mock", "lastname": "test"]
let imageData = UIColor.yellow.image(CGSize(width: 128, height: 128)).jpegData(compressionQuality: 0.7)
URLProtocolStub.stub(data: nil, response: httpURL, error: nil)
let exp = expectation(description: "Waiting to receive response")
// 2
let loggerDelegate = StubHTTPLoggerDelegate(didReceivedResponse: { request in
XCTAssertEqual(request?.httpBody.flatMap { String(data: $0, encoding: .utf8) }, "firstname=mock&lastname=test")
exp.fulfill()
})
// 3
let result = await makeSUT(loggerDelegate: loggerDelegate).requestMultipart(imageDatas: [imageData], pathType: anyPathType(.post, bodyParameters: bodyParams, urlParameters: nil))
do { try result.get() } catch { XCTFail("\(error)") }
wait(for: [exp], timeout: 1.0)
}
我一步一步地解释这个功能:
- 我为我的多部分请求准备了我的 bodyParams 和图像。
- 我创建一个侦听器,用于在收到 HTTP 响应时进行侦听。
- 我通过传递我的侦听器来创建我的 AlamofireHTTPClient(makeSUT 函数),并使用我的 bodyParams 和图像调用我的 Alamorefire requestMultipart。然后执行我的客户端。
我的测试是检查我是否在 Alamofire 多部分请求中发送了正确的参数。 此测试失败,因为请求 (URLRequest) 中的 httpBody 为 nil,我不知道如何获取我的多部分参数来测试它:(。
谢谢你帮助我:)。
答:
在多形式零件数据中,主体或多或少是一个大数据,可以分成多个子数据(如果我们“拆分”它们:Data1+Data2+Data3...+DataN
Data1 //Boundary
Data2 //SomeParam1
Data3 //Boundary
Data4 //SomeParam2
Data5 //Boundary
...
现在,不是任何值都可以转换为 UTF8 字符串。一些“位组合”没有 UTF8 值,因此为什么你可以得到它可以是 nil。
你可以自己尝试一下,因为你有一个图像:它会是.Data
String(data: someData, encoding: .utf8)
print("Image to Str: \(String(data: imageData, encoding: .utf8))")
nil
因此,如果您尝试将大数据转换为字符串,它将不起作用,因为中间的某个部分是图像,并且为零。
所以在现实中,你应该分开你的身体并取回每个部分。 边界应位于 Header 中,因此您应该能够检索它。,通常也有很多“\n”和“\r”和“-”作为分隔符。 然后,您必须迭代并找到所需的值。
在没有看到底层 Alamofire 调用的情况下,我无法确定,但 Alamofire 的分段上传使用文件或内存中的 a,因此执行的调用将没有 .相反,它使用 从磁盘或内存流式传输数据,因此您需要读取该数据以进行解析和比较,同时考虑@Larme提到的问题。URLSessionUploadTask
URLRequest
httpBody
httpBodyStream
评论
XCTAssertEqual(request?.httpBody.flatMap...)
expectation
httpBodyStream
task.currentRequest
URLSession
评论