如何模拟URLSession.DataTaskPublisher



如何模拟URLSession.DataTaskPublisher?我有一个需要注入URLSessionProtocol的类Proxy

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 在这里的解决方案一样:在 Swift 中模拟。

是否可以对组合框架执行相同的操作?

就像你可以注入一个URLSessionProtocol来模拟一个具体的会话一样,你也可以注入一个模拟的Publisher。例如:

let mockPublisher = Just(MockData()).eraseToAnyPublisher()

但是,根据您对此发布者执行的操作,您可能需要解决 Combine 异步发布者的一些奇怪问题,请参阅此帖子以获取其他讨论:

为什么 Combine 的接收(on:( 操作员吞咽错误?

测试客户端的最佳方法是使用URLProtocol

https://developer.apple.com/documentation/foundation/urlprotocol

您可以在她在云上执行真正的请求之前拦截所有请求,从而创建您的期望。一旦你完成了你的期望,她就会被摧毁,所以你永远不会提出真正的要求。 测试更可靠、更快,您拥有控制权!

你这里有一个小例子:https://www.hackingwithswift.com/articles/153/how-to-test-ios-networking-code-the-easy-way

但它的功能远不止于此,您可以做任何您想做的事情:检查您的事件/分析...

希望对您有所帮助!

由于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映射到自定义错误,但您也可以断言给定数据/响应的其他条件(:

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 }
}

这样做的一个问题是URLSessioninit()已从iOS 13中弃用,因此您必须在测试中接受警告。如果有人能看到解决方法,我将不胜感激。

(注意:我正在使用知更鸟进行模拟(。

相关内容

  • 没有找到相关文章

最新更新