В памяти контейнер CoreData пуст при инициализации - PullRequest
1 голос
/ 28 октября 2019

tl; dr:

  1. клонировать проект: https://github.com/Jasperav/CoreDataInMemoryFail
  2. Запустите тест и увидите, что он провалился. Почему в моем контейнере памяти нет данных и как я могу убедиться, что он будет иметь данные?

Long:

У меня есть файл sqlite с заполненными данными, и у меня естьбаза данных в памяти в CoreData. Некоторый код:

// ...
func createInMemoryPerformanceTestDatabase() -> NSPersistentContainer {
    let url = createPathToSomeSQLiteFile()
    let container = NSPersistentContainer(name: dataModelName, managedObjectModel: objectModel)
    let description = NSPersistentStoreDescription(url: url)

    description.type = NSInMemoryStoreType

    container.persistentStoreDescriptions = [description]

    container.loadPersistentStores { description, error in
        XCTAssertNil(error)
    }

    return container
}
// ...

Хотя файл sqlite содержит данные внутри, я не вижу его обратно в своих контекстах, которые я создаю с помощью container.

Когда я создаю базу данных в памяти с CoreData, указывающим на файл sqlite с данными, я не вижу никаких результатов при запросах к базе данных. Я хочу видеть данные внутри файла sqlite. Данные должны просто загрузить все в память. Это для целей тестирования.

Ответы [ 2 ]

2 голосов
/ 31 октября 2019

Проблема с тем, что вы пробовали, состояла в том, что вы устанавливали тип вашего storeDescription как NSInMemoryStoreType перед загрузкой их в контейнер. Поскольку тип storeDescription указан как NSInMemoryStoreType, API не будет читать и заполнять данные из предоставленного вами URL-адреса файла. Чтобы API считал данные из URL-адреса файла, тип storeDescription должен быть тем, который определен инициализацией с инициализатором init(url: URL), который в вашем случае равен SQLite .

Однако если вы хотите иметь persistentStore типа NSInMemoryStoreType с данными, считанными из файла url, вы можете перенести persistentStores вашего persistentContainer с типом NSInMemoryStoreType с помощью функции migratePersistentStore:toURL:options:withType:error:. Вы можете попробовать приведенный ниже фрагмент кода.

import CoreData
import XCTest
@testable import CoreDataInMemoryFail


class CoreDataInMemoryFailTests: XCTestCase {

    private func createContainer(modify: (NSPersistentContainer) -> ()) -> NSPersistentContainer {
        let bundle = Bundle(for: type(of: self))
        let path = bundle.path(forResource: "InMemoryDatabase", ofType: "sqlite")!
        let url = URL(fileURLWithPath: path)
        let persistentContainer = createPersistentContainer(dataModelName: "InMemoryDatabase")
        let storeDescription = NSPersistentStoreDescription(url: url)

        persistentContainer.persistentStoreDescriptions = [storeDescription]
        persistentContainer.loadPersistentStores { description, error in
            XCTAssertEqual(storeDescription.type, description.type)
            XCTAssertNil(error)
        }

        modify(persistentContainer)
        return persistentContainer
    }

    func testFail() {
        let persistentContainer = createContainer(modify: { _ in })
        let inMemoryContainer = createContainer { persistentContainer in
            let coordinator = persistentContainer.persistentStoreCoordinator
            coordinator.persistentStores.forEach { (persistentStore) in
                do {
                    try coordinator.migratePersistentStore(persistentStore, to: NSPersistentContainer.defaultDirectoryURL(), options: nil, withType: NSInMemoryStoreType)
                } catch {
                    print("Error while migrating persistentStore")
                }
            }
        }

        let persistentContainerCoordinator = persistentContainer.persistentStoreCoordinator
        persistentContainerCoordinator.persistentStores.forEach { (persistentStore) in
            XCTAssertEqual(persistentStore.type, "SQLite")
        }

        let inMemoryContainerCoordinator = inMemoryContainer.persistentStoreCoordinator
        inMemoryContainerCoordinator.persistentStores.forEach { (persistentStore) in
            XCTAssertEqual(persistentStore.type, NSInMemoryStoreType)
        }

        let fetchRequest: NSFetchRequest<Person> = Person.fetchRequest()
        let persistentContainerCount = (try! persistentContainer.viewContext.fetch(fetchRequest)).count
        let inMemoryContainerCount = (try! inMemoryContainer.viewContext.fetch(fetchRequest)).count

        XCTAssertEqual(8, persistentContainerCount)
        XCTAssertEqual(persistentContainerCount, inMemoryContainerCount)
    }

}

