web-dev-qa-db-fra.com

Quand les sources d'applications doivent-elles être incluses dans les cibles de test?

Dans un nouveau projet, j'ai ce test simple

#import <XCTest/XCTest.h>
#import "ViewController.h"

@interface ViewControllerTests : XCTestCase
@end

@implementation ViewControllerTests

- (void)testExample
{ 
    // Using a class that is not in the test target.
    ViewController * viewController = [[ViewController alloc] init];
    XCTAssertNotNil(viewController, @"");
}

@end

ViewController.h fait not une partie de la cible de test, mais cela compile et exécute les tests sans aucun problème. 

enter image description here

Je pense que cela est dû au fait que l’application est construite d’abord (en tant que dépendance), puis aux tests. L'éditeur de liens détermine ensuite ce qu'est la classe ViewController. 

Cependant, sur un projet plus ancien, avec exactement le même test et le même fichier ViewController, la construction échoue à la phase de l'éditeur de liens: 

Undefined symbols for architecture i386:
"_OBJC_CLASS_$_ViewController", referenced from:
  objc-class-ref in ViewControllerTests.o

Cette erreur de l'éditeur de liens se produit même si une nouvelle cible de test d'unité XCTest est créée. 

Pour contourner ce problème, il est possible d'inclure les sources dans l'application et les cibles de test (cochez les deux cases dans l'image ci-dessus). Cela provoque des avertissements de construction pour les symboles en double, dans le journal système du simulateur (ouvrez le simulateur et appuyez sur cmd-/pour voir ceci):

Class ViewController is implemented in both 
[...]/iPhone Simulator/ [...] /MyApp.app/MyApp and 
[...]/Debug-iphonesimulator/LogicTests.octest/LogicTests. 
One of the two will be used. Which one is undefined.

Ces avertissements provoquent parfois des problèmes illustrés par l'exemple suivant:

 [viewController isKindOfClass:[ViewController class]]; // = NO
 // Memory address of the `Class` objects are different.

 NSString * instanceClassString = NSStringFromClass([viewController class]);
 NSString * classString         = NSStringFromClass([ViewController class]);

 [instanceClassString isEqualToString:classString]; // = YES
 // The actual class names are identical

La question est donc de savoir quels paramètres de l'ancien projet exigent que les fichiers source de l'application soient inclus dans la cible de test.


Résumé des commentaires

Entre le projet de travail et le projet de non-travail:

  1. Il n'y a pas de différence dans la sortie de l'éditeur de liens (la commande commençant par Ld).
  2. Il n'y a pas de différence dans les dépendances de la cible (il y a 1 dépendance à la cible du test, qui est l'application) 
  3. Il n'y a pas de différence dans les paramètres de l'éditeur de liens.
66
Robert

J'ai passé du temps à comprendre cela.

Si vous lisez cette documentation , vous constaterez que Xcode dispose de deux modes pour exécuter des tests. Tests de logique et tests d'application. La différence réside dans le fait que les tests logiques construisent leur propre cible avec vos classes et symboles intégrés. L'exécutable résultant peut être exécuté dans le simulateur et restitue le résultat du test dans Xcode. Les tests d’application, d’autre part, construisent une bibliothèque dynamique reliant votre code qui est injecté dans l’application au moment de l’exécution. Cela vous permet d'exécuter des tests dans l'environnement iPhone et de tester le chargement Xib et d'autres choses.

Comme les symboles manquent dans votre cible de test lorsque vous dissociez les fichiers source, il semble que votre ancien projet ait une cible de test configurée pour les tests de logique, et non pour les tests d'application (unité).

Comme ces jours-ci, Xcode semble être essayer de ne pas faire la distinction entre les deux et créer une cible de test d’application par défaut permet de parcourir tout ce que vous pourriez avoir à changer pour transformer votre cible de test logique en test unitaire.

Je vais également supposer que vous avez une cible d'application et non une cible de bibliothèque statique, car les instructions seront un peu différentes. 

  1. Dans les paramètres de construction de votre cible de test, supprimez les paramètres de construction "Bundle Loader" et "Test Host". Nous aurons Xcode pour les rajouter plus tard
  2. Vous devez supprimer tous les fichiers .m de votre application à partir de la cible de test. Vous pouvez le faire en sélectionnant tous les fichiers .m et en supprimant la cible de test dans l'inspecteur de fichiers Xcode ou en utilisant la phase de compilation des sources de compilation de la cible de test.
  3. Changez les "chemins de recherche du framework" pour votre cible de test. Pour Xcode 5, ils doivent être $(SDKROOT)/Developer/Library/Frameworks $(inherited) $(DEVELOPER_FRAMEWORKS_DIR) dans cet ordre et sans guillemets ni barres obliques inverses supplémentaires
  4. Accédez à la sous-fenêtre Général des paramètres de construction de votre cible de test et sélectionnez votre cible dans le menu déroulant. Si le menu spécifie déjà la cible de votre application, vous devez l'activer/désactiver. Cela permettra à Xcode de reconfigurer les paramètres du chargeur d’ensemble et de l’hôte de test avec la valeur correcte.
  5. Enfin, vérifiez le schéma de votre application. Dans le menu déroulant Schéma, sélectionnez Editer Schéma. Puis cliquez sur l'action de test. Assurez-vous que la cible de test figure dans la liste du volet d'informations et que tous les tests sont sélectionnés. 

