Поддельный URLSession для тестирования API дает нераспознанный селектор, отправленный экземпляру - PullRequest
0 голосов
/ 03 июня 2019

Я хочу протестировать API, не совершая серверный вызов, поэтому я имитирую URLSession и URLSessionDataTask, чтобы добавить его в мой класс API.

 class MockURLSession: URLSession {
    private let mockTask: MockTask
    var cachedUrl: URL?


    init(data: Data?, urlResponse: URLResponse?, error: Error?) {
        mockTask = MockTask(data: data, urlResponse: urlResponse, error:
            error)
    }

    override func dataTask(with url: URL, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask {

        self.cachedUrl = url
        mockTask.completionHandler = completionHandler
        return mockTask
    }
}

class MockTask: URLSessionDataTask {
    private let data: Data?
    private let urlResponse: URLResponse?
    var completionHandler: ((Data?, URLResponse?, Error?) -> Void)!

    init(data: Data?, urlResponse: URLResponse?, error: Error?) {
        self.data = data
        self.urlResponse = urlResponse
    }

    override func resume() {
        DispatchQueue.main.async {
            self.completionHandler(self.data, self.urlResponse, self.error)
        }
    }
}

Здесь, когда я запускаю этот тест, он проходит в моем классе API, вызывая метод getMovies. Экземпляр, который я получаю, - это своего рода MockURLSession, и это нормально. В следующий момент он выдает этот ApiTests testGetMoviesSuccessReturnsMovies]: не удалось: пойман "NSInvalidArgumentException", "- [MyAppTests.MockTask error]: нераспознанный селектор отправлен в экземпляр 0x600002010500

func testGetMoviesSuccessReturnsMovies() {
        let jsonData = "[{\"title\": \"Spider Man Far From Home\",\"detail\": \"The first Spider-Man featuring Tom Holland in the iconic role\"}]".data(using: .utf8)
        var mockURLSession  = MockURLSession(data: jsonData, urlResponse: nil, error: nil)
        let apiRespository = APIRepository(session: mockURLSession)
        let moviesExpectation = expectation(description: "movies")
        var moviesResponse: Result<[Movie]>?

        apiRespository.getMovies { (movies) in
            moviesResponse = movies
            moviesExpectation.fulfill()
        }
        waitForExpectations(timeout: 10) { (error) in
            XCTAssertNotNil(moviesResponse)
        }
}

Вот мое расширение протокола для API

extension Gettable {

    func get<T:Decodable>(with decodingType: T.Type, url: String, session: URLSession, completion:@escaping(Result<T>) -> Void) {

        let dataTask = session.dataTask(with: URL(string: url)!) { (data, response, error) in
             guard data != nil && error == nil  else {
                return
            }
            do {
                let decoder = JSONDecoder()
                let parsedObj = try decoder.decode(T.self, from: data ?? Data())
                completion(Result.success(parsedObj))
            }
            catch let parsedError {
                completion(Result.failure(parsedError))
            }
        }
        dataTask.resume()
    }
}

Ваша помощь будет высоко оценена.

1 Ответ

2 голосов
/ 03 июня 2019

Вы не переопределяете метод dataTask(with:completionHandler:) в вашем MockURLSession.Таким образом, вызывается исходный dataTask(with:completionHandler:) из URLSession, который внутренне вызывает dataTaskForRequest:completion:.

Если вы успешно переопределяете существующие методы в родительском классе, компилятор Swift заявляет о префиксе ключевого слова override.

Переместить вложенное dataTask(with:completionHandler:) из init(data:urlResponse:error:).

init(data: Data?, urlResponse: URLResponse?, error: Error?) {
    mockTask = MockTask(data: data, urlResponse: urlResponse, error:
        error)
}

override func dataTask(with url: URL, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask {

    self.cachedUrl = url
    mockTask.completionHandler = completionHandler
    return mockTask
}

Для отредактированной части ...

Кажется, что наследование error свойства неработает правильно.(Это может быть ошибка компилятора Swift, связанная с мостовым соединением NSError и Error.)

Пожалуйста, попробуйте это:

class MockTask: URLSessionDataTask {
    private let data: Data?
    private let urlResponse: URLResponse?

    private let _error: Error?
    override var error: Error? {
        return _error
    }

    var completionHandler: ((Data?, URLResponse?, Error?) -> Void)!

    init(data: Data?, urlResponse: URLResponse?, error: Error?) {
        self.data = data
        self.urlResponse = urlResponse
        self._error = error
    }

    override func resume() {
        DispatchQueue.main.async {
            self.completionHandler(self.data, self.urlResponse, self.error)
        }
    }
}
...