diff --git a/KCS/KCS.xcodeproj/project.pbxproj b/KCS/KCS.xcodeproj/project.pbxproj index 761b7a94..aedba1fd 100644 --- a/KCS/KCS.xcodeproj/project.pbxproj +++ b/KCS/KCS.xcodeproj/project.pbxproj @@ -50,6 +50,7 @@ 59F478BB2B5ADC64002FEF9E /* GetStoreInformationUseCaseImplTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59F478BA2B5ADC64002FEF9E /* GetStoreInformationUseCaseImplTests.swift */; }; 59F478BD2B5AE180002FEF9E /* FetchStoresUseCaseImplTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59F478BC2B5AE180002FEF9E /* FetchStoresUseCaseImplTests.swift */; }; 59F478BF2B5BEA08002FEF9E /* RequestLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59F478BE2B5BEA08002FEF9E /* RequestLocation.swift */; }; + 59F478C12B5D0D8D002FEF9E /* ImageRepositoryImplTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59F478C02B5D0D8D002FEF9E /* ImageRepositoryImplTests.swift */; }; 8FE699E5DAEEDFE5A53D5E82 /* Pods_KCS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E11E3144529848C9A0FC6F77 /* Pods_KCS.framework */; }; A802D1F62B5277630091FDE7 /* CertificationLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A802D1F52B5277620091FDE7 /* CertificationLabel.swift */; }; A81EFBB32B5BC57800D0C0D7 /* OpenClosedContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81EFBB22B5BC57800D0C0D7 /* OpenClosedContent.swift */; }; @@ -130,6 +131,7 @@ 59F478BA2B5ADC64002FEF9E /* GetStoreInformationUseCaseImplTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetStoreInformationUseCaseImplTests.swift; sourceTree = ""; }; 59F478BC2B5AE180002FEF9E /* FetchStoresUseCaseImplTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchStoresUseCaseImplTests.swift; sourceTree = ""; }; 59F478BE2B5BEA08002FEF9E /* RequestLocation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestLocation.swift; sourceTree = ""; }; + 59F478C02B5D0D8D002FEF9E /* ImageRepositoryImplTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageRepositoryImplTests.swift; sourceTree = ""; }; 5FF0FF2386EEB69182D6EA4C /* Pods-KCSUnitTest.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-KCSUnitTest.debug.xcconfig"; path = "Target Support Files/Pods-KCSUnitTest/Pods-KCSUnitTest.debug.xcconfig"; sourceTree = ""; }; 9EA5C8EA72EA9E937C11400A /* Pods-KCS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-KCS.debug.xcconfig"; path = "Target Support Files/Pods-KCS/Pods-KCS.debug.xcconfig"; sourceTree = ""; }; A802D1F52B5277620091FDE7 /* CertificationLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CertificationLabel.swift; sourceTree = ""; }; @@ -249,6 +251,7 @@ 5977BE8C2B5966F900725C90 /* StoreRepositoryImplTests.swift */, 59F478BA2B5ADC64002FEF9E /* GetStoreInformationUseCaseImplTests.swift */, 59F478BC2B5AE180002FEF9E /* FetchStoresUseCaseImplTests.swift */, + 59F478C02B5D0D8D002FEF9E /* ImageRepositoryImplTests.swift */, ); path = KCSUnitTest; sourceTree = ""; @@ -755,6 +758,7 @@ buildActionMask = 2147483647; files = ( 59F478BD2B5AE180002FEF9E /* FetchStoresUseCaseImplTests.swift in Sources */, + 59F478C12B5D0D8D002FEF9E /* ImageRepositoryImplTests.swift in Sources */, 5977BE8D2B5966F900725C90 /* StoreRepositoryImplTests.swift in Sources */, 59F478BB2B5ADC64002FEF9E /* GetStoreInformationUseCaseImplTests.swift in Sources */, ); diff --git a/KCS/KCS/Data/ImageCache.swift b/KCS/KCS/Data/ImageCache.swift index 22c7fd28..aff3eb01 100644 --- a/KCS/KCS/Data/ImageCache.swift +++ b/KCS/KCS/Data/ImageCache.swift @@ -23,5 +23,9 @@ final class ImageCache { func setImageData(_ data: NSData, for key: NSURL) { cache.setObject(data, forKey: key as NSURL) } + + func clearCache() { + cache.removeAllObjects() + } } diff --git a/KCS/KCS/Data/Repository/ImageRepositoryImpl.swift b/KCS/KCS/Data/Repository/ImageRepositoryImpl.swift index 6bb61f66..8ad86fb4 100644 --- a/KCS/KCS/Data/Repository/ImageRepositoryImpl.swift +++ b/KCS/KCS/Data/Repository/ImageRepositoryImpl.swift @@ -8,14 +8,20 @@ import RxSwift import Alamofire -final class ImageRepositoryImpl: ImageRepository { +struct ImageRepositoryImpl: ImageRepository { + + var cache: ImageCache + + init(cache: ImageCache = ImageCache.shared) { + self.cache = cache + } func fetchImage( url: String ) -> Observable { return Observable.create { observer -> Disposable in if let imageURL = URL(string: url) { - if let imageData = ImageCache.shared.getImageData(for: imageURL as NSURL) { + if let imageData = cache.getImageData(for: imageURL as NSURL) { observer.onNext(Data(imageData)) } else { AF.request(StoreAPI.getImage(url: url)) @@ -23,7 +29,7 @@ final class ImageRepositoryImpl: ImageRepository { switch response.result { case .success(let result): if let resultData = result { - ImageCache.shared.setImageData(resultData as NSData, for: imageURL as NSURL) + cache.setImageData(resultData as NSData, for: imageURL as NSURL) observer.onNext(resultData) } else { observer.onError(ImageRepositoryError.noImageData) diff --git a/KCS/KCS/Domain/Interface/Repository/ImageRepository.swift b/KCS/KCS/Domain/Interface/Repository/ImageRepository.swift index a9f73452..34e0c1cd 100644 --- a/KCS/KCS/Domain/Interface/Repository/ImageRepository.swift +++ b/KCS/KCS/Domain/Interface/Repository/ImageRepository.swift @@ -9,6 +9,8 @@ import RxSwift protocol ImageRepository { + var cache: ImageCache { get } + func fetchImage( url: String ) -> Observable diff --git a/KCS/KCSUnitTest/ImageRepositoryImplTests.swift b/KCS/KCSUnitTest/ImageRepositoryImplTests.swift new file mode 100644 index 00000000..d5b27de8 --- /dev/null +++ b/KCS/KCSUnitTest/ImageRepositoryImplTests.swift @@ -0,0 +1,75 @@ +// +// ImageRepositoryImplTests.swift +// KCSUnitTest +// +// Created by 조성민 on 1/21/24. +// + +import XCTest +@testable import KCS +import RxSwift +import RxTest +import RxBlocking +import Alamofire + +final class ImageRepositoryImplTests: XCTestCase { + + var imageRepositoryImpl: ImageRepositoryImpl! + var disposeBag: DisposeBag! + + override func setUp() { + ImageCache.shared.clearCache() + imageRepositoryImpl = ImageRepositoryImpl() + disposeBag = DisposeBag() + } + + func test_캐시에_이미지가_존재하는_경우_캐시에서_이미지를_가져온다() { + let urlString = UUID().uuidString + guard let url = URL(string: urlString), + let data = UUID().uuidString.data(using: .utf8) else { + XCTFail("Data생성 실패") + return + } + let imageData = NSData(data: data) + ImageCache.shared.setImageData(imageData, for: url as NSURL) + + let observable = imageRepositoryImpl.fetchImage(url: urlString) + + XCTAssertEqual(data, try observable.toBlocking().first()) + } + + func test_빈_이미지를_받은_경우_noImageData에러_발생() { + let urlString = UUID().uuidString + guard let url = URL(string: urlString) else { + XCTFail("URL 생성 실패") + return + } + let imageData = NSData(data: Data()) + ImageCache.shared.setImageData(imageData, for: url as NSURL) + + let result = imageRepositoryImpl.fetchImage(url: urlString).toBlocking().materialize() + do { + switch result { + case .completed(let elements): + XCTFail("noImageData 에러가 발생해야 합니다.") + case .failed(let elements, let error): + XCTAssertTrue(elements.isEmpty) + XCTAssertTrue(error is ImageRepositoryError) + } + } + } + + func test_잘못된_URL인_경우_에러를_반환한다() { + let wrongURL = "wrongURL" + let result = imageRepositoryImpl.fetchImage(url: wrongURL).toBlocking().materialize() + + switch result { + case .completed(let elements): + XCTFail("잘못된 URL로 요청을 보내면 에러가 발생해야 합니다.") + case .failed(let elements, let error): + XCTAssertTrue(elements.isEmpty) + XCTAssertTrue(error is AFError) + } + } + +} diff --git a/KCS/KCSUnitTest/StoreRepositoryImplTests.swift b/KCS/KCSUnitTest/StoreRepositoryImplTests.swift index 05d6914a..30e00823 100644 --- a/KCS/KCSUnitTest/StoreRepositoryImplTests.swift +++ b/KCS/KCSUnitTest/StoreRepositoryImplTests.swift @@ -78,10 +78,12 @@ final class StoreRepositoryImplTests: XCTestCase { func test_fetchRefreshStores_결과는_에러가_발생하지_않는다() { let observable = storeRepositoryImpl.fetchRefreshStores( - northWestLocation: Location(longitude: 0, latitude: 0), - southWestLocation: Location(longitude: 0, latitude: 0), - southEastLocation: Location(longitude: 0, latitude: 0), - northEastLocation: Location(longitude: 0, latitude: 0) + requestLocation: RequestLocation( + northWest: Location(longitude: 0, latitude: 0), + southWest: Location(longitude: 0, latitude: 0), + southEast: Location(longitude: 0, latitude: 0), + northEast: Location(longitude: 0, latitude: 0) + ) ) XCTAssertNoThrow(try observable.toBlocking().first())