Note importante
Avec Xcode 5.1 (peut-être aussi les Xcode antérieurs) test
est une action de construction valide.
Nous avons pu remplacer l'ensemble du hack ci-dessous par un appel à xcodebuild en utilisant l'action de construction de test et avec les paramètres appropriés de -destination
options. man xcodebuild
pour plus d'informations.
Les informations ci-dessous sont laissées ici pour la postérité
J'ai essayé de pirater les scripts d'Apple pour exécuter les tests unitaires comme mentionné dans
Exécution des tests unitaires de Xcode 4 à partir de la ligne de commande
et
Xcode4 : Exécution des tests d'application à partir de la ligne de commande dans iOS
et de nombreuses publications similaires sur le web.
Cependant, j'ai rencontré un problème avec ces solutions. Certains de nos tests unitaires font appel au trousseau d'iOS et ces appels, lorsqu'ils sont exécutés dans l'environnement qui résulte du piratage des scripts d'Apple, échouent avec une erreur ( errSecNotAvailable
[-25291] pour les curieux morbides). En conséquence, les tests échouaient toujours... une caractéristique indésirable dans un test.
J'ai essayé un certain nombre de solutions basées sur des informations que j'ai trouvées ailleurs sur le web. Certaines de ces solutions impliquaient d'essayer de lancer le démon des services de sécurité du simulateur iOS, par exemple. Après m'être débattu avec ces solutions, il m'a semblé que ma meilleure option était de fonctionner dans le simulateur iOS en profitant pleinement de l'environnement du simulateur.
Ce que j'ai fait, alors, c'est mettre la main sur l'outil de lancement du simulateur iOS. ios-sim . Cet outil en ligne de commande utilise les frameworks privés d'Apple pour lancer une application iOS à partir de la ligne de commande. Ce qui m'a particulièrement intéressé, c'est qu'il me permet de passer à la fois des variables d'environnement et des arguments de ligne de commande à l'application qu'il lance.
Grâce aux variables d'environnement, j'ai pu injecter mon paquet de tests unitaires dans mon application. Grâce aux arguments de la ligne de commande, je peux passer le "-SenTest All" nécessaire pour que l'application exécute les tests unitaires et quitte.
J'ai créé un schéma (que j'ai appelé "CommandLineUnitTests") pour mon paquet de tests unitaires et j'ai coché l'action "Run" dans la section build comme décrit dans les posts ci-dessus.
Mais plutôt que de pirater les scripts d'Apple, j'ai remplacé le script par un script qui lance l'application en utilisant ios-sim et met en place l'environnement pour injecter mon bundle de tests unitaires dans l'application séparément.
Mon script est écrit en Ruby qui m'est plus familier que le script BASH. Voici ce script :
if ENV['SL_RUN_UNIT_TESTS'] then
launcher_path = File.join(ENV['SRCROOT'], "Scripts", "ios-sim")
test_bundle_path= File.join(ENV['BUILT_PRODUCTS_DIR'], "#{ENV['PRODUCT_NAME']}.#{ENV['WRAPPER_EXTENSION']}")
environment = {
'DYLD_INSERT_LIBRARIES' => "/../../Library/PrivateFrameworks/IDEBundleInjection.framework/IDEBundleInjection",
'XCInjectBundle' => test_bundle_path,
'XCInjectBundleInto' => ENV["TEST_HOST"]
}
environment_args = environment.collect { |key, value| "--setenv #{key}=\"#{value}\""}.join(" ")
app_test_host = File.dirname(ENV["TEST_HOST"])
system("#{launcher_path} launch \"#{app_test_host}\" #{environment_args} --args -SenTest All #{test_bundle_path}")
else
puts "SL_RUN_UNIT_TESTS not set - Did not run unit tests!"
end
L'exécuter à partir de la ligne de commande ressemble à ça :
xcodebuild -sdk iphonesimulator -workspace iPhoneApp.xcworkspace/ -scheme "CommandLineUnitTests" clean build SL_RUN_UNIT_TESTS=YES
Après avoir cherché le SL_RUN_UNIT_TESTS
le script trouve le "launcher" (l'exécutable iOS-sim) dans l'arbre source du projet. Il construit ensuite le chemin vers mon Bundle de test unitaire en fonction des paramètres de construction que Xcode transmet dans les variables d'environnement.
Ensuite, je crée l'ensemble des variables d'environnement d'exécution de mon application en cours d'exécution qui injectent le paquet de tests unitaires. Je configure ces variables dans le fichier environment
au milieu du script, puis utiliser un peu de ruby grunge pour les joindre en une série d'arguments de ligne de commande pour la commande ios-sim
application.
Vers le bas, je prends le TEST_HOST
de l'environnement comme l'application que je veux lancer et la system
exécute réellement la commande ios-sim
passant l'application, les arguments de la commande pour configurer l'environnement, et les arguments -SenTest All
et le chemin du paquet de test de l'application en cours d'exécution.
L'avantage de ce système est qu'il exécute les tests unitaires dans l'environnement du simulateur, comme le fait, je crois, Xcode lui-même. L'inconvénient de ce schéma est qu'il dépend d'un outil externe pour lancer l'application. Cet outil externe utilise des frameworks privés d'Apple, donc il peut être fragile avec les versions ultérieures du système d'exploitation, mais il fonctionne pour le moment.
P.S. J'ai beaucoup utilisé le "je" dans ce billet pour des raisons narratives, mais le mérite en revient en grande partie à mon partenaire de crime, Pawel, qui a travaillé sur ces problèmes avec moi.