Bibliotecas que no se encuentran al usar CocoaPods con pruebas lógicas de iOS


148

Estoy tratando de escribir algunas pruebas de lógica de iOS en las clases de mi proyecto que utilizan la funcionalidad de algunas de las bibliotecas de mi podspec. Estoy usando el paquete de prueba de unidad estándar provisto en Xcode (aunque no Pruebas de aplicación, solo Pruebas de unidad).

Por ejemplo, uso Magical Record y tengo esa biblioteca vinculada en mi podspec. Está presente en el proyecto Pods en mi espacio de trabajo y funciona como se esperaba cuando la aplicación se ejecuta en el simulador o en el dispositivo. Sin embargo, cuando intento vincular a la prueba el objeto que usa el Registro Mágico, aparece un error de enlace que indica que no puede encontrar los selectores del Registro Mágico. Intenté actualizar mi HEADER_SEARCH_PATH en mi paquete de pruebas lógicas, incluso codificándolo en el directorio de encabezados creado por CocoaPods, pero no tuve suerte.

Puedo ejecutar pruebas unitarias contra clases que no usan bibliotecas CocoaPods sin ningún problema.

¿Voy por esto mal? ¿Debería hacer algo más para que el compilador vea las bibliotecas de CocoaPods?

Respuestas:


224

CocoaPods 1.0 ha cambiado la sintaxis para esto. Ahora se ve así:

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 respuesta

Lo que quieres usar es link_withde tu Podfile. Algo como:

link_with 'MainTarget', 'MainTargetTests'

Luego corre de pod installnuevo.


77
Esto inmediatamente solucionó el problema para mí.
mttrb

9
Recibo errores extraños con esto: al probar, las isSubclassOfClass:llamadas regresan a NOdonde deberían regresar YES. La única razón por la que puedo explicar esto es que las dependencias realmente se vinculan tanto al objetivo principal como al objetivo de prueba, y cuando el cargador de paquetes del objetivo de prueba carga el paquete principal, no puede decidir qué clase tomar.
fabb

44
Tengo el mismo problema con isKindOfClass:regresar NOcuando debería regresar YES. Si registro el puntero en el Classde mi objeto que estoy probando y el Classde la clase con la que quiero comparar son dos valores diferentes. Claramente, mi código del paquete de aplicaciones está usando un símbolo diferente para la clase que el código de las pruebas de mi unidad. ¿Alguien ha encontrado una manera de resolver esto?
Nicholas Hart

2
No creo que esta sea una buena manera de hacerlo debido a los errores que otros han mencionado. Siga actualizando el archivo de configuración 'basado en' bit. Asegúrese de no haber vinculado libPods.a dos veces.
Bob Spryn

3
Esta debería ser la respuesta aceptada, ya que esta es la forma oficial de CocoaPods de configurar Pods con múltiples objetivos. Muchas gracias Keith!
cschuff

174

Descubrí esto al observar cómo el objetivo principal de mi aplicación estaba recibiendo configuraciones de la biblioteca CocoaPods. CocoaPods incluye un archivo .xcconfig llamado Pods.xcconfig. Este archivo contiene todas las rutas de búsqueda de encabezado.

Si observa su proyecto en el navegador de proyectos y hace clic en la pestaña Información, verá sus configuraciones de compilación en la sección superior. Si abre el triángulo de revelación para sus diferentes configuraciones, verá Pods enumerados debajo de su objetivo principal. Tuve que hacer clic en el menú desplegable y agregar Pods al objetivo de prueba lógica también.

Instantánea de configuraciones

También tuve que copiar la configuración de $(inherited)y ${PODS_HEADERS_SEARCH_PATHS}desde mi objetivo principal y copiarla al objetivo de prueba lógica en Configuración de compilación / HEADER_SEARCH_PATHS.

Finalmente, tuve que agregar libPods.a en la fase de compilación Enlace binario con bibliotecas para mi objetivo de pruebas lógicas.

Espero que esto pueda ayudar a alguien más.


¡Brillante! Uso MagicalRecord y también OCMockito y OCHamcrest para pruebas unitarias. ¡Con esta solución, ahora puedo instalarlos todos a través de CocoaPods! ¡Gracias!
Fogmeister

44
Esto funcionó para mí, gracias. NOTA ... No necesitaba agregar libPods.a tanto en el proyecto de prueba como en el proyecto principal. Esto provoca un error de símbolo duplicado
Craig Bruce

Para mí, también tuve que copiar la configuración de compilación "Definida por el usuario". Las rutas de búsqueda de encabezado se refieren a $ PODS_ROOT que no se definió en el objetivo de prueba. Puede agregarlo yendo a Editor-> Agregar configuración de compilación-> Agregar configuración definida por el usuario y luego copiando el valor $ PODS_ROOT del objetivo principal.
Shinigami el

11
Esta no es la forma correcta de solucionar esto. Ver respuesta con link_with. También puede especificar diferentes pods por objetivo en su archivo de pod, es decir, solo incluir OCMockito en su objetivo de prueba.
dbainbridge

