테스트 피라미드
Testing network requests
class MockURLProtocol: URLProtocol {
static var requestHandler:((URLRequest) throws -> (HTTPURLResponse, Data))?
override class func canInit(with request: URLRequest) -> Bool {
return true
}
override class func canonicalRequest(for request: URLRequest) -> URLRequest {
return request
}
override func startLoading() {
guard let handler = MockURLProtocol.requestHandler else {
XCTFail("Received unexpected request with no handler set")
return
}
do {
let (response, data) = try handler(request)
client?.urlProtocol(self, didReceive: cacheStoragePolicy: .notAllowed)
client?.urlProtocol(self, didLoad: data)
client?.urlProtocolDidFinishLoading(self)
} catch {
cliend?.urlProtocol(self, didFailWithError: error)
}
}
override func stopLoading() {
// startLoading과 비슷하다고만 하고 넘어감
}
}
class APILoaderTests: XCTestCase {
var loader: APIRequestLoader<PointOfInterestRequest>!
override func setUp() {
let request = PointsOfInterestRequest()
let configuration = URLSessionConfiguration.ephemeral
configuration.protocolClasses = [MockURLProtocol.self]
let urlSession = URLSession(configuration: configuration)
loader = APIRequestLoader(apiRequest: request, urlSession: urlSession)
}
func testLoaderSuccess() {
let inputCoordinate = CLLocationCoordinate2D(latitude: 37.3293, longtitude: -121.8893)
let mockJSONData = "[{\\"name\\":\\"MyPointOfInterest\\"}]".data(using: .utf8)!
MockURLProtocol.requestHandler = { request in
XCTAssertEqual(request.url?.query?.contains("lat=37.3293"), true)
return (HTTPURLResponse(), mockJSONData)
}
let expectation = XCTestExpectation(description: "response")
loader.loadAPIRequest(requestData: inputCoordinate) { pointOfInterest, error in
XCTAssertEqual(pointsOfInterest, [PointOfInterest(name: "MyPointOfInterest")])
expectation.fulfill()
}
wait(for: [expectation], timeout: 1)
}
Working with Notification
class CurrentLocationProviderTests: XCTestCase {
func testNotifyAuthChanged() {
let notificationCenter = NotificationCenter()
let poster = CurrentLocationProvider(notificationCenter: notificationCenter) // 기본 인자로 default를 넘기면 프로덕션에도 영향이 없어진다.
let name = CurrentLocationProvider.authChangeNotification
let expectation = XCTNSNotificationExpectation(name: name,
object: poster,
notificationCenter: notificationCenter)
poster.notifyAuthChanged()
wait(for: [expectation], timeout: 0)
}
}
Mocking with Protocols
Test execution speed
class FeaturedPlaceManagerTests: XCTestCase {
func testScheduleNextPlace() {
var timerScheduler = MockTimeScheduler()
var timerDelay = TimeInterval(0)
timerScheduler.handleAddTimer = { timer
timerDelay = timer.fireDate.timeIntervalSinceNow
timer.fire()
}
let manager = FeaturedPlaceMananger(timerScheduler: timerScheduler)
let beforePlace = manager.currentPlace
manager.scheduleNextPlace()
XCTAssertEqual(timerDelay, 10, accuracy: 1)
XCTAssertNotEqual(manager.currentPlace, beforePlace)
}
}