From 86f58e20c377f65bbbda649515c19854b3808098 Mon Sep 17 00:00:00 2001 From: Ralf Ebert Date: Sun, 10 May 2020 13:41:26 +0200 Subject: [PATCH] ExampleRequestsTests ExampleRequestsTests makes actual requests to httpbin.org/jsonplaceholder.typicode.com to see common cases how the Endpoint API might be used in one place Includes examples for GET/POST (json body)/POST (form-encoded) with json results and an example how to setup a typical REST/JSON API using endpoints This test requires to be online and is tightly coupled to these particular APIs but imo it is worth it to see typical examples and have runnable examples for the library. --- Sources/TinyNetworking/Endpoint.swift | 1 + .../ExampleRequestsTests.swift | 159 ++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 Tests/TinyNetworkingTests/ExampleRequestsTests.swift diff --git a/Sources/TinyNetworking/Endpoint.swift b/Sources/TinyNetworking/Endpoint.swift index ffaae74..371c458 100644 --- a/Sources/TinyNetworking/Endpoint.swift +++ b/Sources/TinyNetworking/Endpoint.swift @@ -7,6 +7,7 @@ import FoundationNetworking public enum ContentType: String { case json = "application/json" case xml = "application/xml" + case formUrlEncoded = "application/x-www-form-urlencoded" } /// Returns `true` if `code` is in the 200..<300 range. diff --git a/Tests/TinyNetworkingTests/ExampleRequestsTests.swift b/Tests/TinyNetworkingTests/ExampleRequestsTests.swift new file mode 100644 index 0000000..fc0baa2 --- /dev/null +++ b/Tests/TinyNetworkingTests/ExampleRequestsTests.swift @@ -0,0 +1,159 @@ +@testable import TinyNetworking +import XCTest + +struct ExampleArgs: Codable { + var name: String +} + +struct RequestHeaders: Codable { + var accept: String + + enum CodingKeys: String, CodingKey { + case accept = "Accept" + } +} + +struct GetRequestResult: Codable { + var args: ExampleArgs + var headers: RequestHeaders +} + +struct PostFormRequestResult: Codable { + var form: ExampleArgs + var headers: RequestHeaders +} + +struct PostJsonRequestResult: Codable { + var data: String + var headers: RequestHeaders +} + +struct Todo: Codable { + var id: Int? + var title: String +} + +struct TodosEndpoints { + let url = URL(string: "https://jsonplaceholder.typicode.com/todos/")! + + func get() -> Endpoint<[Todo]> { + Endpoint(json: .get, url: url) + } + + func get(id: Int) -> Endpoint { + Endpoint(json: .get, url: urlFor(id: id)) + } + + func put(todo: Todo) -> Endpoint { + Endpoint(json: .put, url: urlFor(id: todo.id!), body: todo) + } + + func create(todo: Todo) -> Endpoint { + Endpoint(json: .post, url: url, body: todo) + } + + func delete(todoId: Int) -> Endpoint { + Endpoint(.delete, url: urlFor(id: todoId)) + } + + private func urlFor(id: Int) -> URL { + URL(string: String(id), relativeTo: url)! + } +} + +/** + These tests make requests to: + - https://httpbin.org (which returns the request data as json) + - https://jsonplaceholder.typicode.com (which provides a fake REST API) + to see common cases how the Endpoint API might be used in one place and + to check that TinyNetworking carries out the requests as expected. + */ +final class ExampleRequestsTests: XCTestCase { + func testGetJson() throws { + let url = URL(string: "https://httpbin.org/get")! + + let endpoint = Endpoint(json: .get, url: url, query: ["name": "hellö ABC"]) + + if let result = request(endpoint) { + XCTAssertEqual("hellö ABC", result.args.name) + XCTAssertEqual("application/json", result.headers.accept) + } else { + XCTFail("No result") + } + } + + func testPostJson() throws { + let url = URL(string: "https://httpbin.org/post")! + + let endpoint = Endpoint(json: .post, url: url, body: ExampleArgs(name: "hellö ABC")) + if let result = request(endpoint) { + // httpbin doesn't return a JSON structure but the sent data as String + let args = try JSONDecoder().decode(ExampleArgs.self, from: result.data.data(using: .utf8)!) + XCTAssertEqual("hellö ABC", args.name) + XCTAssertEqual("application/json", result.headers.accept) + } + } + + func testPostForm() throws { + let url = URL(string: "https://httpbin.org/post")! + + var urlComponents = URLComponents() + urlComponents.queryItems = [ + URLQueryItem(name: "name", value: "hellö ABC"), + ] + let body = urlComponents.url!.query!.data(using: .utf8)! + + var endpoint = Endpoint(json: .post, url: url) + endpoint.request.httpBody = body + endpoint.request.setValue(ContentType.formUrlEncoded.rawValue, forHTTPHeaderField: "Content-Type") + + if let result = request(endpoint) { + XCTAssertEqual("hellö ABC", result.form.name) + XCTAssertEqual("application/json", result.headers.accept) + } + } + + func testRestAPITodosGet() { + if let todos = request(TodosEndpoints().get()) { + XCTAssert(!todos.isEmpty) + } + if let todo = request(TodosEndpoints().get(id: 1)) { + XCTAssertEqual("delectus aut autem", todo.title) + } + } + + func testRestAPITodosPut() { + if let todo = request(TodosEndpoints().put(todo: Todo(id: 1, title: "Hello"))) { + XCTAssertEqual("Hello", todo.title) + } + } + + func testRestAPITodosPost() { + if let todo = request(TodosEndpoints().create(todo: Todo(title: "Hello"))) { + XCTAssertEqual("Hello", todo.title) + } + } + + func testRestAPITodosDelete() { + request(TodosEndpoints().delete(todoId: 1)) + } + + private func request(_ endpoint: Endpoint) -> Payload? { + let expectation = self.expectation(description: "Request result") + + var payload: Payload? + let task = URLSession.shared.load(endpoint) { result in + switch result { + case let .success(resultPayload): + payload = resultPayload + case let .failure(error): + XCTFail(String(describing: error)) + } + expectation.fulfill() + } + + task.resume() + wait(for: [expectation], timeout: 30) + return payload + } +}