В приведенном выше фрагменте я также добавил утверждения, чтобы проверить, является ли тип persistentStore NSInMemoryStoreType в вашем inMemoryContainer и SQLite в вашем persistentContainer . Надеюсь, это поможет.

0 голосов
/ 30 октября 2019

InMemoryType не загружает дату из вашего URL, как предлагает другой ответ. Если вам нужно загрузить данные из файла, тогда, пожалуйста, используйте упомянутый подход Миграция, однако, если вам нужно только заполнить его случайными данными для целей тестирования, вот другое решение.

import CoreData
import XCTest
@testable import CoreDataInMemoryFail


class CoreDataInMemoryFailTests: XCTestCase {

    var persistentContainer: NSPersistentContainer!
    var inMemoryContainer: NSPersistentContainer!

    override func setUp() {
        super.setUp()
        persistentContainer = createContainer(modify: { _ in })
        inMemoryContainer = createContainer { storeDescription in
            storeDescription.type = NSInMemoryStoreType
        }
        initStubs()
    }

    override class func tearDown() {
        super.tearDown()
    }

    private func createContainer(modify: (NSPersistentStoreDescription) -> ()) -> NSPersistentContainer {
        let bundle = Bundle(for: type(of: self))
        let path = bundle.path(forResource: "InMemoryDatabase", ofType: "sqlite")!
        let url = URL(fileURLWithPath: path)
        let fileManager = FileManager.default
        let uuid = UUID().uuidString

        let saveDirectory = fileManager
                .urls(for: .cachesDirectory, in: .userDomainMask)[0]
                .appendingPathComponent(uuid)

        let saveLocation = saveDirectory.appendingPathComponent(url.lastPathComponent)

        try! fileManager.createDirectory(at: saveDirectory, withIntermediateDirectories: false)
        try! fileManager.copyItem(at: url, to: saveLocation)

        let persistentContainer = createPersistentContainer(dataModelName: "InMemoryDatabase")
        let storeDescription = NSPersistentStoreDescription(url: saveLocation)


        modify(storeDescription)
        print("TYPE OF STORE IS: \(storeDescription)")
        persistentContainer.persistentStoreDescriptions = [storeDescription]
        persistentContainer.loadPersistentStores { description, error in
            XCTAssertEqual(storeDescription.type, description.type)
            XCTAssertNil(error)
        }

        return persistentContainer
    }

    func initStubs() {

        func inserPerson( age: Int32) -> Person? {
            let obj = NSEntityDescription.insertNewObject(forEntityName: "Person", into: inMemoryContainer.viewContext)

            obj.setValue(age, forKey: "age")

            return obj as? Person
        }

        _ = inserPerson(age: 1)
        _ = inserPerson(age: 2)
        _ = inserPerson(age: 3)
        _ = inserPerson(age: 4)
        _ = inserPerson(age: 5)

        do {
            try inMemoryContainer.viewContext.save()
        }  catch {
            print("create fakes error \(error)")
        }

    }

    func removeData() {
        let fetchRequest:NSFetchRequest<NSFetchRequestResult> = NSFetchRequest<NSFetchRequestResult>(entityName: "Person")
        let objs = try! inMemoryContainer.viewContext.fetch(fetchRequest)
        for case let obj as NSManagedObject in objs {
            inMemoryContainer.viewContext.delete(obj)
        }
        try! inMemoryContainer.viewContext.save()
    }

    func testFail() {
        let fetchRequest: NSFetchRequest<Person> = Person.fetchRequest()
        let persistentContainerCount = (try! persistentContainer.viewContext.fetch(fetchRequest)).count
        let inMemoryContainerCount = (try! inMemoryContainer.viewContext.fetch(fetchRequest)).count

        XCTAssertEqual(8, persistentContainerCount)
        XCTAssertEqual(5, inMemoryContainerCount)
    }

}

Подробнееинформацию можно найти здесь

...