2016-06-07 1 views
2

Donc, j'ai eu une situation où nous avons dû frapper un service "reposant" pas sous notre contrôle, où, afin d'obtenir json de retour du service sur un appel GET, nous devons passer Content-Type = "application/json" dans l'en-tête. Le seul problème est que Angular supprime le Content-Type des en-têtes de requête sur un GET. J'ai trouvé un billet de blog qui a suggéré d'utiliser un décorateur sur httpBackend $ qui nous permet d'intercepter l'appel avant qu'il ne soit envoyé et rajoutons le type de contenu:

angular 
.module('MyApp') 
.decorator('$httpBackend', [ 
    '$delegate', function($delegate) { 
     return function() { 
      var contentType, headers; 
      headers = arguments[4]; 
      contentType = headers != null ? headers['X-Force-Content-Type'] : null; 
      if (contentType != null && headers['Content-Type'] == null) 
      headers['Content-Type'] = contentType; 

      return $delegate.apply(null, arguments); 
     }; 
    }]); 

donc, qui fonctionne à merveille! Maintenant, notre problème est qu'il a cassé tous les tests unitaires où nous avons utilisé le service $ httpBackend. La seule erreur que nous obtenons est "indéfini".

Ex. méthode de test unitaire:

it('should return service.model.error if service returns an exception code from EndProject', 
inject(function($httpBackend) { 
     var mockResponse = sinon.stub({ 'exception': 'Unable to retrieve service data' }); 
     $httpBackend.whenPUT(this.endProjectUrl).respond(mockResponse); 
     var data; 
     this.service.EndProject().then(function(fetchedData) { 
      data = fetchedData; 
     }); 

     $httpBackend.flush(); 
     expect(data.error.state).toBe(true); 
     expect(data.error.message).toEqual('Unable to retrieve service data'); 
})); 

PhantomJS 2.1.1 (Mac OS X 0.0.0) projectService EndProject doit retourner service.model.error si le service retourne un code d'exception de EndProject ECHEC non définies /Utilisateurs/mlm1205 /Documents/THDSource/bolt-projects/html_app/src/app/components/services/project/projectService.spec.js:213:41 invoke @/Utilisateurs/mlm1205/Documents/THDSource/bolt-projects/html_app/bower_components/angulaire/angulaire.js: 4560: 22 [email protected]/Users/mlm1205/Documents/THDSource/bolt-projects/html_app/bower_components/angular-mocks/angular-mocks.js: 2518: 26

Répondre

0

Alors que j'ai marqué la réponse d'estus comme la solution, basée purement sur ce que ma question était ... à la fin, en fin de compte ce n'était pas le résultat final, nous sommes allés avec. Dans le cas de ne pas voir la forêt à travers les arbres, la solution la plus simple consistait à ajouter un élément de données vide à la config de $ call http.Je l'avais déjà essayé et cela ne fonctionnait pas (ou du moins c'était ce que je croyais), mais après avoir joué avec, ça a marché et nous avons pu enlever le décorateur de l'application.

return $http.get(getItemInformationUrl + params, { dataType: 'json', data: '', headers: {'Content-Type': 'application/json'} }).then(getItemInformationCompleted).catch(getItemInformationFailed); 
1

Le décorateur énuméré couvre le scénario simple de correction de singe où la fonction corrigée n'est pas un constructeur et n'a pas de propriétés et de méthodes statiques.

Ceci est vrai pour $httpBackend in ng module, il s'agit simplement d'une fonction usine sans fonctionnalités supplémentaires.

Ceci n'est pas vrai pour les modules ngMock et ngMockE2E qui remplacent $httpBackend et ont des méthodes statiques, at least some of them are documented.

Cela signifie que la recette généralement sûre (elle ne couvre pas les propriétés non-dénombrables et héritées) pour le singe-patcher une fonction usine est

app.decorator('$httpBackend', ['$delegate', function ($delegate) { 
    var $httpBackend = function() { 
     ... 
     return $delegate.apply(null, arguments); 
    }; 
    angular.extend($httpBackend, $delegate); 
    return $httpBackend; 
}]); 

Indépendamment de cela, il est une bonne habitude à modularisation la application au niveau où les unités peuvent être testées isolément sans pièces mobiles excessives (cette question est un exemple expressif pourquoi cela est important). Il est commode d'avoir app (bootstrapped en production), app.e2e (bootstrapped dans les tests E2E), app.common (dénominateur commun), app.unitA (chargé dans app.common et peuvent être chargés séparément dans le test unitaire), etc.

La plupart des applications code à l'échelle (config et run blocs, routage) peuvent être déplacés vers des modules séparés et chargés uniquement dans les modules qui en dépendent directement. Sauf s'il s'agit d'une spécification qui teste decorator elle-même, le module décorateur ne doit pas être chargé.

Notez également que Chrome may offer superior experience que PhantomJS lors du débogage des erreurs de spécification.

+0

J'ai essayé, mais l'appel sort toujours avec le type de contenu dépouillé. J'ai un intercepteur qui injecte un autre en-tête dont notre équilibreur de charge a besoin, et qui l'a essayé pour Content-Type mais cela n'a pas fonctionné. Je débogue à travers le code intercepteur et vérifie qu'il ajoute en fait l'en-tête, mais quand vous regardez l'appel réel dans l'onglet Réseau (outils de développement Chrome), l'en-tête Content-Type est parti. – Shaggy13spe

+0

Je suppose que j'ai mal interprété votre cas avant. Vous avez un décorateur existant pour la production qui fonctionne pour vous mais échoue dans les tests unitaires, malgré le fait que les tests unitaires ne font pas de vraies demandes. J'ai mis à jour la réponse. Voir la remarque sur PhantomJS, je suis tout à fait sûr que le message d'erreur est paralysé à cause de cela et devrait dire quelque chose comme 'non défini n'est pas une fonction' en raison de' $ httpBackend.whenPUT (...) '. – estus

+0

Oui, désolé si je ne l'ai pas rendu plus clair. Je vais essayer de jouer un peu avec ce que vous avez suggéré. J'essaie vraiment de repousser sur l'équipe qui a créé le service pour le changer, parce qu'il est ridicule d'exiger cet en-tête sur une demande de GET. – Shaggy13spe