初始化时内存中的 CoreData 容器为空
In memory CoreData container is empty when initialized
tl;博士:
- 克隆项目:https://github.com/Jasperav/CoreDataInMemoryFail
- 运行 测试失败。为什么我的内存容器没有任何数据,我如何确保它有数据?
长:
我有一个包含填充数据的 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
创建的上下文中看不到它。
当我使用指向包含数据的 sqlite 文件的 CoreData 创建内存数据库时,查询数据库时没有看到任何结果。我想查看 sqlite 文件中的数据。数据应该全部加载到内存中。这是为了测试目的。
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)
}
}
您所尝试的问题是在将它们加载到容器之前将 storeDescription 的类型设置为 NSInMemoryStoreType。由于 storeDescription 的类型被声明为 NSInMemoryStoreType api 将不会从您提供的文件 URL 中读取和填充数据。为了让 api 从文件 url 中读取数据,storeDescription 的类型必须是使用初始化程序 init(url: URL)
初始化定义的类型,即 SQLite 你的情况。
但是,如果您想要一个 NSInMemoryStoreType 类型的 persistentStore 以及从文件 url 读取的数据,您可以使用函数 migratePersistentStore:toURL:options:withType:error:
迁移 NSInMemoryStoreType 类型的 persistentContainer 的 persistentStore。您可以尝试下面的代码片段。
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)
}
}
在上面的代码片段中,我还添加了断言来验证您的 inMemoryContainer 和 [=19= 中的 persistentStore 类型是否为 NSInMemoryStoreType ]SQLite 在你的 persistentContainer 中。希望对你有帮助。
tl;博士:
- 克隆项目:https://github.com/Jasperav/CoreDataInMemoryFail
- 运行 测试失败。为什么我的内存容器没有任何数据,我如何确保它有数据?
长:
我有一个包含填充数据的 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
创建的上下文中看不到它。
当我使用指向包含数据的 sqlite 文件的 CoreData 创建内存数据库时,查询数据库时没有看到任何结果。我想查看 sqlite 文件中的数据。数据应该全部加载到内存中。这是为了测试目的。
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)
}
}
您所尝试的问题是在将它们加载到容器之前将 storeDescription 的类型设置为 NSInMemoryStoreType。由于 storeDescription 的类型被声明为 NSInMemoryStoreType api 将不会从您提供的文件 URL 中读取和填充数据。为了让 api 从文件 url 中读取数据,storeDescription 的类型必须是使用初始化程序 init(url: URL)
初始化定义的类型,即 SQLite 你的情况。
但是,如果您想要一个 NSInMemoryStoreType 类型的 persistentStore 以及从文件 url 读取的数据,您可以使用函数 migratePersistentStore:toURL:options:withType:error:
迁移 NSInMemoryStoreType 类型的 persistentContainer 的 persistentStore。您可以尝试下面的代码片段。
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)
}
}
在上面的代码片段中,我还添加了断言来验证您的 inMemoryContainer 和 [=19= 中的 persistentStore 类型是否为 NSInMemoryStoreType ]SQLite 在你的 persistentContainer 中。希望对你有帮助。