WWDC開幕で、話題の中心はもっぱらiOS 6、Mountain Lion、Retina対応Macですが、気にせずテストについて書きます。
2012年6月現在、純正のSenTestingKit以外にも多数のiOSアプリ用テストフレームワークが存在します。今まで試してきた中で、おすすめのテストフレームワークをいくつか紹介します。
標準のテストフレームワーク SenTestingKit / OCUnit
Xcodeに標準で付属しているテストフレームワークです。iOSアプリのテストを初めて学ぶには、素直にSenTestingKitから始めた方がよいと思いますが、非同期処理やUIを含むテストは面倒・困難です。Google Toolbox for Macを使って少し拡張することもできます。
長所
- 標準で用意されているので導入が楽。
- シンプルなので理解しやすい。
- 他のフレームワークのメリット・デメリットを判断する上での一つの基準がわかる。
短所
- 非同期処理が面倒。特にメインスレッドのランループは仕組み上またげない。具体的な面倒臭さについては、過去の記事参照。
- UI関連のテストは困難。
エレガントなKiwi
allending/Kiwi
rspec風にテストをかける事が特徴のテストフレームワークです。SenTestingKit同様メインスレッドのランループはまたげませんが、非同期処理系のテストも書きやすくなっています。
SPEC_BEGIN(MyFooClassSpec)
describe(@"MyFooClass", ^{
beforeAll(^{
[MyFooClass clearAll];
});
it(@"bar api should return anObject within 10 seconds", ^{
__block id someObject = nil1;
[MyFooClass barWithBlock:^(id obj) {
someObject = [obj retain];
}];
[[expectFutureValue(theValue(someObject)) shouldEventuallyBeforeTimingOutAfter(10.0)] beNonNil];
if (someObject) [someObject release];
});
});
SPEC_END
とにかくテストコードが書きやすく読みやすくなるのが特徴です。
アサーションの書き方が他のテストフレームワークと大きく異なり、それぞれのアサーションごとにはメッセージを指定しないような形になるため、テストコードの分量が減って保守性・可読性があがります。失敗したときに問題がおきた箇所を特定する作業は、個別の行ごとにメッセージを出す他のフレームワークに比べると少しだけ面倒ですが、それを踏まえてもテストコードの管理コストが小さいというのは魅力的です。(テストコードの管理コストについてはまた別の機会にでも…)
長所
- 非同期処理のテストが書きやすい。
- テストコードの保守性、可読性が高い。
短所
- メインスレッドのランループはまたげない。
- たまに自動補完がおかしくなる。
- 若干最初の導入が面倒。
多機能なGHUnit
gabriel/gh-unit
エレガントなKiwiに対して、とにかく高機能なのがGHUnitです。メインループをまたいだ非同期処理、JUnit xmlでのテスト結果書き出し、コマンドラインからの実行…等他のフレームワークではできないことが沢山あります。これらの特徴から、Jenkinsとの相性が良いです。
@interface MyAsyncTest : GHAsyncTestCase { }
@end
@implementation MyAsyncTest
- (void)testSuccess {
// Prepare for asynchronous call
[self prepare];
// Do asynchronous task here
[self performSelector:@selector(_succeed) withObject:nil afterDelay:0.1];
// Wait for notify
[self waitForStatus:kGHUnitWaitStatusSuccess timeout:1.0];
}
- (void)_succeed {
// Notify the wait. Notice the forSelector points to the test above.
// This is so that stray notifies don't error or falsely succeed other tests.
// To ignore the check, forSelector can be NULL.
[self notify:kGHUnitWaitStatusSuccess forSelector:@selector(testSuccess)];
}
@end
GHAsyncTestCase Class Referenceから転載
ただし、Kiwiほどのエレガンスさはなく、高機能ではありますがテストの記法に若干無理矢理な部分があったり、Xcodeと統合できない(Cmd + Uによる実行ができない)等の難点もあります。
長所
- とにかく多機能で他のテストフレームワークにできないがGHUnitではできることが沢山ある。
短所
- Xcodeと統合されていない。
- Kiwiほどのエレガントさはない。
- Kiwi同様、若干最初の導入は面倒。
比較とまとめ
他にもUIのテストに特化したUISpecや古くからユーザのいる国産のテストフレームワークiUnitTest等がありますが、最近の開発の頻度と使い勝手を考えると今の段階ではKiwiかGHUnitがおすすめです。
GHUnitは確かに高機能で、テストのカバー率をあげたければこれを使うのが一番ですが、個人的には継続的・効率的にテスト駆動開発を進めていくにはテストケースを書く時のスピードと管理コストの小ささが重要だと考えており、Kiwiをおすすめします。
前述の通り、Kiwiではメインループをまたいだ処理のテスト、UI関連のテストを書けないという制限があるため、既存のプロジェクトにKiwiを使ったテストを導入していくと不都合な点がでてくるかもしれません。既存のプロジェクトに新たにテストを広範囲で導入していきたければ、GHUnitの方が都合がよい場面が多いでしょう。
個人的には、新しいプロジェクトをテスト駆動開発で進める際に、Kiwiの制限を考慮した上でKiwiでテストコードから書いていくことで、低コスト・効率的にテスト駆動開発を実現していく、というのがおすすめです。