Test After Buildの仕組みを利用して、実際にテストを実行してみましょう。
テスト用ターゲット
Xcodeビルドの設定で、テスト用のターゲットを設定します。
iOSの標準のテスト(SenTestingKit)はシミュレータ上でしか走らせる事ができないので、SDKには明示的にiphonesimulatorを指定します。iphonesimulator5.1のようにバージョンを指定することもできます。これを指定しないとデバイス用のSDKが使われる事になるので、エラーが発生します。
この状態で一度ジョブを実行してみましょう。コンソール出力を確認して、テスト実行のログが残っているか確認しましょう。
Test Suite '/Users/foo/.jenkins/jobs/bar/workspace/build/Debug-iphonesimulator/bar.octest(Tests)' finished at 2012-05-25 20:33:30 +0000. Executed 50 tests, with 15 failures (15 unexpected) in 28.232 (28.253) seconds
なお、Test After Buildを使ったテスト実行は「ビルド」というタスクの一環として実行されるので、テストに失敗した場合は「ビルド失敗」という結果になります。
テスト結果の集計
Jenkins上でテスト結果を管理するには、結果をJUnit XMLの形で出力する必要がありますが、XcodeのOCUnitはこのフォーマットでの出力に対応していません。幸い、Xcode PluginはOCUnitでのテストの実行結果をJUnit XMLに変換して出力してくれるので、これを使ってテスト結果をJenkinsに渡すことができるようになっています。Xcode Pluginを使う大きな理由の一つはここにあります。
Xcode Pluginを使ってテストを実行すると、$WORKSPACEのtest-reportsというディレクトリにテスト結果がxmlの形式で保存されます。
Jenkinsのビルド後の処理で「JUnitテスト結果の集計」を有効にして、テスト結果XMLにtest-reports/*.xmlを指定すると、テスト後に結果が集計されてJenkins上で管理されるようになります。
集計されたテスト結果はあとでグラフとして見る事ができるようになります。
失敗時の報告
あとは、「Jenkinsを使ったiOSアプリビルド自動化6 メールでの通知」で設定したようにジョブの結果をメールで通知するように設定しておきましょう。すると、テストが通らなくなったときにメールで通知が届くようになります。
テスト用ターゲットと通常のビルドターゲットとの関係
ややこしい話ですが、テスト用ターゲットと通常のビルドターゲットの関係について整理しておきましょう。
前の記事で触れた通り、通常のビルドターゲット側のTarget Dependenciesにテストターゲットを登録しておくと、アプリのビルドが始まる前にテストターゲットがビルドされ、そこでTest After BuildがYESになっていればテストが実行されるので、ビルド毎にテストを実行させることができるようになります。
Jenkinsで実行する場合も、上記のような設定にしておけばビルド時に同時にテストを実行させることができますが、結論からいうと僕はそのような設定はおすすめしません。
まず、前述の通りテストを実行させるためにはシミュレータ用のSDKを使わなければ行けません。一方で問題なくビルドできるかを検証するにはシミュレータ用のSDKを使うよりも実機用のSDKを使った方が適切です。また、ビルド成果物(ipa等)を後に説明するTestFlight等を使って配布するような場合も、実機用のSDKを使う必要があります。
また、後述の通りTest After Buildでテストを実行する場合、すべてのテストが実行されてしまいます。テストの数が増えてくると毎回のテスト実行時間が長くなり、効率が悪くなってきます。
Test After Buildの設定について
テスト駆動開発を進めていくと、プロジェクトが進むにつれてテストの分量が大きくなり、毎回のテスト実行にかかる時間が長くなっていきます。そこで、実際の開発ではテスト全体ではなく、そのとき開発している機能に関係がある部分のテストのみを実行するスタイルがよくとられます。
XcodeではSchemeの設定をいじることで、Cmd + Uでテスト実行するときに実行するテストケースを選択できるようになっています。しかし、Jenkins用にTest After Buildの設定をYESにしておくと、Schemeの設定とは関係なくテストターゲットのビルド時にすべてのテストが実行されるようになってしまいます。
そこで、レポジトリ上のTest After Buildの設定はNOにしておいて、Jenkins側でビルドをする直前にTest After Buildの設定をYESに書き換えるようにジョブを設計すると、開発時の効率を犠牲にすることなくJenkins上で自動テストを実現できます。
残念ながらXcodeのプロジェクトファイル(pbxproj)の設定をいじる機能・プラグインはJenkinsには今のところないので、そこはスクリプトを書いて自分で設定を書き換える必要があります。pbxprojファイルの構造については複雑なのでまた後ほど触れるとして、ここではsedコマンドを使って置換を行うだけで設定を書き換える事ができます。
sed -i -e "s/TEST_AFTER_BUILD = NO/TEST_AFTER_BUILD = YES/g" $WORKSPACE/foo.xcodeproj/project.pbxproj // プロジェクトの設定でTest After Build = NOを設定しておく必要があります。
Jenkinsのジョブ上で、ビルド手順の追加から「シェルスクリプトの実行」を選び、上のコマンドを書きます。ビルド手順はドラッグで順番を入れ替える事ができるので、Xcodeビルドの前にスクリプトが実行されるように順番を調整します。
シミュレータの起動
公式な情報を見つけられていないのですが、Test Hostが空の状態、すなわちシミュレータを起動せずにコマンドラインからテストを実行すると、Pasteboard等いくつかの機能が利用できないようです。この問題の回避方法として、SDKの中にあるRunPlatformUnitTestsの内容を書き換えろ、というような記事もあったりするのですが、僕の手元で最新のXcode 4.3 / iOS SDK 5.1を使って試してみた感じでは、RunPlatformUnitTestsを書き換えなくても単純にテスト実行時に裏でiPhone シミュレータが起動していれば問題が起きないようでした。
不確かな情報なので載せるべきか悩みますが、Jenkins上で上記の方法でテストを試して不明なエラーが発生するような場合はジョブの設定でビルドを行う前に下記のシェルスクリプトを実行してシミュレータを起動してみるとよいかもしれません。
open /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Applications/iPhone\ Simulator.app
誰かこの現象について、情報を持っている人がいたら教えてもらえるとうれしいです。
結論
というわけでiOSアプリの継続的なテストが実現しました。本来であればcoverageをとりたいところですが、残念ながらXcode 4.3では今のところ相当ハッキーな手順を踏まなければCoverageをとる事ができないようです。
次の記事で、アプリのディストリビューションの手順に入る前に、省略してきた箇所についての補足を加えて、継続的なビルド・テストについて一度まとめようと思います。
launchDaemon で、pasteboard を実行させて、Application ApplicationUnit test をコマンドから実行しテストを実行していたのですが、Version 10.7.4 にアップデートした後、動かなくなってしまいました。10.7.4にアップデート前までは、書かれてるように、open コマンドでsimulator を立ち上げ後、ApplicationUnit Test をコマンドラインから実行できていました。
ApplicationUnit Test をコマンドから実行は結構トリッキーに感じます。。。
情報ありがとうございます。
確かに、こちらの環境でも一昨日OSを10.7.4にアップデートしてから挙動が不安定になってしまったんですよね。
失敗する時とうまく行く時があって全然なにがおきているのかわからないのですが…。
いずれにせよ、注意が必要ですね。
総裁 様、早々のご返事ありがとうございます。10.7.4に上げてからの不安定な状況のデバッグをしてますが、まだ分かりません。総裁様の方で、何か分かりましたら、ご連絡頂ければ幸いです。私の方も何かわかりましたら、アップデート致します。 Appleが早くofficial にコマンドラインからのApplicationUnitTest実行をサポートしてくれればいいのですが、、、
「SDKの中にあるRunPlatformUnitTestsの内容を書き換えろ」というのですが、去年の4月書いたブログだし、もはや古いんだろうと既に思ってたんですけど、調べる時間がなくてそのままは現状です。
こちらに書いてあるようにトライしてみ、できるのであれば是非こちらのブログを更新します。最新の「研究/実験」でありがとうございましたw
結構”TEST_AFTER_BUILD”と戦ってきたしー-
お、参考にさせていただきました、ありがとうございます!
まさかご本人からこっちにコメントをもらえるとは(しかも日本語で!)
総裁 様 動くようになりました。下記のマークさんのサイトにソルーションを投稿しておきました。
http://longweekendmobile.com/2011/04/17/xcode4-running-application-tests-from-the-command-line-in-ios/
おお、ありがとうございます。
返信遅れてしまってすみません。
こっちでも確認してみます!