Kiwiをjenkinsで動かしてCIする

今までGHUnitを使っていたのだけど、久しぶりに新しいプロジェクトで使おうとしたらXcode4.5以降でCLIから実行ができないバグがあって困った。
githubを見ると、Oct 25, 2012でコミットが途絶えている。

ちょっと悩んだけど、せっかくなので別のテストフレームワークを試してみることにした。
最近(今頃になって)RSpecすごいなーと思うことがあったので、BDDなKiwiを使ってみることにした。

半日くらいハマりながら、jenkinsで動かす所まで来たのでメモ。

kiwiを動かす

まずはkiwi wikiを参考に、kiwiを動かせるようにする。
https://github.com/allending/Kiwi/wiki/Getting-Started-with-Kiwi-2.0

環境は、Xcode4.6.1

Cocoa Podsが入っている前提で。
kiwiSampleという新規プロジェクト作成。
プロジェクト作成時に、include unit testsにチェックを入れておくと、TARGETSにkiwiSampleTestsというユニットテストターゲットができている。
この状態で、cmd+Uすると単体テストが実行される。

次に、Xcodeを閉じる。
Podfileという名前のファイルを、kiwiSample.xcodeprojと同じ階層(プロジェクトのroot)に作成する。
中身↓

# Podfile

# Select the appropriate platform below
platform :ios
# platform :osx

#
# Some other entries might already exist in the file
# ...
#

# Add Kiwi as an exclusive dependency for the AmazingAppTests target
target :kiwiSampleTests, :exclusive => true do
   pod 'Kiwi'
end

保存したら、pod install

$ pod install                                                                                                          (master✱)
Analyzing dependencies

Cocoapods 0.18.1 is available.

Downloading dependencies
Installing Kiwi (2.0.6)
Generating Pods project
Integrating client project

[!] From now on use `kiwiSample.xcworkspace`.

[!] The target `kiwiSampleTests [Debug]` overrides the `FRAMEWORK_SEARCH_PATHS` build setting defined in `Pods/Pods-kiwiSampleTests.xcconfig'.
    - Use the `$(inherited)` flag, or
    - Remove the build settings from the target.

[!] The target `kiwiSampleTests [Debug - Release]` overrides the `FRAMEWORK_SEARCH_PATHS` build setting defined in `Pods/Pods-kiwiSampleTests.xcconfig'.
    - Use the `$(inherited)` flag, or
    - Remove the build settings from the target.

入った。

プロジェクトrootにkiwiSample.xcworkspaceというファイルができているので、開く。
この状態でcmd+Uすると、自分の環境ではエラーが出た。

clang: error: no such file or directory: '/Users/kenjikinukawa/Library/Developer/Xcode/DerivedData/kiwiSample-dmefjsutstmowmevdnpeufzrwtvw/Build/Intermediates/PrecompiledHeaders/Pods-kiwiSampleTests-prefix-ekzmocesgielqsbgusghojymigra/Pods-kiwiSampleTests-prefix.pch.dia'
Command /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang failed with exit code 1

試行錯誤の末見つけた解決方法は、
http://stackoverflow.com/questions/9457510/apple-llvm-compiler-3-1-error-clang
で、具体的には、
f:id:k_kinukawa:20130412001218j:plain
上の写真のOther C FlagsのDebugの方に何も書いてないので、-DNS_BLOCK_ASSERTIONS=1を入れてやる。
すると、cmd+Uでテストが実行できるようになる。
kiwiSampleTestフォルダの中に、SampleSpec.mというファイルを作成する。
中身は↓。

#import "Kiwi.h"

SPEC_BEGIN(MathSpec)

describe(@"Math", ^{
    it(@"is pretty cool", ^{
        NSUInteger a = 16;
        NSUInteger b = 26;
        [[theValue(a + b) should] equal:theValue(43)];
    });
});

SPEC_END

cmd+Uでテストが実行され、[FAILED], expected subject to equal 43, got 42 というエラーが発生する。
これでkiwiを動かす準備は整った。

jenkinsでテストする

jenkinsは動いている前提で。
http://9elements.com/io/index.php/continuous-integration-of-ios-projects-using-jenkins-cocoapods-and-kiwi/
を参考に。

kiwiSampleTestsのBuild PhasesでRun Scriptの中身を
https://gist.github.com/mbinna/4492087
に書き換える。

f:id:k_kinukawa:20130412002605j:plain
kiwiSampleをDuplicate > Duplicate onlyで複製して、名前をCommandLineUnitTestsに変更する。
Build Settingsの検索窓でkiwiSample copyで検索するとproduct name等が引っかかるので、CommandLineUnitTestsに修正する。
XcodeのProduct > Scheme > Manage Schemesを開くと、kiwiSample copyがあるのでここもなおす。

XcodeのProduct > Scheme > Edit Schemesを開く。
f:id:k_kinukawa:20130412003512j:plain
左のBuildを選択した状態で、下の方の+を押す。
kiwiSampleTestsを選択してAdd。

コマンドラインからXcodeのテストを呼び出す方法として、シミュレーターを利用する手があるらしく、参考ページそのままios-simを使う。

$ brew install ios-sim

ここまで来たら、コマンドラインからkiwiを実行できる。

