Skip to content

Instantly share code, notes, and snippets.

@volonbolon
Created January 16, 2018 13:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save volonbolon/45683f472e45001f83e6b4b9fad01176 to your computer and use it in GitHub Desktop.
Save volonbolon/45683f472e45001f83e6b4b9fad01176 to your computer and use it in GitHub Desktop.
Testing using mocks
import XCTest
@testable import WeatherApp
extension XCTestCase { // JSON support
func jsonData(payload: Any) throws -> Data {
let options: JSONSerialization.WritingOptions = [.prettyPrinted, .sortedKeys]
let data = try JSONSerialization.data(withJSONObject: payload, options: options)
return data
}
}
class WeatherAppTests: XCTestCase {
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testOpenWeatherMap() {
let exp = expectation(description: "Get weather data")
let session = MockURLSession()
self.prepareRetreiveWeatherSessionDataSucess(session)
let controller = OpenWeatherMapNetworkController(session: session)
let input = Input(location: "Campana", unit: TemperatureUnit.metric)
controller.fetchCurrentWeatherData(input: input) { (result: Either<NetworkControllerError, WeatherData>) in
switch result {
case .left:
XCTFail("no data returned by fetchWeatherData()")
case .right(let data):
let city = input.location
let condition = data.condition
let temperature = data.temperature
XCTAssertEqual(city, "Campana")
XCTAssertEqual(condition, "Clear")
XCTAssertEqual(temperature, 42.8)
exp.fulfill()
}
}
waitForExpectations(timeout: 10, handler: nil)
}
func testOpenWeatherShouldFailWithError() {
let exp = expectation(description: "Get weather data")
let session = MockURLSession()
let error = NSError(domain: "asd", code: 123, userInfo: nil)
session.error = error as Error
self.prepareRetreiveWeatherSessionDataSucess(session)
let controller = OpenWeatherMapNetworkController(session: session)
let input = Input(location: "Campana", unit: TemperatureUnit.metric)
controller.fetchCurrentWeatherData(input: input) { (result: Either<NetworkControllerError, WeatherData>) in
switch result {
case .left(let error):
XCTAssertNotNil(error)
exp.fulfill()
case .right:
XCTFail("It should fail")
}
}
waitForExpectations(timeout: 10, handler: nil)
}
}
extension WeatherAppTests { // MARK: Session mock production
fileprivate func prepareRetreiveWeatherSessionDataSucess(_ session: MockURLSession) {
let dataPayload: [String: Any] = [
"weather": [
[
"id": 800,
"main": "Clear",
"description": "clear sky",
"icon": "01n"
]
],
"main":
[
"temp": 42.8,
"pressure": 1019,
"humidity": 100,
"temp_min": 42.8,
"temp_max": 42.8
]
]
do {
let data = try self.jsonData(payload: dataPayload)
session.data = data
} catch {
print(error)
}
}
}
class MockURLSessionDataTask: URLSessionDataTask {
// I know, against Apple naming convention,
// but I rather have the `Mock` part right on front
// to prevent autocompletion to introduce errors.
private let closure: () -> Void
init(closure: @escaping () -> Void) {
self.closure = closure
}
override func resume() {
self.closure()
}
}
class MockURLSession: URLSession {
typealias CompletionHandler = (Data?, URLResponse?, Error?) -> Void
var data: Data?
var error: Error?
var response: URLResponse?
override func dataTask(with request: URLRequest,
completionHandler: @escaping CompletionHandler) -> URLSessionDataTask {
let data = self.data
let response = self.response
let error = self.error
let dataTask = MockURLSessionDataTask(closure: {
completionHandler(data, response, error)
})
return dataTask
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment