-
Notifications
You must be signed in to change notification settings - Fork 337
11.2 GHUnitを用いた単体テスト
iOSにおけるロジックテストにはGHUnitを用いた単体テストについて紹介します。
iOSにおける単体テストの環境には
- GHUnit
- OCUnit
の二種類があります。それぞれを簡単に比較すると
OCUnit | GHUnit | |
---|---|---|
Xcodeによるサポート | あり | なし |
実行UI | Xcodeと統合 | GUI上で実行可能 |
setup/tearDown | なし | あり |
テストの範囲 | ロジックのみ | ロジックに加えて非同期やUIViewも可能 |
導入のしやすさ | 易 | やや難 |
CIからの実行 | 可能 | 可能 |
のようになります。コンパクトなテストを行う場合はOCUnitを、高機能なテスト環境を用いたい場合はGHUnitという使い分けができるかと思います。
この章では、公式クライアントアプリの開発でも用いてきたGHUnitについて、導入方法と利用方法を説明します。
導入には次のステップで行います。
- 新しいターゲットの追加
- CocoaPodsを用いてインストール
- Build Settingを修正
- main.mの修正
では順番に解説していきます。
すてにあるプロジェクトに対して追加する方法を解説します。
- メニューバーより File → New → Target として新規ターゲットを追加します。
- アプリケーションテンプレートはEmpty Templateを選択します
- ターゲット名はここでは UnitTest としました。それ以外の名称にした場合は適宜読み替えてください
ターゲットの追加が終わり次第、不要なファイルの削除とフレームワークの追加を行います。
- QuartzCore.frameworkを追加します
- プロジェクトの設定 → ターゲットをUnitTest → Link Binary With Libraries より 追加できます
- CocoaPodsでインストールしたときにQuartzCore.frameworkが必要となるためです
- UnitTest以下にあるAppDelegate.h, AppDelegate.m の削除
完了すると、以下のようになっていると思います。
(QuartzCore.frameworkはFramework以下に移動しても構いません)
GHUnitはCocoaPodsを用いてインストールできます。Podfileは以下のように書きます
platform :ios, '6.1'
target :UnitTest, :exclusive => true do
pod 'GHUnitIOS', '~> 0.5.5'
end
書けたら
pod install
でインストールします。完了したら .xcworkspace からプロジェクトを再起動します
シミュレータ上で動かすために、Build Settingに以下の修正を加えます。
Other Linker Flags に -ObjC -all_load
を追加します。
なお、-ObjC
は最初から入っていると思います。
ApplicationMainではなく、GHUnitのメインを立ち上げるために、UnitTest側のmain.mファイルを修正します。
int main(int argc, char *argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, @"GHUnitIOSAppDelegate");
}
}
四つ目の引数を @"GHUnitIOSAppDelegate"
に変えています。
ターゲットを UnitTest に変更して起動すると次のようにシミュレータが立ち上がれば成功です。
導入ができたらGHUnitを使ってテストを書き、実行してみましょう
今回はロジックであるMixiJankenDecider
についてテストを書きます。
まず、テストを書きたいファイルをテストターゲットに追加します。
テストを書きたいファイルを選び、Xcode右側のUtilities、Target Membership の UnitTestにチェックを入れます。
次にテストファイルを作成します。
新規ファイル作成画面へ移動し、GHTestCaseのサブクラスとしてクラスを作成し、TargetはUnitTestのみにチェックを入れて作成します。 ヘッダファイルは不要となるので削除してください。 そして、.mファイルに以下のテンプレートを貼付けます。クラス名などは適宜読み替えてください。
//
//
// Template of Unit Test Case
//
//
#import <GHUnit.h>
@interface MixiJankenDeciderTest : GHTestCase
@end
@implementation MixiJankenDeciderTest
- (void)testHogeFuga
{
}
@end
GHUnitではtestHogeFugaと書いたメソッドについてテストが実行されます。今回は試しに、ジャンケンの勝敗を決めるメソッドについてテストを書いてみます。
- (void)testJankenWithPeoples
{
MixiJankenPeople *alice = [[MixiJankenPeople alloc] init];
MixiJankenPeople *bob = [[MixiJankenPeople alloc] init];
MixiJankenPeople *winner;
alice.hand = JankenHandTypePaper;
bob.hand = JankenHandTypePaper;
winner = [MixiJankenDecider jankenWithPeoples:@[alice, bob]];
GHAssertNil(winner, @"あいこではnilが返ってくるべき");
alice.hand = JankenHandTypeScissors;
winner = [MixiJankenDecider jankenWithPeoples:@[alice, bob]];
GHAssertTrue(winner == alice, @"アリスが勝つべき");
}
Assertなどについては、また後ほど説明しますが、 GHAssertNil
では引数がnilであることを期待します。 GHAssertTrue
では引数がYESとなることを期待します。
それでは実行してみましょう。実行は、ターゲットをUnitTestに変更してRunすることで実行できます。
テストが失敗した時は、該当するテストの行が赤く表示されます
ここでは、GHUnitに関するいくつかのアサーションと、テスト開始前/後のメソッドについて紹介します。
アサーション | 解説 |
---|---|
GHAssertEquals(a1, a2, description, ...) |
a1とa2が同じであることを期待します。判断にはポインタを用います。 |
GHAssertNotEquals(a1, a2, description, ...) |
a1とa2が同じでないことを期待します。判断にはポインタを用います。 |
GHAssertEqualObjects(a1, a2, description, ...) |
a1とa2が同じであることを期待します。オブジェクトの構造を調べて同じかどうかを判断します。 |
GHAssertNotEqualObjects(a1, a2, desc, ...) |
a1とa2が同じでないことを期待します。オブジェクトの構造を調べて同じかどうかを判断します。 |
GHAssertGreaterThan(a1, a2, description, ...) |
a > b を期待します |
GHAssertLessThan(a1, a2, description, ...) |
a < b を期待します |
GHAssertEqualStrings(a1, a2, description, ...) |
文字列 a1とa2が同じであることを期待します |
GHAssertNotEqualStrings(a1, a2, description, ...) |
文字列 a1とa2が同じでないことを期待します |
GHAssertEqualCStrings(a1, a2, description, ...) |
Cの文字列char*について同じかどうかを比較します |
GHAssertNil(a1, description, ...) |
nilであることを期待します |
GHAssertNotNil(a1, description, ...) |
nilでないことを期待します |
GHAssertTrue(expr, description, ...) |
引数がYESであることを期待します |
GHAssertEqualsとGHAssertEqualObjectsについては以下のような違いがあります
NSArray *a = @[obj];
NSArray *b = @[obj];
GHAssertEquals(a, b, nil); // fail
GHAssertEqualObjects(a, b, nil); // OK
詳細についてはこちらをご覧ください
http://gabriel.github.io/gh-unit/docs/appledoc_include/guide_testing.html
あるテストクラスの開始前と後に、初期化や後片付けを行う時に呼び出されるメソッドがあります。
メソッド名 | 解説 |
---|---|
- (void)setUp |
各テスト実行前に呼ばれる |
- (void)tearDown |
各テスト実行後に呼ばれる |
- (void)setUpClass |
テストクラス実行前に呼ばれる |
- (void)tearDownClass |
テストクラス実行後に呼ばれる |
たとえば、あるデータをクラス内で共有して使いたい、何かしら副作用を生むのでその初期化を行いたいなどの場合に用います
Jenkins で定期的にテストを実行したい、と言った時にはXcodeからRunなどは難しいのでコマンドラインからテストを実行します。 次のステップで実行可能です
- ビルドスクリプトの追加
- main.mの修正
- xcodebuildを用いてコマンドラインからテストを実行
GHUnitのgithubよりビルドスクリプトを取得します。xcworkspaceやxcodeprojのあるディレクトリで
curl https://raw.github.com/gabriel/gh-unit/master/Scripts/RunTests.sh > RunTests.sh
としてスクリプトファイルを保存します。
次にプロジェクトの設定→ Build Phases より Add Build Phaseを選択して、スクリプトを追加します。
スクリプトには
sh RunTests.sh
と入力します。
次のコマンドをプロジェクトルートから実行します
GHUNIT_CLI=1 xcodebuild ONLY_ACTIVE_ARCH=NO -workspace <xcworkspaceファイルへのパス> -scheme UnitTest -configuration Debug -sdk iphonesimulator build
はじめに
-
導入
-
1.3 UIViewController1 UIViewController のカスタマイズ(xib, autoresizing)
-
UIKit 1 - container, rotate-
-
UIKit 2- UIView -
-
UIKit 3 - table view -
-
UIKit 4 - image and text -
-
ネットワーク処理
-
ローカルキャッシュと通知
-
Blocks, GCD
-
設計とデザインパターン
-
開発ツール
-
テスト
-
In-App Purchase
-
付録