web-dev-qa-db-fra.com

Test du trafic HTTP dans l'application Alamofire

J'ai du mal à trouver le meilleur moyen de tester une application qui utilise Alamofire pour aider à la synchronisation avec les données du serveur.

Je veux pouvoir tester mon code qui utilise Alamofire et traite les réponses JSON d'un serveur. Je voudrais simuler ces tests afin de pouvoir fournir les données de réponse attendues à ces tests sans générer de trafic réseau réel.

Ce billet de blog ( http://nshipster.com/xctestcase/ ) décrit à quel point il est facile de se moquer d'un objet dans Swift - mais je ne sais pas comment de le faire avec Alamofire et ses réponses enchaînées.

Vais-je me moquer du gestionnaire? la demande? Réponse? Toute aide serait appréciée!

32
Daniel D

J'ajoute une autre réponse car je viens de trouver cette approche qui à mon avis est plus facile et vraiment simple à lire et à utiliser.

J'ai créé une classe factice Alamofire qui contient uniquement les fonctions et les types nécessaires aux tests. Maintenant, j'inclus ce fichier dans la cible de test au lieu du vrai Alamofire.

Par exemple, j'ai créé ma version de la classe Request où je définis quelques variables statiques que je valorise en fonction du test, et pour cette classe, j'ai implémenté uniquement les init et la fonction responseJSON.

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
    }
}

Maintenant, je peux me moquer d'une réponse dans un test:

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
    }
}

La fonction de requête est définie ici:

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")
}
21
MatterGoal

Cette question vieillit, mais je viens de rencontrer le même problème, et la solution est très simple lors de l'utilisation de OHHTTPStubs.

OHHTTPStubs se moque simplement des réponses que vous obtenez de NSURLSession, donc cela fonctionne bien avec Alamofire, et vous obtenez une très bonne couverture de votre chemin de code.

Par exemple, dans votre scénario de test, simulez simplement la réponse en utilisant:

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)
})
9
user541160

En attendant une réponse de @mattt je poste un exemple de mon code.

Disons que nous avons une classe Client qui est chargée d'appeler un simple service Web. Cette classe implémente une fonction appelée userSignIn qui effectue une connexion à l'aide du WS.

Voici le code de la fonction 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)
            }
    }

Le but de la fonction est d'obtenir un jeton du service Web si les informations transmises par l'utilisateur sont correctes.

La fonction checkSignInResponse (je ne rapporte pas son code car elle n'est pas utile pour la réponse) a pour rôle de valoriser les 3 variables operationComplete, accessToken et error selon la réponse JSON reçue.

Maintenant que les 3 variables ont une valeur, nous appelons les completionHandler en les utilisant.

Comment se moquer de cette fonction?!

Pour simuler la réponse, je remplace la fonction userSignIn directement dans la fonction de test (comme expliqué par l'article 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")

}

puis je remplace le client à l'intérieur du contrôleur de vue que je teste en utilisant mon client "simulé". Dans ce cas, je teste que le contrôleur passe aux informations de fonction qui sont non valides donc je vérifie que la propriété error du contrôleur n'est pas nulle. Pour forcer ces données, je mets simplement operationComplete à false et je génère manuellement un NSError.

Cela a-t-il un sens pour vous? Je ne suis pas sûr que ce test soit un bon test ... mais au moins je peux vérifier le flux de données.

1
MatterGoal