From 263aa261b4a0fe5bb069ff850b03db846271da79 Mon Sep 17 00:00:00 2001 From: SungMinCho-Kor Date: Sun, 21 Jan 2024 19:43:07 +0900 Subject: [PATCH] =?UTF-8?q?test=20:=20test=20=EA=B0=80=EB=8A=A5=ED=95=9C?= =?UTF-8?q?=20=EA=B5=AC=EC=A1=B0=EB=A1=9C=20=EC=88=98=EC=A0=95=20=ED=9B=84?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20(#74)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- KCS/KCS.xcodeproj/project.pbxproj | 4 + KCS/KCS/Data/ImageCache.swift | 4 + .../Data/Repository/ImageRepositoryImpl.swift | 12 ++- .../Repository/ImageRepository.swift | 2 + .../ImageRepositoryImplTests.swift | 75 +++++++++++++++++++ .../StoreRepositoryImplTests.swift | 10 ++- 6 files changed, 100 insertions(+), 7 deletions(-) create mode 100644 KCS/KCSUnitTest/ImageRepositoryImplTests.swift diff --git a/KCS/KCS.xcodeproj/project.pbxproj b/KCS/KCS.xcodeproj/project.pbxproj index 65de3674..0164c350 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())