Session
이란 부분이 있다.URLSession의 init을 위해선 URLSessionConfiguration도 init을 해줘야 한다.
init(configuration: URLSessionConfiguration)
URLSessionConfiguration에 대한 내용은 공식문서에 이렇게 나온다.
URLSessionConfiguration.default을 사용하되 기존의 객체를 변경해주는 방법으로 Mock을 만들 수 있다.
Urlsessionconfiguration 공식문서의 "Supporting Custom Protocols" 부분을 보면 된다.
var protocolClasses: [AnyClass]?
// 세션에서 요청을 처리하는 추가 프로토콜 하위 클래스의 배열
class URLProtocol
// 프로토콜별 URL 데이터 로드를 처리하는 추상 클래스
MockURLProtocol을 만들어서 URLSessionConfiguration.default의 protocolClasses에 넣어주는 방법으로 MockSession을 사용해보자.
import Foundation
final class MockURLProtocol: URLProtocol {
// 1. 프로토콜 하위 클래스가 지정된 요청을 처리할 수 있는지 여부를 결정 -> 당연히 true
override class func canInit(with request: URLRequest) -> Bool {
return true
}
// 2. 지정된 요청의 정식 버전을 반환 -> 이건 따로 지정할 필요없이 요청을 그대로 반환한다.
override class func canonicalRequest(for request: URLRequest) -> URLRequest {
return request
}
// 3. 요청의 프로토콜별 로드를 시작 -> 이 부분에서 우리가 전달해야할 데이터를 주입해줘야 한다.
override func startLoading() {
// 3.1 내가 테스트에 사용할 MockData
let data = MockNetworkData(fileName: "Box_Office_Sample").data!
// client란 ??
// 정의(var client: URLProtocolClient?) 프로토콜이 URL 로드 시스템과 통신하는 데 사용하는 개체
// 아래부터 URLProtocolClient의 메서드를 사용해서 데이터를 전달하는 부분.
// 3.2 프로토콜 구현이 일부 데이터를 로드했음을 클라이언트에 알림 (Required) -> 데이터 전달.
client?.urlProtocol(self, didLoad: data)
// 3.3 프로토콜 구현이 요청에 대한 응답 개체를 생성했음을 클라이언트에 알림 (Required)
client?.urlProtocol(self, didReceive: HTTPURLResponse(), cacheStoragePolicy: .allowed)
// 3.4 프로토콜 구현이 로드를 완료했음을 클라이언트에 알림 (Required)
client?.urlProtocolDidFinishLoading(self)
}
// 요청의 프로토콜별 로드를 중지
override func stopLoading() {}
}
struct NetworkManager {
private let session: Session
// 기본적으론 Session.default를 사용하지만, Mock Session을 넣을 수 있도록 의존성을 주입받도록 설정.
init(session: Session = Session.default) {
self.session = session
}
func request(url: String, parameters: Parameters? = nil) -> Observable<Data> {
return Observable<Data>.create { observer in
session
.request(url, method: .get, parameters: parameters)
.validate(statusCode: 200...299)
.response { (response) in
guard let optionalData = response.value,
let data = optionalData else { return }
observer.onNext(data)
observer.onCompleted()
}
return Disposables.create()
}
}
}
import XCTest
import Foundation
import RxSwift
import Alamofire
@testable import MyBoxOffice
final class NetworkManagerTests: XCTestCase {
private let disposeBag = DisposeBag()
func test_MockSessionNetworkManager_Daily_BoxOffice_request() {
let expectation = expectation(description: "비동기 처리")
//given
let configuration = URLSessionConfiguration.af.default
configuration.protocolClasses?.insert(contentsOf: [MockURLProtocol.self], at: 0)
let mockSession = Session(configuration: configuration)
// 만들어준 mockSession을 NetworkManager init에서 의존성을 주입하는 방법으로 Session을 가로챈다.
let networkManager = NetworkManager(session: mockSession)
var movieNm: String?
let url = "MockDailyBoxOfficeURL"
networkManager.request(url: url)
.decode(type: BoxOfficeDTO.self, decoder: JSONDecoder())
.subscribe { event in
guard let data = event.element else { return }
movieNm = data.boxOfficeResult.dailyBoxOfficeList.first?.movieNm
expectation.fulfill()
}.disposed(by: disposeBag)
//when
let result = "경관의 피"
//then
waitForExpectations(timeout: 3)
XCTAssertEqual(result, movieNm)
}
}
let networkManager = NetworkManager(session: mockSession)
이 부분을 위해서 지금까지 모든걸함..https://leeari95.tistory.com/71
https://github.com/WeTransfer/Mocker#mock-callbacks
https://github.com/WeTransfer/Mocker
https://www.avanderlee.com/swift/mocking-alamofire-urlsession-requests/
https://github.com/Alamofire/Alamofire