34

Alamofire を使用してサーバー データとの同期を支援するアプリを最適にテストする方法を見つけるのに少し苦労しています。

Alamofire を使用し、サーバーからの JSON 応答を処理するコードをテストできるようにしたいと考えています。実際のネットワーク トラフィックを発生させずに、期待される応答データをこれらのテストにフィードできるように、これらのテストをモックしたいと考えています。

このブログ投稿 ( http://nshipster.com/xctestcase/ ) では、Swift でオブジェクトをモックするのがいかに簡単かを説明していますが、Alamofire とその連鎖応答でそれを行う方法がわかりません。

マネージャーを嘲笑しますか?リクエスト?応答?どんな助けでも大歓迎です!

4

4 に答える 4

23

私の意見では、読みやすく、使いやすいこのアプローチを見つけたので、別の回答を追加します。

テストに必要な関数と型のみを含むダミーの Alamofire クラスを作成しました。ここで、実際の Alamofire の代わりに、このファイルをテスト ターゲットに含めます。

たとえばRequest、テストに応じて評価するいくつかの静的変数を定義するクラスのバージョンを作成しました。このクラスでは、initresponseJSON関数のみを実装しました。

public class Request {

    var request:String?
    struct response{
        static var data:NSHTTPURLResponse?
        static var json:AnyObject?
        static var error:NSError?
    }

    init (request:String){
        self.request = request
    }

    public func responseJSON(options: NSJSONReadingOptions = .AllowFragments, completionHandler: (NSURLRequest, NSHTTPURLResponse?, AnyObject?, NSError?) -> Void) -> Self {

        completionHandler(NSURLRequest(URL: NSURL(string:self.request!)!), Request.response.data, Request.response.json, Request.response.error)
        return self
    }
}

これで、テストで応答をモックできます。

func testMytestFunction(){
    var HTMLResponse = NSHTTPURLResponse(URL: NSURL(string: "myurl")!, statusCode: 200, HTTPVersion: "HTTP/1.1", headerFields: nil)

    Request.response.data = HTMLResponse
    Request.response.json = LoadDataFromJSONFile("MyJsonFile")

    request(.POST, "myurl", parameters: nil, encoding: ParameterEncoding.JSON).responseJSON {
        (request, response, JSON, error) -> Void in
        // the JSON and response variable now contains exactly the data that you have passed to Request.response.data and Request.response.json
    }
}

リクエスト関数は次のように定義されています。

public func request(method: Method, URLString: URLStringConvertible, parameters: [String: AnyObject]? = nil, encoding: ParameterEncoding = .URL) -> Request {

    return Request(request: URLString.URLString)
}

public func request(URLRequest: URLRequestConvertible) -> Request {

    return Request(request: "fakecall")
}
于 2015-04-30T10:50:04.377 に答える
9

この質問は古くなりつつありますが、同じ問題に遭遇したばかりで、OHHTTPStubs を使用すると解決策は非常に簡単です。

OHHTTPStubs は、NSURLSession から取得した応答をモックするだけなので、Alamofire とうまく連携し、コード パスを非常によくカバーします。

たとえば、テスト ケースでは、次を使用して応答をモックするだけです。

OHHTTPStubs.stubRequestsPassingTest({
  (request: NSURLRequest) -> Bool in
    return request.URL!.host == "myhost.com"
  }, withStubResponse: {
  (request: NSURLRequest) -> OHHTTPStubsResponse in
    let obj = ["status": "ok", "data": "something"]
    return OHHTTPStubsResponse(JSONObject: obj, statusCode:200, headers:nil)
})
于 2015-05-22T22:06:58.327 に答える
1

@mattt による回答を待って、コードの例を投稿します。

Client簡単な Web サービスの呼び出しを担当するクラスがあるとします。このクラスはuserSignIn、WS を使用してサインインを実行する関数を実装します。

これはuserSignIn関数のコードです:

func userSignIn(
        #email:String,
        password:String,
        completionHandler: (Bool, String?, NSError?) -> Void
        )-> Void
        {

            var parameters:[String:AnyObject] = [
                "email":email,
                "password":password,
            ]


            Alamofire.request(.POST, Client.urlPath, parameters: parameters, encoding: ParameterEncoding.JSON).responseJSON {
                (request, response, JSON, responseError) -> Void in

                // Setup callback params

                // HERE WE INJECT THE "FAKE" DATA--------
                var operationComplete = false
                var accessToken:String?
                var error:NSError?
                // --------------------------------------

                if let statusCode = response?.statusCode {

                    // Check for errors and build response data
                    (operationComplete, accessToken, error) = self.checkSignInResponse(statusCode, JSON: JSON)
                }

                // Call the completion handler
                completionHandler(operationComplete, accessToken, error)
            }
    }

この関数の目的は、ユーザーから渡された情報が正しい場合に Web サービスからトークンを取得することです。

関数checkSignInResponse(答えには役に立たないので、そのコードは報告しません) には、受け取った JSON 応答に応じて、3 つの変数を評価する役割がありますoperationCompleteaccessTokenerror

3 つの変数に値が設定されたので、completionHandlerそれらを使用して呼び出します。

この関数をモックする方法は?!

応答を模擬するために、userSignIn関数をテスト関数に直接オーバーライドします (NSHipster の記事で説明されています)。

func testUserSignIn_whenParamsAreInvalid(){

    class MockClient:Client {

        override func userSignIn(#email: String, password: String, completionHandler:
            (Bool, String?, NSError?) -> Void) {

            // Set callback params
            var operationComplete = false
            var accessToken:String? = nil
            var error:NSError? = NSError(domain: "Testing", code: 99, userInfo: nil)

            completionHandler(operationComplete, accessToken, error)
        }
    }

    signInViewController!.client = MockClient()
    signInViewController!.loadView()

    fillRegisterFieldsWithDataAndSubmit(femail(), password: fpassword())

    XCTAssertNotNil(signInViewController!.error, "Expect error to be not nil")

}

client次に、「モック」クライアントを使用してテストしているView Controllerの内部を置き換えます。この場合、コントローラーが無効なerror情報を関数に渡すことをテストしているので、コントローラーのプロパティが nil でないことを確認します。このデータを強制するには、単純operationCompleteに false に設定し、手動でNSError.

それはあなたにとって意味がありますか?このテストが良いテストかどうかはわかりませんが、少なくともデータ フローを確認できます。

于 2015-04-21T15:37:34.913 に答える