Objective-C (iOS): XCTest での単体テスト

Xcode 5 からは XCTest というテストフレームワークが使えます。
XCTest フレームワークとテストコードは、Xcode 5 で新プロジェクトを作成すると勝手に追加されています。

■ XCTest で単体テストする方法

プロジェクト作成時にできる {プロジェクト名}Test.m に単体テスト用のコードを書いていきます。

xctest1

「- (void)テストメソッド名」でテスト用のメソッドを作成して、そこに書いていきます。

xctest3

プロジェクトペインの上にある、このマークをクリックすると、

xctest2

テストメソッドが列挙されているペインが開きます。
テストナビゲーターと呼ぶそうです。

xctest4

ここのプロジェクトにマウスをホバーしたときに出る三角マークをクリックすると、すべてのテストメソッドが実行されます。

xctest5

しかしこのままだと、

- (void)testExample
{
    XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__);
}

という部分でエラーになっています。
『何もこの関数に書かれてないよ!』というアサーションが発生します。

xctest6

とりあえずアサーション発生部分をコメントアウトして、

- (void)testExample
{
//    XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__);
}

三角マークを押します。

xctest5

チェックが入ってテストメソッドをクリアしたことがわかります。

xctest7

■ 単体テストする理由

この要領で、いろんなテストコードを書いておいて、適宜実行し、メインプログラムの実装が進んできた時点でロジックがデグレっていないか、各モジュール単位で要件を満たしているかなどをチェックする単体テストを行います。

バグの発見、未実装部分の穴埋め、納品要件を満たしているかのチェックが行えます。

また、基本的にこの方式は MVC の部分の M の部分を中心にテストすることになります。
Apple では MVC でプログラムは分離し、特にロジック部分の M と表示部分の V はこういう観点からも切り離すことを推奨しています。

というわけで iOS アプリ開発の流れは、インターフェースを定義して、XCTest でテストコードを作成。メインプログラムを実装していき、テストコードを実行しつつ、iOS シミュレータで触診して、完璧になったら App Store にサブミット、がよさそうですね。
(この流れは iOS アプリだけでなくてもだけど。)

■ テスト例

奇数かどうかを判定する機能を持ったクラスが欲しいという要件があるとします。
そんなユーティリティクラスを作成しました。

Objective-C: Utilities.h

#import <Foundation/Foundation.h>

@interface Utilities : NSObject

+ (BOOL)isOdd:(NSInteger)number;

@end

Objective-C: Utilities.m

#import "Utilities.h"

@implementation Utilities

+ (BOOL)isOdd:(NSInteger)number {
    if (number % 2 == 0) {
        return false;
    } else {
        return true;
    }
}

@end

テストクラスをこんな感じで作ります。
10 を入れた時は True が帰らないので、アサーションが発生することを期待したテストコードを書きます。

#import <XCTest/XCTest.h>
#import "Utilities.h"

@interface EmptyProjectFirstTests : XCTestCase

@end

@implementation EmptyProjectFirstTests

- (void)setUp
{
    [super setUp];
    // Put setup code here. This method is called before the invocation of each test method in the class.
    
}

- (void)tearDown
{
    // Put teardown code here. This method is called after the invocation of each test method in the class.
    [super tearDown];
}

- (void)testOdd
{
    XCTAssertTrue([Utilities isOdd:301]);
    XCTAssertTrue([Utilities isOdd:-101]);
    XCTAssertTrue([Utilities isOdd:10]);
}

@end

テストナビゲーターには testOdd が追加されています。
三角ボタンを押すと testOdd が実行されます。

XCTAssertTrue([Utilities isOdd:10]);

ここで failed になり、テストが停止します。

おすすめ記事
http://www.slideshare.net/basuke/do-you-test-your-code
Xcode 4 時代のテスト手法ですが、単体テストと周辺のプログラミングに関する解説が非常にわかりやすいです。