测试时未实现MoceUrlProtocol requestHandler数据的Urlsession提取方法
由于网络测试的官方接受嘲笑了URLProtocol,因此我尝试过这样的尝试:
@testable import MovieTask
import Foundation
class MockURLProtocol: URLProtocol{
static var contactedURLs : [URL] = []
static var callCount = 0
static func clear() {
callCount = 0
contactedURLs.removeAll()
}
static var requestHandler: ((URLRequest)throws-> (HTTPURLResponse,Data?))?
override class func canInit(with request: URLRequest) -> Bool {
callCount += 1
if let url = request.url{
contactedURLs.append(url)
}
return true
}
override class func canonicalRequest(for request: URLRequest) -> URLRequest {
return request
}
override func startLoading() {
guard let handler = MockURLProtocol.requestHandler else {
fatalError("Handler unavailable")
}
do {
let (response, data) = try handler(request)
client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
if let data = data {
client?.urlProtocol(self, didLoad: data)
}
client?.urlProtocolDidFinishLoading(self)
}
catch {
client?.urlProtocol(self, didFailWithError: error)
}
}
override func stopLoading() {}
}
为了获取数据,我制作了使用urlsession的getMoviewMethod的struct movierequest:
struct MovieRequest {
var urlSession : URLSession
init(endpoint:MovieEndpoint, urlSession: URLSession = URLSession.shared){
self.urlSession = urlSession
}
func getMovies(completion: @escaping (Result<[Movie], MovieError>)-> Void){
urlSession.dataTask(with: resourceUrl){ data,response,error in
//(code for resourceUrl is redundant)
if error != nil {
completion(.failure(.networkError))
} else if let response = response as? HTTPURLResponse,response.statusCode != 200{
completion(.failure(.networkError))
} else if let data = data {
do{
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let decoded = try decoder.decode(MovieResponse.self, from: data)
let decodero = decoded.results
completion(.success(decodero))
} catch {
completion(.failure(.noData))
}
}
}.resume()
我开始了测试:
class MovieTaskTests: XCTestCase {
var sut: ViewController!
var movieRequest: MovieRequest!
let apiURL = URL(string: "https://api.themoviedb.org/3/")
override func setUp() {
#if DEBUG
let configuration = URLSessionConfiguration.default
configuration.protocolClasses = [MockURLProtocol.self]
let urlSession = URLSession.init(configuration: configuration)
#else
let urlSession = URLSession.shared
#endif
sut = ViewController()
movieRequest = MovieRequest(endpoint: .popular,urlSession: urlSession)
sut.loadViewIfNeeded()
}
这是获取JSON数据的测试。问题在于MoviereQuest始终使用Urlsession。共享,从来都不是MockurlProtocol,因此结果总是很大的JSON。
func test_FetchShouldBeCalledOnceWithThisURL(){
let expectation = expectation(description: "Wait")
let data = jsonData()//json func code redudant
MockURLProtocol.requestHandler = {request in
guard let url = request.url, url == self.apiURL else{
throw MYError.noError
}
let response = HTTPURLResponse(url: self.apiURL!, statusCode: 200, httpVersion: nil, headerFields: nil)!
return(response,data)
}
movieRequest.getMovies{ (result) in
switch result{
case .success(let post):
XCTAssertNotNil(post)
self.sut.listOfMovies = post
case.failure(let error):
XCTFail("Error was not expected: \(error)")
}
expectation.fulfill()
}
XCTAssertEqual(sut.listOfMovies, [Movie(originalTitle: "Lol", overview: "lol", posterPath: "lol", releaseDate: "lol")])//Very big json or empty when #else deleted
XCTAssertEqual(MockURLProtocol.contactedURLs.first?.description, self.apiURL?.description)//Works well
XCTAssertEqual(MockURLProtocol.callCount, 1)//for some reason its 3 instead of 1
wait(for: [expectation], timeout: 0.1)
}
我尝试删除#else零件,但后来我得到了空数组。
As official reccommendation for network test is mocking urlProtocol I've tried it like this:
@testable import MovieTask
import Foundation
class MockURLProtocol: URLProtocol{
static var contactedURLs : [URL] = []
static var callCount = 0
static func clear() {
callCount = 0
contactedURLs.removeAll()
}
static var requestHandler: ((URLRequest)throws-> (HTTPURLResponse,Data?))?
override class func canInit(with request: URLRequest) -> Bool {
callCount += 1
if let url = request.url{
contactedURLs.append(url)
}
return true
}
override class func canonicalRequest(for request: URLRequest) -> URLRequest {
return request
}
override func startLoading() {
guard let handler = MockURLProtocol.requestHandler else {
fatalError("Handler unavailable")
}
do {
let (response, data) = try handler(request)
client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
if let data = data {
client?.urlProtocol(self, didLoad: data)
}
client?.urlProtocolDidFinishLoading(self)
}
catch {
client?.urlProtocol(self, didFailWithError: error)
}
}
override func stopLoading() {}
}
For fetching data I made struct MovieRequest that has getMoviewMethod which uses urlSession:
struct MovieRequest {
var urlSession : URLSession
init(endpoint:MovieEndpoint, urlSession: URLSession = URLSession.shared){
self.urlSession = urlSession
}
func getMovies(completion: @escaping (Result<[Movie], MovieError>)-> Void){
urlSession.dataTask(with: resourceUrl){ data,response,error in
//(code for resourceUrl is redundant)
if error != nil {
completion(.failure(.networkError))
} else if let response = response as? HTTPURLResponse,response.statusCode != 200{
completion(.failure(.networkError))
} else if let data = data {
do{
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let decoded = try decoder.decode(MovieResponse.self, from: data)
let decodero = decoded.results
completion(.success(decodero))
} catch {
completion(.failure(.noData))
}
}
}.resume()
I started my tests:
class MovieTaskTests: XCTestCase {
var sut: ViewController!
var movieRequest: MovieRequest!
let apiURL = URL(string: "https://api.themoviedb.org/3/")
override func setUp() {
#if DEBUG
let configuration = URLSessionConfiguration.default
configuration.protocolClasses = [MockURLProtocol.self]
let urlSession = URLSession.init(configuration: configuration)
#else
let urlSession = URLSession.shared
#endif
sut = ViewController()
movieRequest = MovieRequest(endpoint: .popular,urlSession: urlSession)
sut.loadViewIfNeeded()
}
This is test for fetching json data. The problem is that movieRequest always use URLSession.shared, never MockURLProtocol, so result is always very big json.
func test_FetchShouldBeCalledOnceWithThisURL(){
let expectation = expectation(description: "Wait")
let data = jsonData()//json func code redudant
MockURLProtocol.requestHandler = {request in
guard let url = request.url, url == self.apiURL else{
throw MYError.noError
}
let response = HTTPURLResponse(url: self.apiURL!, statusCode: 200, httpVersion: nil, headerFields: nil)!
return(response,data)
}
movieRequest.getMovies{ (result) in
switch result{
case .success(let post):
XCTAssertNotNil(post)
self.sut.listOfMovies = post
case.failure(let error):
XCTFail("Error was not expected: \(error)")
}
expectation.fulfill()
}
XCTAssertEqual(sut.listOfMovies, [Movie(originalTitle: "Lol", overview: "lol", posterPath: "lol", releaseDate: "lol")])//Very big json or empty when #else deleted
XCTAssertEqual(MockURLProtocol.contactedURLs.first?.description, self.apiURL?.description)//Works well
XCTAssertEqual(MockURLProtocol.callCount, 1)//for some reason its 3 instead of 1
wait(for: [expectation], timeout: 0.1)
}
I have tried deleting #else part but then I get empty array.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论