Comment faire le test d'unité avec angular-translate

j'ai utilisé la traduction angulaire d'ici ( http://pascalprecht.github.io/angular-translate / ) et cela fonctionne très bien, mais cela casse le test de l'Unité de mon contrôleur avec une erreur:

Unexpected request: GET scripts/i18n/locale-en.json

Je ne comprends pas pourquoi?

j'utilise yeoman et tester avec karma.

app.js:

'use strict';

(function() {

  angular.module('wbApp', ['authService', 'authUserService', 'checkUserDirective', 'ui.bootstrap', 'pascalprecht.translate'])
    .config(function($routeProvider) {
      $routeProvider
        .when('/', {
          templateUrl: 'views/login.html',
          controller: 'LoginCtrl',
          access: {
            isFree: true
          }
        })
        .when('/main', {
          templateUrl: 'views/main.html',
          controller: 'MainCtrl',
          access: {
            isFree: false
          }
        })
        .otherwise({
          redirectTo: '/'
        });
    });

})();

configTranslate.js:

'use strict';

(function() {

  angular.module('wbApp')
    .config(['$translateProvider',
      function($translateProvider) {

        $translateProvider.useStaticFilesLoader({
            prefix: 'scripts/i18n/locale-',
            suffix: '.json'
        });

        $translateProvider.preferredLanguage('en');

      }]);

})();

Karma.conf.js:

files = [

  ...

  'app/bower_components/angular-translate/angular-translate.js',
  'app/bower_components/angular-translate-loader-static-files/angular-translate-loader-static-files.js',

  ...

];

test du contrôleur:

'use strict';

describe('Controller: LoginCtrl', function() {

  // load the controller's module
  beforeEach(module('wbApp'));

  var LoginCtrl, scope, location, httpMock, authUser;

  // Initialize the controller and a mock scope
  beforeEach(inject(function($controller, $rootScope, $location, $httpBackend, AuthUser) {
    authUser = AuthUser;
    location = $location;
    httpMock = $httpBackend;
    scope = $rootScope.$new();

    LoginCtrl = $controller('LoginCtrl', {
      $scope: scope
    });


    httpMock.when('GET', 'scripts/i18n/locale-en.json').passThrough();

  }));

  it(...);

  ...

});

si j'ajoute cela dans le contrôleur de test, produit même erreur:

httpMock.when('GET', 'scripts/i18n/locale-en.json').respond(200);
httpMock.flush();

ou

httpMock.when('GET', 'scripts/i18n/locale-en.json').passThrough();
httpMock.flush();

je trouve ce post Comment puis-je tester les contrôleurs avec Angular Translate initialisé dans App Config? mais pas qui m'a aidé :/

j'utilise largement $httpBackend dans mes tests et ça marche très bien, mais dans ce cas, il est inefficace. Si je commente la ligne:

$translateProvider.preferredLanguage('en');

évidemment une erreur, si j'ajoute sur l'exécution (dans Mes controllers)

$translate.uses(local);

je me retrouve avec la même erreur?

donc je me tourne vers la configuration de la traduction (configTranslate.js) ou à l'exécution est le même résultat:

Unexpected request: GET scripts/i18n/locale-en.json

Voici la syntaxe que j'AI testée, soit dans un "beforeeeach(inject(function(...});"

ou dans un test " ("...', fonction.{ )(..}); "

httpMock.expectGET('scripts/i18n/locale-en.json');
httpMock.when('GET', 'scripts/i18n/locale-en.json').passThrough();
httpMock.when('GET', 'scripts/i18n/locale-en.json').respond(data);

avec à la fin

httpMock.flush();

j'ai aussi essayé un $ s'appliquent

httpMock.expectGET('scripts/i18n/locale-fr.json');
scope.$apply(function(){
  $translate.uses('fr');
});
httpMock.flush();

rien ne se passe, pourtant cette erreur me rend fou ..

si vous avez une suggestion""

39
demandé sur Community 2013-09-18 19:23:40

11 réponses

c'est un problème connu, s'il vous plaît suivez la documentation ici: unité d'essai angulaire

La solution

malheureusement, ce problème est causé par la conception de Angular-translate. Pour contourner ces erreurs, tout ce que nous pouvons faire est de écraser notre configuration de module dans notre suite de test, qu'il ne utilisez un chargeur asynchrone. Quand il n'y a pas asynchrone chargeur, il n'y a pas de XHR et donc pas d'erreur.

alors comment réécrire notre configuration de module à l'exécution pour notre suite de test? Lors de l'instanciation d'un module angulaire, nous pouvons toujours appliquer une fonction en ligne qui est exécutée comme fonction de configuration. Ce la fonction de configuration peut être utilisée pour écraser les modules configuration puisque nous avons accès à tous les fournisseurs.

avec le fournisseur $ provide, nous pouvons construire une usine de chargeurs sur mesure, qui devrait alors être utilisé à la place du chargeur de fichiers statiques.

beforeEach(module('myApp', function ($provide, $translateProvider) {

  $provide.factory('customLoader', function () {
    // loader logic goes here
  });

  $translateProvider.useLoader('customLoader');

}));

s'il vous Plaît lisez la suite dans le lien ci-dessus prévues.

28
répondu nolimit 2014-11-28 17:39:12

nous avons pris l'approche d'ignorer le chargeur de traduction dans les tests unitaires, plutôt que d'être forcé de modifier chacun des fichiers spec.

une façon de le faire pourrait être de séparer la configuration du chargeur dans un fichier séparé, puis de l'exclure dans karma.

Ainsi, par exemple, vous pouvez créer un fichier app-i18n-loader.js (toutes les autres configurations de modules a lieu dans un autre fichier):

    angular
    .module('myApp')
    .config(loaderConfig);

loaderConfig.$inject = ['$translateProvider', '$translatePartialLoaderProvider'];

function loaderConfig($translateProvider, $translatePartialLoaderProvider) {

    $translateProvider.useLoader('$translatePartialLoader', {
        urlTemplate: 'assets/i18n/{part}/{lang}.json'
    });

    $translatePartialLoaderProvider.addPart('myApp');
}

et dans votre Karma.conf.js exclure le fichier:

        files: [
        'bower_components/angular/angular.js',
        'bower_components/angular-mocks/angular-mocks.js',
        //...
        'bower_components/angular-translate/angular-translate.js',
        'bower_components/angular-translate-loader-partial/angular-translate-loader-partial.js',
        'app/**/*.mdl.js',
        'app/**/*.js'
    ],

    exclude: [
        'app/app-i18n-loader.js'
    ],

(Note: réponse modifiée à une solution qui ne nécessite pas de grunt/gulfp).

15
répondu Erez Cohen 2015-04-08 08:38:46

je voulais une solution,

  1. ce qui n'était pas trop hacky
  2. qui ne m'obligeait pas à changer le code de ma demande,
  3. qui n'interférerait pas avec la capacité de charger des modules supplémentaires
  4. et le plus important qui ne me demanderait pas de changer chaque test unique.

c'est ce que j'ai fini par dire:

// you need to load the 3rd party module first
beforeEach(module('pascalprecht.translate'));
// overwrite useStaticFilesLoader to get rid of request to translation file
beforeEach(module(function ($translateProvider) {
    $translateProvider.useStaticFilesLoader = function () {
    };
}));

en supposant que vous n'avez pas besoin des traductions pour vos tests unitaires, cela fonctionne très bien. Mettez juste la beforeEach au niveau global, de préférence dans son propre fichier à l'intérieur du dossier test. Il sera exécuté avant chaque autre test alors.

11
répondu hugo der hungrige 2015-12-15 19:54:18

essayer de mettre à la méthode d'essai:

it('should ...', function() {
    httpMock.when('GET', 'scripts/i18n/locale-en.json').respond({});
    httpMock.expectGET('scripts/i18n/locale-en.json');
    scope.resetForm(); // Action which fires a http request
    httpMock.flush(); // Flush must be called after the http request
}

voir les exemples de Angular docs

3
répondu Matti Lehtinen 2013-09-20 12:24:41

consultez https://github.com/PascalPrecht/angular-translate/blob/master/test/unit/service/loader-static-files.spec.js comme référence.

en général, je recommande d'utiliser un chargeur de traduction standard pour les tests unitaires (sans les tracas des chargements http) ce qui signifie que vous pouvez fournir les étiquettes avec $translateProvider.translations() . Pourquoi? Parce que vous n'avez pas à tester la fonctionnalité de chargement à distance qui fait partie d'angular-translate projet.

3
répondu knalli 2013-12-15 15:48:50

j'ai rencontré ce problème avec les essais du rapporteur. Ma solution était de simuler des traductions comme celle-ci:

angular.module('app')
        .config(function ($translateProvider) {
            $translateProvider.translations('en', {});
            $translateProvider.preferredLanguage('en');
        })

maintenant aucun fichier de langue ne sont téléchargés, aucune chaîne de caractères ne sont traduits et je viens de tester contre les clés de chaîne de caractères dans les spécifications:

expect(element(by.css('#title')).getText()).toEqual('TITLE_TEXT');
3
répondu AndyTheEntity 2015-09-25 08:53:43

aucune des solutions n'a fonctionné pour moi mais je suis venu avec ces solutions:

1) Si vous avez besoin d'utiliser scope.$apply() , ou devrait traiter avec les états dans votre test( après le $apply() la deuxième approche ne fonctionnera pas), outrepasser les traductions de votre application avec la méthode $translateProvider.translations() , en utilisant un plugin à chargez des fichiers JSON

beforeEach(module(function ($translateProvider) {
    $translateProvider.translations('en', readJSON('scripts/i18n/locale-en.json'));
}));

2) Si votre contrôleur testé dépend du service $translate vous pouvez utiliser un plugin pour chargez les fichiers JSON et combinez-les avec $httpBackend pour charger votre fichier local quand angular-translate le demande.