xcodebuild -sdk iphonesimulator \
             -workspace kiwiSample.xcworkspace \
             -scheme CommandLineUnitTests \
             -configuration Debug \
             RUN_APPLICATION_TESTS_WITH_IOS_SIM=YES \ ONLY_ACTIVE_ARCH=NO clean build 2>&1

ビルドが走り、ログの途中にテスト結果が出力されている。

/bin/sh -c /Users/kenjikinukawa/Library/Developer/Xcode/DerivedData/kiwiSample-dmefjsutstmowmevdnpeufzrwtvw/Build/Intermediates/kiwiSample.build/Debug-iphonesimulator/kiwiSampleTests.build/Script-9AB6D732171706DC00791D61.sh
[DEBUG] Test Suite 'All tests' started at 2013-04-11 15:41:13 +0000

[DEBUG] Test Suite '/Users/kenjikinukawa/Library/Developer/Xcode/DerivedData/kiwiSample-dmefjsutstmowmevdnpeufzrwtvw/Build/Products/Debug-iphonesimulator/kiwiSampleTests.octest(Tests)' started at 2013-04-11 15:41:13 +0000
Test Suite 'KWSpec' started at 2013-04-11 15:41:13 +0000
Test Suite 'KWSpec' finished at 2013-04-11 15:41:13 +0000.
Executed 0 tests, with 0 failures (0 unexpected) in 0.000 (0.000) seconds
Test Suite 'KWTestCase' started at 2013-04-11 15:41:13 +0000

[DEBUG] Test Suite 'KWTestCase' finished at 2013-04-11 15:41:13 +0000.
Executed 0 tests, with 0 failures (0 unexpected) in 0.000 (0.000) seconds
Test Suite 'MathSpec' started at 2013-04-11 15:41:13 +0000

[DEBUG] Test Case '-[MathSpec Math_IsPrettyCool]' started.

[DEBUG] 2013-04-12 00:41:13.702 kiwiSample[53905:c07] + 'Math, is pretty cool' [PASSED]

[DEBUG] Test Case '-[MathSpec Math_IsPrettyCool]' passed (0.027 seconds).

[DEBUG] Test Suite 'MathSpec' finished at 2013-04-11 15:41:13 +0000.
Executed 1 test, with 0 failures (0 unexpected) in 0.027 (0.028) seconds
Test Suite 'kiwiSampleTests' started at 2013-04-11 15:41:13 +0000

[DEBUG] Test Case '-[kiwiSampleTests testExample]' started.
Test Case '-[kiwiSampleTests testExample]' passed (0.000 seconds).
Test Suite 'kiwiSampleTests' finished at 2013-04-11 15:41:13 +0000.
Executed 1 test, with 0 failures (0 unexpected) in 0.000 (0.000) seconds
Test Suite '/Users/kenjikinukawa/Library/Developer/Xcode/DerivedData/kiwiSample-dmefjsutstmowmevdnpeufzrwtvw/Build/Products/Debug-iphonesimulator/kiwiSampleTests.octest(Tests)' finished at 2013-04-11 15:41:13 +0000.
Executed 2 tests, with 0 failures (0 unexpected) in 0.028 (0.029) seconds

[DEBUG] Test Suite 'All tests' finished at 2013-04-11 15:41:13 +0000.
Executed 2 tests, with 0 failures (0 unexpected) in 0.028 (0.030) seconds

Finished running tests with ios-sim

あとはjenkinsでgitからcloneしてきて上のコマンドを実行すれば自動的にビルドとテストが走る。
ただ、このままだとテストがコケた時にもjenkinsは検知してくれない。
参考ページの最後の方に書いてある、OCUnit2JUnitを入れた。
プロジェクトrootにGemfileを作成。

source 'https://rubygems.org'

gem 'ocunit2junit', '~>1.2'

ruby1.9が入っている環境で、bundle installすると、OCUnit2JUnitがインストールされる。
この状態で、先ほどのスクリプトを書き換えてjenkinsに実行させる。

#!/usr/bin/env bash

bundle install

xcodebuild -sdk iphonesimulator \
           -workspace kiwiSample.xcworkspace \
           -scheme CommandLineUnitTests \
           -configuration Debug \
           RUN_APPLICATION_TESTS_WITH_IOS_SIM=YES \
           ONLY_ACTIVE_ARCH=NO \
           clean build 2>&1 | bundle exec ocunit2junit

ビルド後の処理のJUnitテスト結果の集計に、test-reports/*.xmlを書いて保存する。
テストの成功、失敗がグラフで出力されるようになる。

思ったこと

ios-simが出てきたあたりで、あれ、これってもしかしてGHUnitもコマンドラインからGUIテスト起動できるんじゃないかな、と思ったけど試していない。
あと、ios-sim使って動いているせいか、試しにテストの中でUIFontのインスタンスを作成しても問題ありませんでした。
実は、GHUnitをCLIから実行していた時、UIKitのテストを実行するとクラッシュしてました。
これは、UIKitがシミュレーターに依存しているからで、同じくシミュレーターに依存しているKeychainなんかもGHUnit CLIからいじることができませんでした。
GUIからは問題なく実行できるのですが、いちいち手動でテスト動かすのは非常に面倒でした。
ios-simが裏で動いているためなのか、もしかすると上のような制限はなくなるかもしれません。
もしそうならとても嬉しい!
これからガッツリ試します。

なんか、長いことモヤモヤしていたことも解決されそうで、明日からスッキリ開発を進められそうです。