Using extensions for functions common to all tests
Besides the setUp() and tearDown() methods in the Kitura test boilerplate code, you'll also find the same code reusing strategy in the extension URLRequest. Extensions in Swift add new functionality to an existing class for which you do not have access to the source code. In the URLRequest extension, the Kitura test boilerplate code extends the class URLRequest in the Swift Foundation library to include URL parsing and handling code specific to the Kitura server environment:
private extension URLRequest { // [1]
// [2]
init?(forTestWithMethod method: String, route: String = "", body: Data? = nil) {
if let url = URL(string: "http://127.0.0.1:\(RouteTests.port)/" + route){ // [3]
self.init(url: url)
addValue("application/json", forHTTPHeaderField: "Content-Type")
httpMethod = method
cachePolicy = .reloadIgnoringCacheData
if let body = body {
httpBody = body
}
} else {
XCTFail("URL is nil...")
return nil
}
}
func sendForTestingWithKitura(fn: @escaping (Data, Int) -> Void) { // [4]
guard let method = httpMethod, var path = url?.path, let headers = allHTTPHeaderFields else {
XCTFail("Invalid request params")
return
}
if let query = url?.query {
path += "?" + query
}
let requestOptions: [ClientRequest.Options] = [.method(method), .hostname("localhost"), \
.port(8080), .path(path), .headers(headers)]
let req = HTTP.request(requestOptions) { resp in
if let resp = resp, resp.statusCode == HTTPStatusCode.OK || resp.statusCode ==
HTTPStatusCode.accepted {
do {
var body = Data()
try resp.readAllData(into: &body)
fn(body, resp.statusCode.rawValue)
} catch {
print("Bad JSON document received from Kitura-Starter.")
}
} else {
if let resp = resp {
print("Status code: \(resp.statusCode)")
var rawUserData = Data()
do {
let _ = try resp.read(into: &rawUserData)
let str = String(data: rawUserData, encoding: String.Encoding(rawValue:
String.Encoding.utf8.rawValue))
print("Error response from Kitura-Starter: \(String(describing: str))")
} catch {
print("Failed to read response data.")
}
}
}
}
if let dataBody = httpBody {
req.end(dataBody)
} else {
req.end()
}
}
}
The following explains what this extension does:
- Declares an extension to URLRequest
- Includes a failable initializer
- Wraps the URL optional and retrieves its associated networking parameters
- Implements a function that constructs an HTTP request and processes its corresponding HTTP response
The init?() method in [2] is called a failable initializer. It means that the initialization can fail. For example, failure may occur when the networking parameters are incorrect or when a required external resource is not present. The pass/fail condition is checked when the supplied URL optional is unwrapped and validated.
The function in [4] is used by each test when sending an HTTP request and waiting for its HTTP response. It is a manifestation of the Separation of Concerns concept so each test can focus on validating the test results while the URLRequest extension function can focus on handling the HTTP request/response.