Новый основной стек данных (PersistenceController) в модульных тестах

#unit-testing #core-data

#модульное тестирование #core-data

Вопрос:

С новым шаблоном Swift App Xcode я вижу, что PersistenceController структура была добавлена с экземпляром shared и preview , который служит основным стеком данных. Насколько я понимаю, preview экземпляр предназначен для предварительного просмотра SwiftUI.

Если у меня есть класс обслуживания, который использует экземпляр моего основного стека данных, должен ли я использовать preview экземпляр как часть моих модульных тестов? Или я должен создать какой-то шпион / подделку для перехода в класс обслуживания, который я тестирую?

В целом, каковы наилучшие методы тестирования классов, использующих основные данные?

Ответ №1:

Я также просто изучаю это. Я не нашел лучшей практики. Итак, я подведу итог тому, что я делаю. PersistenceController — хороший шаблон.

Статический предварительный просмотр var позволяет создать другое хранилище и добавить в него некоторые объекты, например

   static var preview: PersistenceController = {
    let result = PersistenceController(inMemory: true)
    let viewContext = result.container.viewContext
    // this is the part where you can add mock data
    for _ in 0..<10 {
        let newItem = Note(context: viewContext)
        newItem.creationDate = Date()
    }
    do {
        try viewContext.save()
    } catch {
        let nsError = error as NSError
        fatalError("Unresolved error (nsError), (nsError.userInfo)")
    }
    return result
}()
  

Для модульных тестов мы хотели бы начать с пустого хранилища, поэтому я просто добавил еще одну переменную в PersistenceController

 static let unitTest: PersistenceController = {
    let result = PersistenceController(inMemory: true)
    //empty store
    return result
}()
  

Это делает мой модульный тест похожим

 class TestCoreDataStack: XCTestCase {

var coreDataStack: PersistenceController!

override func setUp() {
    super.setUp()
    coreDataStack = PersistenceController.unitTest
}

override func tearDown() {
  super.tearDown()
   //called after the invocation of each test method in the class.
   // I am deleted all notes to start fresh for the next test
  UnitTestHelpers.deleteAllNotes(container: coreDataStack.container)  
  coreDataStack = nil
}

func testAddNote() {
    let context = coreDataStack.container.viewContext
    
    let title = "my new title"
    let note = Note(title: title, context: context)
    
    XCTAssertNotNil(note, "note should not be nil")
    XCTAssertTrue(note.title == title)
    XCTAssertNotNil(note.creationDate, "notes date should not be nil")

} }
  

Комментарии:

1. Мне понравилось это решение, но я обнаружил, что у меня возникли проблемы с контроллером сохраняемости модульных тестов, который не инициализировался повторно между тестами, и я не хотел каждый раз вручную удалять все. В конце я просто вызываю coreDataStack = PersistenceController(inMemory: true) в настройках, и это работает до сих пор.

2. @kprater Я попробовал аналогичную настройку, но затем я получаю Multiple NSEntityDescriptions claim the NSManagedObject subclass предупреждение и Failed to find a unique match for an NSEntityDescription to a managed object subclass ошибку, поскольку как статический shared (из инициализации приложения), так и статический test инициализируются при запуске модульного теста.