beforeEach(inject(function (_$httpBackend_) {
    $httpBackend = _$httpBackend_;

    $httpBackend.whenGET('scripts/i18n/locale-en.json').respond(readJSON('scripts/i18n/locale-en.json'));
    $httpBackend.flush();
})));

notez que ceci doit être en dessous de votre beforeEach(module('myApp')); ou vous obtiendrez une erreur $injector .

1
répondu Bruno Peres 2015-09-05 04:27:30

j'ai fait un service simulé simple pour $ traduire

$translate=function (translation) {
    return {
      then: function (callback) {
        var translated={};
        translation.map(function (transl) {
          translated[transl]=transl;
        });
        return callback(translated);
      }
    }
  };

exemple d'utilisation ici: https://gist.github.com/dam1/5858bdcabb89effca457

1
répondu dam1 2016-01-20 16:47:05

j'utilise ce modèle.

  • ApplicationModule ensemble régulier angular-translate config.
  • code d'essai charge "testModule" au lieu de "applicationModule"

// application module .js 
(function() {
  'use strict'; 
  
  angular
   .module('applicationModule', [
    'ngAnimate',
    'ngResource',
    'ui.router',
    'pascalprecht.translate'
  ])
  .config(['$stateProvider', '$urlRouterProvider', '$translateProvider', '$translatePartialLoaderProvider', config]);

  function config($stateProvider, $urlRouterProvider, $translateProvider, $translatePartialLoaderProvider) {
    // set routing ... 
        
    $translateProvider.useStaticFilesLoader({
      prefix: 'i18n/locale-',
      suffix: '.json'
    });

    $translateProvider.useMessageFormatInterpolation();
    $translateProvider.fallbackLanguage(['en']);
    $translateProvider
    .registerAvailableLanguageKeys(['en', 'ko'], {
      'en_US': 'en',
      'ko_KR': 'ko'
    })
    .determinePreferredLanguage(navigator.browserLanguage);

            
    $translateProvider.addInterpolation('$translateMessageFormatInterpolation');    
    $translateProvider.useSanitizeValueStrategy('escaped');
  }

})();
// test.module.js
(function() {
  'use strict';

  angular
    .module('testModule', ['applicationModule'])
    .config(['$translateProvider', '$translatePartialLoaderProvider', config])
    .run(['$httpBackend', run]);

  function config($translateProvider, $translatePartialLoaderProvider) {
    $translateProvider.useLoader('$translatePartialLoader', {
        urlTemplate: 'i18n/locale-en.json'
    });
    $translatePartialLoaderProvider.addPart('applicationModule');
  }

  function run($httpBackend) {
    $httpBackend.when('GET', 'i18n/locale-en.json').respond(200);
  }

})();


