From 50ae716c65549070512e6875f3a03b601067f9e5 Mon Sep 17 00:00:00 2001 From: Chris Jennewein Date: Sun, 12 Jul 2020 20:41:47 -0700 Subject: [PATCH] fix: Replace ResourceService baseURL - Update example and unit test with resource call --- .../CombineExampleViewController.swift | 18 +++-- PokemonAPI.podspec | 2 +- PokemonAPI.xcodeproj/project.pbxproj | 8 +++ PokemonAPI/WebServices/ResourceService.swift | 3 +- Tests/PokemonAPITests/MockData.swift | 11 ++- .../ServiceTests/ResourceServiceTests.swift | 72 +++++++++++++++++++ 6 files changed, 107 insertions(+), 7 deletions(-) create mode 100644 Tests/PokemonAPITests/ServiceTests/ResourceServiceTests.swift diff --git a/Example/PokemonAPI-Example/CombineExampleViewController.swift b/Example/PokemonAPI-Example/CombineExampleViewController.swift index 449800c..4369c7c 100644 --- a/Example/PokemonAPI-Example/CombineExampleViewController.swift +++ b/Example/PokemonAPI-Example/CombineExampleViewController.swift @@ -13,21 +13,31 @@ import Combine @available(iOS 13, *) class CombineExampleViewController: UIViewController { + let pokemonAPI = PokemonAPI() var cancellables = Set() override func viewDidLoad() { super.viewDidLoad() - PokemonAPI().berryService.fetchBerry(5) + pokemonAPI.berryService.fetchBerry(5) + .tryMap { berry -> PKMNamedAPIResource in + print(berry) + guard let berryFirmness = berry.firmness else { + throw HTTPError.unexpectedResponse + } + return berryFirmness + } + .flatMap { berryFirmness in + return self.pokemonAPI.resourceService.fetch(berryFirmness) + } .sink(receiveCompletion: { completion in if case .failure(let error) = completion { print(error.localizedDescription) } - }, receiveValue: { berry in - print(berry) + }, receiveValue: { berryFirmness in + print(berryFirmness) }) .store(in: &cancellables) } - } diff --git a/PokemonAPI.podspec b/PokemonAPI.podspec index 5aaaf66..179b31e 100644 --- a/PokemonAPI.podspec +++ b/PokemonAPI.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = "PokemonAPI" - s.version = "6.0.0" + s.version = "6.0.2" s.summary = "A wrapper for pokeapi v2" # This description is used to generate tags and improve search results. diff --git a/PokemonAPI.xcodeproj/project.pbxproj b/PokemonAPI.xcodeproj/project.pbxproj index 8e2b906..a240ee2 100644 --- a/PokemonAPI.xcodeproj/project.pbxproj +++ b/PokemonAPI.xcodeproj/project.pbxproj @@ -7,6 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 5926AD3024BB83D9000CD4BC /* ResourceServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5926AD2F24BB83D9000CD4BC /* ResourceServiceTests.swift */; }; + 5926AD3124BB83D9000CD4BC /* ResourceServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5926AD2F24BB83D9000CD4BC /* ResourceServiceTests.swift */; }; + 5926AD3224BB83D9000CD4BC /* ResourceServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5926AD2F24BB83D9000CD4BC /* ResourceServiceTests.swift */; }; 593FD62A20E54EBB00CF8862 /* Evolution.swift in Sources */ = {isa = PBXBuildFile; fileRef = 593FD62120E54EBA00CF8862 /* Evolution.swift */; }; 593FD62B20E54EBB00CF8862 /* Evolution.swift in Sources */ = {isa = PBXBuildFile; fileRef = 593FD62120E54EBA00CF8862 /* Evolution.swift */; }; 593FD62C20E54EBB00CF8862 /* Evolution.swift in Sources */ = {isa = PBXBuildFile; fileRef = 593FD62120E54EBA00CF8862 /* Evolution.swift */; }; @@ -262,6 +265,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 5926AD2F24BB83D9000CD4BC /* ResourceServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResourceServiceTests.swift; sourceTree = ""; }; 593FD62120E54EBA00CF8862 /* Evolution.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Evolution.swift; sourceTree = ""; }; 593FD62220E54EBB00CF8862 /* Locations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Locations.swift; sourceTree = ""; }; 593FD62320E54EBB00CF8862 /* Pokemon.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Pokemon.swift; sourceTree = ""; }; @@ -459,6 +463,7 @@ isa = PBXGroup; children = ( 596476382496FF9F004C7E93 /* BerryServiceTests.swift */, + 5926AD2F24BB83D9000CD4BC /* ResourceServiceTests.swift */, ); path = ServiceTests; sourceTree = ""; @@ -939,6 +944,7 @@ buildActionMask = 2147483647; files = ( 596476392496FF9F004C7E93 /* BerryServiceTests.swift in Sources */, + 5926AD3024BB83D9000CD4BC /* ResourceServiceTests.swift in Sources */, 597CEC5824A3EA5C00E90A7C /* MockedResponse.swift in Sources */, 597CEC5424A3B78A00E90A7C /* RequestMocking.swift in Sources */, 74B038131D56714D00F48B92 /* PokemonAPI_IntegrationTests.swift in Sources */, @@ -1047,6 +1053,7 @@ buildActionMask = 2147483647; files = ( 5964763A2496FFA0004C7E93 /* BerryServiceTests.swift in Sources */, + 5926AD3124BB83D9000CD4BC /* ResourceServiceTests.swift in Sources */, 597CEC5924A3EA5C00E90A7C /* MockedResponse.swift in Sources */, 597CEC5524A3B78A00E90A7C /* RequestMocking.swift in Sources */, 74B0388B1D56798100F48B92 /* PokemonAPI_IntegrationTests.swift in Sources */, @@ -1107,6 +1114,7 @@ buildActionMask = 2147483647; files = ( 5964763B2496FFA0004C7E93 /* BerryServiceTests.swift in Sources */, + 5926AD3224BB83D9000CD4BC /* ResourceServiceTests.swift in Sources */, 597CEC5A24A3EA5C00E90A7C /* MockedResponse.swift in Sources */, 597CEC5624A3B78A00E90A7C /* RequestMocking.swift in Sources */, 74B0388C1D56798300F48B92 /* PokemonAPI_IntegrationTests.swift in Sources */, diff --git a/PokemonAPI/WebServices/ResourceService.swift b/PokemonAPI/WebServices/ResourceService.swift index a9db2a0..9b533cd 100644 --- a/PokemonAPI/WebServices/ResourceService.swift +++ b/PokemonAPI/WebServices/ResourceService.swift @@ -35,7 +35,8 @@ public struct ResourceService: PKMResourceService { public var session: URLSession - public var baseURL: String = "https://pokeapi.co/api/v2" + // Resources do not need a baseURL, the full URL is in the PKMAPIResource itself. + public var baseURL: String = "" /** diff --git a/Tests/PokemonAPITests/MockData.swift b/Tests/PokemonAPITests/MockData.swift index 0767c7e..da188b1 100644 --- a/Tests/PokemonAPITests/MockData.swift +++ b/Tests/PokemonAPITests/MockData.swift @@ -7,6 +7,7 @@ // import Foundation +import PokemonAPI struct MockBerryData { @@ -531,7 +532,15 @@ struct MockPokemonData { struct MockResourceData { - + static var berryResource: PKMNamedAPIResource { + let resourceData = """ + { + "name": "cherri", + "url": "https://pokeapi.co/api/v2/berry/1/" + } + """.data(using: .utf8)! + return try! PKMNamedAPIResource.decode(from: resourceData) + } } diff --git a/Tests/PokemonAPITests/ServiceTests/ResourceServiceTests.swift b/Tests/PokemonAPITests/ServiceTests/ResourceServiceTests.swift new file mode 100644 index 0000000..5b670dc --- /dev/null +++ b/Tests/PokemonAPITests/ServiceTests/ResourceServiceTests.swift @@ -0,0 +1,72 @@ +// +// ResourceServiceTests.swift +// PokemonAPI +// +// Created by Christopher Jennewein on 7/12/20. +// Copyright © 2020 Prismatic Games. All rights reserved. +// + +import XCTest +import Combine +@testable import PokemonAPI + + +@available(OSX 10.15, iOS 13, tvOS 13.0, watchOS 6.0, *) +class ResourceServiceTests: XCTestCase { + typealias API = ResourceService.API + typealias Mock = RequestMocking.MockedResponse + + var service: ResourceService! + var cancellables = Set() + + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + cancellables = Set() + service = ResourceService(session: .mockedResponsesOnly) + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + RequestMocking.removeAllMocks() + } + + + + // MARK: - Helper + + private func mock(_ apiCall: API, result: Result, httpCode: Int = 200, paginated: Bool = false) throws { + let mock = try Mock(apiCall: apiCall, baseURL: service.baseURL, result: result, httpCode: httpCode) + RequestMocking.add(mock: mock) + } + + + + // MARK: - Tests + + func testFetchResource_success() throws { + let asyncExpectation = expectation(description: "Completion") + + try mock(.fetchResource(MockResourceData.berryResource), result: .success(MockBerryData.berry)) + + service.fetch(MockResourceData.berryResource) + .sinkToResult { result in + switch result { + case .success(let berry): + do { + let berryName = try XCTUnwrap(berry.name, "The berry should have a name") + XCTAssertTrue(berryName == "cheri", "Expected the first berry to be cheri but found \(berryName)") + } catch { + XCTFail("The response was not valid") + } + case .failure(let error): + XCTFail("The service should not fail: \(error.localizedDescription)") + } + + asyncExpectation.fulfill(); + } + .store(in: &cancellables) + + self.wait(for: [asyncExpectation], timeout: 5) + } +}