Skip to content

Commit

Permalink
adding unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
xramos committed May 30, 2022
1 parent 4e1f14f commit 28cd231
Show file tree
Hide file tree
Showing 12 changed files with 335 additions and 13 deletions.
16 changes: 16 additions & 0 deletions Pokemon/Pokemon.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
objects = {

/* Begin PBXBuildFile section */
DA2FBEAE2844CCB800170EE7 /* URLProtocolMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA2FBEAD2844CCB800170EE7 /* URLProtocolMock.swift */; };
DA2FBEB02844CD0B00170EE7 /* RemotePokemonDataSourceUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA2FBEAF2844CD0B00170EE7 /* RemotePokemonDataSourceUnitTests.swift */; };
DA2FBEB22844CF6B00170EE7 /* PokemonRepositoryImplementationUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA2FBEB12844CF6B00170EE7 /* PokemonRepositoryImplementationUnitTests.swift */; };
DA2FBEB42844CFEA00170EE7 /* LocalPokemonDataSourceUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA2FBEB32844CFEA00170EE7 /* LocalPokemonDataSourceUnitTests.swift */; };
DAD0D31C283FC74100436272 /* PokemonApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD0D31B283FC74100436272 /* PokemonApp.swift */; };
DAD0D320283FC74200436272 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DAD0D31F283FC74200436272 /* Assets.xcassets */; };
DAD0D323283FC74200436272 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DAD0D322283FC74200436272 /* Preview Assets.xcassets */; };
Expand Down Expand Up @@ -74,6 +78,10 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
DA2FBEAD2844CCB800170EE7 /* URLProtocolMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLProtocolMock.swift; sourceTree = "<group>"; };
DA2FBEAF2844CD0B00170EE7 /* RemotePokemonDataSourceUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemotePokemonDataSourceUnitTests.swift; sourceTree = "<group>"; };
DA2FBEB12844CF6B00170EE7 /* PokemonRepositoryImplementationUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PokemonRepositoryImplementationUnitTests.swift; sourceTree = "<group>"; };
DA2FBEB32844CFEA00170EE7 /* LocalPokemonDataSourceUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalPokemonDataSourceUnitTests.swift; sourceTree = "<group>"; };
DAD0D318283FC74100436272 /* Pokemon.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Pokemon.app; sourceTree = BUILT_PRODUCTS_DIR; };
DAD0D31B283FC74100436272 /* PokemonApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PokemonApp.swift; sourceTree = "<group>"; };
DAD0D31F283FC74200436272 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
Expand Down Expand Up @@ -439,6 +447,10 @@
isa = PBXGroup;
children = (
DAE2433428412D2400AD4FE1 /* MockPokemonRepository.swift */,
DA2FBEAD2844CCB800170EE7 /* URLProtocolMock.swift */,
DA2FBEAF2844CD0B00170EE7 /* RemotePokemonDataSourceUnitTests.swift */,
DA2FBEB12844CF6B00170EE7 /* PokemonRepositoryImplementationUnitTests.swift */,
DA2FBEB32844CFEA00170EE7 /* LocalPokemonDataSourceUnitTests.swift */,
);
path = Repositories;
sourceTree = "<group>";
Expand Down Expand Up @@ -650,9 +662,13 @@
DAE2433128412A6B00AD4FE1 /* DBPokemonUnitTests.swift in Sources */,
DAE2432A2841261400AD4FE1 /* TestCoreDataStack.swift in Sources */,
DAE2434028412FC600AD4FE1 /* Pokemon+Mock.swift in Sources */,
DA2FBEB02844CD0B00170EE7 /* RemotePokemonDataSourceUnitTests.swift in Sources */,
DAE2433928412E7F00AD4FE1 /* GetPokemonsUseCaseUnitTests.swift in Sources */,
DAE2433728412DDF00AD4FE1 /* GetPokemonUseCaseUnitTests.swift in Sources */,
DAE2433328412BB800AD4FE1 /* ServerPokemonUnitTests.swift in Sources */,
DA2FBEB42844CFEA00170EE7 /* LocalPokemonDataSourceUnitTests.swift in Sources */,
DA2FBEAE2844CCB800170EE7 /* URLProtocolMock.swift in Sources */,
DA2FBEB22844CF6B00170EE7 /* PokemonRepositoryImplementationUnitTests.swift in Sources */,
DAE2433D28412F2D00AD4FE1 /* ExistsPokemonUseCaseUnitTests.swift in Sources */,
DAE2433528412D2400AD4FE1 /* MockPokemonRepository.swift in Sources */,
DAE2432C284126C600AD4FE1 /* DBManagerUnitTests.swift in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion Pokemon/Pokemon/Repositories/PokemonRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Combine

protocol PokemonRepository {

func getPokemon() -> AnyPublisher<Pokemon, Error>
func getPokemon(id: Int) -> AnyPublisher<Pokemon, Error>

func savePokemon(pokemon: Pokemon)
func existsPokemon(pokemon: Pokemon) -> Bool
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ class PokemonRepositoryImplementation: PokemonRepository {

// MARK: - PokemonRepository

func getPokemon() -> AnyPublisher<Pokemon, Error> {
func getPokemon(id: Int) -> AnyPublisher<Pokemon, Error> {

return remoteDataSource.getPokemon().map { serverPokemon -> Pokemon in
return remoteDataSource.getPokemon(id: id).map { serverPokemon -> Pokemon in

// convert to entity
let pokemon = serverPokemon.convertToEntity()
Expand Down
8 changes: 3 additions & 5 deletions Pokemon/Pokemon/Repositories/RemotePokemonDataSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ class RemotePokemonDataSource {
self.session = session
}

func getPokemon() -> AnyPublisher<ServerPokemon, Error> {
func getPokemon(id: Int) -> AnyPublisher<ServerPokemon, Error> {

let apiManager = ApiManager(baseURL: baseURL, session: session)

let urlRequest = getPokemonEndpoint()
let urlRequest = getPokemonEndpoint(id: id)

return apiManager.performRequest(urlRequest: urlRequest)
}
Expand All @@ -35,9 +35,7 @@ class RemotePokemonDataSource {

extension RemotePokemonDataSource {

func getPokemonEndpoint() -> URLRequest {

let id: Int = Int.random(in: 1..<1000)
func getPokemonEndpoint(id: Int) -> URLRequest {

let endpoint = "\(baseURL)\(RemotePokemonDataSource.getPokemonURL)\(id)"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ class PokemonWorldViewModel: ObservableObject {

state = .loading

cancellable = GetPokemonUseCase().execute()
let id: Int = Int.random(in: 1..<1000)

cancellable = GetPokemonUseCase().execute(id: id)
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { completion in

Expand Down
4 changes: 2 additions & 2 deletions Pokemon/Pokemon/UseCases/GetPokemonUseCase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ class GetPokemonUseCase {
self.repository = repository
}

func execute() -> AnyPublisher<Pokemon, Error> {
func execute(id: Int) -> AnyPublisher<Pokemon, Error> {

return repository.getPokemon()
return repository.getPokemon(id: id)
}
}
12 changes: 12 additions & 0 deletions Pokemon/PokemonTests/Extensions/Pokemon+Mock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,16 @@ extension XCTestCase {
date: "",
types: "electric")
}

func getPokemonMock2() -> Pokemon {

return Pokemon(id: 2,
name: "bulbasur",
weight: 4,
height: 1,
experience: 7,
url: "url",
date: "",
types: "plant")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//
// LocalPokemonDataSourceUnitTests.swift
// PokemonTests
//
// Created by Xavier Ramos on 30/5/22.
//

import XCTest
@testable import Pokemon

class LocalPokemonDataSourceUnitTests: XCTestCase {

var sut: LocalPokemonDataSource!

override func setUpWithError() throws {

try super.setUpWithError()

sut = LocalPokemonDataSource(dbManager: DBManager(coreDataStack: TestCoreDataStack()))
}

override func tearDownWithError() throws {

sut = nil

try super.tearDownWithError()
}

func testSavePokemon() {

// Given
let pokemon = getPokemonMock()

// When
sut.savePokemon(pokemon: pokemon)
let pokemons = sut.getPokemons()

// Then
XCTAssertEqual(pokemons.count, 1)
}

func testExistsPokemon() {

// Given
let pokemon = getPokemonMock()

// When
sut.savePokemon(pokemon: pokemon)
let response = sut.existsPokemon(pokemon: pokemon)

// Then
XCTAssertTrue(response)
}

func testGetPokemonsEmpty() {

// When
let pokemons = sut.getPokemons()

// Then
XCTAssertEqual(pokemons.count, 0)
}

func testGetPokemons() {

// Given
let pokemon1 = getPokemonMock()
let pokemon2 = getPokemonMock2()

// When
sut.savePokemon(pokemon: pokemon1)
sut.savePokemon(pokemon: pokemon2)
let pokemons = sut.getPokemons()

// Then
XCTAssertEqual(pokemons.count, 2)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class MockPokemonRepository: PokemonRepository {
var isExistsPokemonCalled = false
var isGetPokemonsCalled = false

func getPokemon() -> AnyPublisher<Pokemon, Error> {
func getPokemon(id: Int) -> AnyPublisher<Pokemon, Error> {

self.isGetPokemonCalled = true

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
//
// PokemonRepositoryImplementationUnitTests.swift
// PokemonTests
//
// Created by Xavier Ramos on 30/5/22.
//

import XCTest
import Combine
@testable import Pokemon

class PokemonRepositoryImplementationUnitTests: XCTestCase {

var sut: PokemonRepositoryImplementation!

var cancellable: AnyCancellable?

let baseUrlString = "http://jsonplaceholder.typicode.com/"

let successStatusCode = 200
let failureStatusCode = 401
let timeoutTime: TimeInterval = 2

override func setUpWithError() throws {

try super.setUpWithError()

sut = PokemonRepositoryImplementation()
}

override func tearDownWithError() throws {

sut = nil

try super.tearDownWithError()
}

func testGetPokemonOK() {

// Given
let id = 1
let session = getPokemonSession(statusCode: successStatusCode, id: id)
let remote = RemotePokemonDataSource(baseURL: baseUrlString, session: session)
sut = PokemonRepositoryImplementation(remoteDataSource: remote)

let exp = expectation(description: "expected values")

// When
cancellable = sut.getPokemon(id: id)
.sink(receiveCompletion: { completion in

switch completion {
case .finished:
exp.fulfill()
case .failure:
break
}

}, receiveValue: { pokemon in
XCTAssertEqual(pokemon.id, 2)
})

wait(for: [exp], timeout: timeoutTime)

// Then
XCTAssertNotNil(cancellable)
}

func testGetPokemonKO() {

// Given
let id = 1
let session = getPokemonSession(statusCode: failureStatusCode, id: id)
let remote = RemotePokemonDataSource(baseURL: baseUrlString, session: session)
sut = PokemonRepositoryImplementation(remoteDataSource: remote)

let exp = expectation(description: "expected values")

// When
cancellable = sut.getPokemon(id: id)
.sink(receiveCompletion: { completion in

switch completion {
case .finished:
break
case .failure:
exp.fulfill()
}
}, receiveValue: { _ in

// Nothing to recieve
})

wait(for: [exp], timeout: timeoutTime)

// Then
XCTAssertNotNil(cancellable)
}
}

// MARK: - Session

extension PokemonRepositoryImplementationUnitTests {

func getPokemonSession(statusCode: Int, id: Int) -> URLSession {

// URL we expect to call
let url = URL(string: "http://jsonplaceholder.typicode.com/pokemon/\(id)")

// data we expect to receive
let data = getPokemonData()

// attach that to some fixed data in our protocol handler
URLProtocolMock.testURLs = [url: data]
URLProtocolMock.response = HTTPURLResponse(url: URL(string: "http://jsonplaceholder.typicode.com:8080")!,
statusCode: statusCode,
httpVersion: nil,
headerFields: nil)

// now setup a configuration to use our mock
let config = URLSessionConfiguration.ephemeral
config.protocolClasses = [URLProtocolMock.self]

// and create the URLSession from that
let session = URLSession(configuration: config)

return session
}

func getPokemonData() -> Data {

let dataString = """
{
"base_experience": 142,
"height": 10,
"id": 2,
"name": "ivysaur",
"weight": 130,
"sprites": {
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/2.png"
},
"types": [
{
"slot": 1,
"type": {
"name": "grass"
}
},
{
"slot": 2,
"type": {
"name: "poison"
}
}
]
"""
return Data(dataString.utf8)
}
}
Loading

0 comments on commit 28cd231

Please sign in to comment.