测试时未实现MoceUrlProtocol requestHandler数据的Urlsession提取方法

发布于 2025-02-08 11:52:36 字数 4547 浏览 2 评论 0原文

由于网络测试的官方接受嘲笑了URLProtocol,因此我尝试过这样的尝试:

  @testable import MovieTask
  import Foundation


class MockURLProtocol: URLProtocol{
    
    static var contactedURLs : [URL] = []
    static var callCount = 0
    static func clear() {
        callCount = 0
        contactedURLs.removeAll()
    }
    
    static var requestHandler: ((URLRequest)throws-> (HTTPURLResponse,Data?))?
    override class func canInit(with request: URLRequest) -> Bool {
        
        callCount += 1
        if let url = request.url{
            contactedURLs.append(url)
        }
        return true
    }
    
    override class func canonicalRequest(for request: URLRequest) -> URLRequest {
        
        return request
    }
    
    override func startLoading() {
        
        guard let handler = MockURLProtocol.requestHandler else {
            fatalError("Handler unavailable")
        }
        do {
            let (response, data) = try handler(request)
            
            client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
            
            if let data = data {
                client?.urlProtocol(self, didLoad: data)
            }
            client?.urlProtocolDidFinishLoading(self)
        }
        catch {
            client?.urlProtocol(self, didFailWithError: error)
        }
    }
    
    override func stopLoading() {}
    
}

为了获取数据,我制作了使用urlsession的getMoviewMethod的struct movierequest:

