Моя личная философия заключается в том, что тест не является тестом, если он не тестирует реальную вещь, поэтому я косо смотрю на любой метод, который тестирует фрагменты изолированно. Хотя он будет работать во многих случаях, особенно в процедурном коде, он может дать сбой в сложном коде, например, в графах объектов Core Data.
Большинство точек сбоя в базовых данных происходят из-за неверной модели данных, например отсутствует взаимное отношение, так что график выходит из равновесия, и у вас появляются осиротевшие объекты. Единственный способ проверить плохой граф - это создать известный граф, а затем провести стресс-тестирование вашего кода, чтобы посмотреть, сможет ли он найти объекты на графике и манипулировать ими.
Чтобы реализовать этот тип теста, я делаю следующее:
- Запускайте каждый тестовый запуск, удаляя ранее существующий файл хранилища Core Data, чтобы хранилище всегда начиналось в известном состоянии.
- Предоставьте новое хранилище для каждого прогона, желательно, каждый раз генерируя его в коде, но вы можете просто поменять копию файла хранилища перед каждым прогоном. Я предпочитаю первый метод, потому что он на самом деле легче в долгосрочной перспективе.
- Убедитесь, что данные испытаний содержат относительно экстремальные примеры, например, длинные имена, строки с символами мусора, очень большие и очень маленькие цифры и т. д.
Состояние графа тестового объекта должно быть абсолютно известно во время каждого теста. Я регулярно выкидываю весь граф в тестирование, и у меня есть методы для подробного вывода как сущностей, так и живых объектов.
Обычно я разрабатываю и тестирую всю модель данных приложения в отдельной настройке проекта приложения, чтобы ничего не делать, кроме разработки модели данных. Только после того, как модель данных работает точно так, как необходимо в приложении, я перемещаю ее в полный проект и начинаю добавлять контроллеры и интерфейс.
Поскольку модель данных является фактическим ядром правильно реализованного приложения для проектирования Model-View-Controller, правильная модель данных покрывает 50-75% разработки. Остальное - прогулка по пирогу.
В этом конкретном случае вам действительно нужно только проверить, что предикат запроса на выборку возвращает правильные объекты. Единственный способ проверить это - предоставить ему полный график тестирования.
(Я хотел бы отметить, что этот метод на самом деле довольно бесполезен на практике. Он не будет возвращать какой-либо конкретный объект по атрибуту, а просто любой из произвольного числа объектов, имеющих атрибут этого значения. Например, если у вас есть Граф объектов с 23 462 Person
объектами со значением атрибута firstName
, равным John
, этот метод вернет ровно одну произвольную сущность Person из 23 462. Я не вижу смысла в этом. Я думаю, что вы думаете в процедурном SQL условия. Это может привести к путанице при работе с менеджером объект-графа, таким как Core Data.)
Обновление:
Я собираюсь предположить, что ваша ошибка вызвана тем, что компилятор рассматривает использование value
в предикате и предполагает, что это должен быть объект NSString. Когда вы отбрасываете объект в строковом формате, например, используемом predicateWithFormat:
, реальное возвращаемое значение - это объект NSString, содержащий результаты метода description
объекта. Итак, компилятор, который вы предсказываете, на самом деле выглядит так:
[NSPredicate predicateWithFormat:@"%K == %@", (NSString *)attribute, (NSString *)value]
... поэтому, когда он работает в обратном направлении, он будет искать строку NSString в параметре value
, хотя технически это не должно происходить. Такое использование id на самом деле не лучшая практика, потому что оно будет принимать любой класс, но вы не всегда знаете, какой будет строка описания, возвращенная методом -description
экземпляра.
Как я сказал выше, у вас здесь есть некоторые концептуальные проблемы. Когда вы говорите в комментарии ниже:
Я намеревался сделать метод
аналогичный ActiveRecord для find_by_
динамический искатель.
... вы подходите к базовым данным с неправильной точки зрения. Active Record в значительной степени является оберткой объектов для SQL, чтобы упростить интеграцию существующих серверов SQL с Ruby on Rails. В этом качестве преобладают процедурные концепции SQL.
Это совершенно противоположный подход, используемый Core Data. Core Data - это, прежде всего, система управления графами объектов для создания слоев модели в проекте приложения Model-View-Controller. Таким образом, объекты являются всем. Например. Можно даже иметь объекты без атрибутов, только отношения. Такие объекты также могут иметь очень сложное поведение. Это то, чего на самом деле не существует в SQL или даже в активной записи.
Вполне возможно иметь произвольное количество объектов с одинаковыми атрибутами. Это делает метод, который вы пытаетесь создать, бесполезным и опасным, потому что вы никогда не будете знать, какой объект вы получите. Это делает его "хаотичным" методом. Если у вас есть несколько объектов с одним и тем же атрибутом, метод будет произвольно возвращать любой отдельный объект, который соответствует значению атрибута.
Если вы хотите идентифицировать конкретный объект, вам нужно захватить ManagedObjectID
объекта, а затем использовать -[NSManagedObjectContext objectForID:]
, чтобы получить его. После сохранения объекта ManagedObjectID
становится уникальным.
Однако эта функция обычно используется только тогда, когда вам приходится ссылаться на объекты в разных магазинах или даже в разных приложениях. Обычно нет смысла иначе. При использовании Базовых данных вы ищете объекты, основанные не только на их атрибутах, но и на их положении, то есть их отношении к другим объектам в графе объектов.
Позвольте мне скопировать и вставить несколько очень важных советов: Базовые данные - это не SQL. Сущности не являются таблицами. Объекты не являются строками. Столбцы не являются атрибутами. Базовые данные - это система управления графом объектов, которая может сохранять или не сохранять объектный граф, а может и не использовать SQL для этого далеко за кулисами. Попытка представить базовые данные в терминах SQL приведет к тому, что вы полностью неправильно поймете базовые данные и приведет к большим трудностям и потерянному времени.
Естественно попытаться запрограммировать новый API, используя конструкции API, с которыми вы уже знакомы, но это опасная ловушка, когда новый API имеет существенно отличную философию дизайна от старого API.
Если вы пытаетесь написать базовую и основную функцию старого API в новом, это само по себе должно предупредить вас, что вы не синхронизированы с новой философией API. В этом случае вы должны спросить, почему, если универсальный метод findByAttribute
был полезен в Core Data, почему Apple не предоставил его? Не более ли вероятно, что вы пропустили важную концепцию в Core Data?