Как правильно смоделировать этот обработчик завершения для тестирования - PullRequest
0 голосов
/ 26 декабря 2018

Я хотел бы написать модульные тесты для моего сетевого кода.

Я бы хотел сказать, что мой хост настроен правильно.

В моем сетевом коде используются общие шаблоны, поэтому я могу передать ожидаемую модель ответа на основе конечной точки, которую я вызываю.

Мой XCTTestCase выглядит так:

@testable import Home
import XCTest

class APIClientTests: XCTestCase {
    var sut: APIClient!
    var mockURLSession: MockURLSession!

    override func setUp() {
        sut = APIClient()
        mockURLSession = MockURLSession(data: nil, urlResponse: nil, error: nil)
        sut.session = mockURLSession
    }

    func test_Execute_UsesCorrectHost() {
        guard let request = createURLObject("https://foo.bar.com") else { XCTFail("Could not create URL object"); return }

        let completion = { (Result<String>) in }

        sut.call(with: request, completion: completion)

        XCTAssertEqual(mockURLSession?.urlComponents?.host, "foo.bar")
    }
}

extension APIClientTests {
    class MockURLSession: SessionProtocol {
        var url: URL?
        private let dataTask: MockTask
        var urlComponents: URLComponents? {
            guard let url = url else { return nil }
            return URLComponents(url: url, resolvingAgainstBaseURL: true)
        }

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

        func dataTask(with url: URL, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask {
            self.url = url
            dataTask.completionHandler = completionHandler
            return dataTask
        }
    }

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

        typealias CompletionHandler = (Data?, URLResponse?, Error?) -> Void
        var completionHandler: CompletionHandler?

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

        override func resume() {
            DispatchQueue.main.async { [weak self] in
                self?.completionHandler?(self?.data, self?.urlResponse, self?.responseError)
            }
        }
    }

    func createURLObject(_ url: String) -> URLRequest? {
        guard let url = URL(string: url) else { return nil }
        return URLRequest(url: url)
    }
}

Код, который я пытаюсь проверить, выглядит следующим образом:

import Foundation

enum Result<T> {
    case success(T)
    case failure(String)
}

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

protocol APIClientProtocol {
    var session: SessionProtocol { mutating get }
    func call<T: Codable>(with request: URLRequest, completion: @escaping (Result<T>) -> Void) -> Void

}

struct APIClient: APIClientProtocol {

    lazy var session: SessionProtocol = URLSession.shared

    func call<T: Codable>(with request: URLRequest, completion: @escaping (Result<T>) -> Void) -> Void { }

}

extension URLSession: SessionProtocol { }

Вместо этого в этой строке:

let completion = { (Result<String>) in }

Я получаю следующие ошибки:

Невозможно определить тип возвращаемого значения сложного замыкания;добавить явный тип для устранения неоднозначности Ожидаемое имя параметра, за которым следует ':'

...