Librerie non trovate quando si utilizzano CocoaPods con test logici iOS


148

Sto provando a scrivere alcuni test logici iOS contro le classi nel mio progetto che utilizzano funzionalità di alcune delle librerie del mio podspec. Sto usando il pacchetto standard di unit test fornito in Xcode (anche se non i test di applicazione, ma solo i test di unità).

Ad esempio, utilizzo Magical Record e ho quella libreria collegata nel mio podspec. È presente nel progetto Pods nel mio spazio di lavoro e funziona come previsto quando l'app è in esecuzione nel simulatore o sul dispositivo. Quando provo a collegare al test l'oggetto che utilizza Magical Record, tuttavia, viene visualizzato un errore del linker che indica che non è possibile trovare i selettori da Magical Record. Ho provato ad aggiornare HEADER_SEARCH_PATH nel mio bundle di test logici, anche codificandolo duramente nella directory headers creata da CocoaPods, ma senza fortuna.

Posso eseguire unit test contro classi che non utilizzano le librerie CocoaPods senza alcun problema.

Sto sbagliando? Dovrei fare qualcos'altro per convincere il compilatore a vedere le librerie CocoaPods?

Risposte:


224

CocoaPods 1.0 ha modificato la sintassi per questo. Ora sembra così:

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

target 'Sail' do
    shared_pods
end

target 'Sail-iOS' do
    shared_pods
end

Risposta di Pre CocoaPods 1.0

Quello che vuoi usare è link_withdal tuo Podfile. Qualcosa di simile a:

link_with 'MainTarget', 'MainTargetTests'

Quindi corri di pod installnuovo.


7
Ciò ha immediatamente risolto il problema per me.
mttrb

9
Ottengo strani errori con questo: durante il test, le isSubclassOfClass:chiamate ritornano NOdove dovrebbero tornare YES. L'unico motivo per cui posso spiegare questo è che le dipendenze sono realmente collegate sia al target principale sia al target di test e quando il caricatore del bundle del target di test carica il bundle principale, non può decidere quale classe prendere.
Fabb,

4
Ho lo stesso problema con la isKindOfClass:restituzione NOquando dovrebbe tornare YES. Se registro il puntatore al Classmio oggetto che sto testando e quello Classdella classe con cui voglio confrontare sono due valori diversi. Chiaramente il mio codice dal pacchetto dell'app sta usando un simbolo diverso per la classe rispetto al codice dei test delle mie unità. Qualcuno ha trovato un modo per risolvere questo?
Nicholas Hart,

2
Non penso che questo sia un buon modo di procedere a causa degli errori che alcuni altri hanno menzionato. Attenersi all'aggiornamento del bit di configurazione 'basato su'. Assicurati di non aver collegato libPods.a due volte.
Bob Spryn,

3
Questa dovrebbe essere la risposta accettata in quanto questo è il modo ufficiale CocoaPods di configurare i Pod con più target. Grazie mille Keith!
cschuff

174

L'ho capito guardando come l'obiettivo principale della mia app stava ricevendo le impostazioni dalla libreria CocoaPods. CocoaPods include un file .xcconfig chiamato Pods.xcconfig. Questo file contiene tutti i percorsi di ricerca dell'intestazione.

Se guardi il tuo progetto nel navigatore del progetto e fai clic sulla scheda Informazioni, vedrai le tue configurazioni di build elencate nella sezione in alto. Se apri il triangolo di apertura per le tue diverse configurazioni, vedrai i Pod elencati sotto il tuo obiettivo principale. Ho dovuto fare clic sul menu a discesa e aggiungere anche Pod alla destinazione del test logico.

Istantanea delle configurazioni

Ho anche dovuto copiare le impostazioni di $(inherited)e ${PODS_HEADERS_SEARCH_PATHS}dal mio target principale e copiarle sul target del test logico in Build Settings / HEADER_SEARCH_PATHS.

Alla fine, ho dovuto aggiungere libPods.a nella fase di costruzione Link Binary with Libraries per il mio target dei test logici.

Spero che questo sia in grado di aiutare qualcun altro.


Brillante! Uso MagicalRecord e anche OCMockito e OCHamcrest per test unitari. Con questa correzione ora posso installarli tutti tramite CocoaPods! Grazie!
Nebbia

4
Questo ha funzionato per me, grazie. NOTA ... Non ho avuto bisogno di aggiungere libPods.a sia nel proj test che nel proj principale. Ciò provoca un errore di simbolo duplicato
Craig Bruce il

Per me, ho anche dovuto copiare le impostazioni di build "Definite dall'utente". I percorsi di ricerca dell'intestazione si riferiscono a $ PODS_ROOT che non è stato definito sulla destinazione del test. Puoi aggiungerlo andando su Editor-> Aggiungi impostazione build-> Aggiungi impostazione definita dall'utente, quindi copiando il valore $ PODS_ROOT dalla destinazione principale.
Shinigami,

