From 62a07ff873861e1f7cc5a1f0ed364a4d7681e467 Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Tue, 18 Apr 2023 01:05:13 +0200 Subject: [PATCH] Implemented some more tests to the FetcherTests test cases. --- Sources/Persistence/Classes/Fetcher.swift | 18 +-- Tests/Persistence/Classes/FetcherTests.swift | 153 +++++++++++++++++-- 2 files changed, 148 insertions(+), 23 deletions(-) diff --git a/Sources/Persistence/Classes/Fetcher.swift b/Sources/Persistence/Classes/Fetcher.swift index f8b3db2..60bda74 100644 --- a/Sources/Persistence/Classes/Fetcher.swift +++ b/Sources/Persistence/Classes/Fetcher.swift @@ -24,7 +24,7 @@ public class Fetcher: NSObject, NSFetchedResultsControll fetchedResultsController.sections?.count ?? 0 } - private var inProgressChanges: [Change] = [] + private var changesToNotify: [Change] = [] // MARK: Initialisers @@ -87,11 +87,11 @@ public class Fetcher: NSObject, NSFetchedResultsControll // MARK: NSFetchedResultsControllerDelegate public func controllerWillChangeContent(_ controller: NSFetchedResultsController) { - inProgressChanges.removeAll() + changesToNotify.removeAll() } public func controllerDidChangeContent(_ controller: NSFetchedResultsController) { - didChangePublisher.send(inProgressChanges) + didChangePublisher.send(changesToNotify) } public func controller( @@ -101,9 +101,9 @@ public class Fetcher: NSObject, NSFetchedResultsControll for type: NSFetchedResultsChangeType ) { if type == .insert { - inProgressChanges.append(.section(.inserted(sectionIndex))) + changesToNotify.append(.section(.inserted(sectionIndex))) } else if type == .delete { - inProgressChanges.append(.section(.deleted(sectionIndex))) + changesToNotify.append(.section(.deleted(sectionIndex))) } } @@ -118,19 +118,19 @@ public class Fetcher: NSObject, NSFetchedResultsControll case .insert: guard let newIndexPath else { return } - inProgressChanges.append(.object(.inserted(at: newIndexPath))) + changesToNotify.append(.object(.inserted(at: newIndexPath))) case .delete: guard let indexPath else { return } - inProgressChanges.append(.object(.deleted(from: indexPath))) + changesToNotify.append(.object(.deleted(from: indexPath))) case .move: guard let indexPath, let newIndexPath else { return } - inProgressChanges.append(.object(.moved(from: indexPath, to: newIndexPath))) + changesToNotify.append(.object(.moved(from: indexPath, to: newIndexPath))) case .update: guard let indexPath else { return } - inProgressChanges.append(.object(.updated(at: indexPath))) + changesToNotify.append(.object(.updated(at: indexPath))) default: break } diff --git a/Tests/Persistence/Classes/FetcherTests.swift b/Tests/Persistence/Classes/FetcherTests.swift index d9bb0cc..facead0 100644 --- a/Tests/Persistence/Classes/FetcherTests.swift +++ b/Tests/Persistence/Classes/FetcherTests.swift @@ -6,6 +6,7 @@ // Copyright © 2023 Röck+Cöde. All rights reserved. // +import Combine import Persistence import XCTest @@ -13,21 +14,16 @@ final class FetcherTests: XCTestCase { // MARK: Properties - private let persistence = TestPersistenceService.shared - - private var fetcher: Fetcher! + private lazy var persistence: TestPersistenceService = .shared + private lazy var fetcher: Fetcher = .init( + fetchRequest: .allTestEntities(), + managedObjectContext: persistence.viewContext + ) // MARK: Setup - - override func setUpWithError() throws { - fetcher = .init( - fetchRequest: .allTestEntities(), - managedObjectContext: persistence.viewContext - ) - } - + override func tearDownWithError() throws { - fetcher = nil + try persistence.clean() } // MARK: Number of sections tests @@ -132,7 +128,7 @@ final class FetcherTests: XCTestCase { try fetcher.fetch() // WHEN & THEN - let indexPath = IndexPath( + let _ = IndexPath( item: 0, section: fetcher.numberOfSections - 1 ) @@ -169,7 +165,7 @@ final class FetcherTests: XCTestCase { try fetcher.fetch() // WHEN & THEN - let indexPath = IndexPath( + let _ = IndexPath( item: entities.count, section: fetcher.numberOfSections ) @@ -191,4 +187,133 @@ final class FetcherTests: XCTestCase { } } + // MARK: Did change publisher tests + + func test_didChangePublisher_whenModelIsEmpty() throws { + let expectation = self.expectation(description: "didChangePublisher when model is filled.") + + var result: [Change]? + + // GIVEN + let cancellable = fetcher + .didChangePublisher + .sink(receiveValue: { value in + result = value + + expectation.fulfill() + }) + + // WHEN + try fetcher.fetch() + + // THEN + let waiter = XCTWaiter.wait(for: [expectation], timeout: 1.0) + + guard waiter == .timedOut else { + XCTFail("Waiter expected to time out.") + return + } + + cancellable.cancel() + + XCTAssertNil(result) + } + + func test_didChangePublisher_whenModelIsFilled() throws { + let expectation = self.expectation(description: "didChangePublisher when model is filled.") + + var result: [Change]? + + // GIVEN + let context = persistence.makeChildContext() + let _ = [ + TestEntity(context: context), + TestEntity(context: context), + TestEntity(context: context) + ] + + let cancellable = fetcher + .didChangePublisher + .sink(receiveValue: { value in + result = value + + expectation.fulfill() + }) + + // WHEN + try persistence.save(childContext: context) + try fetcher.fetch() + + // THEN + let waiter = XCTWaiter.wait(for: [expectation], timeout: 1.0) + + guard waiter == .timedOut else { + XCTFail("Waiter expected to time out.") + return + } + + cancellable.cancel() + + XCTAssertNil(result) + } + + func test_didChangePublisher_whenModelIsUpdated() throws { + let expectation = self.expectation(description: "didChangePublisher when model is updated.") + + var result: [Change]? + + // GIVEN + let context = persistence.makeChildContext() + let entities = [ + TestEntity(context: context), + TestEntity(context: context), + TestEntity(context: context) + ] + + let cancellable = fetcher + .didChangePublisher + .sink(receiveValue: { value in + result = value + + expectation.fulfill() + }) + + // WHEN + try fetcher.fetch() + try persistence.save(childContext: context) + + // THEN + waitForExpectations(timeout: 1.0) + + cancellable.cancel() + + XCTAssertNotNil(result) + XCTAssertEqual(result?.count, entities.count) + XCTAssertEqual(result, [ + .object(.inserted(at: .init(item: 2, section: 0))), + .object(.inserted(at: .init(item: 1, section: 0))), + .object(.inserted(at: .init(item: 0, section: 0))), + ]) + } + +} + +// MARK: - TestPersistenceService+Functions + +private extension TestPersistenceService { + + // MARK: Functions + + func clean() throws { + let context = makeChildContext() + + try context.performAndWait { + try context + .fetch(.allTestEntities()) + .forEach(context.delete) + } + + try save(childContext: context) + } + }