Objective-CとTDD
このエントリーは、TDD Advent Calendar 2013 の 6日目です。
ある日友人に「iOSアプリを作る人はあまりユニットテストしない」と言われた。自分もあまり書いた事はなかった。よく職場とかで言われるのは、
- ユニットテストを書く工数>受けられる恩恵
- iOSアプリはなんとなくでも作れるので、TDDとかいらない。
- テスト維持するの大変じゃん。
よし、良い機会だ、自分の為にもまとめよう。
TDDとは
そもそも、TDDって何さ。@biacさんが詳しくまとめてくれました。
ユニットテストをベースに開発を進める事だと理解している。ロジックのエラーを捉えたり、デグレードを防げたり、依存関係の少ないコードを書けたりと利点が多い。TDDを行うには依存関係の解決が大事。
これを実現する為に、ユニットテストが使われる。では、ユニットテストとは? 「 最小である1つの機能をテストする事」を指し、小さく、早く、単位ごとにテストができ非常に有用だ。
逆に、ユニットテストにもできないことはあり、その一つにUIテストがある。これはBDDの話になるので、以下の記事を見て欲しい。
iOS 向けTDD/BDDフレームワークやモックフレームワークの現状 - laiso - iPhoneアプリ開発グループ
Objective-Cでのテスト:XCTest
Xcode5でプロジェクトを作成すると、XCTestというテストフレームワークのクラスが自動生成される。また、Xcode5からはBotsと呼ばれるCIも導入された。さらに、Xcode4で使われていたOCUnitからのマイグレーションツールもあり移行も割りとスムーズ。
また、テストナビゲーターから、ワンクリックでテストケースを一つづつ実行可能になった。
テストケースは通常のObjective-Cと同様に書ける。例を以下に示す。
@interface ExampleTests:XCTestCase @end @implementation ExampleTests - (void) testExample { XCTAssertTrue(2 + 2 = 4); }
TDDで依存関係の少ないコードを書く
では、テストケースは分かったが、どうテストを書くのだろうか。以下のメソッドにTDD的な考えを導入しよう。
- (BOOL) saveIDNumberToDB:(NSNumber *)ID { JSKDataManager *dataManager = [JSKDataManager sharedInstance]; [dataManager addID:ID]; return [dataManager save]; }
まず、何かしらのシングルトンのデータマネジャーに、IDのNSNumberを渡し、保存し、その成否をリターンしている。 最小単位でテストするために、依存関係を取り除く必要がある。依存箇所をプロパティにすることを今回は考える。すると、シングルトンこの場合最小単位のテストは、
- (void)testSharedInstanceCreated { JSKViewController *viewController = [[JSKViewController alloc] init]; XCTAssert([viewController.dataManager class] == [JSKDataManager class]); }
これに合わせてコードを変更すると、
@interface JSKViewController () @property (nonatomic, strong) JSKDataManager *dataManager; @end @implementation JSKViewController - (BOOL) saveIDNumberToDB:(NSNumber *)ID { self.dataManager = [JSKDataManager sharedInstance]; [self.dataManager addID:ID]; return [self.dataManager save]; }
という形になり、依存関係の少ないきれいなコードに近づけるというわけだ。さらに、TDDで開発をしていれば、仕様変更時のテストの変更も最小限になるため、かかる工数よりも受けれる恩恵が多そうだ(もちろん出来ないことは出来ないけどね)
まとめ
今回はObjective-CとXcodeにおけるTDDについて調べた。TDDは、デグレードを防ぎ、メソッド感の依存関係を少なく記述するための指針となる。iOSアプリの世界でも、モデルコントローラー間では多くの恩恵を受けれそうだ。