Bibliotecas não encontradas ao usar o CocoaPods com testes de lógica do iOS


148

Estou tentando escrever alguns testes de lógica do iOS em classes do meu projeto que usam a funcionalidade de algumas das bibliotecas do meu podspec. Estou usando o pacote de teste de unidade padrão fornecido no Xcode (embora não seja o Application Tests, apenas Unit Tests).

Por exemplo, eu uso o Magical Record e tenho essa biblioteca vinculada no meu podspec. Está presente no projeto Pods no meu espaço de trabalho e funciona conforme o esperado quando o aplicativo está sendo executado no simulador ou no dispositivo. Quando tento vincular ao teste o objeto que usa o Magical Record, no entanto, recebo um erro de vinculador informando que ele não consegue encontrar os seletores do Magical Record. Tentei atualizar meu HEADER_SEARCH_PATH no meu pacote de testes lógicos, codificando-o até o diretório de cabeçalhos criado pelo CocoaPods, mas sem sorte.

Posso executar testes de unidade em classes que não usam as bibliotecas CocoaPods sem nenhum problema.

Estou fazendo isso errado? Devo estar fazendo outra coisa para que o compilador veja as bibliotecas do CocoaPods?

Respostas:


224

CocoaPods 1.0 mudou a sintaxe para isso. Agora fica assim:

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

target 'Sail' do
    shared_pods
end

target 'Sail-iOS' do
    shared_pods
end

Pre CocoaPods 1.0 resposta

O que você deseja usar é link_withdo seu Podfile. Algo como:

link_with 'MainTarget', 'MainTargetTests'

Então corra pod installnovamente.


7
Isso imediatamente resolveu o problema para mim.
mttrb

9
Eu recebo erros estranhos com isso - ao testar, as isSubclassOfClass:chamadas retornam para NOonde deveriam retornar YES. A única razão pela qual posso explicar isso é que as dependências realmente são vinculadas ao destino principal e ao destino de teste e, quando o carregador de pacotes do destino de teste carrega o pacote principal, ele não pode decidir qual classe tomar.
Fabb 26/09/2013

4
Tenho o mesmo problema ao isKindOfClass:retornar NOquando deveria retornar YES. Se eu registrar o ponteiro no Classobjeto que estou testando e o Classda classe que eu quero comparar, eles são dois valores diferentes. Claramente, meu código do pacote de aplicativos está usando um símbolo diferente para a classe que o código dos meus testes de unidade. Alguém encontrou uma maneira de resolver isso?
Nicholas Hart

2
Não acho que esse seja um bom caminho devido aos erros que alguns outros mencionaram. Continue atualizando o arquivo de configuração 'com base em' bit. Certifique-se de não ter vinculado libPods.a duas vezes.
Bob Spryn

3
Essa deve ser a resposta aceita, já que esta é a maneira oficial do CocoaPods de configurar Pods com vários destinos. Muito obrigado Keith!
cschuff

174

Eu descobri isso analisando como o principal objetivo do meu aplicativo estava recebendo configurações da biblioteca CocoaPods. CocoaPods inclui um arquivo .xcconfig chamado Pods.xcconfig. Este arquivo contém todos os caminhos de pesquisa do cabeçalho.

Se você olhar para o seu projeto no navegador do projeto e clicar na guia Informações, verá as configurações de compilação listadas na seção superior. Se você abrir o triângulo de divulgação para suas diferentes configurações, verá os Pods listados abaixo do seu objetivo principal. Eu tive que clicar no menu suspenso e adicionar Pods ao destino do teste lógico também.

Configurações Instantâneo

Também tive que copiar as configurações de $(inherited)e ${PODS_HEADERS_SEARCH_PATHS}do meu destino principal e copiá-las para o destino de teste lógico em Configurações de compilação / HEADER_SEARCH_PATHS.

Finalmente, tive que adicionar libPods.a na fase de construção Link Binary with Libraries para o meu destino de testes lógicos.

Espero que isso seja capaz de ajudar alguém.


Brilhante! Uso MagicalRecord e também OCMockito e OCHamcrest para testes de unidade. Com esta correção, agora posso instalá-los através do CocoaPods! Obrigado!
Fogmeister

4
Isso funcionou para mim, obrigado. NOTA .. Eu não precisei adicionar o libPods.a no projeto de teste e no projeto principal. Isso causa um erro de símbolo duplicado
Craig Bruce

Para mim, também tive que copiar as configurações de compilação "Definidas pelo Usuário". Os caminhos de pesquisa de cabeçalho referem-se a $ PODS_ROOT, que não foi definido no destino de teste. Você pode adicioná-lo acessando Editor-> Adicionar configuração de compilação-> Adicionar configuração definida pelo usuário e copiando o valor $ PODS_ROOT do destino principal.
Shinigami

