Implemented some more tests to the FetcherTests test cases.

This commit is contained in:
Javier Cicchelli 2023-04-18 01:05:13 +02:00
parent a96c762b85
commit 62a07ff873
2 changed files with 148 additions and 23 deletions

View File

@ -24,7 +24,7 @@ public class Fetcher<Model: NSManagedObject>: NSObject, NSFetchedResultsControll
fetchedResultsController.sections?.count ?? 0 fetchedResultsController.sections?.count ?? 0
} }
private var inProgressChanges: [Change] = [] private var changesToNotify: [Change] = []
// MARK: Initialisers // MARK: Initialisers
@ -87,11 +87,11 @@ public class Fetcher<Model: NSManagedObject>: NSObject, NSFetchedResultsControll
// MARK: NSFetchedResultsControllerDelegate // MARK: NSFetchedResultsControllerDelegate
public func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) { public func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
inProgressChanges.removeAll() changesToNotify.removeAll()
} }
public func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) { public func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
didChangePublisher.send(inProgressChanges) didChangePublisher.send(changesToNotify)
} }
public func controller( public func controller(
@ -101,9 +101,9 @@ public class Fetcher<Model: NSManagedObject>: NSObject, NSFetchedResultsControll
for type: NSFetchedResultsChangeType for type: NSFetchedResultsChangeType
) { ) {
if type == .insert { if type == .insert {
inProgressChanges.append(.section(.inserted(sectionIndex))) changesToNotify.append(.section(.inserted(sectionIndex)))
} else if type == .delete { } else if type == .delete {
inProgressChanges.append(.section(.deleted(sectionIndex))) changesToNotify.append(.section(.deleted(sectionIndex)))
} }
} }
@ -118,19 +118,19 @@ public class Fetcher<Model: NSManagedObject>: NSObject, NSFetchedResultsControll
case .insert: case .insert:
guard let newIndexPath else { return } guard let newIndexPath else { return }
inProgressChanges.append(.object(.inserted(at: newIndexPath))) changesToNotify.append(.object(.inserted(at: newIndexPath)))
case .delete: case .delete:
guard let indexPath else { return } guard let indexPath else { return }
inProgressChanges.append(.object(.deleted(from: indexPath))) changesToNotify.append(.object(.deleted(from: indexPath)))
case .move: case .move:
guard let indexPath, let newIndexPath else { return } 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: case .update:
guard let indexPath else { return } guard let indexPath else { return }
inProgressChanges.append(.object(.updated(at: indexPath))) changesToNotify.append(.object(.updated(at: indexPath)))
default: default:
break break
} }

View File

@ -6,6 +6,7 @@
// Copyright © 2023 Röck+Cöde. All rights reserved. // Copyright © 2023 Röck+Cöde. All rights reserved.
// //
import Combine
import Persistence import Persistence
import XCTest import XCTest
@ -13,21 +14,16 @@ final class FetcherTests: XCTestCase {
// MARK: Properties // MARK: Properties
private let persistence = TestPersistenceService.shared private lazy var persistence: TestPersistenceService = .shared
private lazy var fetcher: Fetcher<TestEntity> = .init(
private var fetcher: Fetcher<TestEntity>! fetchRequest: .allTestEntities(),
managedObjectContext: persistence.viewContext
)
// MARK: Setup // MARK: Setup
override func setUpWithError() throws {
fetcher = .init(
fetchRequest: .allTestEntities(),
managedObjectContext: persistence.viewContext
)
}
override func tearDownWithError() throws { override func tearDownWithError() throws {
fetcher = nil try persistence.clean()
} }
// MARK: Number of sections tests // MARK: Number of sections tests
@ -132,7 +128,7 @@ final class FetcherTests: XCTestCase {
try fetcher.fetch() try fetcher.fetch()
// WHEN & THEN // WHEN & THEN
let indexPath = IndexPath( let _ = IndexPath(
item: 0, item: 0,
section: fetcher.numberOfSections - 1 section: fetcher.numberOfSections - 1
) )
@ -169,7 +165,7 @@ final class FetcherTests: XCTestCase {
try fetcher.fetch() try fetcher.fetch()
// WHEN & THEN // WHEN & THEN
let indexPath = IndexPath( let _ = IndexPath(
item: entities.count, item: entities.count,
section: fetcher.numberOfSections 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)
}
} }