Lors de la création d'un nouveau projet avec des tests unitaires, Xcode définit la configuration de la construction sur Debug pour le schéma de test (identique pour le schéma d'exécution).
Devrais-je différencier les modèles Exécuter (Command-R) et Test (Command-U)?
Par exemple, devrais-je créer une nouvelle configuration de construction appelée Test, y ajouter une macro de préprocesseur TEST = 1 et l'utiliser comme configuration de construction pour le schéma de test? Ou, devrais-je simplement garder Run & Test comme Debug?
Je viens de Ruby/Rails, où vous avez généralement des environnements de test, de développement et de production. Il me semble que Debug est comme le développement et que Release est comme la production, mais il nous manque un test, c'est pourquoi je pense qu'il pourrait être judicieux d'ajouter Test.
Commentaires? Des avis? Suggestions?
Je demande spécifiquement ceci parce que je veux compiler quelque chose pour Test avec:
#ifdef TEST
// Do something when I test.
#endif
Je ne pense pas que ce soit important si je compile aussi ceci pour Debug. Donc, je pourrais vraiment faire:
#ifdef DEBUG
// Do something when I run or test.
#endif
Mais je n'ai vraiment l'intention que de le faire pour des tests pour le moment. C'est pourquoi je pense que je devrais faire la distinction entre debug et test, mais je me demande pourquoi Xcode ne le fait pas pour vous par défaut? Apple pense-t-il que vous ne devriez pas les différencier?
Au lieu de créer une configuration de construction de test, je:
créé un fichier Tests-Prefix.pch
:
#define TEST 1
#import <SenTestingKit/SenTestingKit.h>
#import "CocoaPlant-Prefix.pch"
a entré son chemin dans le champ En-tête du préfixe des paramètres de construction de la cible de tests.
ajouté le code suivant en haut d'un fichier que j'ai créé et appelé MyAppDefines.h
, importé dans MyApp-Prefix.pch
:
#ifdef TEST
#define TEST_CLASS NSClassFromString(@"AppDelegateTests") // any test class
#define BUNDLE [NSBundle bundleForClass:TEST_CLASS]
#define APP_NAME @"Tests"
#else
#define BUNDLE [NSBundle mainBundle]
#define APP_NAME [[BUNDLE infoDictionary] objectForKey:(NSString *)kCFBundleNameKey]
#endif
Cela me permet d'utiliser BUNDLE
partout où je veux dire [NSBundle mainBundle]
et aussi de le faire fonctionner lorsque j'exécute des tests.
L'importation de SenTestingKit dans Tests-Prefix.pch
accélère également la compilation du framework SenTestingKit et me permet de laisser de côté #import <SenTestingKit/SenTestingKit.h>
du haut de tous les fichiers de test.
Les macros de préprocesseur ne fonctionneront pas, vous devez vérifier l'environnement au moment de l'exécution.
static BOOL isRunningTests(void)
{
NSDictionary* environment = [[NSProcessInfo processInfo] environment];
return (environment[@"XCInjectBundleInto"] != nil);
}
(Mise à jour pour Xcode 7.3)
Vous pourriez envisager d'ajouter une nouvelle configuration de construction.
Dans xcode 4, cliquez sur votre projet dans le navigateur de gauche.
Dans la fenêtre principale, cliquez sur votre projet, puis sélectionnez l'onglet "Info".
Cliquez sur le bouton "+" pour ajouter une nouvelle configuration (vous pouvez appeler le "test" si vous le souhaitez ").
Maintenant, cliquez sur votre cible et allez dans l'onglet des paramètres de construction.
Recherche de "macros de préprocesseur"
Ici, vous pouvez ajouter des macros de préprocesseur pour votre nouvelle configuration de construction.
Double-cliquez simplement sur votre nouvelle configuration "test" et ajoutez TESTING = 1.
Enfin, éditez votre schéma de construction. Sélectionnez les options de test pour votre schéma. Il devrait y avoir un menu déroulant "Build Configuration". Sélectionnez votre configuration "test".
J'ai décidé d'ajouter une vérification d'une variable d'environnement dans le code lui-même, au lieu d'utiliser la suggestion de isRunningTests () suggérée par Robert.
+ (BOOL) isTesting { NSDictionary* environment = [[NSProcessInfo processInfo] environment]; return [environment objectForKey:@"TESTING"] != nil; }
L'écran devrait ressembler à ceci lorsque vous avez terminé.
Le code ci-dessus trouvera la variable d'environnement TESTING lors de l'exécution en mode test ou en mode application. Ce code va dans votre application, pas dans les fichiers de test unitaires. Vous pouvez utiliser
#ifdef DEBUG
...
#endif
Pour empêcher le code d'être exécuté en production.
La réponse de Robert dans Swift 3.0:
func isRunningTests() -> Bool {
let environment = ProcessInfo().environment
return (environment["XCInjectBundleInto"] != nil);
}
J'ai testé si longtemps, j'ai trouvé un résultat:
Non seulement vous ajoutez la macro pré-processeur à votre cible de test unitaire (vous pouvez utiliser plusieurs méthodes en utilisant des variables pour le test unitaire uniquement , Et suivez les méthodes @MattDiPasquale),
mais vous devez également ajouter le fichier de conformité à la condition dans votre cible de test . Nous devons recomplier ce fichier, car vous disposez d'une nouvelle macro de préprocesseur pour ce fichier, macro n'a pas défini.
J'espère que cela vous aidera.
Si vous créez une configuration de construction Test, puis définissez la propriété "Autres drapeaux Swift" de votre cible sur "-DTEST", elle définira une macro TEST qui fonctionnera dans votre code Swift. Assurez-vous de le définir dans les paramètres de construction de votre cible d'application afin de pouvoir l'utiliser dans le code Swift de votre application.
Ensuite, avec cet ensemble, vous pouvez tester votre code comme suit:
func testMacro() {
#if !TEST
// skipping over this block of code for unit tests
#endif
}
Sur iOS, [UIApplication sharedApplication]
renverra nil
lorsqu'un test unitaire est en cours d'exécution.
Examinez les variables d'environnement pour voir si les tests unitaires sont en cours d'exécution. Semblable à la réponse de Robert, mais je ne vérifie qu'une fois le rendement.
+ (BOOL)isRunningTests {
static BOOL runningTests;
static dispatch_once_t onceToken;
// Only check once
dispatch_once(&onceToken, ^{
NSDictionary* environment = [[NSProcessInfo processInfo] environment];
NSString* injectBundle = environment[@"XCInjectBundle"];
NSString* pathExtension = [injectBundle pathExtension];
runningTests = ([pathExtension isEqualToString:@"octest"] ||
[pathExtension isEqualToString:@"xctest"]);
});
return runningTests;
}
Version modifiée de la réponse de Kev qui fonctionne pour moi sur Xcode 8.3.2
+(BOOL)isUnitTest {
static BOOL runningTests;
static dispatch_once_t onceToken;
// Only check once
dispatch_once(&onceToken, ^{
NSDictionary* environment = [[NSProcessInfo processInfo] environment];
if (environment[@"XCTestConfigurationFilePath"] != nil && ((NSString *)environment[@"XCTestConfigurationFilePath"]).length > 0) {
runningTests = true;
} else {
runningTests = false;
}
});
return runningTests;
}