11
Esta não é a maneira correta de corrigir isso. Veja a resposta com link_with. Você também pode especificar diferentes pods por alvo no seu arquivo de pod, ou seja, incluir apenas o OCMockito no seu alvo de teste.
dbainbridge

Sim Sim Sim! Antes desta resposta, tive que excluir o destino de teste dos meus projetos! Homem obrigado :)
Josip B.

53

Há uma solução que encontrei aqui Testes de unidade com CocoaPods :

Abra o arquivo do projeto no Xcode e escolha o Projeto (não o destino). No painel direito, há uma seção chamada Configurações. Escolha Pods na coluna "Com base no arquivo de configuração" para o seu destino de teste.

insira a descrição da imagem aqui


Bem, e se houver dependências específicas de teste, assim Spectavocê deseja vincular ao projeto de teste, mas não ao projeto principal? : S
fatuhoku 17/01

Isso funcionou e não requer nenhuma alteração na configuração ou configuração do pod ... Excelente solução.
Richard

1
Embora esta solução possa criar um erro: Class Foo is implemented in both MyApp and MyAppTestCase. One of the two will be used. Which one is undefined. Isso parece ser causado por um bug no Cocoapods; veja a resposta @JRV abaixo.
Richard

Esses não são apenas avisos. Com essa configuração, nenhum dado de cobertura de código Xcode é gerado e na maioria dos casos, os testes de unidade são interrompidos durante o lançamento.
I4niac

Importei o Estimote SDK manualmente, arrastando e soltando, não estou recebendo pods. Como resolver isso?
Guru Teja

18

Eu concordo com as outras respostas dizendo que é necessário vincular as bibliotecas aos alvos de teste. No entanto, nenhuma das sugestões até agora me ajudou. Como o @fabb escreve em um comentário: "ao testar, as isSubclassOfClass:chamadas retornam NÃO, onde devem retornar SIM. A única razão pela qual posso explicar isso é que as dependências são realmente vinculadas ao principal e ao alvo de teste e quando o pacote do alvo de teste loader carrega o pacote principal, ele não pode decidir qual classe tomar ". Eu recebo o mesmo problema com todas as sugestões anteriores neste tópico.

A solução que eu trabalhei foi atualizar meu Podfile para definir Pods específicos para o meu destino principal e o meu destino de teste:

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

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

Era necessário especificar um Pod para o meu alvo de teste, mesmo que eu não usasse nenhum Pod específico. Caso contrário, o CocoaPods não inserirá a lógica de vinculação necessária no meu projeto.

Esse link foi o que me ajudou a chegar a essa conclusão.


1
Obrigado pelo link para a questão CocoaPods - que me ajudou a resolver meu problema!
karlbecker_com

SIM!!!! Esse problema está me atormentando. Esta é a única resposta sensata dos cocoapods que encontrei.
DonnaLea

Há uma maneira melhor de lidar com isso sob 1.x: stackoverflow.com/a/40866889/2799670
Darren Preto

6

Eu adicionei :exclusive => truepara evitar erros de símbolo duplicados no destino de teste do aplicativo.

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

link_with 'myProject', 'myProjectTests'

Quando mudei o destino de teste do aplicativo para o teste de unidade lógica um, ocorre o erro do vinculador. Depois que eu removo :exclusive => true, tudo funciona novamente.

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

link_with 'myProject', 'myProjectTests'

:exclusive => trueafirma que tudo fora do...endNÃO deve ser vinculado myProjectTests, o que é razoável nos destinos de teste do aplicativo, mas causará erros do vinculador nos destinos de teste lógico.


Exclusiva foi a solução para mim, como mostrado na resposta de kylef sobre esta questão do CocoaPods , que foi encontrada graças à resposta da JRV nesta pergunta!
karlbecker_com

1
Sim, todos devem ler esse problema no github vinculado por @karlbecker_com. Parece que isso é apenas uma limitação de longo prazo dos cocoapods. De acordo com a discussão, link_with não é necessário. Basta adicionar a meta de teste e usar: exclusive. Se o seu alvo de teste não precisar de nenhum pod específico, adicione um de qualquer maneira, caso contrário os cocoapods não o processarão.
Kball

@kball Qual deles não precisa de link_with? O teste de aplicação ou o teste de unidade lógica?
Hai Feng Kao

A menos que você tenha outro motivo para usá-lo, você não deve precisar de link_with. E de um modo geral, você não deseja vincular esses pods ao seu pacote de teste. Eles devem ser vinculados apenas uma vez, no pacote de aplicativos e, em seguida, referenciados por seus testes através da dependência (garantindo que Símbolos ocultos por padrão esteja desativado). Caso contrário, o comportamento será indefinido porque existirão duas versões dos pods - uma incluída no destino do aplicativo e uma no destino do teste.
Kball

6

Você pode usar o link_with de acordo com a solução @Keith Smiley.

