Bibliothèques introuvables lors de l'utilisation de CocoaPods avec des tests logiques iOS

148

J'essaie d'écrire des tests de logique iOS sur des classes de mon projet qui utilisent des fonctionnalités de certaines des bibliothèques de mon podspec. J'utilise le bundle de tests unitaires standard fourni dans Xcode (mais pas les tests d'application, juste les tests unitaires).

Par exemple, j'utilise Magical Record, et j'ai cette bibliothèque liée dans mon podspec. Il est présent dans le projet Pods de mon espace de travail et fonctionne comme prévu lorsque l'application s'exécute dans le simulateur ou sur l'appareil. Lorsque j'essaie de lier au test l'objet qui utilise Magical Record, cependant, j'obtiens une erreur de l'éditeur de liens indiquant qu'il ne peut pas trouver les sélecteurs de Magical Record. J'ai essayé de mettre à jour mon HEADER_SEARCH_PATH dans mon bundle de test logique, même en le codant en dur dans le répertoire d'en-têtes créé par CocoaPods, mais pas de chance.

Je peux exécuter des tests unitaires sur des classes qui n'utilisent pas les bibliothèques CocoaPods sans problème.

Est-ce que je me trompe? Dois-je faire autre chose pour que le compilateur voie les bibliothèques CocoaPods?

Mark Struzinski
la source

Réponses:

224

CocoaPods 1.0 a changé la syntaxe pour cela. Cela ressemble maintenant à ceci:

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

target 'Sail' do
    shared_pods
end

target 'Sail-iOS' do
    shared_pods
end

Réponse avant CocoaPods 1.0

Ce que vous souhaitez utiliser provient link_withde votre Podfile. Quelque chose comme:

link_with 'MainTarget', 'MainTargetTests'

Puis exécutez à pod installnouveau.

Keith Smiley
la source
7
Cela a immédiatement résolu le problème pour moi.
mttrb
9
J'obtiens d'étranges erreurs avec cela - lors des tests, les isSubclassOfClass:appels retournent NOlà où ils devraient revenir YES. La seule raison pour laquelle je peux expliquer cela est que les dépendances sont vraiment liées à la fois à la cible principale et à la cible de test, et lorsque le chargeur de bundle de la cible de test charge le bundle principal, il ne peut pas décider quelle classe prendre.
fabb du
4
J'ai le même problème avec le isKindOfClass:retour NOquand il devrait revenir YES. Si je connecte le pointeur vers le Classde mon objet que je teste et le Classde la classe que je veux comparer, ce sont deux valeurs différentes. Il est clair que mon code du bundle d'applications utilise un symbole différent pour la classe que le code de mes tests unitaires. Quelqu'un a-t-il trouvé un moyen de résoudre ce problème?
Nicholas Hart
2
Je ne pense pas que ce soit une bonne façon de procéder en raison des erreurs que certains autres ont mentionnées. Tenez-vous en à mettre à jour le fichier de configuration «basé sur» bit. Assurez-vous que vous n'avez pas lié libPods.a deux fois.
Bob Spryn
3
Cela devrait être la réponse acceptée car il s'agit du moyen officiel de CocoaPods de configurer des pods avec plusieurs cibles. Merci beaucoup Keith!
cschuff
174

J'ai compris celui-ci en regardant comment la cible principale de mon application recevait les paramètres de la bibliothèque CocoaPods. CocoaPods inclut un fichier .xcconfig nommé Pods.xcconfig. Ce fichier contient tous les chemins de recherche d'en-tête.

Si vous regardez votre projet dans le navigateur de projet et cliquez sur l'onglet Info, vous verrez vos configurations de construction répertoriées dans la section supérieure. Si vous ouvrez le triangle de divulgation pour vos différentes configurations, vous verrez des pods répertoriés sous votre cible principale. J'ai dû cliquer sur le menu déroulant et ajouter des pods à la cible de test logique.

Instantané des configurations

J'ai également dû copier les paramètres de $(inherited)et ${PODS_HEADERS_SEARCH_PATHS}de ma cible principale et les copier dans la cible de test logique sous Build Settings / HEADER_SEARCH_PATHS.

