Swift не может тестировать данные ядра в тестах Xcode?
Я работаю над проектом iOS, который использует основные данные. Я использую быстро.
Стек Core Data настроен правильно, и все кажется прекрасным.
Я создал класс для объекта (NSManagedObject) под названием TestEntity.
Класс выглядит следующим образом:
import UIKit
import CoreData
class TestEntity: NSManagedObject {
@NSManaged var name: NSString
@NSManaged var age: NSNumber
}
Итак, я пытаюсь вставить новый код TestEntity в код, используя эту строку кода:
let te: TestEntity = NSEntityDescription.insertNewObjectForEntityForName("TestEntity", inManagedObjectContext: ctx) as TestEntity
Затем я получаю эту ошибку:
![enter image description here]()
Я видел несколько ответов на переполнение стека, которые говорят, что мне нужно беспокоиться о имени модуля. Итак, я посмотрел на документы:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WritingSwiftClassesWithObjective-CBehavior.html
Затем я попал в основной объект данных для TestEntity, и в поле класса я ввел myAppName.TestEntity
Когда я запустил приложение в этой строке:
let te: TestEntity = NSEntityDescription.insertNewObjectForEntityForName("TestEntity", inManagedObjectContext: ctx) as TestEntity
все равно дает мне ту же ошибку.
Что еще я могу делать неправильно?
EDIT:
Итак, я смог сделать приложение не аварийным, изменив класс TestEntity NSManagedObject:
импортировать UIKit
import CoreDatap >
@objc(TestEntity) class TestEntity: NSManagedObject {
@NSManaged var name: NSString
@NSManaged var age: NSNumber
}
Итак, я добавил в него @objc (TestEntity). Это работает с или без добавления имени приложения перед именем класса TestEntity в инспекторе модели данных основных данных.
Это работает, но когда я запускаю тесты, эта строка все еще вылетает:
let te: TestEntity = NSEntityDescription.insertNewObjectForEntityForName("TestEntity", inManagedObjectContext: ctx) as TestEntity
Итак, я обнаружил, что это проблема для других людей:
Как получить доступ к классам Obj-C, генерированным Core Data, в тестовых целях?
Как мы можем получить основные данные для работы в тестах с быстрым. Я НЕ использую заголовок моста в целевой программе приложения, и все это отлично работает. Тестовая цель все еще падает.
Как я могу исправить тестовую цель, чтобы она могла запускать тесты данных ядра?
Ответы
Ответ 1
С Xcode 7 и @testable
вам больше не нужно обновлять managedObjectClassName
или использовать другие хаки. Вот что я сделал, чтобы заставить его работать в Xcode 7.2.
- Задайте тестовое целевое приложение-хост и установите флажок "Разрешить тестирование API-интерфейсов хост-приложений".
![введите описание изображения здесь]()
- Убедитесь, что none ваших обычных классов имеет целевое членство, указывающее на цель тестирования. Для целей тестирования следует установить только классы с кодом unit test.
![введите описание изображения здесь]()
- Добавьте строку
@testable
в начало всех тестовых классов:
import XCTest
@testable import MyApp
class MyAppTests: XCTestCase {
}
Если у вас все еще есть проблемы, вы можете попробовать следующие дополнительные советы: https://forums.developer.apple.com/message/28773#28949
Я сражался с этим на какое-то время, поэтому надеюсь, что это поможет кому-то другому.
Ответ 2
Это потому, что Framework CoreData все еще находится в Objective-C. Swift использует классы с именами, поэтому для CoreData для поиска ваших быстрых классов вам нужно указать имя класса с этим пространством имен следующим образом:
![enter image description here]()
Проблема, которую вы будете иметь, заключается в том, что ваше приложение не имеет того же пространства имен, что и при выполнении вами тестов.
<AppName>.<ClassName>
vs <AppName>Tests.<ClassName>
EDIT: решение для работы в качестве приложения и тестов
Я просто написал фрагмент кода для решения проблемы <AppName>.<ClassName>
vs <AppName>Tests.<ClassName>
. Решение, которое я использую в это время (Xcode 6.1), НЕ должно заполнить поле Class
в пользовательском интерфейсе CoreData (см. Выше) и сделать это вместо кода.
Этот код обнаружит, что вы работаете как приложение vs Tests и используете правильное имя модуля и обновляете managedObjectClassName
.
lazy var managedObjectModel: NSManagedObjectModel = {
// The managed object model for the application. This property is not optional...
let modelURL = NSBundle.mainBundle().URLForResource("Streak", withExtension: "momd")!
let managedObjectModel = NSManagedObjectModel(contentsOfURL: modelURL)!
// Check if we are running as test or not
let environment = NSProcessInfo.processInfo().environment as [String : AnyObject]
let isTest = (environment["XCInjectBundle"] as? String)?.pathExtension == "xctest"
// Create the module name
let moduleName = (isTest) ? "StreakTests" : "Streak"
// Create a new managed object model with updated entity class names
var newEntities = [] as [NSEntityDescription]
for (_, entity) in enumerate(managedObjectModel.entities) {
let newEntity = entity.copy() as NSEntityDescription
newEntity.managedObjectClassName = "\(moduleName).\(entity.name)"
newEntities.append(newEntity)
}
let newManagedObjectModel = NSManagedObjectModel()
newManagedObjectModel.entities = newEntities
return newManagedObjectModel
}()
Ответ 3
Я думаю, что получаю схожие результаты. Мне не удалось заставить мои тесты работать с линией
var newDept = NSEntityDescription.insertNewObjectForEntityForName("Department", inManagedObjectContext: moc) as Department
Но я мог бы запустить тесты с помощью:
let entity = NSEntityDescription.entityForName("Department", inManagedObjectContext: moc)
let department = Department(entity: entity!, insertIntoManagedObjectContext: moc)
Моя сущность выглядит следующим образом:
@objc(Department)
class Department: NSManagedObject {
@NSManaged var department_description: String
...
}
Ответ 4
Пример кода от Ludovic не охватывает сущности. Поэтому при установке родительского объекта в CoreData приложение аварийно завершает работу.
Адаптирован код для учета сущностей:
private func createManagedObjectModel() {
// Get module name
var moduleName: String = "ModuleName"
let environment = NSProcessInfo.processInfo().environment as! [String : AnyObject]
let isTest = (environment["XCInjectBundle"] as? String)?.pathExtension == "xctest"
if isTest { moduleName = "ModuleNameTests" }
// Get model
let modelURL = NSBundle.mainBundle().URLForResource(self.storeName, withExtension: "momd")!
let model = NSManagedObjectModel(contentsOfURL: modelURL)!
// Create entity copies
var newEntities = [NSEntityDescription]()
for (_, entity) in enumerate(model.entities) {
let newEntity = entity.copy() as! NSEntityDescription
newEntity.managedObjectClassName = "\(moduleName).\(entity.managedObjectClassName)"
newEntities.append(newEntity)
}
// Set correct subentities
for (_, entity) in enumerate(newEntities) {
var newSubEntities = [NSEntityDescription]()
for subEntity in entity.subentities! {
for (_, entity) in enumerate(newEntities) {
if subEntity.name == entity.name {
newSubEntities.append(entity)
}
}
}
entity.subentities = newSubEntities
}
// Set model
self.managedObjectModel = NSManagedObjectModel()
self.managedObjectModel.entities = newEntities
}
Ответ 5
Мне также пришлось столкнуться с подобной проблемой, когда я попытался написать примеры unit test для примера приложения (MedicationSchedulerSwift3.0), написанный в Swift 3.0, кроме внедрения решения, предоставленного johnford. Я создал категорию на XCTestCase, чтобы настроить NSManagedObjectContext с хранилищем в памяти, используя приведенный ниже код:
// XCTestCase+CoreDataHelper.swift
import CoreData
import XCTest
@testable import Medication
extension XCTestCase {
func setUpInMemoryManagedObjectContext() -> NSManagedObjectContext {
let managedObjectModel = NSManagedObjectModel.mergedModel(from: [Bundle.main])!
let persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)
do {
try persistentStoreCoordinator.addPersistentStore(ofType: NSInMemoryStoreType, configurationName: nil, at: nil, options: nil)
} catch {
print("Adding in-memory persistent store failed")
}
let managedObjectContext = NSManagedObjectContext(concurrencyType:.privateQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = persistentStoreCoordinator
return managedObjectContext
}
}
И использовал его вот так:
// NurseTests.swift
import XCTest
import CoreData
@testable import Medication
class NurseTests: XCTestCase {
var managedObjectContext: NSManagedObjectContext?
//MARK: Overriden methods
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
if managedObjectContext == nil {
managedObjectContext = setUpInMemoryManagedObjectContext()
}
}
//MARK:- Testing functions defined in Nurse.swift
// testing : class func addNurse(withEmail email: String, password: String, inManagedObjectContext managedObjectContext: NSManagedObjectContext) -> NSError?
func testAddNurse() {
let nurseEmail = "[email protected]"
let nursePassword = "clara"
let error = Nurse.addNurse(withEmail: nurseEmail, password: nursePassword, inManagedObjectContext: managedObjectContext!)
XCTAssertNil(error, "There should not be any error while adding a nurse")
}
}
Если кому-то нужно больше примеров, они могут посмотреть здесь unit test - MedicationTests