CocoaPodsでGHUnit + OCMockなiOS開発環境を構築する
なにこれ
新しいプロジェクト作るたびにいつも構築手順を忘れてるのでメモ。 おまけでコンソールからのテスト実行(Jenkinsとの統合が目的)のやり方も書いた。
対象者
- Objective-CによるiOS開発者
- CocoaPodsを知っている・使いたい
- GHUnitを知っている・使いたい
- (JenkinsでCIしたい)←必須じゃない
検証環境
2012年12月9日時点で最新のもの。
手順
1. Xcodeでプロジェクト作成
Unit TestはGHUnitを使うので不要。
このドキュメントでは MyProject
とする。
2. 新しいターゲット作成
プロジェクト設定を開いて、テスト実行用のターゲットを作成する。
ターゲットの種類はiOSのEmpty Application、名前は Tests
にしておく。
名前は変更してもいいけど、以下の説明で Tests
と出てきたら自分で読み替えること。
自動生成されるファイルのうち、次のものだけ残して全部消す(ファイルごと)
Tests-Info.plist
InfoPlist.strings
main.m
Tests-Prefix.pch
3. Podfile作成
MyProject.xcodeproj
と同じ所に Podfile
を作成する。
platform :ios, '5.0' pod 'AFNetworking', '~> 1.0.1' pod 'CocoaLumberjack', '~> 1.6' target :Tests, :exclusive => true do pod 'GHUnitIOS', '~> 0.5.5' pod 'OCMock', '~> 2.0.1' end
一行目でiOS 5.0以上を指定しているが、実際には作成するプロジェクトに合わせて指定する。
AFNetworkingとCocoaLumberjackは実際に作ったPodfileから取ってきただけのただのサンプル。実際に必要な物を指定して欲しい。
このとき target :Tests
を2.で作ったターゲット名にするのを忘れないこと。
終わったらコンソールからworkspaceを作成してもらう。
pod install
4. xcodeproj閉じてxcworkspace開く
両方同じxcodeprojを参照するのでわけわからないことになる前に xcodeproj
を閉じる。
5. ターゲット Tests
の Build Settings
いじる
Other Linker Flags
に -ObjC
-all_load
を追加する。
俺の環境では -ObjC
はすでに追加されていた。
6. main.m
いじる
// // main.m // Tests // // Created by <ユーザ名> on 12/8/12. // Copyright (c) 2012 <組織名>. All rights reserved. // #import <UIKit/UIKit.h> int main(int argc, char *argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, @"GHUnitIOSAppDelegate"); } }
ここまでやってiPhone Simulatorで起動するとGHUnitのテスト実行画面が表示される。
7. ユニットテスト作ってみる
GHUnit
ここまでうまくいってるか確認するために一個テスト作ってみる。
Tests
に一個Objective-Cのクラスを作る。
GHUnitのガイド にあるコードのままだとコンパイルが通らないのでちょいと手直し。
MyTest.h
#import <GHUnitIOS/GHUnit.h> @interface MyTest : GHTestCase @end
MyTest.m
#import "MyTest.h" @implementation MyTest - (void)testStrings { NSString *string1 = @"a string"; GHTestLog(@"I can log to the GHUnit test console: %@", string1); // Assert string1 is not NULL, with no custom error description GHAssertNotNil(string1, nil); // Assert equal objects, add custom error description NSString *string2 = @"a string"; GHAssertEqualObjects(string1, string2, @"A custom error message. string1 should be equal to: %@.", string2); } @end
修正前はGHAssertNotNULLを使ってたのでGHAssertNotNilに変えた。
もっかいiPhone Simulator起動してみてテストできることを確認する。
OCMockも確認する
MyMockTest.h
#import <GHUnitIOS/GHUnit.h> #import <OCMock/OCMock.h> @interface MyTest : GHTestCase @end
MyMockTest.m
#import "MyMockTest.h" @implementation MyMockTest - (void)testMock { id mock = [OCMockObject mockForClass:[NSString class]]; [[[mock stub] andReturnValue:OCMOCK_VALUE((NSUInteger){100})] length]; GHAssertEquals(100U, [mock length], @"ocmock works"); } @end
iPhone Simulator起動してみてテストできることを確認する。
8. コンソールからテスト実行してみる
Jenkins用なんで興味ない人はスルー。 基本は GHUnitのガイド 通り。
i. シェルスクリプトを取ってくる
xcodeproj, xcworkspaceがあるところで以下を実行。
wget https://raw.github.com/gabriel/gh-unit/master/Scripts/RunTests.sh wget https://raw.github.com/gabriel/gh-unit/master/Scripts/RunIPhoneSecurityd.sh
ii. テスト実行時にスクリプトを実行するようにする
プロジェクト設定内のTests
のBuild Phases
を開き、Add Build Phase
-> Add Run Script
を選択。
入力欄にsh RunTests.sh
と入れる。
iii. Makefile
GHUnitのドキュメントにはworkspaceのときの例がないのでそのままだと動かない。 適当に作ってみる。
clean: -rm -rf build/* test: GHUNIT_CLI=1 xcodebuild ONLY_ACTIVE_ARCH=NO -workspace <xcworkspaceファイルへのパス> -scheme Tests -configuration Debug -sdk iphonesimulator build
ワークスペースへのパスに書き換えるのを忘れないこと。 連続空白に見えるところはタブ文字にすること。
iv. (Base SDKが6.0のときのみ) main.mの修正
2012年12月9日現在、iOS SDKが6.0だと、コンソールから動かせない問題がある(GHUnitのIssue at GitHub)。 暫定措置だが一番下のgfxさんのコメント通りにmain.mを書き換えるとコンソールから動かせるようになる。
v. テストを実行する
make test
echo $?
して0だったら、おめでとう。すべてのテストをパスしたぞ!
9. Jenkinsからテスト実行
まず前章で作ったRunTests.sh
, RunIPhoneSecurityd.sh
, Makefile
をgit commitしておく。
公開前提のプロジェクトだと、git cloneしたユーザにpod install
を実行させるのでxcworkspaceをソースコードリポジトリ管理しないと思う。
なのでその流儀に則り、Jenkinsでもpod install
することにする。
i. ジョブを作る
名前はMyProject
にしてみた。フリースタイル・プロジェクトを選択。
ii. 設定
ソースコード管理システムでソースの位置を指定する。
俺はローカルホストにJenkinsを立ててるので、GitのRepository URLがfile:///
からはじまる。
ビルドに シェルの実行
を追加する。
WORKSPACE_FILE=MyProject.xcworkspace if [ ! -d "$WORKSPACE_FILE" ]; then pod install open -R "$WORKSPACE_FILE" echo "We need to init the workspace file. Please try re-run test after you open workspace file." exit 1 fi make clean && WRITE_JUNIT_XML=YES JUNIT_XML_DIR=tmp/test-results make test
最初のビルドではxcworkspaceの初期化が終わっていないため、
xcodebuild: error: The workspace 'MyProject' does not contain a scheme named 'Tests'.
と失敗してしまう。
少し悩んだが、ユーザにXcodeでxcworkspaceを開かせることにした。 一度Xcodeで開いてしまえばもうこの作業はやらなくてOKだ。
※open -R <ファイルパス>
はFinderでファイルを表示するコマンド。
ビルド後の処理
から JUnitテスト結果の集計
を選び、tmp/test-results/*.xml
と指定する。
ビルドしてみると一回目は失敗すると思う。 xcworkspaceがFinderで表示されていると思うので、ダブルクリックして初期化してから再度ビルドすればテストが実行されるはずだ。
このへんはバッドノウハウなんで、もっといい方法あるって人は教えて欲しい。
サンプル
https://github.com/mtgto/CocoaPodsSample
ここまでの説明があってるかの確認のために作ってみた。
参考
全部英語のページ。