// someDirective.spec.js
describe("a3Dashboard", function() {
    beforeEach(module("testModule"))

    var element, $scope;
    beforeEach(inject(function($compile, $rootScope) {
        $scope = $rootScope;
        element = angular.element("<div>{{2 + 2}}</div>");
        $compile(element)($rootScope)
    }))

    it('should equal 4', function() {
      $scope.$digest();
      expect(element.html()).toBe("4");
    })

})
0
répondu nulpulum 2015-04-20 06:12:01

en retard à la table avec ceci, mais j'ai contourné ceci en spécifiant que Karma sert simplement les fichiers selon cette entrée dans karma.conf.js :

files: [
    ...
    {pattern: 'scripts/i18n/*.json', included: false, served: true},
    ...
]
0
répondu TheFoot 2016-04-11 20:08:15

la réponse pour 2016 est de pré-traiter votre json dans vos tests et de tester correctement le travail de traduction sur vos directives.

j'utilise Karma-ng-json2js-préprocesseur. Suivez toutes les étapes pour configurer votre karma.conf ensuite dans votre fichier de test, préprogrammez le fichier concerné en tant que module, puis définissez cette information dans $translateProvider.

beforeEach(module('myApp', '/l10n/english-translation.json'));

// Mock translations for this template
beforeEach(module(function($translateProvider, englishTranslation) {
    $translateProvider.translations('en_us', englishTranslation);
    $translateProvider.useSanitizeValueStrategy(null);
    $translateProvider.preferredLanguage('en_us');
}));

Note selon le plugin, il utilise votre nom de fichier pour générer un nom de module camelcased. Vous peut jouer avec la fonction à l'intérieur du module /lib, mais essentiellement il supprime tous les tirets, mais garde les underscores dans une camelCase. Donc en_us devient En_us.

vous aurez également besoin de dire à votre test qu'il attend ce fichier comme une GEt.

    $httpBackend.expect('GET', '/l10n/english-translation.json').respond(200);
0
répondu efwjames 2016-11-14 02:50:26