NSURL* url = [NSURL URLWithString:@"http://example.com/api/hi.json"]; NSString* fakeResponse = @"{\"title\": \"Hello, 世界!\"}"; [URLRaider order:url body:fakeResponse]; NSURLRequest* request = [NSURLRequest requestWithURL:url]; NSData* responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil]; NSString* responseText = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]; STAssertEqualObjects(responseText, fakeResponse, nil); STAssertEqualObjects(responseData, [fakeResponse dataUsingEncoding:NSUTF8StringEncoding], nil); [URLRaider purge];
URLRaider:order:body でURL とレスポンスのテキストを指定するだけ。
URLRaider:purge でオーダーしたルールを忘れる
使いどころとしては
- リモートのWeb APIからデータを取ってきてその結果を元にhogeするんだけど、Web API まだない
- 特定のWeb サービスが提供しているAPI の結果にアプリケーションの動作が依存してる……
- オフラインでテストしたい
- 外部リソース呼び出しが多くてテストランナーdone に時間がかかる
- 開発中にUIWebView のレスポンスをローカルのHTMLにしてデバッグ
など
使う
git clone https://github.com/laiso/URLRaider.git
とくに依存するフレームワークとかないので、URLRaider/URLRaider/ ディレクトリ以下のクラスファイルをそのままプロジェクトにインポートするのがいいでしょう(あっ、そう言えばすっかりARC側の住人になっていたのでARC無効のプロジェクトでどうなるか試していない)。
サンプルアプリが以下にあります。サードパーティのライブラリなどを利用しているので最初にgit-submodule で取得してください。
git submodule update --init open Examples/Examples.xcodeproj
どのような実装か
method swizzling 的なことをしているわけでもない。Cocoa 標準のNSURLProtocol の拡張でリクエストを乗っ取ってメモリ上に登録したテキストに置き換えているだけ。なのでURLリクエスト(http://, https://, file://, ftp:// ? 全部確認したわけではない) の動的置き換えに特化した。
- URL Loading System Programming Guide - Protocol Support
- Using method swizzling and blocks to test Class methods in Objective-C. — Gist
「モックライブラリで代用できないの?」と最初は思ってて、OCMock とかCocoa の世界にもあるんだけど、そもそもモックとかスタブとかスパイとかフェイクとか依存性の注入とか。この辺のテスト用語周辺がよくわかんなくて、自分が思う「テスト用にHTTPリクエストの結果を置き換え」という最小の問題解決がなかなかできずにいたので(これは定義でいうとスタブの範疇っぽい)、必要最小限のものを作ってみた感じ。
抽象化されたモックオブジェクトベースのものと比べると、ランタイムの低レベルの部分なのでObjective-C ベースのいろんなサードパーティのライブラリにも効果が適用されることとかが良さげ。プロジェクトのExamples アプリケーションでは。
で動作確認をしている(https://github.com/laiso/URLRaider/tree/master/Examples 参照) 。AFNetworking はー、忘れてた。あとでやってみます。
ただ、ほぼ自分用なので実験的なモジュール扱いだと思う。iOS アプリケーションのテストでしたまだ試していない。
ロードマップ として考えられるのは例えば
- MIME type やレスポンスの文字エンコーディングなどの諸情報を設定できたり
- テキスト以外のデータ、フォーマットやバイナリを返すようにしたり
- 特定のマッチするURLのパス以下を MyApp.app/www/example.com/*.html のバンドルリソースを返すとか(CocProxy みたいな用途)
- purge とかいちいちしなくてblocks オブジェクトをAPI に渡せばいいのかもしれない
ぐらい