Xcode 4で新しいiOSプロジェクトを作成し、単体テストを含めました。デフォルトアプリには、メインアプリケーションと単体テストバンドルの2つのターゲットがあります。 [製品]> [テスト](Command-U)を使用して、アプリケーションをビルドし、ユニットテストバンドルをビルドし、iOSシミュレーターを起動して、テストを実行します。ここで、コマンドラインから同じことを実行できるようにしたいと思います。コマンドラインツール(xcodebuild)には「テスト」アクションがありませんが、アプリケーション自体に依存するため、ユニットテストバンドルターゲットを直接ビルドできるはずです。ただし、実行中:
xcodebuild -target TestAppTests -sdk iphonesimulator4.3 -configuration Debug build
次のメッセージが表示されます。
/Developer/Platforms/iPhoneSimulator.platform/Developer/Tools/Tools/RunPlatformUnitTests:95: warning: Skipping tests; the iPhoneSimulator platform does not currently support application-hosted tests (TEST_Host set).
GUIからCommand-Uを実行すると、テストホストがユニットテストバンドルターゲットに設定されるため、それは嘘のようです。ロジックテストとアプリケーションテストの分離に関する以前の投稿を見てきましたが、Xcode 4はその区別を排除しているようです。コマンドラインからテストを実行する方法はありますか?
Xcode 5.1では(おそらく以前のXcodeも)test
は有効なビルドアクションです。
Testのビルドアクションと適切な-destination
オプションを使用して、以下のハック全体をxcodebuildの呼び出しに置き換えることができました。詳細については、man xcodebuild
。
以下の情報は後世のためにここに残されています
Appleのスクリプトをハッキングしてユニットテストを実行してみました
そして
Xcode4:iOSのコマンドラインからのアプリケーションテストの実行
また、ウェブ全体に多数の同様の投稿があります。
しかし、私はそれらのソリューションで問題に遭遇しました。ユニットテストの一部はiOSキーチェーンを実行し、それらの呼び出しは、Appleのスクリプトをハッキングすることから来る環境で実行すると、エラーで失敗しました(病的な好奇心のためのerrSecNotAvailable
[-25291])。その結果、テストは常に失敗しました...テストの望ましくない機能です。
ウェブ上の他の場所で見つけた情報に基づいて、いくつかのソリューションを試しました。たとえば、iOSシミュレータのセキュリティサービスデーモンを起動しようとするソリューションが含まれていました。それらに苦労した後、私の最善の策は、シミュレータの環境を最大限に活用してiOSシミュレータで実行することでした。
その後、iOSシミュレーター起動ツール ios-sim を入手しました。このコマンドラインツールは、private Appleフレームワークを使用して、コマンドラインからiOSアプリケーションを起動します。ただし、特に使用したのは、環境変数とコマンドラインの両方を渡すことができるという事実です起動するアプリへの引数。
環境変数ですが、ユニットテストバンドルをアプリケーションに挿入することができました。コマンドライン引数を使用して、アプリで単体テストを実行して終了するために必要な「-SenTest All」を渡すことができます。
ユニットテストバンドル用のスキーム( "CommandLineUnitTests"と呼びます)を作成し、上記の投稿で説明したように、ビルドセクションで "実行"アクションをチェックしました。
ただし、Appleのスクリプトをハッキングするのではなく、ios-simを使用してアプリケーションを起動し、単体テストバンドルをアプリケーションに個別に挿入する環境をセットアップするスクリプトに置き換えました。
私のスクリプトはRubyで書かれています。これはBASHスクリプトよりも馴染み深いものです。そのスクリプトを次に示します。
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
これをコマンドラインから実行すると次のようになります。
xcodebuild -sdk iphonesimulator -workspace iPhoneApp.xcworkspace/ -scheme "CommandLineUnitTests" clean build SL_RUN_UNIT_TESTS=YES
SL_RUN_UNIT_TESTS
環境変数を探した後、スクリプトはプロジェクトのソースツリー内で「ランチャー」(iOS-sim実行可能ファイル)を見つけます。次に、Xcodeが環境変数で渡すビルド設定に基づいて、単体テストバンドルへのパスを構築します。
次に、実行中のアプリケーション用に、単体テストバンドルを注入するランタイム環境変数のセットを作成します。スクリプトの中央にあるenvironment
ハッシュにこれらの変数を設定し、いくつかのRubyグランジを使用して、ios-sim
アプリケーションの一連のコマンドライン引数に結合します。
最下部近くで、起動するアプリとして環境からTEST_Host
を取得し、system
コマンドが実際にios-sim
を実行し、アプリケーション、環境をセットアップするコマンド引数、および引数-SenTest All
とテストバンドルを渡します実行中のアプリケーションへのパス。
このスキームの利点は、Xcode自体と同じように、シミュレーター環境で単体テストを実行できることです。このスキームの短所は、外部ツールに依存してアプリケーションを起動することです。その外部ツールはprivate Appleフレームワークを使用しているため、以降のOSリリースでは脆弱になる可能性がありますが、現時点では機能します。
追伸この記事では、物語的な理由で「私」をよく使いましたが、多くの功績は、犯罪のパートナーであるパウエルにあります。
私はジョナの投稿に触発され、それを行う方法を見つけました。
基本的に、Xcode 4が必要であり、スクリプトをハッキングして機能させる必要がありますが、必要です。
重要な点は、Xcode 4がiOSテストバンドルをMacOS Xバンドルであるかのように説得することです。これはプラットフォームの問題であり、Xcodeはコマンドラインですぐにアプリケーションテストを実行したくありません。おかしい、うまくいくようだから。
サイトにはサンプルプロジェクトもあります。
あなたが探しているのは、ターミナルからOCUnitテストを実行するためのこの文書化されていない引数です(sdkとtargetも必要です)
xcodebuild -target MyTarget -sdk iphonesimulator TEST_AFTER_BUILD=YES
不完全なソリューションですが、独自のスキームでロジックテストのコマンドラインビルドを実行し、ターゲットをビルドすることができました: http://blog.carbonfive.com/2011/04/06/running-xcode-4-コマンドラインからのユニットテスト/
xctoolはこの問題を解決します: https://github.com/facebook/xctool
継続的インテグレーションサーバーで問題なく使用します