Enfin, j'ai dû ajouter libPods.a dans la phase de construction Link Binary with Libraries pour ma cible de tests logiques.

J'espère que cela pourra aider quelqu'un d'autre.

Mark Struzinski
la source
Brillant! J'utilise MagicalRecord et aussi OCMockito et OCHamcrest pour les tests unitaires. Avec ce correctif, je peux maintenant les installer tous via CocoaPods! Merci!
Fogmeister
4
Cela a fonctionné pour moi, merci. REMARQUE .. Je n'ai pas eu besoin d'ajouter le libPods.a dans le projet de test et le projet principal. Cela provoque une erreur de symbole en double
Craig Bruce
Pour moi, j'ai également dû copier les paramètres de construction "définis par l'utilisateur". Les chemins de recherche d'en-tête font référence à $ PODS_ROOT qui n'a pas été défini sur la cible de test. Vous pouvez l'ajouter en allant dans Editor-> Add Build Setting-> Add User-Defined Setting puis en copiant la valeur $ PODS_ROOT depuis la cible principale.
Shinigami
11
Ce n'est pas la bonne façon de résoudre ce problème. Voir la réponse avec link_with. Vous pouvez également spécifier différents pods sur une base par cible dans votre fichier pod, c'est-à-dire, n'incluez qu'OCMockito dans votre cible de test.
dbainbridge
Oui oui oui! Avant cette réponse, je devais supprimer la cible de test de mes projets! Thanks man :)
Josip B.18
53

Il existe une solution que j'ai trouvée ici Tests unitaires avec CocoaPods :

Ouvrez le fichier de projet dans Xcode, puis choisissez le projet (pas la cible), dans le panneau de droite, il y a une section appelée Configurations. Choisissez Pods dans la colonne "Basé sur le fichier de configuration" pour votre cible de test.

entrez la description de l'image ici

Mingming
la source
Eh bien, que se passe-t-il s'il existe des dépendances spécifiques au test, comme Spectacelle que vous souhaitez lier avec le projet de test mais pas avec le projet principal? : S
fatuhoku
Cela a fonctionné et ne nécessite aucune modification de la configuration ou de l'installation du pod ... Excellente solution.
Richard
1
Bien que cette solution puisse créer une erreur: Class Foo is implemented in both MyApp and MyAppTestCase. One of the two will be used. Which one is undefined. Cela semble être dû à un bogue dans Cocoapods; voir la réponse @JRV ci-dessous.
Richard
Ce ne sont pas que des avertissements. Avec une telle configuration, aucune donnée de couverture de code Xcode appropriée n'est générée et les tests unitaires se bloquent lors du lancement dans la plupart des cas.
i4niac
J'ai importé manuellement le SDK Estimote par glisser-déposer, je ne reçois pas de pods. Comment résoudre ce problème?
Guru Teja
18

Je suis d'accord avec les autres réponses disant qu'il est nécessaire de relier les bibliothèques aux cibles de test. Cependant, aucune des suggestions jusqu'à présent ne m'a aidé. Comme @fabb l'écrit dans un commentaire: "lors du test, les isSubclassOfClass:appels retournent NON là où ils devraient renvoyer OUI. La seule raison pour laquelle je peux expliquer cela est que les dépendances sont vraiment liées à la fois à la cible principale et à la cible de test, et lorsque le bundle de la cible de test loader charge le bundle principal, il ne peut pas décider quelle classe prendre. " J'obtiens le même problème avec toutes les suggestions précédentes dans ce fil.

La solution que j'ai mise au travail a été de mettre à jour mon Podfile pour définir des pods spécifiques pour ma cible principale et ma cible de test:

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

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

Il était nécessaire de spécifier un pod pour ma cible de test même si je n'ai utilisé aucun pod spécifique au test. Sinon, CocoaPods n'insérerait pas la logique de liaison nécessaire dans mon projet.

Ce lien est ce qui m'a aidé à arriver à cette conclusion.