¡Si si si! Antes de esta respuesta, ¡tenía que eliminar Test target de mis proyectos! Gracias hombre :)
Josip B.

53

Hay una solución que encontré aquí Pruebas unitarias con CocoaPods :

Abra el archivo del proyecto en Xcode, luego elija el Proyecto (no el objetivo), en el panel derecho, hay una sección llamada Configuraciones. Elija Pods en la columna "Basado en el archivo de configuración" para su objetivo de prueba.

ingrese la descripción de la imagen aquí


Bueno, ¿qué pasa si hay dependencias específicas de prueba, como la Spectaque desea vincular con el proyecto de prueba pero no con el proyecto principal? : S
fatuhoku

Esto funcionó y no requiere ningún cambio en la configuración o configuración del pod ... Excelente solución.
Richard

1
Aunque esta solución puede crear un error: Class Foo is implemented in both MyApp and MyAppTestCase. One of the two will be used. Which one is undefined. esto parece ser causado por un error en Cocoapods; vea la respuesta de @JRV a continuación.
Richard

Esas no son solo advertencias. Con tal configuración, no se generan datos de cobertura de código Xcode adecuados y las pruebas unitarias simplemente se bloquean durante el lanzamiento en la mayoría de los casos.
i4niac

Importé el Estimote SDK manualmente arrastrando y soltando, no obtengo pods ¿Cómo resolver esto?
Guru Teja

18

Estoy de acuerdo con las otras respuestas que dicen que es necesario vincular las bibliotecas a los objetivos de prueba. Sin embargo, ninguna de las sugerencias hasta ahora me ayudó. Como @fabb escribe en un comentario: "al probar, las isSubclassOfClass:llamadas devuelven NO donde deberían devolver SÍ. La única razón por la que puedo explicar esto es que las dependencias realmente se vinculan tanto al objetivo principal como al objetivo de prueba, y cuando el paquete del objetivo de prueba el cargador carga el paquete principal, no puede decidir qué clase tomar ". Me sale el mismo problema con todas las sugerencias anteriores en este hilo.

La solución que me puse a trabajar fue actualizar mi Podfile para definir Pods específicos para mi objetivo principal y mi objetivo de prueba:

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

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

Era necesario especificar un Pod para mi objetivo de prueba aunque no utilicé ningún Pod específico de prueba. De lo contrario, CocoaPods no insertaría la lógica de enlace necesaria en mi proyecto.

Este enlace es lo que me ayudó a llegar a esta conclusión.


1
Gracias por el enlace al problema de CocoaPods, ¡eso me ayudó a resolver mi problema!
karlbecker_com

¡¡¡¡SI!!!! Este problema me ha estado atormentando. Esta es la única respuesta sensata de cocoapods que encontré.
DonnaLea

Hay una mejor manera de manejar esto en 1.x: stackoverflow.com/a/40866889/2799670
Darren Black el

6

Agregué :exclusive => truepara evitar errores de símbolos duplicados en el objetivo de prueba de la aplicación.

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

link_with 'myProject', 'myProjectTests'

Cuando cambié el objetivo de prueba de la aplicación a la prueba de unidad lógica, se produce el error del vinculador. Después de eliminar :exclusive => true, todo funciona de nuevo.

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

link_with 'myProject', 'myProjectTests'

:exclusive => trueestablece que todo lo externo do...endNO debe estar vinculado a myProjectTests, lo cual es razonable en los objetivos de prueba de la aplicación, pero causará errores de enlazador en los objetivos de prueba lógica.


Exclusivo fue la solución para mí, como se muestra en la respuesta de kylef sobre este tema de CocoaPods , que se encontró gracias a la respuesta de JRV a esta pregunta.
karlbecker_com

1
Sí, todos deberían leer ese problema en github vinculado por @karlbecker_com. Parece que esto es solo una limitación a largo plazo de los cacaopodos. De acuerdo con la discusión allí, link_with no es necesario. Simplemente agregue el objetivo de prueba y use: exclusivo. Si su objetivo de prueba no necesita ninguna cápsula específica, agregue una de todos modos, de lo contrario los cacaopodos no la procesarán.
kball

@kball ¿Cuál no necesita link_with? ¿La prueba de aplicación o la prueba de unidad lógica?
Hai Feng Kao

A menos que tenga otra razón para usarlo, no debería necesitar link_with en absoluto. Y, en general, no desea vincular esas cápsulas con su paquete de prueba. Solo deben vincularse una vez, en el paquete de la aplicación, y luego hacer referencia a sus pruebas a través de la dependencia (asegurando que los símbolos ocultos por defecto estén desactivados). De lo contrario, el comportamiento no está definido porque existirán dos versiones de los pods: una incluida en el objetivo de la aplicación y otra en el objetivo de la prueba.
kball

6

Puede usar link_with de acuerdo con la solución @Keith Smiley.