Cette information provient plus ou moins de la documentation liée ci-dessus, mais j'ai mis à jour les étapes pour Xcode 5.

MODIFIER:

Hmm 100% note ce que dit eph515 à propos de la visibilité des symboles de débogage, mais vous pouvez également vérifier que quelqu'un n'a pas défini l'action de test de votre schéma pour intégrer la configuration Release ou une autre configuration. Cliquez sur le sélecteur de schéma, puis choisissez modifier le schéma. Cliquez sur l'action de test, puis assurez-vous que la configuration de construction est Debug

build configuration screen for test action in a scheme

Si vous avez une cible de bibliothèque statique

Donc, si vous avez une cible de bibliothèque statique, vous avez deux options: 1. Tests de logique 2. Tests d'application dans une application hôte

Pour 1. vous devez vous assurer que Bundle Loader et Test Host sont vides pour votre cible de bibliothèque statique. Vos sources doivent ensuite être compilées dans la cible de test car elles n'auraient aucun autre moyen de fonctionner.

Pour 2. Vous devez créer un nouveau projet d'application dans Xcode et ajouter votre projet de bibliothèque statique en tant que sous-projet. Vous devez ensuite copier manuellement les paramètres de construction Bundle Loader et Test Host de la cible de test de votre nouvelle application vers votre cible de test Static Lib. Ensuite, vous ouvrez le schéma de votre nouvelle application test et ajoutez votre cible test à l'action tests de la nouvelle application . Pour exécuter les tests sur votre bibliothèque, exécutez l'action test de votre application hôte. 

44
jackslash

Sur Xcode 6, j'ai pu résoudre ce problème en cochant la case "Autoriser le test des API d'application hôte" dans la cible de test> Général> Test.

Xcode Screenshot

20
yood

J'ai également rencontré ce problème et suivi la recommandation de jackslash, mais avec un dernier ajout: sélectionnez votre cible principale et recherchez les symboles masqués par défaut (sous Apple LVM 5.0 - Génération de code); si la valeur est Oui, remplacez-le par Non pour "masquer" tous les symboles des sources compilées que la cible de test unitaire recherche. Travaille pour moi. Assurez-vous d'inclure toutes les étapes décrites dans jackslash.

16
eph515

La réponse était une combinaison des réponses de jackslash et de eph515.

Comme dans la réponse de eph515, symbols hidden by default devrait être Non pour le débogage.

enter image description here

De plus, deployment postprocessing devrait être Non pour le débogage.

enter image description here

De même, toutes les bibliothèques incluses dans la cible de test doivent être supprimées du test unitaire. Il ne reste que les 3 dans la capture d'écran, ainsi que tout ce qui est spécifique aux tests unitaires.

enter image description here

De même, s'il existe une phase de génération de script d'exécution à la fin de la liste, vous devez la supprimer (car il s'agit d'un artefact de test unitaire).

Ensuite, faites tout dans la réponse de jackslash .

7
Robert

Dans mon cas, dans Xcode 6.2, une erreur était survenue dans différentes architectures de cible de projet et cible de test.

La cible du projet a uniquement les architectures armv7 et armv7s (à cause de certaines bibliothèques plus anciennes)

La cible Tests du projet a les architectures armv7, armv7s et arm64.

La suppression de l'architecture arm64 résout ce problème dans mon cas.

Project Editor -> Project Tests target -> Build Settings -> Valid Architectures = armv7 armv7s

(Peut-être est-il également nécessaire de définir "Architectures" au lieu de $ (ARCHS_STANDARD) sur $ (ARCHS_STANDARD_32_BIT))

0
Lukáš Mareda

Pour moi, il s'agissait simplement de ne pas ajouter de cibles de test pour le programme.

Pour la cible de l’application, allez à Edit Scheme, puis cliquez sur Test sur le côté droit puis ajoutez une cible de test avec le bouton + en bas:  enter image description here

0
richy