You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用URLProtocol子类化做单元测试:Data返回0字节而非nil

iOS单元测试中MockURLProtocol返回nil data却变成0字节Data的问题

首先,抱歉我的英文表述可能不够流畅。我正在为学校项目开发一款iOS应用,遇到了一个问题。我通过URLProtocol方法对API调用进行单元测试。

API调用代码

task?.cancel()
task = exchangeSession.dataTask(with: request) { data, response, error in
     // The dataTask method will execute in a separate queue, so we 
     // get back into the main one because
     // we will modify the user interface with our exchange result
     
     DispatchQueue.main.async {
          guard let data = data, error == nil else {
               callback(false, nil, error)
               return
          }
                
          guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
               callback(false, nil, nil)
               return
          }
                
          guard let responseJSON = try? JSONDecoder().decode(ConvertResponse.self, from: data), let result = responseJSON.result else {
               callback(false, nil, nil)
               return
          }
          callback(true, result, nil)
     }
}
task?.resume()

MockURLProtocol实现

final class MockURLProtocol: URLProtocol {
    
    // We return true in order to allow URLSession to use this protocol for any URL Request
    override class func canInit(with request: URLRequest) -> Bool {
        return true
    }
    
    override class func canonicalRequest(for request: URLRequest) -> URLRequest {
        return request
    }

    static var loadingHandler: ((URLRequest) -> (Data?, HTTPURLResponse?, Error?))?
    
    override func startLoading() {
        guard let handler = MockURLProtocol.loadingHandler else {
            print("Loading handler is not set.")
            return
        }
        
        let (data, response, error) = handler(request)
        
        guard error == nil else {
            client?.urlProtocol(self, didFailWithError: error!)
            return
        }
        
        if let data = data {
            client?.urlProtocol(self, didLoad: data)
        }
        
        if let response = response {
            client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
        }
        
        client?.urlProtocolDidFinishLoading(self)
    }
    
    override func stopLoading() {}
}

如你所见,它包含一个可返回data、response和error的handler。

在其中一个测试用例中,我想验证无数据的场景,因此将data参数设为nil。

测试代码

func testConvertShouldPostFailedCallbackIfNoData() {
        MockURLProtocol.loadingHandler = { request in
            return (nil, nil, nil)
        }
                
        let expectation = XCTestExpectation(description: "Wait for queue change.")
        client.convert(from: from, to: to, amount: amount) { success, result, error in
            XCTAssertFalse(success)
            XCTAssertNil(result)
            XCTAssertNil(error)
            expectation.fulfill()
        }
        
        wait(for: [expectation], timeout: 0.01)
    }

我的问题是:data参数并未返回nil,而是显示为“0 Bytes”,但error和response设为nil却能正常生效。

测试结果:测试中传入的nil data被转为0字节的Data,导致代码无法进入预期的逻辑分支。

我想请教为什么data参数不是nil?0字节的Data不会被判定为nil,导致我的代码无法进入正确的逻辑分支。

我尝试了很多断点调试仍未找到原因,但发现一个奇怪的现象:如果在handler中设置error并将data设为nil,此时data会是nil。这可能和didFailWithError函数有关吗?该函数是否会强制将data设为nil?


内容的提问来源于stack exchange,提问作者Jintae

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 05:05:20