En caso de que tenga pods comunes y detalles específicos para cada objetivo, es posible que desee utilizar la opción "def" para definir un grupo de pods. y usa el "def" más tarde en objetivo 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

en el ejemplo anterior, agregué 'SSKeychain' a ambos objetivos, y 'Typhoon' solo al objetivo 'MyProject'


5

Mi solución a este problema fue cambiar mi Podfile para incluir la biblioteca en ambos objetivos como este

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

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

Y como estoy usando Swift, también tuve que configurar el objetivo de prueba para incluir el MyApp-Bridging-Header.harchivo. (En el grupo Compilador rápido en la pestaña Configuración de compilación)


3
Cuidado: ¡esto aumentará tus tiempos de construcción por lotes, a medida que sigas agregando más pods!
fatuhoku

@fatuhoku no lo sabía. ¿Puedes darnos una idea de por qué aumenta el tiempo de construcción?
Qw4z1

2
Bueno, cada mención de un pod es un objetivo en su Podsproyecto. Al mencionar sus pods dos veces (una para pruebas y otra para la aplicación), tendrá dos conjuntos de objetivos. Esto efectivamente duplica el trabajo de configuración que pod installtiene que hacer. Sin embargo, esto no será un problema hasta que tenga> 15 pods, así que no se preocupe demasiado hasta entonces.
fatuhoku

1
Esta es la única solución que funciona para mí con Cocoapods 1.0
William Entriken

A partir de 1.x, este es el método oficial para las pruebas que heredan dependencias de aplicaciones: stackoverflow.com/a/40866889/2799670
Darren Black

4

Tuve una ocurrencia similar cuando perdí algunos archivos de la biblioteca durante algún control de versión. Todavía vi el archivo de la biblioteca en mis Pods, pero al faltar el código real, XCode dijo que se había ido. Para mi consternación, ejecutar 'pod install' no devolvió de inmediato los archivos perdidos.

Tuve que quitar y reemplazar el pod manualmente haciendo lo siguiente:

  1. Eliminar la biblioteca del Podfile
  2. Ejecute 'pod install' para eliminar la biblioteca por completo
  3. Vuelva a colocar la biblioteca en el Podfile.
  4. Ejecute 'pod install' nuevamente

Esto debería volver a poner la biblioteca en cuestión en su forma original.


2

También vale la pena señalar que si ha libPods.aagregado dos veces, obtendrá un error desagradable como este:

232 duplicate symbols for architecture i386

Para solucionarlo, simplemente elimine una de las libPods.areferencias en su Explorador de proyectos.


2

A partir de CocoaPods 1.x, hay una nueva forma de declarar dependencias compartidas entre un objetivo y el objetivo de prueba correspondiente. Había estado usando la solución aceptada por Mark Struzinski hasta este punto, pero el uso de este método arrojó una gran cantidad de advertencias al ejecutar mis pruebas 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.

Con CocoaPods 1.x podemos declarar nuestro objetivo de prueba como heredado a través de las rutas de búsqueda del objetivo principal, de esta manera:

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

Esto dará como resultado que el objetivo de prueba tenga acceso a las dependencias del objetivo de la aplicación, sin múltiples copias binarias. Esto me ha acelerado seriamente los tiempos de construcción de prueba.



1

Estoy trabajando con la integración de GoogleMaps Objective-C POD en iOS con mi aplicación Swift, por lo que para mí el problema era que el objetivo de prueba no tenía una referencia al archivo de encabezado de puente ( SWIFT_OBJC_BRIDGING_HEADER ) en la configuración de compilación. Asegúrese de que tanto su aplicación como los objetivos de la aplicación de prueba apunten a eso para que las llamadas de API de terceros (API de mapas, etc.) puedan usarse en pruebas de unidades rápidas.


1
Tengo una configuración similar a la tuya. Ya he agregado el encabezado de puente al objetivo de prueba, sin embargo, aparece el error "No existe tal módulo 'GoogleMaps'" import GoogleMaps.
Nicolas Miari

0

La siguiente sintaxis me da el mejor resultado (probado 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

Sin esto, tengo advertencias mientras se ejecuta la prueba sobre símbolos duplicados.

Después de esto, las advertencias desaparecieron.


0

Tuve problemas al usar OpenCV bajo XCTest. Me estaba dando errores de enlazador de Undefined symbols for architecture arm64para clases como cv::Mat. Estoy instalando OpenCV a través de CocoaPods usando pod 'OpenCV', '~> 2.0'debajo del objetivo principal. No importa cuánto intente poner la dependencia de OpenCV bajo el objetivo de prueba o inherit! :search_pathsno use nada de eso funcionó. La solución fue crear un abstract_targetlike así:

# 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 

También son útiles los comandos pod deintegrate& pod cleanque ayudan a limpiar el proyecto y aseguran que comiences de nuevo al probar. Puedes instalar esos dos usando [sudo] gem install cocoapods-deintegrate cocoapods-clean.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.