Fix test infrastructure for Rooms feature and improve testability
- Update Plant test fixtures to use roomID instead of deprecated location - Add URLDataFetcher protocol to ImageCache for dependency injection - Update ImageCacheTests to use protocol-based mock instead of URLSession subclass - Add missing cancelReminders(for:plantID:) method to MockNotificationService - Add Equatable conformance to ImageCacheError for test assertions Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,16 +9,16 @@
|
||||
import XCTest
|
||||
@testable import PlantGuide
|
||||
|
||||
// MARK: - MockURLSession
|
||||
// MARK: - MockURLDataFetcher
|
||||
|
||||
/// Mock URL session for testing image downloads
|
||||
final class MockURLSessionForCache: URLSession, @unchecked Sendable {
|
||||
/// Mock URL data fetcher for testing image downloads
|
||||
final class MockURLDataFetcher: URLDataFetcher, @unchecked Sendable {
|
||||
var dataToReturn: Data?
|
||||
var errorToThrow: Error?
|
||||
var downloadCallCount = 0
|
||||
var lastRequestedURL: URL?
|
||||
|
||||
override func data(from url: URL) async throws -> (Data, URLResponse) {
|
||||
func data(from url: URL) async throws -> (Data, URLResponse) {
|
||||
downloadCallCount += 1
|
||||
lastRequestedURL = url
|
||||
|
||||
@@ -44,7 +44,7 @@ final class ImageCacheTests: XCTestCase {
|
||||
// MARK: - Properties
|
||||
|
||||
private var sut: ImageCache!
|
||||
private var mockSession: MockURLSessionForCache!
|
||||
private var mockFetcher: MockURLDataFetcher!
|
||||
private var testDirectory: URL!
|
||||
private var fileManager: FileManager!
|
||||
|
||||
@@ -54,7 +54,7 @@ final class ImageCacheTests: XCTestCase {
|
||||
try await super.setUp()
|
||||
|
||||
fileManager = FileManager.default
|
||||
mockSession = MockURLSessionForCache()
|
||||
mockFetcher = MockURLDataFetcher()
|
||||
|
||||
// Create a unique test directory for each test
|
||||
let tempDir = fileManager.temporaryDirectory
|
||||
@@ -63,7 +63,7 @@ final class ImageCacheTests: XCTestCase {
|
||||
|
||||
sut = ImageCache(
|
||||
fileManager: fileManager,
|
||||
urlSession: mockSession
|
||||
urlDataFetcher: mockFetcher
|
||||
)
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ final class ImageCacheTests: XCTestCase {
|
||||
}
|
||||
|
||||
sut = nil
|
||||
mockSession = nil
|
||||
mockFetcher = nil
|
||||
testDirectory = nil
|
||||
fileManager = nil
|
||||
|
||||
@@ -103,21 +103,21 @@ final class ImageCacheTests: XCTestCase {
|
||||
// Given
|
||||
let plantID = UUID()
|
||||
let url = URL(string: "https://example.com/plant.jpg")!
|
||||
mockSession.dataToReturn = createTestImageData()
|
||||
mockFetcher.dataToReturn = createTestImageData()
|
||||
|
||||
// When
|
||||
try await sut.cacheImage(from: url, for: plantID)
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(mockSession.downloadCallCount, 1)
|
||||
XCTAssertEqual(mockSession.lastRequestedURL, url)
|
||||
XCTAssertEqual(mockFetcher.downloadCallCount, 1)
|
||||
XCTAssertEqual(mockFetcher.lastRequestedURL, url)
|
||||
}
|
||||
|
||||
func testCacheImage_WhenDownloadFails_ThrowsDownloadFailed() async {
|
||||
// Given
|
||||
let plantID = UUID()
|
||||
let url = URL(string: "https://example.com/plant.jpg")!
|
||||
mockSession.errorToThrow = URLError(.notConnectedToInternet)
|
||||
mockFetcher.errorToThrow = URLError(.notConnectedToInternet)
|
||||
|
||||
// When/Then
|
||||
do {
|
||||
@@ -139,7 +139,7 @@ final class ImageCacheTests: XCTestCase {
|
||||
// Given
|
||||
let plantID = UUID()
|
||||
let url = URL(string: "https://example.com/plant.jpg")!
|
||||
mockSession.dataToReturn = createInvalidImageData()
|
||||
mockFetcher.dataToReturn = createInvalidImageData()
|
||||
|
||||
// When/Then
|
||||
do {
|
||||
@@ -156,14 +156,14 @@ final class ImageCacheTests: XCTestCase {
|
||||
// Given
|
||||
let plantID = UUID()
|
||||
let url = URL(string: "https://example.com/plant.jpg")!
|
||||
mockSession.dataToReturn = createTestImageData()
|
||||
mockFetcher.dataToReturn = createTestImageData()
|
||||
|
||||
// When - Cache twice
|
||||
try await sut.cacheImage(from: url, for: plantID)
|
||||
try await sut.cacheImage(from: url, for: plantID)
|
||||
|
||||
// Then - Should only download once
|
||||
XCTAssertEqual(mockSession.downloadCallCount, 1)
|
||||
XCTAssertEqual(mockFetcher.downloadCallCount, 1)
|
||||
}
|
||||
|
||||
// MARK: - getCachedImage() Tests
|
||||
@@ -184,7 +184,7 @@ final class ImageCacheTests: XCTestCase {
|
||||
// Given
|
||||
let plantID = UUID()
|
||||
let url = URL(string: "https://example.com/plant.jpg")!
|
||||
mockSession.dataToReturn = createTestImageData()
|
||||
mockFetcher.dataToReturn = createTestImageData()
|
||||
|
||||
// Cache the image first
|
||||
try await sut.cacheImage(from: url, for: plantID)
|
||||
@@ -201,7 +201,7 @@ final class ImageCacheTests: XCTestCase {
|
||||
let plantID1 = UUID()
|
||||
let plantID2 = UUID()
|
||||
let url = URL(string: "https://example.com/plant.jpg")!
|
||||
mockSession.dataToReturn = createTestImageData()
|
||||
mockFetcher.dataToReturn = createTestImageData()
|
||||
|
||||
// Cache for plant 1
|
||||
try await sut.cacheImage(from: url, for: plantID1)
|
||||
@@ -218,7 +218,7 @@ final class ImageCacheTests: XCTestCase {
|
||||
let plantID = UUID()
|
||||
let url1 = URL(string: "https://example.com/plant1.jpg")!
|
||||
let url2 = URL(string: "https://example.com/plant2.jpg")!
|
||||
mockSession.dataToReturn = createTestImageData()
|
||||
mockFetcher.dataToReturn = createTestImageData()
|
||||
|
||||
// Cache url1
|
||||
try await sut.cacheImage(from: url1, for: plantID)
|
||||
@@ -235,7 +235,7 @@ final class ImageCacheTests: XCTestCase {
|
||||
let plantID = UUID()
|
||||
let url = URL(string: "https://example.com/plant.jpg")!
|
||||
let urlHash = url.absoluteString.sha256Hash
|
||||
mockSession.dataToReturn = createTestImageData()
|
||||
mockFetcher.dataToReturn = createTestImageData()
|
||||
|
||||
// Cache the image
|
||||
try await sut.cacheImage(from: url, for: plantID)
|
||||
@@ -255,7 +255,7 @@ final class ImageCacheTests: XCTestCase {
|
||||
let plantID2 = UUID()
|
||||
let url1 = URL(string: "https://example.com/plant1.jpg")!
|
||||
let url2 = URL(string: "https://example.com/plant2.jpg")!
|
||||
mockSession.dataToReturn = createTestImageData()
|
||||
mockFetcher.dataToReturn = createTestImageData()
|
||||
|
||||
// Cache images for both plants
|
||||
try await sut.cacheImage(from: url1, for: plantID1)
|
||||
@@ -267,9 +267,9 @@ final class ImageCacheTests: XCTestCase {
|
||||
// Then - Plant 1's image should be gone, plant 2's should still exist
|
||||
// Note: Due to memory cache clearing behavior, we need to re-cache
|
||||
// This test verifies the disk cache behavior
|
||||
mockSession.downloadCallCount = 0
|
||||
mockFetcher.downloadCallCount = 0
|
||||
try await sut.cacheImage(from: url1, for: plantID1)
|
||||
XCTAssertEqual(mockSession.downloadCallCount, 1) // Had to redownload
|
||||
XCTAssertEqual(mockFetcher.downloadCallCount, 1) // Had to redownload
|
||||
}
|
||||
|
||||
func testClearCache_ForPlant_RemovesMultipleImages() async throws {
|
||||
@@ -277,7 +277,7 @@ final class ImageCacheTests: XCTestCase {
|
||||
let plantID = UUID()
|
||||
let url1 = URL(string: "https://example.com/plant1.jpg")!
|
||||
let url2 = URL(string: "https://example.com/plant2.jpg")!
|
||||
mockSession.dataToReturn = createTestImageData()
|
||||
mockFetcher.dataToReturn = createTestImageData()
|
||||
|
||||
// Cache multiple images for the same plant
|
||||
try await sut.cacheImage(from: url1, for: plantID)
|
||||
@@ -301,7 +301,7 @@ final class ImageCacheTests: XCTestCase {
|
||||
let plantID2 = UUID()
|
||||
let url1 = URL(string: "https://example.com/plant1.jpg")!
|
||||
let url2 = URL(string: "https://example.com/plant2.jpg")!
|
||||
mockSession.dataToReturn = createTestImageData()
|
||||
mockFetcher.dataToReturn = createTestImageData()
|
||||
|
||||
// Cache images for multiple plants
|
||||
try await sut.cacheImage(from: url1, for: plantID1)
|
||||
@@ -333,7 +333,7 @@ final class ImageCacheTests: XCTestCase {
|
||||
// Given
|
||||
let plantID = UUID()
|
||||
let url = URL(string: "https://example.com/plant.jpg")!
|
||||
mockSession.dataToReturn = createTestImageData()
|
||||
mockFetcher.dataToReturn = createTestImageData()
|
||||
|
||||
// Cache an image
|
||||
try await sut.cacheImage(from: url, for: plantID)
|
||||
@@ -349,7 +349,7 @@ final class ImageCacheTests: XCTestCase {
|
||||
// Given
|
||||
let plantID = UUID()
|
||||
let url = URL(string: "https://example.com/plant.jpg")!
|
||||
mockSession.dataToReturn = createTestImageData()
|
||||
mockFetcher.dataToReturn = createTestImageData()
|
||||
|
||||
// Cache an image
|
||||
try await sut.cacheImage(from: url, for: plantID)
|
||||
@@ -370,7 +370,7 @@ final class ImageCacheTests: XCTestCase {
|
||||
// Given
|
||||
let plantID = UUID()
|
||||
let url = URL(string: "https://example.com/plant.jpg")!
|
||||
mockSession.dataToReturn = createTestImageData()
|
||||
mockFetcher.dataToReturn = createTestImageData()
|
||||
|
||||
// Cache the image
|
||||
try await sut.cacheImage(from: url, for: plantID)
|
||||
@@ -435,7 +435,7 @@ final class ImageCacheTests: XCTestCase {
|
||||
let plantID = UUID()
|
||||
let longPath = String(repeating: "path/", count: 50) + "image.jpg"
|
||||
let url = URL(string: "https://example.com/\(longPath)")!
|
||||
mockSession.dataToReturn = createTestImageData()
|
||||
mockFetcher.dataToReturn = createTestImageData()
|
||||
|
||||
// When/Then - Should not throw
|
||||
try await sut.cacheImage(from: url, for: plantID)
|
||||
@@ -445,7 +445,7 @@ final class ImageCacheTests: XCTestCase {
|
||||
// Given
|
||||
let plantID = UUID()
|
||||
let url = URL(string: "https://example.com/plant%20image%231.jpg")!
|
||||
mockSession.dataToReturn = createTestImageData()
|
||||
mockFetcher.dataToReturn = createTestImageData()
|
||||
|
||||
// When/Then - Should not throw
|
||||
try await sut.cacheImage(from: url, for: plantID)
|
||||
@@ -455,7 +455,7 @@ final class ImageCacheTests: XCTestCase {
|
||||
// Given
|
||||
let plantID = UUID()
|
||||
let urls = (0..<5).map { URL(string: "https://example.com/plant\($0).jpg")! }
|
||||
mockSession.dataToReturn = createTestImageData()
|
||||
mockFetcher.dataToReturn = createTestImageData()
|
||||
|
||||
// When - Cache all images
|
||||
for url in urls {
|
||||
@@ -473,7 +473,7 @@ final class ImageCacheTests: XCTestCase {
|
||||
// Given
|
||||
let plantID = UUID()
|
||||
let urls = (0..<10).map { URL(string: "https://example.com/plant\($0).jpg")! }
|
||||
mockSession.dataToReturn = createTestImageData()
|
||||
mockFetcher.dataToReturn = createTestImageData()
|
||||
|
||||
// When - Cache concurrently
|
||||
await withTaskGroup(of: Void.self) { group in
|
||||
|
||||
Reference in New Issue
Block a user