JRV
la source
1
Merci pour le lien vers le problème CocoaPods - qui m'a aidé à résoudre mon problème!
karlbecker_com
OUI!!!! Ce problème me tourmente. C'est la seule réponse raisonnable sur les cocoapodes que j'ai rencontrée.
DonnaLea
Il existe une meilleure façon de gérer cela sous 1.x: stackoverflow.com/a/40866889/2799670
Darren Black
6

J'ai ajouté :exclusive => truepour éviter les erreurs de symboles dupliqués dans la cible de test d'application.

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

link_with 'myProject', 'myProjectTests'

Lorsque j'ai remplacé la cible de test d'application par celle de test d'unité logique, l'erreur de l'éditeur de liens se produit. Après avoir supprimé :exclusive => true, tout fonctionne à nouveau.

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

link_with 'myProject', 'myProjectTests'

:exclusive => trueindique que tout ce qui se trouve à l'extérieur do...endne doit PAS être lié myProjectTests, ce qui est raisonnable dans les cibles de test d'application, mais cela provoquera des erreurs de l'éditeur de liens dans les cibles de test logiques.

Hai Feng Kao
la source
L'exclusif était la solution pour moi, comme le montre la réponse de kylef sur ce problème de CocoaPods , qui a été trouvée grâce à la réponse de JRV à cette question!
karlbecker_com
1
Oui, tout le monde devrait lire ce numéro sur github lié par @karlbecker_com. Il semble que ce ne soit qu'une limitation de longue date des cocoapodes. D'après la discussion, link_with n'est pas nécessaire. Ajoutez simplement la cible de test et utilisez: exclusif. Si votre cible de test n'a pas besoin de pods spécifiques, ajoutez-en quand même un sinon les cocoapodes ne le traiteront pas.
kball
@kball Lequel n'a pas besoin de link_with? Le test d'application ou le test d'unité logique?
Hai Feng Kao
À moins que vous n'ayez une autre raison de l'utiliser, vous ne devriez pas du tout avoir besoin de link_with. Et de manière générale, vous ne souhaitez pas lier ces pods à votre bundle de test. Ils ne doivent être liés qu'une seule fois, dans le bundle d'applications, puis référencés par vos tests via la dépendance (en veillant à ce que Symboles masqués par défaut soit désactivé). Sinon, le comportement n'est pas défini car deux versions des pods existeront: une incluse dans la cible de l'application, une dans la cible de test.
kball
6

Vous pouvez utiliser link_with selon la solution @Keith Smiley.

Si vous avez des pods communs et des spécificités pour chaque cible, vous pouvez utiliser l'option "def" pour définir un groupe de pods. et utilisez le "def" plus tard dans la cible exclusive.

def import_pods
    pod 'SSKeychain'
end

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

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

dans l'exemple ci-dessus, j'ai ajouté 'SSKeychain' aux deux cibles et 'Typhoon' uniquement à la cible 'MyProject'

Elihay
la source
5

Ma solution à ce problème était de changer mon Podfile pour inclure la bibliothèque dans les deux cibles comme celle-ci

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

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

Et comme j'utilise swift, j'ai également dû configurer la cible de test pour inclure le MyApp-Bridging-Header.hfichier. (Dans le groupe Swift Compiler sous l'onglet Build Settings)

Qw4z1
la source
3
Attention - cela augmentera vos temps de construction de beaucoup, car vous continuez à ajouter plus de pods!
fatuhoku
@fatuhoku ne le savait pas. Pouvez-vous expliquer pourquoi cela augmente le temps de construction?
Qw4z1
2
Eh bien, chaque mention d'un pod est une cible dans votre Podsprojet. En mentionnant vos pods deux fois (une fois pour les tests et une fois pour l'application), vous aurez deux ensembles de cibles. Cela double efficacement le travail de configuration pod installà effectuer. Ce ne sera pas un problème tant que vous n'aurez pas plus de 15 pods, alors ne vous inquiétez pas trop d'ici là.
fatuhoku
1
C'est la seule solution qui fonctionne pour moi avec Cocoapods 1.0
William Entriken
À partir de 1.x, il s'agit de la méthode officielle pour les tests héritant des dépendances des applications: stackoverflow.com/a/40866889/2799670
Darren Black
4

