-
Notifications
You must be signed in to change notification settings - Fork 336
11.2 GHUnitを用いた単体テスト
iOSにおけるロジックテストにはGHUnitを用いた単体テストについて紹介します。
iOSにおける単体テストの環境には
- GHUnit
- OCUnit
の二種類があります。それぞれを簡単に比較すると
OCUnit | GHUnit | |
---|---|---|
Xcodeによるサポート | あり | なし |
実行UI | Xcodeと統合 | GUI上で実行可能 |
setup/tearDown | なし | あり |
テストの範囲 | ロジックのみ | ロジックに加えて非同期やUIViewも可能 |
導入のしやすさ | 易 | やや難 |
CIからの実行 | 可能 | 可能 |
のようになります。コンパクトなテストを行う場合はOCUnitを、高機能なテスト環境を用いたい場合はGHUnitという使い分けができるかと思います。
この章では、公式クライアントアプリの開発でも用いてきたGHUnitについて、導入方法と利用方法を説明します。
追記 Xcode5よりサポートされるテストフレームワークがOCUnitからXCTestへと変化しました。 XCTestはXcodeのサポートもあるため簡単に導入することができます。実際、新しいプロジェクトを作成したときに 自動的にXCTestのターゲットが追加されています。 このページの文末にXCTestを用いた単体テストの書き方について記述しています。
導入には次のステップで行います。
- 新しいターゲットの追加
- 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
XCTestはXcode5よりOCUnitの代わりに用いられるようになった単体テストフレームワークです。 Xcodeで新規プロジェクトを作成した時に自動的にテストターゲットに追加されているため導入の手間はほとんどありません。
リファレンスや使い方については iOS Developer Library — Pre-Release Testing with Xcode をごらんください
以下ではXcodeでプロジェクトを作った時にできるスケルトンを用いてテストの全体を解説します。
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
// テストクラスは XCTest のサブクラスにします
@interface MyAppTests : XCTestCase
@end
@implementation MyAppTests
// 各テスト実行前にこの setup が実行されます。
// テストを行う前に共通する準備が必要な場合はここに記述します。
- (void)setUp {
[super setUp];
}
// 各テスト実行後にこの tearDown が実行されます。
// テストで何か副作用を産むコードがある場合(一時ファイルを作るなど)はここで後片付けをします。
- (void)tearDown {
[super tearDown];
}
// 各テストのメソッドです。
// `test` で始まるメソッドがテストのメソッドとして自動的にテストランナーに登録されて実行されます。
- (void)testExample {
// アサーションは XCTAssert... のような記述となります。
XCTAssert(YES, @"Pass");
}
// パフォーマンステストも行うことができます。
- (void)testPerformanceExample {
[self measureBlock:^{
// このブロック内に計測したい処理を入れると
// 実行時間を計測してくれます。
}];
}
@end
XCTAssert(YES, @"Pass");
のように書かれているアサーションが条件を満たすと各テストは成功とみなされ、
すべてのアサーションが成功すると全体でテストは成功になります。
テストの実行はCmd+Uやメニューの Product → Run から実行することができます。 実行するとシミュレータが立ち上がり、各テストが実行されます。
テストは各テストメソッドごと、テストクラスごとに実行することもできます。 Xcode左ペインのTest navigatorや行番号にあるひし形をクリックするとそのテスト範囲だけ実行することができます。
単体テストで満たすべき条件を記述していくのがアサーションになります。 以下に主要なものを解説します。
すべてのアサーションについてはiOS SDKのXCTestAssertions.h に記述してあります。
アサーション | 解説 |
---|---|
XCTAssertNil(obj) |
objがnilであることを期待します |
XCTAssertTrue(条件文) |
条件文が真になることを期待します |
XCTAssertFalse(条件文) |
条件文が偽になることを期待します |
XCTAssertEqual(a, b) |
a == b となることを期待します. オブジェクト同士の比較ではなく、プリミティブの比較を行います |
XCTAssertEqualObjects(a, b) |
aとbが同じオブジェクトであることを期待します. 例えば XCTAssertEqualObjects(@"foo", @"foo") とすると真になります |
XCTAssertThrows(実行文) |
実行結果が例外を投げることを期待します |
基本的な使い方はOCUnit/GHUnitとほぼ同様です。
はじめに
-
導入
-
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
-
付録