测试 NSURLSession "resume cannot be sent to abstract instance of class NSURLSessionDataTask"

Testing NSURLSession "resume cannot be sent to abstract instance of class NSURLSessionDataTask"

我想测试 NSURLSession 但我收到错误 "resume cannot be sent to abstract instance of class NSURLSessionDataTask"。

示例代码在https://github.com/stevencurtis/abstract-instanceofclassNSURLSessionDataTask

我的 HTTP 管理器工作正常:

class HTTPManager {

    static let shared: HTTPManager = HTTPManager()

    private let session: URLSessionProtocol

    init(session: URLSessionProtocol = URLSession.shared) {
        self.session = session

    }

    public func get(urlString: String, completionBlock: ((Data?) -> Void)?) {

        let url = URL(string: urlString)
        if let usableUrl = url {
            let request = URLRequest(url: usableUrl)
            let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
                (completionBlock?(data))!
            })
            task.resume()
        }
    }

}

然而我试图嘲笑它

class MockURLSession: URLSessionProtocol {
    func dataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask {
        return URLSessionDataTask()
    }
    var searchedURL = URL(string: "asd")
}

然后创建测试:

func testGetRequest() {
    let url = URL(string: "url")
    let session = MockURLSession()
    let sub = HTTPManager(session: session)


    sub.get(urlString: "url", completionBlock: { [weak self] (data: Data?) -> Void in
        print ("vc")
        }
    )
}

测试错误:resume cannot be sent to abstract instance of class NSURLSessionDataTask”,我想不出解决这个错误的方法!

HERE有人重写了这篇文章,您可以在 Playground 中测试以下代码:

import UIKit
import XCTest
import PlaygroundSupport
import Foundation

// Protocol for MOCK/Real
protocol URLSessionProtocol {
    typealias DataTaskResult = (Data?, URLResponse?, Error?) -> Void

    func dataTask(with request: URLRequest, completionHandler: @escaping DataTaskResult) -> URLSessionDataTaskProtocol
}

protocol URLSessionDataTaskProtocol {
    func resume()
}

//MARK: HttpClient Implementation
class HttpClient {

    typealias completeClosure = ( _ data: Data?, _ error: Error?)->Void

    private let session: URLSessionProtocol

    init(session: URLSessionProtocol) {
        self.session = session

    }

    func get( url: URL, callback: @escaping completeClosure ) {
        var request = URLRequest(url: url)
        request.httpMethod = "GET"
        let task = session.dataTask(with: request) { (data, response, error) in
            callback(data, error)
        }
        task.resume()
    }

}

//MARK: Conform the protocol
extension URLSession: URLSessionProtocol {
    func dataTask(with request: URLRequest, completionHandler: @escaping URLSessionProtocol.DataTaskResult) -> URLSessionDataTaskProtocol {
        return dataTask(with: request, completionHandler: completionHandler) as URLSessionDataTask
    }
}

extension URLSessionDataTask: URLSessionDataTaskProtocol {}

//MARK: MOCK
class MockURLSession: URLSessionProtocol {

    var nextDataTask = MockURLSessionDataTask()
    var nextData: Data?
    var nextError: Error?

    private (set) var lastURL: URL?

    func successHttpURLResponse(request: URLRequest) -> URLResponse {
        return HTTPURLResponse(url: request.url!, statusCode: 200, httpVersion: "HTTP/1.1", headerFields: nil)!
    }

    func dataTask(with request: URLRequest, completionHandler: @escaping DataTaskResult) -> URLSessionDataTaskProtocol {
        lastURL = request.url

        completionHandler(nextData, successHttpURLResponse(request: request), nextError)
        return nextDataTask
    }

}

class MockURLSessionDataTask: URLSessionDataTaskProtocol {
    private (set) var resumeWasCalled = false

    func resume() {
        resumeWasCalled = true
    }
}

//MARK: Test
class HttpClientTests: XCTestCase {

    var httpClient: HttpClient!
    let session = MockURLSession()

    override func setUp() {
        super.setUp()
        httpClient = HttpClient(session: session)
    }

    override func tearDown() {
        super.tearDown()
    }

    func test_get_request_with_URL() {

        guard let url = URL(string: "http://gojek-contacts-app.herokuapp.com/contacts.json") else {
            fatalError("URL can't be empty")
        }

        httpClient.get(url: url) { (success, response) in
            // Return data
        }

        XCTAssert(session.lastURL == url)

    }

    func test_get_resume_called() {

        let dataTask = MockURLSessionDataTask()
        session.nextDataTask = dataTask

        guard let url = URL(string: "http://gojek-contacts-app.herokuapp.com/contacts.json") else {
            fatalError("URL can't be empty")
        }

        httpClient.get(url: url) { (success, response) in
            // Return data
        }

        XCTAssert(dataTask.resumeWasCalled)
    }

    func test_get_should_return_data() {
        let expectedData = "{}".data(using: .utf8)

        session.nextData = expectedData

        var actualData: Data?
        httpClient.get(url: URL(string: "http://gojek-contacts-app.herokuapp.com/contacts.json")!) { (data, error) in
            actualData = data
        }

        XCTAssertNotNil(actualData)
    }

}

HttpClientTests.defaultTestSuite.run()