struct MovieRequest {

var urlSession : URLSession

init(endpoint:MovieEndpoint, urlSession: URLSession = URLSession.shared){
    self.urlSession = urlSession
   
}

func getMovies(completion: @escaping (Result<[Movie], MovieError>)-> Void){
    
    urlSession.dataTask(with: resourceUrl){ data,response,error in
                    //(code for resourceUrl is redundant)
        if error != nil {
            completion(.failure(.networkError))
        } else if let response = response as? HTTPURLResponse,response.statusCode != 200{
            completion(.failure(.networkError))
        } else if let data = data {
            do{
                let decoder = JSONDecoder()
                decoder.keyDecodingStrategy = .convertFromSnakeCase
                let decoded = try decoder.decode(MovieResponse.self, from: data)
                let decodero = decoded.results
                completion(.success(decodero))
            } catch {
                
                completion(.failure(.noData))
            }
        }
        
    }.resume()

我开始了测试:

    class MovieTaskTests: XCTestCase {

   var sut: ViewController!
    var movieRequest: MovieRequest!

  let apiURL = URL(string: "https://api.themoviedb.org/3/")
   override func setUp() {
 #if DEBUG
    let configuration = URLSessionConfiguration.default
    configuration.protocolClasses = [MockURLProtocol.self]
    let urlSession = URLSession.init(configuration: configuration)
 #else
    let urlSession = URLSession.shared
 #endif
    sut = ViewController()
    
    movieRequest = MovieRequest(endpoint: .popular,urlSession: urlSession)
    sut.loadViewIfNeeded()
 }

这是获取JSON数据的测试。问题在于MoviereQuest始终使用Urlsession。共享,从来都不是MockurlProtocol,因此结果总是很大的JSON。

    func test_FetchShouldBeCalledOnceWithThisURL(){
    let expectation = expectation(description: "Wait")
    let data = jsonData()//json func code redudant
    MockURLProtocol.requestHandler = {request in
        guard let url = request.url, url == self.apiURL else{
            throw MYError.noError
        }
        let response = HTTPURLResponse(url: self.apiURL!, statusCode: 200, httpVersion: nil, headerFields: nil)!
        return(response,data)
    }
    movieRequest.getMovies{ (result) in
        switch result{
        case .success(let post):
            XCTAssertNotNil(post)
            self.sut.listOfMovies = post
        case.failure(let error):
            XCTFail("Error was not expected: \(error)")
        }
        expectation.fulfill()
        
    }
    
    XCTAssertEqual(sut.listOfMovies, [Movie(originalTitle: "Lol", overview: "lol", posterPath: "lol", releaseDate: "lol")])//Very big json or empty when #else deleted
    XCTAssertEqual(MockURLProtocol.contactedURLs.first?.description, self.apiURL?.description)//Works well
    XCTAssertEqual(MockURLProtocol.callCount, 1)//for some reason its 3 instead of 1
              wait(for: [expectation], timeout: 0.1)
        
    }

我尝试删除#else零件,但后来我得到了空数组。

As official reccommendation for network test is mocking urlProtocol I've tried it like this:

  @testable import MovieTask
  import Foundation


class MockURLProtocol: URLProtocol{
    
    static var contactedURLs : [URL] = []
    static var callCount = 0
    static func clear() {
        callCount = 0
        contactedURLs.removeAll()
    }
    
    static var requestHandler: ((URLRequest)throws-> (HTTPURLResponse,Data?))?
    override class func canInit(with request: URLRequest) -> Bool {
        
        callCount += 1
        if let url = request.url{
            contactedURLs.append(url)
        }
        return true
    }
    
    override class func canonicalRequest(for request: URLRequest) -> URLRequest {
        
        return request
    }
    
    override func startLoading() {
        
        guard let handler = MockURLProtocol.requestHandler else {
            fatalError("Handler unavailable")
        }
        do {
            let (response, data) = try handler(request)
            
            client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
            
            if let data = data {
                client?.urlProtocol(self, didLoad: data)
            }
            client?.urlProtocolDidFinishLoading(self)
        }
        catch {
            client?.urlProtocol(self, didFailWithError: error)
        }
    }
    
    override func stopLoading() {}
    
}

For fetching data I made struct MovieRequest that has getMoviewMethod which uses urlSession:

struct MovieRequest {

var urlSession : URLSession

init(endpoint:MovieEndpoint, urlSession: URLSession = URLSession.shared){
    self.urlSession = urlSession
   
}

func getMovies(completion: @escaping (Result<[Movie], MovieError>)-> Void){
    
    urlSession.dataTask(with: resourceUrl){ data,response,error in
                    //(code for resourceUrl is redundant)
        if error != nil {
            completion(.failure(.networkError))
        } else if let response = response as? HTTPURLResponse,response.statusCode != 200{
            completion(.failure(.networkError))
        } else if let data = data {
            do{
                let decoder = JSONDecoder()
                decoder.keyDecodingStrategy = .convertFromSnakeCase
                let decoded = try decoder.decode(MovieResponse.self, from: data)
                let decodero = decoded.results
                completion(.success(decodero))
            } catch {
                
                completion(.failure(.noData))
            }
        }
        
    }.resume()

I started my tests:

    class MovieTaskTests: XCTestCase {

   var sut: ViewController!
    var movieRequest: MovieRequest!

  let apiURL = URL(string: "https://api.themoviedb.org/3/")
   override func setUp() {
 #if DEBUG
    let configuration = URLSessionConfiguration.default
    configuration.protocolClasses = [MockURLProtocol.self]
    let urlSession = URLSession.init(configuration: configuration)
 #else
    let urlSession = URLSession.shared
 #endif
    sut = ViewController()
    
    movieRequest = MovieRequest(endpoint: .popular,urlSession: urlSession)
    sut.loadViewIfNeeded()
 }

This is test for fetching json data. The problem is that movieRequest always use URLSession.shared, never MockURLProtocol, so result is always very big json.

    func test_FetchShouldBeCalledOnceWithThisURL(){
    let expectation = expectation(description: "Wait")
    let data = jsonData()//json func code redudant
    MockURLProtocol.requestHandler = {request in
        guard let url = request.url, url == self.apiURL else{
            throw MYError.noError
        }
        let response = HTTPURLResponse(url: self.apiURL!, statusCode: 200, httpVersion: nil, headerFields: nil)!
        return(response,data)
    }
    movieRequest.getMovies{ (result) in
        switch result{
        case .success(let post):
            XCTAssertNotNil(post)
            self.sut.listOfMovies = post
        case.failure(let error):
            XCTFail("Error was not expected: \(error)")
        }
        expectation.fulfill()
        
    }
    
    XCTAssertEqual(sut.listOfMovies, [Movie(originalTitle: "Lol", overview: "lol", posterPath: "lol", releaseDate: "lol")])//Very big json or empty when #else deleted
    XCTAssertEqual(MockURLProtocol.contactedURLs.first?.description, self.apiURL?.description)//Works well
    XCTAssertEqual(MockURLProtocol.callCount, 1)//for some reason its 3 instead of 1
              wait(for: [expectation], timeout: 0.1)
        
    }

I have tried deleting #else part but then I get empty array.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文