如何模拟 URLSession.DataTaskPublisher
How to mock URLSession.DataTaskPublisher
如何模拟 URLSession.DataTaskPublisher
?我有一个 class Proxy
需要注入 URLSessionProtocol
protocol URLSessionProtocol {
func loadData(from url: URL) -> URLSession.DataTaskPublisher
}
class Proxy {
private let urlSession: URLSessionProtocol
init(urlSession: URLSessionProtocol) {
self.urlSession = urlSession
}
func get(url: URL) -> AnyPublisher<Data, ProxyError> {
// Using urlSession.loadData(from: url)
}
}
此代码最初与带有完成处理程序的 URLSession
的传统版本一起使用。这是完美的,因为我可以很容易地模拟 URLSession
来像 Sundell 的解决方案一样进行测试:Mocking in Swift.
是否可以用 Combine Framework 做同样的事情?
就像您可以注入 URLSessionProtocol
来模拟具体会话一样,您也可以注入模拟的 Publisher
。例如:
let mockPublisher = Just(MockData()).eraseToAnyPublisher()
但是,根据您对该发布者所做的操作,您可能必须解决 Combine 异步发布者的一些奇怪问题,请参阅此 post 以获取更多讨论:
因为 DataTaskPublisher
使用 URLSession
创建它,你可以模拟它。我最终创建了一个 URLSession
子类,覆盖了 dataTask(...)
到 return 一个 URLSessionDataTask
子类,我用我需要的 data/response/error 喂养了它...
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: [=12=])
.mapError { APIError.urlError([=12=])} } }
.eraseToAnyPublisher()
}
现在您可以使用 URLSession
子类在测试中传递一个模拟工厂(这个断言 URLError
s 被映射到自定义错误,但您也可以断言给定的其他条件data/response):
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: [=13=])
}
let api = API(dataTaskPublisherFactory: dataTaskPublisherFactory)
let publisher: AnyPublisher<TestCodable, APIError> =
api.performRequest(URLRequest(url: URL(string: "www.someURL.com")!))
let _ = publisher.sink(receiveCompletion: {
switch [=13=] {
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 中弃用,因此您必须忍受测试中的警告。如果有人能找到解决方法,我将不胜感激。
(注意:我使用 Mockingbird 进行模拟)。
测试客户端的最佳方法是使用 URLProtocol
。
https://developer.apple.com/documentation/foundation/urlprotocol
你可以在她在云端执行真正的请求之前拦截你所有的请求,从而创造你的期望。一旦你完成了你的期望,她就会被摧毁,所以你永远不会提出真正的要求。
测试更可靠、更快,一切尽在掌握!
这里有一个小例子:https://www.hackingwithswift.com/articles/153/how-to-test-ios-networking-code-the-easy-way
但它比这更强大,你可以做任何你想做的事情:检查你的 Events/Analytics...
希望对您有所帮助!
如何模拟 URLSession.DataTaskPublisher
?我有一个 class Proxy
需要注入 URLSessionProtocol
protocol URLSessionProtocol {
func loadData(from url: URL) -> URLSession.DataTaskPublisher
}
class Proxy {
private let urlSession: URLSessionProtocol
init(urlSession: URLSessionProtocol) {
self.urlSession = urlSession
}
func get(url: URL) -> AnyPublisher<Data, ProxyError> {
// Using urlSession.loadData(from: url)
}
}
此代码最初与带有完成处理程序的 URLSession
的传统版本一起使用。这是完美的,因为我可以很容易地模拟 URLSession
来像 Sundell 的解决方案一样进行测试:Mocking in Swift.
是否可以用 Combine Framework 做同样的事情?
就像您可以注入 URLSessionProtocol
来模拟具体会话一样,您也可以注入模拟的 Publisher
。例如:
let mockPublisher = Just(MockData()).eraseToAnyPublisher()
但是,根据您对该发布者所做的操作,您可能必须解决 Combine 异步发布者的一些奇怪问题,请参阅此 post 以获取更多讨论:
因为 DataTaskPublisher
使用 URLSession
创建它,你可以模拟它。我最终创建了一个 URLSession
子类,覆盖了 dataTask(...)
到 return 一个 URLSessionDataTask
子类,我用我需要的 data/response/error 喂养了它...
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: [=12=])
.mapError { APIError.urlError([=12=])} } }
.eraseToAnyPublisher()
}
现在您可以使用 URLSession
子类在测试中传递一个模拟工厂(这个断言 URLError
s 被映射到自定义错误,但您也可以断言给定的其他条件data/response):
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: [=13=])
}
let api = API(dataTaskPublisherFactory: dataTaskPublisherFactory)
let publisher: AnyPublisher<TestCodable, APIError> =
api.performRequest(URLRequest(url: URL(string: "www.someURL.com")!))
let _ = publisher.sink(receiveCompletion: {
switch [=13=] {
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 中弃用,因此您必须忍受测试中的警告。如果有人能找到解决方法,我将不胜感激。
(注意:我使用 Mockingbird 进行模拟)。
测试客户端的最佳方法是使用 URLProtocol
。
https://developer.apple.com/documentation/foundation/urlprotocol
你可以在她在云端执行真正的请求之前拦截你所有的请求,从而创造你的期望。一旦你完成了你的期望,她就会被摧毁,所以你永远不会提出真正的要求。 测试更可靠、更快,一切尽在掌握!
这里有一个小例子:https://www.hackingwithswift.com/articles/153/how-to-test-ios-networking-code-the-easy-way
但它比这更强大,你可以做任何你想做的事情:检查你的 Events/Analytics...
希望对您有所帮助!