Библиотеки, не найденные при использовании CocoaPods с логическими тестами iOS

148

Я пытаюсь написать некоторые тесты логики iOS для классов в моем проекте, которые используют функциональность некоторых библиотек в моем подспецификации. Я использую стандартный пакет модульных тестов, предоставленный в XCode (хотя не Тесты приложений, только Модульные тесты).

Например, я использую Magical Record, и у меня есть эта библиотека, связанная в моем подспецификации. Он присутствует в проекте Pods в моей рабочей области и работает, как ожидается, когда приложение работает в симуляторе или на устройстве. Однако, когда я пытаюсь связать с тестом объект, который использует Magical Record, я получаю ошибку компоновщика, утверждающую, что он не может найти селекторы из Magical Record. Я попытался обновить мой HEADER_SEARCH_PATH в моем пакете логического тестирования, даже жестко закодировать его в каталог заголовков, созданный CocoaPods, но безуспешно.

Я могу без проблем запускать модульные тесты для классов, которые не используют библиотеки CocoaPods.

Я иду по этому поводу неправильно? Должен ли я делать что-то еще, чтобы компилятор увидел библиотеки CocoaPods?

Марк Струзинский
источник

Ответы:

224

CocoaPods 1.0 изменил синтаксис для этого. Теперь это выглядит так:

def shared_pods
    pod 'SSKeychain', '~> 0.1.4'
    ...
end

target 'Sail' do
    shared_pods
end

target 'Sail-iOS' do
    shared_pods
end

Предварительно CocoaPods 1.0 ответ

То, что вы хотите использовать, link_withот вас Podfile. Что-то вроде:

link_with 'MainTarget', 'MainTargetTests'

Тогда беги pod installснова.

Кит Смайли
источник
7
Это сразу решило проблему для меня.
Mttrb
9
Я получаю странные ошибки с этим - при тестировании isSubclassOfClass:звонки возвращаются NOтуда, куда они должны вернуться YES. Единственная причина, по которой я могу объяснить это, заключается в том, что зависимости действительно связаны как с основной, так и с целевой целью, и когда загрузчик пакета целевой цели загружает основной пакет, он не может решить, какой класс выбрать.
Фабб
4
У меня та же проблема с isKindOfClass:возвратом, NOкогда он должен вернуться YES. Если я регистрирую указатель на Classобъект, который я тестирую, и Classкласс, который я хочу сравнить, это два разных значения. Ясно, что мой код из комплекта приложения использует другой символ для класса, чем код из моих модульных тестов. Кто-нибудь нашел способ решить эту проблему?
Николас Харт
2
Я не думаю, что это хороший путь из-за ошибок, упомянутых другими. Придерживайтесь обновления файла конфигурации «на основе» бит. Убедитесь, что вы не связали libPods.a дважды.
Боб Сприн
3
Это должен быть принятый ответ, так как это официальный способ CocoaPods настроить Pod с несколькими целями. Большое спасибо Кит!
cschuff
174

Я понял это, посмотрев на то, как основной целью моего приложения было получение настроек из библиотеки CocoaPods. CocoaPods включает в себя файл .xcconfig с именем Pods.xcconfig. Этот файл содержит все пути поиска заголовка.

Если вы посмотрите на свой проект в навигаторе проекта и перейдете на вкладку «Информация», вы увидите конфигурации вашей сборки, перечисленные в верхнем разделе. Если вы откроете треугольник раскрытия для ваших различных конфигураций, вы увидите список стручков под вашей основной целью. Мне пришлось нажать на выпадающий список и добавить стручки к цели логического теста.

Снимок конфигурации

Мне также пришлось скопировать настройки $(inherited)и ${PODS_HEADERS_SEARCH_PATHS}из моей главной цели и скопировать их в цель логического теста в разделе Build Settings / HEADER_SEARCH_PATHS.

Наконец, мне пришлось добавить libPods.a на этапе сборки Link Binary with Libraries для моей цели логических тестов.

Надеюсь, что это может помочь кому-то еще.

Марк Струзинский
источник
Brilliant! Я использую MagicalRecord, а также OCMockito и OCHamcrest для модульного тестирования. С этим исправлением теперь я могу установить их через CocoaPods! Спасибо!
Fogmeister
4
Это сработало для меня, спасибо. ПРИМЕЧАНИЕ. Мне не нужно было добавлять libPods.a как в тестовый proj, так и в основной proj. Это приводит к дублированию ошибки символа
Крейг Брюс
Мне также пришлось скопировать настройки сборки «Определяемые пользователем». Пути поиска в заголовке ссылаются на $ PODS_ROOT, который не был определен для цели теста. Вы можете добавить его, зайдя в Editor-> Add Build Setting-> Add User-Defined Setting, затем скопировав значение $ PODS_ROOT из основной цели.
Синигами
11
Это не правильный способ исправить это. Смотрите ответ с помощью link_with. Вы также можете указывать различные модули для каждого целевого объекта в файле модуля, то есть включать в свой тестовый объект только OCMockito.
dbainbridge
Да, да, да! Перед этим ответом мне пришлось удалить Test target из моих проектов! Спасибо, человек :)
Иосип Б.
53

Существует решение, которое я нашел здесь, модульные тесты с CocoaPods :

Откройте файл проекта в XCode, затем выберите Project (не цель), на правой панели есть раздел под названием Configurations. Выберите «Модули» в столбце «На основе файла конфигурации» для своей цели тестирования.

введите описание изображения здесь

Mingming
источник
Ну, что, если есть тест-зависимые зависимости, например Specta, вы хотите связать с тестовым проектом, но не с основным проектом? : S
фатухоку
Это сработало и не требует каких-либо изменений в конфигурации или настройке pod ... Отличное решение.
Ричард
1
Хотя это решение может создать ошибку: Class Foo is implemented in both MyApp and MyAppTestCase. One of the two will be used. Which one is undefined. кажется, это вызвано ошибкой в ​​Cocoapods; см. ответ @JRV ниже.
Ричард
Это не просто предупреждения. При такой настройке не создаются правильные данные о покрытии кода Xcode, и в большинстве случаев модульные тесты просто зависают при запуске.
i4niac
Я импортировал Estimote SDK вручную путем перетаскивания, я не получаю капсулы. Как решить это?
Гуру Теджа
18

Я согласен с другими ответами о том, что необходимо связать библиотеки с целями тестирования. Однако ни одно из предложений до сих пор не помогло мне. Как пишет @fabb в комментарии: «при тестировании isSubclassOfClass:вызовы возвращают NO, где они должны возвращать YES. Единственная причина, по которой я могу объяснить это, заключается в том, что зависимости действительно связаны как с основной, так и с целевой целью теста, а также когда пакет цели теста Загрузчик загружает основной пакет, он не может решить, какой класс выбрать. " Я получаю ту же проблему со всеми предыдущими предложениями в этой теме.

Решение, которое я получил, состояло в том, чтобы обновить мой Podfile, чтобы определить определенные Pod для моей главной цели и моей цели тестирования:

target 'MyTarget' do
   pod 'AFNetworking', '~> 2.5.0'
   pod 'Mantle', '~> 1.5'
end

target 'MyTargetTests' do
   pod 'OCMockito', '~> 1.3.1'
end

Это было необходимо , чтобы указать стручок для моей тестовой цели , даже если я не использовал никаких конкретные тесты стручков. В противном случае CocoaPods не вставит необходимую логику связывания в мой проект.

Именно эта ссылка помогла мне прийти к такому выводу.

JRV
источник
1
Спасибо за ссылку на проблему CocoaPods - это помогло мне решить мою проблему!
karlbecker_com
ДА!!!! Эта проблема преследует меня. Это единственный разумный ответ cocoapods, с которым я столкнулся.
DonnaLea
Есть лучший способ справиться с этим под 1.x: stackoverflow.com/a/40866889/2799670
Даррен Блэк,
6

Я добавил, :exclusive => trueчтобы избежать дублирования ошибок символов в цели теста приложения.

target 'myProjectTests', :exclusive => true do
   pod 'OCMock', :head
   pod 'XCTAsyncTestCase', :git => 'https://github.com/iheartradio/xctest-additions.git'
end

link_with 'myProject', 'myProjectTests'

Когда я изменил цель тестирования приложения на тестовую единицу логики, возникает ошибка компоновщика. После удаления :exclusive => trueвсе снова работает.

target 'myProjectTests', do
   pod 'OCMock', :head
   pod 'XCTAsyncTestCase', :git => 'https://github.com/iheartradio/xctest-additions.git'
end

link_with 'myProject', 'myProjectTests'

:exclusive => trueутверждает, что все, что снаружи, do...endНЕ должно быть связано myProjectTests, что разумно для целей тестирования приложения, но это вызовет ошибки компоновщика в целях логического теста.

Хай фэн као
источник
Исключительным было решение для меня, как показано в ответе kylef по этому вопросу CocoaPods , который был найден благодаря ответу JRV на этот вопрос!
karlbecker_com
1
Да, каждый должен прочитать эту проблему на github, связанном с @karlbecker_com. Кажется, это всего лишь длительное ограничение для кокапод. Согласно обсуждению, link_with не является обязательным. Просто добавьте цель теста и используйте: эксклюзив. Если вашей тестовой цели не нужны какие-либо особые стручки, все равно добавьте одну, иначе кокоподы не будут ее обрабатывать.
Кбол
@kball Какой из них не нужен link_with? Тест приложения или тест логического модуля?
Хай Фенг Као
Если у вас нет другой причины использовать его, вам вообще не нужен link_with. И вообще говоря, вы не хотите связывать эти модули со своим тестовым комплектом. Они должны быть связаны только один раз, в комплекте приложений, а затем на них ссылаются ваши тесты через зависимость (при условии, что символы, скрытые по умолчанию отключены). В противном случае поведение не определено, поскольку будут существовать две версии модулей: одна включена в цель приложения, другая - в цель теста.
Кбал
6

Вы можете использовать link_with согласно решению @Keith Smiley.

Если у вас есть общие модули и особенности для каждой цели, вы можете использовать опцию «def» для определения группы модулей. и использовать «def» позже в исключительной цели.

def import_pods
    pod 'SSKeychain'
end

target 'MyProjectTests', :exclusive => true do
  import_pods
end

target 'MyProject', :exclusive => true do
  import_pods
  pod 'Typhoon'
end

В приведенном выше примере я добавил «SSKeychain» для обеих целей, а «Тайфун» только для цели «MyProject»

Elihay
источник
5

Мое решение этой проблемы состояло в том, чтобы изменить мой Podfile, чтобы включить библиотеку в обе цели, как это

target "MyApp" do  
    pod 'GRMustache', '~> 7.0.2'
end

target "MyAppTests" do
    pod 'GRMustache', '~> 7.0.2'
end

И так как я использую swift, мне также пришлось настроить цель теста для включения MyApp-Bridging-Header.hфайла. (В группе Swift Compiler на вкладке «Настройки сборки»)

Qw4z1
источник
3
Осторожно - это значительно увеличит время сборки, так как вы продолжаете добавлять больше стручков!
фатухоку
@fatuhoku этого не знал. Можете ли вы дать некоторое представление о том, почему это увеличивает время сборки?
Qw4z1
2
Ну, каждое упоминание о стручке является целью вашего Podsпроекта. Упоминая свои стручки дважды (один раз для тестирования и один раз для приложения), вы получите два набора целей. Это эффективно удваивает конфигурацию работы pod install. Это не будет проблемой, пока у вас не будет> 15 стручков, так что не волнуйтесь до тех пор.
фатухоку
1
Это единственное решение, которое работает для меня с Cocoapods 1.0
William Entriken
Начиная с 1.x, это официальный метод для тестов, наследующих зависимости приложений: stackoverflow.com/a/40866889/2799670
Даррен Блэк
4

У меня был похожий случай, когда я потерял некоторые библиотечные файлы во время контроля версий. Я все еще видел библиотечный файл в моих модулях, но при отсутствии действующего кода XCode сказал, что его больше нет. К моему ужасу, запуск 'pod install' не сразу возвращал потерянные файлы.

Мне пришлось удалить и заменить модуль вручную, выполнив следующие действия:

  1. Удалить библиотеку из подфайла
  2. Запустите 'pod install', чтобы полностью удалить библиотеку
  3. Поместите библиотеку обратно в Podfile
  4. Запустите «pod install» снова

Это должно вернуть библиотеку в ее первоначальный вид.

максвелл
источник
2

Также стоит отметить, что если вы libPods.aдобавили дважды, вы получите некую неприятную ошибку, подобную этой:

232 duplicate symbols for architecture i386

Чтобы исправить это, просто удалите одну из libPods.aссылок в Project Explorer.

Мэт Райер
источник
2

В CocoaPods 1.x появился новый способ объявления общих зависимостей между целью и соответствующей целью теста. До этого момента я использовал общепринятое решение Марка Струзинского, но использование этого метода дало огромное количество предупреждений при выполнении моих тестов, которые:

Class SomeClass is implemented in both /Path/To/Test/Target and /Path/To/App/Target. One of the two will be used. Which one is undefined.

С CocoaPods 1.x мы можем объявить нашу цель -Test как наследующую через пути поиска родительской цели следующим образом:

target 'MyApp' do
    pod 'aPod'
    pod 'anotherPod'
    project 'MyApp.xcodeproj'
end
target 'MyAppTests' do
    inherit! :search_paths
    project 'MyApp.xcodeproj'
end

Это приведет к тому, что цель -Test получит доступ к зависимостям цели приложения без нескольких двоичных копий. Это серьезно ускорило время сборки тестов для меня.

Даррен Блэк
источник
2

Попробуйте это у меня работает,

Нам нужно установить блоки в конфигурациях,

Проект-> Информация-> Конфигурации в проекте XCode (ваш проект) должен быть установлен в основной проект 'Pods' для Отладки, Выпуска (и что еще у вас есть). Смотрите "Заголовки не найдены - пути поиска не включены"

введите описание изображения здесь

Надеюсь, это поможет кому-то.

Джейвант Хедкар
источник
1

Я работаю с интеграцией POD GoogleMaps Objective-C на iOS с моим приложением Swift, и поэтому для меня проблема заключалась в том, что цель теста не имела ссылки на файл заголовка моста ( SWIFT_OBJC_BRIDGING_HEADER ) в настройках сборки. Убедитесь, что как ваше приложение, так и цели тестового приложения указывают на это, чтобы сторонние вызовы API (API карт и т. Д.) Могли использоваться в быстрых модульных тестах.

appledevguru
источник
1
У меня есть такая же настройка, как у вас. Я уже добавил заголовок моста к цели теста, однако я получаю сообщение об ошибке «Нет такого модуля« GoogleMaps »» import GoogleMaps.
Николас Миари
0

Следующий синтаксис дает лучший результат для меня (протестировано под cocoapod v.1.2.1):

https://github.com/CocoaPods/CocoaPods/issues/4626#issuecomment-210402349

 target 'App' do
    pod 'GoogleAnalytics' , '~> 3.0'
    pod 'GoogleTagManager' , '~> 3.0'

     pod 'SDWebImage', '~>3.7'
     platform :ios, '8.0'
     use_frameworks!

     target 'App Unit Tests' do
         inherit! :search_paths
     end
 end

Без этого у меня есть предупреждения во время теста о дубликатах символов.

После этого предупреждения исчезли.

Максим Холявкин
источник
0

У меня были проблемы с использованием OpenCV под XCTest. Это давало мне ошибки компоновщика Undefined symbols for architecture arm64для таких классов, как cv::Mat. Я устанавливаю OpenCV через CocoaPods используя pod 'OpenCV', '~> 2.0'основную цель. Независимо от того, как сильно я пытался поместить зависимость OpenCV под цель тестирования или использовать inherit! :search_pathsничего из этого не получалось. Решение было создать abstract_targetтак:

# Uncomment the next line to define a global platform for your project
platform :ios, '6.1.6'

abstract_target 'Shows' do
  pod 'RMVision', path: '../..'
  pod 'RMShared', path: '../../../RMShared'
  pod 'OpenCV', '~> 2.0'

  target 'RMVisionSample' do
    # Uncomment the next line if you're using Swift or would like to use dynamic frameworks
    # use_frameworks!

    # Pods for RMVisionSample
  end

  target 'RMVisionSampleTests' do
    # inherit! :search_paths
    # Pods for testing
  end

  target 'RMVisionBenchmarks' do
    # inherit! :search_paths
    # Pods for testing
  end

end 

Также полезны команды pod deintegrate&, pod cleanкоторые помогают очистить проект и убедиться, что вы начинаете заново при тестировании. Вы можете установить эти два, используя [sudo] gem install cocoapods-deintegrate cocoapods-clean.

Фоти Дим
источник