Quantumleap
760 words
4 minutes
Mockerでテスト環境をつくる
2021-10-01

Mocker#

使い方#

JSON を扱うライブラリの場合、Mocker とは別に SwiftyJSON もあると良い。

func testExample() throws {
    // This is an example of a functional test case.
    // Use XCTAssert and related functions to verify your tests produce the correct results.

    /// 非同期で値を返す(completion内の内容が返るのを待つ)テストの場合、これを定義しないとダメ
    let expectation = expectation(description: "Expectation")
    /// 返ってくるダミーデータ
    let response: [String: String] = [
        "x_product_version": "9.99.0",
        "api_version": "99991231"
    ]

    /// アクセスする先のURL
    let url = URL(string: "https://h505nylwxl.execute-api.ap-northeast-1.amazonaws.com/dev/version")!
    /// Mockの定義(内容については後述)
    let mock = Mock(url: url, dataType: .json, statusCode: 200, data: [
        .get : try! JSON(response).rawData() // Data containing the JSON response
    ])
    /// Mockを登録
    mock.register()

    /// URLSessionでリクエストを送る
    URLSession.shared.dataTask(with: url) { (data, response, error) in
        /// data, responseがnilならエラーを返す
        XCTAssertNotNil(data)
        XCTAssertNotNil(response)
        /// errorがnilでなければエラーを返す
        XCTAssertNil(error)

        /// アンラップして中身を取り出す
        if let data = data {
            do {
                XCTAssertNoThrow(try JSON(data: data))
                let json = try JSON(data: data)
                /// 中身を表示
                print(json)
            } catch {
            }
        }
        /// 終了を伝えるおまじない
        expectation.fulfill()
    }.resume()
    /// expectationの内容が終わる(fulfill()が呼ばれる)のを最大10秒間待つ
    wait(for: [expectation], timeout: 10.0)
}

大雑把にこういうような内容になっている。公式ドキュメントが古く、そのまま書いただけでは全く動作しないのが困る。

Mock の定義#

/// Mockの定義(内容については後述)
let mock = Mock(url: url, dataType: .json, statusCode: 200, data: [
    .get : try! JSON(response).rawData() // Data containing the JSON response
])

さて、ここの定義の意味なのだがちゃんと検証したわけではないのでいろいろ疑問は残るのだが、

「url で指定された URL に対してアクセスしたときに、dataType で指定される Content-Type、statusCode で指定されるステータスコードでデータを返す」ということを定義するようだ。

また、そのときに GET と POST といったようにメソッドによって返す値を切り替えることもできる。

値は Data 型でないといけないので、Dictionary->JSON->Dataという変換を経て値を返すようにしている。

::: tip 返り値について

よく考えたらわざわざJSONを経由する必要はないかもしれない。

:::

Alamofire#

実はこの Mocker はURLSessionだけでなくAlamofireに対しても使うことができます。

ただし、ちょっと使い方が異なるのでそれをメモしておきます。

// 通常の使い方
AF.request("https://httpbin.org/get").responseJSON { response in
    debugPrint(response)
}

Alamofire はAFというインスタンス(モジュール名?)を持っているため、これを利用して上のようにリクエストを送ることが多いと思います。

ですが、この書き方では Mocker は動作しません。

let configuration = URLSessionConfiguration.af.default
configuration.protocolClasses = [MockingURLProtocol.self]
let sessionManager = Alamofire.Session(configuration: configuration)

sessionManager.request("https://httpbin.org/get").responseJSON { response in
    debugPrint(response)
}

上のように一度MockerURLProtocolConfigurationとして設定してセッション用のインスタンスを作成し、それを利用して通信を行う必要があります。

Mockerでテスト環境をつくる
https://fuwari.vercel.app/posts/2021/10/mocker/
Author
tkgling
Published at
2021-10-01