URLリクエストを指定した結果に書き換えるテスト用ライブラリを作った

  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:// ? 全部確認したわけではない) の動的置き換えに特化した。

「モックライブラリで代用できないの?」と最初は思ってて、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 に渡せばいいのかもしれない

ぐらい

Fakeリクエスト系

他のプログラミング言語環境の似たような動作するモジュールないかなと探してた過程。この手のものはFakeリクエストと呼ばれるみたい。ただ今回のはCocoa のNSURLProtocol で置き換えているだけで、リクエスト自体は本物だよなー(結果は偽物だけど)と思ったので採用しなかった