Caso você possua pods comuns e detalhes específicos para cada destino, convém usar a opção "def" para definir o grupo de pods. e use o "def" posteriormente no destino exclusivo.

def import_pods
    pod 'SSKeychain'
end

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

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

no exemplo acima, adicionei 'SSKeychain' aos dois destinos e 'Typhoon' apenas ao destino 'MyProject'


5

Minha solução para esse problema foi alterar meu Podfile para incluir a biblioteca em ambos os destinos como este

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

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

E como estou usando o swift, também tive que configurar o alvo de teste para incluir o MyApp-Bridging-Header.harquivo. (No grupo Swift Compiler na guia Build Settings)


3
Cuidado - isso aumentará seus tempos de criação em lotes, à medida que você adiciona mais pods!
Fatuhoku

@fatuhoku não sabia disso. Você pode fornecer algumas dicas sobre por que isso aumenta o tempo de construção?
Qw4z1

2
Bem, cada menção de um pod é um alvo em seu Podsprojeto. Ao mencionar seus pods duas vezes (uma vez para testes e outra para o aplicativo), você terá dois conjuntos de destinos. Isso efetivamente dobra o trabalho de configuração pod install. Isso não será um problema até que você tenha mais de 15 pods; portanto, não se preocupe muito até então.
fatuhoku 6/01/2015

1
Esta é a única solução que funciona para mim com o Cocoapods 1.0
William Entriken

A partir do 1.x, este é o método oficial para testes que herdam dependências de aplicativos: stackoverflow.com/a/40866889/2799670
Darren Black

4

Tive uma ocorrência semelhante quando perdi alguns arquivos de biblioteca durante algum controle de versão. Eu ainda vi o arquivo da biblioteca em meus Pods, mas com o código real ausente, o XCode disse que ele havia sumido. Para minha consternação, executar 'pod install' não estava trazendo imediatamente os arquivos perdidos.

Eu tive que remover e substituir o pod manualmente, fazendo o seguinte:

  1. Remova a biblioteca do Podfile
  2. Execute 'pod install' para remover completamente a biblioteca
  3. Coloque a biblioteca de volta no Podfile
  4. Execute 'pod install' novamente

Isso deve colocar a biblioteca em questão de volta em sua forma original.


2

Também é importante notar que, se você libPods.aadicionou duas vezes, receberá um erro grave como este:

232 duplicate symbols for architecture i386

Para corrigi-lo, basta excluir uma das libPods.areferências no seu Project Explorer.


2

No CocoaPods 1.x, há uma nova maneira de declarar dependências compartilhadas entre um destino e o destino de teste correspondente. Eu estava usando a solução aceita por Mark Struzinski até esse momento, mas o uso desse método produziu um grande número de avisos ao executar meus testes que:

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.

Com o CocoaPods 1.x, podemos declarar nosso destino -Test como herdado pelos caminhos de pesquisa do destino pai, da seguinte maneira:

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

Isso fará com que o destino -Test tenha acesso às dependências do destino do aplicativo, sem várias cópias binárias. Isso acelerou seriamente os tempos de teste para mim.



1

Estou trabalhando com a integração do GoogleMaps Objective-C POD no iOS com meu aplicativo Swift e, portanto, para mim, o problema foi que o destino de teste não tinha uma referência ao arquivo de cabeçalho da ponte ( SWIFT_OBJC_BRIDGING_HEADER ) nas configurações de compilação. Verifique se o aplicativo e os destinos de aplicativo de teste apontam para isso, para que as chamadas da API de terceiros (API de mapas etc.) possam ser usadas em testes rápidos de unidade.


1
Eu tenho uma configuração semelhante à sua. Eu já adicionei o cabeçalho da ponte ao destino do teste. No entanto, recebo o erro "Nenhum módulo 'GoogleMaps'" ativado import GoogleMaps.
Nicolas Miari 17/02

0

A próxima sintaxe fornece o melhor resultado para mim (testado no 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

Sem isso, tenho avisos durante o teste sobre símbolos duplicados.

Depois disso, os avisos desapareceram.


0

Eu tive problemas ao usar o OpenCV no XCTest. Isso estava me dando erros de vinculador Undefined symbols for architecture arm64para classes como cv::Mat. Estou instalando o OpenCV através do CocoaPods usando pod 'OpenCV', '~> 2.0'o objetivo principal. Não importa o quanto tentei colocar a dependência do OpenCV sob o objetivo do teste ou usar inherit! :search_pathsnada disso funcionou. A solução foi criar um abstract_targetassim:

# 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 

Também são úteis os comandos pod deintegrate&, pod cleanque ajudam a limpar o projeto e garantem que você comece do zero ao testar. Você pode instalar esses dois usando [sudo] gem install cocoapods-deintegrate cocoapods-clean.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.