SwiftのQuickでAlamofireを使った非同期のテストを書く

# 概要
HTTP通信を行うようなテストケースを書くには、通常、スタブを使います。
サーバが落ちていたりしてテストが失敗するケースがありますし、連続してリクエストするのはサーバのリソースを食いますし、遅いですし…。
SwiftのQuickでAlamofireを使ったテストを書くのに躓いたので、同じ人がいるかもと思い、筆(キーボード)を執りました。

# 環境
- Xcode7(Swift2.0)
- [Quick](https://github.com/Quick/Quick)
    - RSpec風にかけるテストライブラリです。
    - 非同期のテストも書け、今回はこの機能を使います。
    - XCTestなど他のテストフレームワークを使う場合は、[TRVSMonitor](https://github.com/travisjeffery/TRVSMonitor)とかで非同期のテストをするといいかもしれません(試してません)。[TKRGuard](https://github.com/tokorom/TKRGuard)はSwiftではマクロが使えないためか動きませんでした。
- [Nimble](https://github.com/Quick/Nimble) 
    - Quickが依存するmatcher
- [Mockingjay](https://github.com/kylef/Mockingjay)
    - HTTPモックライブラリです。
    - 他にもOHHttpStubsが有名ですが、Quick + Alamofireとは[相性が悪そう](https://github.com/AliSoftware/OHHTTPStubs/issues/126)で断念。
- [Alamofire](https://github.com/Alamofire/Alamofire)
    - 説明不要、事実上標準といっても過言ではないHTTPライブラリ
- [SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON)
    - こちらも有名なJSONパーサですね。

# コード

http://api.pokosho.com/v1/hoge.json は以下のJSONを返す謎のAPIの予定です。
```
{ "name": "kaiba" }
```
が、まだ実装していないため、404が返ります。
正しい結果が得られた時のテストを書きます。

```swift:RequestHogeAPISpec.swift
import Quick
import Nimble
import Mockingjay
import Alamofire
import SwiftyJSON

class RequestHogeAPISpec: QuickSpec {
    var jsonData: JSON!
    var statusCode: Int = 0
    
    override func spec() {
        describe("hoge API") {
            context("when you request hoge API http://api.pokosho.com/v1/hoge.json") {
                beforeEach() {
                    // setup stub
                    let body = [ "name": "kaiba" ]
                    self.stub(http(.GET, uri: "http://api.pokosho.com/v1/hoge.json"), builder: json(body))
                }
                
                it("returns json") {
                    // request async
                    Alamofire.request(.GET, "http://api.pokosho.com/v1/hoge.json")
                        .responseJSON { request, response, result in
                            self.jsonData = JSON(result.value!)
                            self.statusCode = response!.statusCode
                        }
                    expect(self.statusCode).toEventually(equal(200))
                    expect(self.jsonData["name"]).toEventually(equal("kaiba"))
                }
            }
        }
    }
}
```

# 結果
```
Test Suite 'All tests' passed at 2015-10-12 23:42:19.177.
	 Executed 1 test, with 0 failures (0 unexpected) in 0.041 (0.046) seconds
```

1つのテストが実行され、失敗はありませんでした！
(試しにわざと失敗になるように書き換えて見ると良いと思います)

# 解説
beforeEachでAPIのURLに対してスタブを登録しています。

```swift:RequestHogeAPISpec.swift
self.stub(http(.GET, uri: "http://api.pokosho.com/v1/hoge.json"), builder: json(body))
```

Alamofireの結果は非同期で帰ってくるため、QuickのtoEventuallyを使って、非同期に得られる値のチェックをしています。
今回はデフォルトのタイムアウトですが、引数でタイムアウトを指定することもできます。

```swift:RequestHogeAPISpec.swift
expect(self.statusCode).toEventually(equal(200))
```

# 追記 URLパラメータを付加するとき
以下のように正規表現を使って、stubを登録するとうまくいきます?

```swift:RequestHogeAPISpec.swift
self.stub(http(.GET, uri: "http://api.pokosho.com/v1/hoge.json?.*"), builder: json(body))
```

？ ワイルドカードで `hoge.json?*` かも…? よくわからない。すみません。

# 追記

- [最新のAlamofireだとスタブが効かないらしい](https://github.com/kylef/Mockingjay/issues/51)。以下で回避できる。

```swift
NSURLSessionConfiguration.mockingjaySwizzleDefaultSessionConfiguration()
```

# 終わりに
http://pokosho.com は僕の使ってるドメインです^^;
iOSやその他のプログラミング情報もありますので、良かったらどうぞ。