Ответили на оригинальный вопрос, но перепишут здесь:
Поскольку DataTaskPublisher
использует URLSession
, из которого он создан, вы можете просто высмеять это. Я закончил тем, что создал URLSession
подкласс, переопределив dataTask(...)
, чтобы вернуть URLSessionDataTask
подкласс, который я снабдил необходимыми данными / ответом / ошибкой ...
class URLSessionDataTaskMock: URLSessionDataTask {
private let closure: () -> Void
init(closure: @escaping () -> Void) {
self.closure = closure
}
override func resume() {
closure()
}
}
class URLSessionMock: URLSession {
var data: Data?
var response: URLResponse?
var error: Error?
override func dataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask {
let data = self.data
let response = self.response
let error = self.error
return URLSessionDataTaskMock {
completionHandler(data, response, error)
}
}
}
Тогда, очевидно, вы просто Если ваш сетевой уровень использует этот URLSession
, я пошел с фабрикой, чтобы сделать это:
protocol DataTaskPublisherFactory {
func make(for request: URLRequest) -> URLSession.DataTaskPublisher
}
Затем на вашем сетевом уровне:
func performRequest<ResponseType>(_ request: URLRequest) -> AnyPublisher<ResponseType, APIError> where ResponseType : Decodable {
Just(request)
.flatMap {
self.dataTaskPublisherFactory.make(for: $0)
.mapError { APIError.urlError($0)} } }
.eraseToAnyPublisher()
}
Теперь вы можете просто передать макет Фабрика в тесте с использованием подкласса URLSession
(этот утверждает, что URLError
s сопоставлены с пользовательской ошибкой, но вы также можете утверждать некоторые другие условия, данные / ответ):
func test_performRequest_URLSessionDataTaskThrowsError_throwsAPIError() {
let session = URLSessionMock()
session.error = TestError.test
let dataTaskPublisherFactory = mock(DataTaskPublisherFactory.self)
given(dataTaskPublisherFactory.make(for: any())) ~> {
session.dataTaskPublisher(for: $0)
}
let api = API(dataTaskPublisherFactory: dataTaskPublisherFactory)
let publisher: AnyPublisher<TestCodable, APIError> =
api.performRequest(URLRequest(url: URL(string: "www.someURL.com")!))
let _ = publisher.sink(receiveCompletion: {
switch $0 {
case .failure(let error):
XCTAssertEqual(error, APIError.urlError(URLError(_nsError: NSError(domain: "NSURLErrorDomain", code: -1, userInfo: nil))))
case .finished:
XCTFail()
}
}) { _ in }
}
Один проблема в том, что URLSession
init()
устарела с iOS 13, поэтому вы должны жить с предупреждением в своем тесте. Если кто-то может найти способ обойти это, я был бы очень признателен.
(Примечание: я использую Пересмешник для насмешек).