J'ai eu un événement similaire lorsque j'ai perdu des fichiers de bibliothèque lors d'un contrôle de version. J'ai toujours vu le fichier de bibliothèque dans mes pods mais avec le code réel manquant, XCode a dit qu'il avait disparu. À ma grande consternation, exécuter 'pod install' ne ramena pas immédiatement les fichiers perdus.

J'ai dû retirer et remplacer le pod manuellement en procédant comme suit:

  1. Supprimer la bibliothèque du Podfile
  2. Exécutez 'pod install' pour supprimer complètement la bibliothèque
  3. Remettez la bibliothèque dans le Podfile
  4. Exécutez à nouveau 'pod install'

Cela devrait remettre la bibliothèque en question dans sa forme originale.

Maxwell
la source
2

Il est également intéressant de noter que si vous avez libPods.aajouté deux fois, vous obtiendrez une erreur désagréable comme celle-ci:

232 duplicate symbols for architecture i386

Pour résoudre ce problème, supprimez simplement l'une des libPods.aréférences dans votre Explorateur de projet.

Mat Ryer
la source
2

Depuis CocoaPods 1.x, il existe une nouvelle façon de déclarer les dépendances partagées entre une cible et la cible de test correspondante. J'avais utilisé la solution acceptée par Mark Struzinski jusqu'à présent, mais l'utilisation de cette méthode a généré un nombre massif d'avertissements lors de l'exécution de mes tests:

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.

Avec CocoaPods 1.x, nous pouvons déclarer notre cible -Test comme héritant via les chemins de recherche de la cible parent, comme ceci:

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

Cela permettra à la cible -Test d'accéder aux dépendances de la cible d'application, sans plusieurs copies binaires. Cela a considérablement accéléré les temps de construction des tests pour moi.

Darren Black
la source
2

Essayez ça, ça marche pour moi

Nous devons définir les pods dans les configurations,

Le projet-> Info-> Configurations dans le projet Xcode (votre projet) doit être défini sur le projet principal «Pods» pour le débogage, la publication (et ce que vous avez d'autre). Voir "En-têtes non trouvés - chemins de recherche non inclus"

entrez la description de l'image ici

J'espère que cela aide quelqu'un.

Jaywant Khedkar
la source
1

Je travaille avec l'intégration de GoogleMaps Objective-C POD sur iOS avec mon application Swift.Pour moi, le problème était que la cible de test n'avait pas de référence au fichier d'en-tête de pont ( SWIFT_OBJC_BRIDGING_HEADER ) dans les paramètres de construction. Assurez-vous que les cibles de votre application et de votre application de test pointent vers cela afin que les appels d'API tiers (API de cartes, etc.) puissent être utilisés dans les tests unitaires rapides.

appledevguru
la source
1
J'ai une configuration similaire à la vôtre. J'ai déjà ajouté l'en-tête de pontage à la cible de test, mais j'obtiens l'erreur "Aucun module de ce type" GoogleMaps "" import GoogleMaps.
Nicolas Miari
0

La syntaxe suivante donne le meilleur résultat pour moi (testé sous 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

Sans cela, j'ai des avertissements lors du test sur les symboles en double.

Après ces avertissements ont disparu.

Maxim Kholyavkin
la source
0

J'ai eu des problèmes avec OpenCV sous XCTest. Cela me donnait des erreurs d'éditeur de liens Undefined symbols for architecture arm64pour des classes comme cv::Mat. J'installe OpenCV via CocoaPods en utilisant pod 'OpenCV', '~> 2.0'sous la cible principale. Peu importe à quel point j'ai essayé de placer la dépendance OpenCV sous la cible de test ou d'utiliser inherit! :search_pathsrien de tout cela n'a fonctionné. La solution était de créer un abstract_targetlike so:

# 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 

Les commandes pod deintegrate& pod cleanqui aident à nettoyer le projet et à vous assurer que vous recommencez lors du test sont également utiles . Vous pouvez installer ces deux en utilisant [sudo] gem install cocoapods-deintegrate cocoapods-clean.

Foti Dim
la source