Skip to content

모모의 URLSession Testable하게 만들기

DOHYUN CHUNG edited this page Sep 2, 2022 · 3 revisions

URLSession을 어떻게 Mocking 하는가

Mocking을 하는 방법은 2가지가 있다. protocol을 만들어서 채택시키고, 1. 그 protocol을 채택한 mocking 객체를 만들기 2. 상속한 다른 객체를 만들어서 init을 override 하기이다. 하지만 URLSession의 init은 deprecated 되어 있고, 다른 이니셜라이저가 다 public으로 되어있고 open으로 되어 있지 않다. 우리는 1번 방법으로 URLSession을 mocking 해서 Testable 하게 만들 수 있다.

Creating the session protocols

URLSessionProtocol을 만들고, 실제의 URLSessionTask를 만드는 대신 URLSessionTaskProtocol을 만든다. URLSessionTaskProtocol을 사용해서 resume()을 선언한다.

protocol URLSessionProtocol: AnyObject {
  
  func makeDataTask(with url: URL, completionHandler: @escaping(Data?, URLResponse?, Error?) -> Void) -> URLSessionTaskProtocol
  
  func makeDataTask(with request: URLRequest, completionHandler: @escaping(Data?, URLResponse?, Error?) -> Void) -> URLSessionTaskProtocol
}
protocol URLSessionTaskProtocol: AnyObject {
  func resume()
}

Session protocol 채택하기

URLSession이 URLSessionProtocol을 채택하도록 하고, URLSessionTask가 URLSessionTaskProtocol을 채택하도록 한다.

extension URLSessionTask: URLSessionTaskProtocol { }
extension URLSession: URLSessionProtocol {
    
    /// makeDataTask를 활용해서 dataTask를 빼낸다. dataTask는 URLSessionDataTask를 return하는데 이는 URLSessionTaskProtocol을 채택하고 있다. 이렇게 Return을 URLSessionTaskProtocol을 하면서 URLSessionTaskProtocol을 채택한 어떠한 객체를 빼도 되기 때문에 mock을 하기에 유용하다.
    /// - Parameters:
    ///   - url: URL
    ///   - completionHandler: (Data?, URLResponse?, Error?) -> Void 로 caller에서 처리할 때, Data로 뭐할것인지, Error로 뭐할것인지, URLResponse로 뭐할 것인지 적을 수 있다.
    /// - Returns: URLSessionTaskProtocol
  func makeDataTask(with url: URL, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionTaskProtocol {
    return dataTask(with: url, completionHandler: completionHandler)
  }
  
    
    /// makeDataTask를 활용해서 dataTask를 빼낸다. dataTask는 URLSessionDataTask를 return하는데 이는 URLSessionTaskProtocol을 채택하고 있다. 이렇게 Return을 URLSessionTaskProtocol을 하면서 URLSessionTaskProtocol을 채택한 어떠한 객체를 빼도 되기 때문에 mock을 하기에 유용하다.
    /// - Parameters:
    ///   - request: URLRequest를 만들어서 param으로 던지기
    ///   - completionHandler: (Data?, URLResponse?, Error?) -> Void 로 caller에서 처리할 때, Data로 뭐할것인지, Error로 뭐할것인지, URLResponse로 뭐할 것인지 적을 수 있다.
    /// - Returns: URLSessionTaskProtocol
  func makeDataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionTaskProtocol {
    return dataTask(with: request, completionHandler: completionHandler)
  }
}