iOSアプリのテストをTravis CIで走らせて、コードカバレッジをCoverallsで取る方法
この記事では以下のことについての手順を簡単に説明します。
Objective-C勉強会@東京 6月 でNSDateについて発表してきた | Web scratch で書いていたように、
NSDateについてのライブラリ azu/NSDate-Escort · GitHub を書いてて、このライブラリは、
Travis CIでテストを動かして
Coveralls でコードカバレッジ をとっています。
NSDate-Escort を例にして設定を見ていきます。
iOSアプリのテストをTravis CIで動かす
CLIでテストを動かすには
xcodebuild
を直接使ってテストを走らせる方法と
xctool を使ってテストを走らせる方法がよく取られてると思います。
NSDate-Escort では、xctoolの設定ファイルである.xctool-argsにテストを動かす設定を書いておき、
単純に xctool test
を実行すればテストを走るようにしてあります。
また、この時にプロジェクトのschemeにxctool用のschemeを追加しておくといいです。
xctoolは Find Implicit Dependencies をサポートしていないため、自動でPodsのlinkしてくれないので、
xctool用のschemeにはTest Bundleより前に 依存関係となるPodsを追加しておきましょう。
後は、忘れずにxctool用のschemeにはSharedにチェックを入れておきます。
Sharedにチェックを入れると NSDate-Escort.xcodeproj/xcshareddata/xcschemes/xctool.xcscheme
という感じの場所にスキームが移動されるので、一般的なObjective-C.gitignoreを設定してるならGitで管理できる位置になると思います。
Schemeをちゃんと含めないとTravis CIからスキームがないといわれて xctool test
でエラーになったりします。
.travis.yml
Travis CIの設定は.travis.ymlファイルに書きますが、NSDate-Escortでは以下のような感じです。
language: objective-c
before_install:
- sudo easy_install cpp-coveralls
- brew update
- brew uninstall xctool # xctool 0.1.4 broken
- brew install https://raw.github.com/mxcl/homebrew/308395c2fc03399acbc24d226b8558f18e509e5b/Library/Formula/xctool.rb
- gem update cocoapods
script:
- xctool test GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES GCC_GENERATE_TEST_COVERAGE_FILES=YES
after_success:
- ./script/coveralls.sh -r ./ -e Pods -e Tests
xctool 0.1.4が壊れてる対処はそのうち要らないはず 又 gem update cocoapodsは別にいらなかったりするので最小限の場合だと以下で足りると思います。
language: objective-c
before_install:
- sudo easy_install cpp-coveralls
script:
- xctool test GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES GCC_GENERATE_TEST_COVERAGE_FILES=YES
after_success:
- ./script/coveralls.sh -r ./ -e Pods -e Tests
今のTravis CIは brew install xctool --HEAD
されたものがデフォルトで入ってるようです。。(なんでHEAD…)
before_install: after_success:
はCoveraills用なので、今は無視して
scriptで xctool test
を実行していることがわかれば十分です。
script:
- xctool test
これで、Travis CIでテストを動かす設定ができてるので後はTravis CIにログインしてGithubのレポジトリを選ぶだけです。
今回は Test Targetがひとつなので、”.xctool-args”に依存したものになっていますが、複数のTarget(iOS/Macとか)をやる場合はMakefileなどを書いて xctoolに引数を渡して実行されば問題ないと思います。
AFNetworking/Rakefile at master · AFNetworking/AFNetworking · GitHub ではRakefileを書いて、複数のTargetのテストを動かしています。
Coverallsにコードカバレッジを渡す
次に本題の Coveralls でコードカバレッジを取る方法。
若干語弊があって、コードカバレッジ自体はXcodeでビルドするときに、Build Settingの Generate Test Coverage Files と Instrument Program Flow をYESにしておくと、 gcno, gcda というファイルを吐き出してくれるので、このファイルをCoverallsに渡すことでCoverallsでコードカバレッジを閲覧できるようになります。
コマンドラインからこの設定を渡すには xctoo test GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES GCC_GENERATE_TEST_COVERAGE_FILES=YES
という感じで渡せば、プロジェクト側に設定しなくて有効に出来ます。
gcno, gcda から、 gcov
コマンドで .gcov
ファイルとして取得できるのでこれを使って、Coverallsにコードカバレッジの結果を送るようなスクリプトを作ります。
まとめると
xctool test(オプション付き) -> gcno, gcdaを生成 -> gcovコマンドで .gcov を生成
幸いにもgcovに対応したCoverallsのCLIであるcpp-coveralls があるので、これを利用します。(シェルスクリプトで無理やりやる方法もあるそうです PHP 拡張 (PECL) の開発で Coveralls を利用してみる|PHP|忍者ツールズ開発ブログ)
ここで先程の最小の.travis.ymlを振り返って見ると
language: objective-c
before_install:
- sudo easy_install cpp-coveralls
script:
- xctool test GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES GCC_GENERATE_TEST_COVERAGE_FILES=YES
after_success:
- ./script/coveralls.sh -r ./ -e Pods -e Tests
- before_install: で cpp-coveralls をインストールしています。
- script: で コードカバレッジファイルを作る設定をつけてテストを走らせます
- after_success: で コードカバレッジファイル を coveralls に送るシェルスクリプトを呼び出しています
(ここでの引数は、そのままcoveralls
コマンドに渡しています)
次に、 coveralls.sh を見ていきます。
以下の事をしたかったのでシェルスクリプトじゃなくてBashスクリプトですが…
#!/bin/bash
trim()
{
trimmed=$1
trimmed=${trimmed%% }
trimmed=${trimmed## }
<span class="nb">echo</span> <span class="nv">$trimmed</span>
}
# declare BUILT_PRODUCTS_DIR CURRENT_ARCH OBJECT_FILE_DIR_normal SRCROOT OBJROOT
declare -r xctoolVars=$(xctool -showBuildSettings | egrep '(BUILT_PRODUCTS_DIR)|(CURRENT_ARCH)|(OBJECT_FILE_DIR_normal)|(SRCROOT)|(OBJROOT)' | egrep -v 'Pods')
while read line; do
declare key=$(echo "${line}" | cut -d "=" -f1)
declare value=$(echo "${line}" | cut -d "=" -f2)
printf -v "trim ${key}
" "trim ${value}
" # https://sites.google.com/a/tatsuo.jp/programming/Home/bash/hentai-bunpou-saisoku-masuta
done < <( echo "${xctoolVars}" )
declare -r gcov_dir="${OBJECT_FILE_DIR_normal}/${CURRENT_ARCH}/"
## ======
generateGcov()
{
# doesn't set output dir to gcov…
cd "${gcov_dir}"
for file in ${gcov_dir}/*.gcda
do
gcov-4.2 "${file}" -o "${gcov_dir}"
done
cd -
}
copyGcovToProjectDir()
{
cp -r "${gcov_dir}" gcov
}
removeGcov(){
rm -r gcov
}
main()
{
# generate + copy
generateGcov
copyGcovToProjectDir
# post
coveralls ${@+"$@"}
# clean up
removeGcov
}
main ${@+"$@"}
おおまかにやっていることは
- コードカバレッジファイル(gcno, gcda) がある場所を見つける
gcov-4.2
コマンドで .gcov ファイルを作成する- プロジェクトファイルの所に、 .gcov ファイル をコピーする(cpp-coveralls の制約)
coveralls
コマンドで coveralls にコードカバレッジ情報を送信する
最初に 1. の コードカバレッジファイル(gcno, gcda) がある場所を見つけるために、
xctool -showBuildSettings
を利用しています。(xcodebuildにも同じようなのがあります)
これは、プロジェクトのBuild SettingをCLIから取得できるので、出された情報を元にコードカバレッジファイルの場所を取り出します。
そのまま値としては持ってなくて、恐らく ${OBJECT_FILE_DIR_normal}/${CURRENT_ARCH}/
のディレクトリに吐き出されているので、
その辺を処理してるのが while read line;
あたりでぐるぐるまわしながらとってきて $gcov_dir
に入れています。(もっとスッキリ書きたい…)
gcovコマンドは gcov
と gcov-4.2
があると思いますが、gcov-4.2の方を使って、コードカバレッジファイルを全部 .gcov
を作成しておきます。
そして、gcovファイルをコピーしてきて 4. で coveralls
コマンドを実行しています。
結果的には coveralls -r ./ -e Pods -e Tests
という感じで実行されているはずです。
これはrootを./にして再帰的に、コードカバレッジの対象ファイルを見つけるという感じで、-e
で指定するのは除外ディレクトリです。
-t
で coveralls のトークンを指定すればローカルでも動かせます(もしくは.coveralls.ymlを置く)が、Travis CIとcoveralls連携をしておくとトークンの指定はしなくても、Travis CIで走らせれば自動的にcoverallsに送ってくれます。
後は、coverallsにログインして使いたいGithubのレポジトリをチェックしておけば、
Travis CI -> テスト成功 -> coveralls.sh -> coverallsに送信
という事をやってくれて、以下のようにコードカバレッジをcoverallsで見ることができます。
おわり
これで、Travis CIでテストを走らせて、コードカバレッジをcoverallsで取る方法についての説明は終わりです。
シェルスクリプトとか結構雑なので、もっと良い書き方があればPull Request送りつけるとか記事を書いてくれるといいかと思います。
NSDate-Escort はコードカバレッジを100%にすることを目的に書き始めました。
おまけ
Travis CIやcoverallsのようにステータス画像を発行してくれるサービスが最近は多くなってます。
そういうサービスについて最近まとめたスライド書いてたので一緒に置いておきます。
Githubでコードを公開する時に便利なサービス – いまならBadge付き -
上記のスライドには入ってないですが、最近CocoaPodsのステータスバッジを取れるCocoapod Badgesというサービスもできてました。
お知らせ欄
JavaScript Primerの書籍版がAmazonで購入できます。
JavaScriptに関する最新情報は週一でJSer.infoを更新しています。
GitHub Sponsorsでの支援を募集しています。