11
Questo non è il modo corretto per risolvere questo problema. Vedi risposta con link_with. È inoltre possibile specificare diversi pod in base al target nel file pod, ovvero includere OCMockito solo nel target di test.
dbainbridge,

Sì sì sì! Prima di questa risposta ho dovuto eliminare Target test dai miei progetti! Grazie amico :)
Josip B.

53

C'è una soluzione che ho trovato qui Test di unità con CocoaPods :

Apri il file di progetto in Xcode, quindi scegli il Progetto (non la destinazione), nel pannello di destra, c'è una sezione chiamata Configurazioni. Scegli Pod nella colonna "Basato sul file di configurazione" per la destinazione del test.

inserisci qui la descrizione dell'immagine


Bene, e se ci fossero dipendenze specifiche per i test, come Spectaquella che vuoi collegare al progetto di test ma non al progetto principale? : S
fatuhoku,

Questo ha funzionato e non richiede alcuna modifica alla configurazione o alla configurazione del pod ... Ottima soluzione.
Richard,

1
Sebbene questa soluzione possa creare un errore: Class Foo is implemented in both MyApp and MyAppTestCase. One of the two will be used. Which one is undefined. ciò sembra essere causato da un bug nei Cocoapods; vedi la risposta @JRV di seguito.
Richard,

Questi non sono solo avvertimenti. Con tale impostazione non vengono generati dati di copertura del codice Xcode adeguati e nella maggior parte dei casi i test unitari si bloccano durante l'avvio.
i4niac,

Ho importato manualmente Estimote SDK mediante trascinamento della selezione, non ricevo pod. Come risolvere questo?
Guru Teja,

18

Concordo con le altre risposte affermando che è necessario collegare le librerie agli obiettivi del test. Tuttavia nessuno dei suggerimenti finora mi ha aiutato. Come scrive @fabb in un commento: "durante il test, le isSubclassOfClass:chiamate restituiscono NO dove dovrebbero restituire SÌ. L'unico motivo per cui posso spiegare questo è che le dipendenze sono realmente collegate sia al target principale che a quello di test, e quando il bundle del target di test loader carica il bundle principale, non può decidere quale classe prendere. " Ottengo lo stesso problema con tutti i suggerimenti precedenti in questa discussione.

La soluzione che ho avuto modo di lavorare era aggiornare il mio Podfile per definire Pod specifici per il mio obiettivo principale e il mio obiettivo di prova:

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

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

È stato necessario specificare un Pod per il mio target di test anche se non ho utilizzato alcun Pod specifico del test. Altrimenti CocoaPods non inserisce la logica di collegamento necessaria nel mio progetto.

Questo link è ciò che mi ha aiutato a giungere a questa conclusione.


1
Grazie per il link al problema CocoaPods - che mi ha aiutato a risolvere il mio problema!
karlbecker_com,

SÌ!!!! Questo problema mi ha tormentato. Questa è l'unica risposta sensibile ai cocoapodi che ho incontrato.
DonnaLea,

Esiste un modo migliore di gestirlo in 1.x: stackoverflow.com/a/40866889/2799670
Darren Black

6

Ho aggiunto :exclusive => trueper evitare errori di simboli duplicati nella destinazione del test dell'applicazione.

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

link_with 'myProject', 'myProjectTests'

Quando ho cambiato la destinazione del test dell'applicazione con l'unità di test 1, si verifica l'errore del linker. Dopo aver rimosso :exclusive => true, tutto funziona di nuovo.

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

link_with 'myProject', 'myProjectTests'

:exclusive => trueafferma che tutto ciò che è esterno do...endNON deve essere collegato myProjectTests, il che è ragionevole nelle destinazioni del test dell'applicazione, ma causerà errori del linker nelle destinazioni del test logico.


Esclusiva è stata la soluzione per me, come mostrato nella risposta di Kylef su questo problema di CocoaPods , che è stato trovato grazie alla risposta del JRV su questa domanda!
karlbecker_com,

1
Sì, tutti dovrebbero leggere quel problema su github collegato da @karlbecker_com. Sembra che questa sia solo una limitazione a lungo termine dei cocoapodi. Secondo la discussione qui, link_with non è necessario. Aggiungi semplicemente il test target e usa: esclusivo. Se il target del test non necessita di pod specifici, aggiungerne comunque uno altrimenti i cocoapod non lo elaboreranno.
kball,

@kball Quale non necessita di link_with? Il test dell'applicazione o il test dell'unità logica?
Hai Feng Kao,

A meno che tu non abbia un altro motivo per usarlo, non dovresti aver bisogno di link_with. E in generale non vuoi collegare quei pod con il tuo pacchetto di test. Dovrebbero essere collegati una sola volta, nel bundle dell'app e quindi indicati dai test attraverso la dipendenza (assicurando che i simboli nascosti per impostazione predefinita siano disattivati). In caso contrario, il comportamento non è definito perché esistono due versioni dei pod: una inclusa nella destinazione dell'app, una nella destinazione del test.
kball,

6

Puoi usare link_with secondo la soluzione @Keith Smiley.

