こんにちは、 株式会社ジェネシックス の徐 廷(@TonnyXu)です。iPhoneの共通モジュールのベストプラクティスに関してです。
 文章が長くなりますので、5回に分けてお届けします。

今回は、5回目となります。

その1

■ 共通プロジェクトを作る

その2

■ コンシューマPJを作る

■ コンシューマPJで共通プロジェクトを引用

その3

■ 引用する後コンシューマPJでの必須設定

その4

■ コンシューマPJを実行する

その5

■ さらに

・ 共通プロジェクトに単体テストを追加

・ オープンソースライブラリを作る

■ さらに

コード品質を向上するために、単体テストは不可欠です。特に共通モジュールでバグがあると、影響は非常に大きなものになるので、単体テストをしっかり作ったほうが良いと思います。

・ 共通プロジェクトに単体テストを追加

iPhone SDKは3.0から、単体テストをサポートしています。では、共通モジュールに追加してみましょう。


adding lib test target.
図23:共通プロジェクトにテストtargetを追加

新しいTargetのタイプは「Unit Test Bundle」です。名前は「LogicTests」にしましょう。まずは図24のように、テストTargetの依存性を指定しましょう。


adding dependency for test target.
図24:テストTargetの依存関係画面

図24の依存関係の下の「+」ボタンを押してください。


select dependency module for test target.
図25:共通モジュールを依存先として追加

これで、単体テストのtargetができました。次はテストケースを追加しましょう。Testというグループを作って、右クリックして、Add New Files...を選択してください。


add test case files.
図26:テストケースを選択


name the test case.
図27:テストケースを追加

注意 所属targetはLogicTest *のみを選択してください。

テストケースのテンプレートが自動的に適用されます。下記のようなヘッダーファイルとコードファイルが自動的に生成されます。

GCDeviceTests.h
//
//  GCDevicesTests.h
//  Common
//
//  Created by Tonny Xu on 10/05/26.
//  Copyright 2010 genesix, Inc. All rights reserved.
//
//  See Also: http://developer.apple.com/iphone/library/documentation/Xcode/Conceptual/iphone_development/135-Unit_Testing_Applications/unit_testing_applications.html

//  Application unit tests contain unit test code that must be injected into an application to run correctly.
//  Define USE_APPLICATION_UNIT_TEST to 0 if the unit test code is designed to be linked into an independent test executable.

#define USE_APPLICATION_UNIT_TEST 0

#import <SenTestingKit/SenTestingKit.h>
#import <UIKit/UIKit.h>
//#import "application_headers" as required

@interface GCDevicesTests : SenTestCase {

}

#if USE_APPLICATION_UNIT_TEST
- (void) testAppDelegate;       // simple test on application
#else
- (void) testMath;              // simple standalone test
#endif

@end


GCDeviceTests.m
//
//  GCDevicesTests.m
//  Common
//
//  Created by Tonny Xu on 10/05/26.
//  Copyright 2010 genesix, Inc. All rights reserved.
//

#import "GCDevicesTests.h" 

@implementation GCDevicesTests

#if USE_APPLICATION_UNIT_TEST     // all code under test is in the iPhone Application

- (void) testAppDelegate {

    id yourApplicationDelegate = [[UIApplication sharedApplication] delegate];
    STAssertNotNil(yourApplicationDelegate, @"UIApplication failed to find the AppDelegate");

}

#else                           // all code under test must be linked into the Unit Test bundle

- (void) testMath {

    STAssertTrue((1+1)==2, @"Compiler isn't feeling well today :-(" );

}

#endif

@end

ヘッダーファイルの「USE_APPLICATION_UNIT_TEST」を0に設定しましょう。この時点では本番のテストケースをまだ書いていませんが、テストを実行することはできます。では、プロジェクトの実行targetを下図のようにLogicTestに変更しましょう。


select new target.
図28:テストケースのtargetに設定

「テストケースを実行する」というより実はコンパイルするだけです。Active targetをLogicTestに設定したら、コンパイル(Build→build)してみましょう。間違っていなければ、ビルド結果画面を開く(Build->Build Results)と、下図のような画面が出てきます。


compile test target and get the test result.
図29:テストケースの実行結果

クラスGCDeviceをテストするために、GCDevice.mファイルをLogicTests Targetに追加しなければいけません。GCDevice.mファイルを右クリックして, 出てくるメニューのGet Infoをクリックしましょう。下図の画面が出てきます。


add .m files to test target.
図30:テスト対象クラスの.mファイルをテストtargetに追加

図30のように、二つのtargetの左側のチェックボックスを全部チェックしてから、GCDeviceTests.mファイルに新しいテストケースを追加しましょう。

GCDeviceTests.h
//...上省略
#else
- (void) testMath;              // simple standalone test
- (void) testIsIPad;
#endif
//...下省略

GCDeviceTests.m
//...上省略
- (void) testMath {

    STAssertTrue((1+1)==2, @"Compiler isn't feeling well today :-(" );

}

- (void) testIsIPad{
    STAssertFalse( [GCDevice isIPad], @"単体テストとしてはFalseしかない.");
}
//...下省略

追加したテストケースの実行結果を見てみましょう。


new test case running result.
図31:testIsIPadのテストケース実行結果

これで、共通モジュールの作成、利用、テストの説明がすべて終わりました。

・ オープンソースライブラリを作る

この共通モジュールは自分のプロジェクトで使えるだけではなく、ほかの人のプロジェクトでも動きます。もしこの共通モジュールを公開したければ、最低限libCommon.aファイルとすべてのヘッダーファイル(テストケースのヘッダーファイルを除く)を一緒にアーカイブして提供すれば、だれでも手軽に使えます。本文で使用したサンプルコードです。
オープンソースライブラリにするなら、このプロジェクトのすべてのフォルダとファイル(buildフォルダを削除したほうが良いです)をGoogle CodeあるいはGitHubにアップロードしましょう。

皆さんが作ったすばらしいライブラリをお待ちしております。