Nel caso in cui si disponga di pod comuni e specifici per ciascun target, è possibile utilizzare l'opzione "def" per definire un gruppo di pod. e usa la "def" più tardi come target esclusivo.

def import_pods
    pod 'SSKeychain'
end

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

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

nell'esempio sopra, ho aggiunto "SSKeychain" a entrambi i target e "Typhoon" solo al target "MyProject"


5

La mia soluzione a questo problema era cambiare il mio Podfile per includere la libreria in entrambi i target in questo modo

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

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

E dal momento che sto usando swift ho anche dovuto configurare la destinazione del test per includere il MyApp-Bridging-Header.hfile. (Nel gruppo Swift Compiler nella scheda Impostazioni build)


3
Attenzione: questo aumenterà i tempi di costruzione di molti, mentre continui ad aggiungere altri pod!
Fatuhoku,

@fatuhoku non lo sapeva. Puoi fornire alcune informazioni sul perché aumenta i tempi di costruzione?
Qw4z1

2
Bene, ogni menzione di un pod è un obiettivo nel tuo Podsprogetto. Citando due volte i tuoi pod (una volta per i test e una per l'app), avrai due serie di obiettivi. Questo raddoppia in modo efficace il lavoro di configurazione pod install. Questo non sarà un problema fino a quando non avrai> 15 capsule, quindi non preoccuparti troppo fino ad allora.
Fatuhoku,

1
Questa è l'unica soluzione che funziona per me con Cocoapods 1.0
William Entriken il

A partire da 1.x, questo è il metodo ufficiale per i test che ereditano le dipendenze delle app: stackoverflow.com/a/40866889/2799670
Darren Black

4

Ho avuto un evento simile quando ho perso alcuni file di libreria durante un controllo della versione. Ho ancora visto il file della libreria nei miei pod ma con il codice effettivo mancante, XCode ha detto che non c'era più. Con mio sgomento, eseguire "pod install" non ha riportato immediatamente i file persi.

Ho dovuto rimuovere e sostituire il pod manualmente procedendo come segue:

  1. Rimuovi la libreria dal Podfile
  2. Eseguire 'pod install' per rimuovere completamente la libreria
  3. Rimetti la libreria nel Podfile
  4. Eseguire di nuovo 'pod install'

Ciò dovrebbe riportare la libreria in questione nella sua forma originale.


2

Vale anche la pena notare che se hai libPods.aaggiunto due volte, otterrai alcuni errori cattivi come questo:

232 duplicate symbols for architecture i386

Per risolverlo, basta eliminare uno dei libPods.ariferimenti in Esplora progetti.


2

A partire da CocoaPods 1.x, esiste un nuovo modo per dichiarare dipendenze condivise tra un target e il target di test corrispondente. A questo punto avevo usato la soluzione accettata da Mark Struzinski, ma l'uso di questo metodo ha prodotto un numero enorme di avvisi durante l'esecuzione dei test che:

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.

Con CocoaPods 1.x possiamo dichiarare il nostro target -Test come ereditario tramite i percorsi di ricerca del target principale, in questo modo:

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

Ciò comporterà che la destinazione -Test abbia accesso alle dipendenze della destinazione dell'app, senza più copie binarie. Ciò ha notevolmente accelerato i tempi di costruzione dei test per me.



1

Sto lavorando con l'integrazione POD Objective-C di GoogleMaps su iOS con la mia app Swift e quindi per me il problema era che la destinazione Test non aveva un riferimento al File di intestazione Bridge ( SWIFT_OBJC_BRIDGING_HEADER ) nelle Impostazioni di costruzione. Assicurati che sia la tua app sia i target delle app di test puntino a quello in modo che le chiamate API di terze parti (API mappe, ecc.) Possano essere utilizzate nei test di unità rapidi.


1
Ho una configurazione simile a te. Ho già aggiunto l'intestazione ponte al target di test, tuttavia viene visualizzato l'errore "No tal modulo 'GoogleMaps'" import GoogleMaps.
Nicolas Miari,

0

La sintassi successiva mi dà il miglior risultato (testato con 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

Senza questo ho degli avvertimenti mentre il test funziona su simboli duplicati.

Dopo questo avviso sono scomparsi.


0

Ho avuto problemi con OpenCV in XCTest. Mi stava dando errori di linker Undefined symbols for architecture arm64per classi come cv::Mat. Sto installando OpenCV tramite CocoaPods usando pod 'OpenCV', '~> 2.0'sotto l'obiettivo principale. Non importa quanto ho provato a mettere la dipendenza OpenCV sotto la destinazione del test o utilizzare inherit! :search_pathsnessuno di questi ha funzionato. La soluzione era di creare un abstract_targetsimile così:

# 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 

Sono anche utili i comandi pod deintegrate& pod cleanche aiutano a ripulire il progetto e assicurarsi di ricominciare da capo durante il test. Puoi installare quei due usando [sudo] gem install cocoapods-deintegrate cocoapods-clean.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.