web-dev-qa-db-fra.com

Comment faire des tests unitaires avec angular-translate

J'ai des utilisations angulaires qui traduisent à partir d'ici ( http://pascalprecht.github.io/angular-translate/ ) et cela fonctionne parfaitement, mais le test unitaire de mon contrôleur est interrompu:

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

Je ne comprends pas pourquoi?

J'utilise yeoman et teste avec le 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 ceci dans le contrôleur de test, produit la 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 cet article Comment puis-je tester les contrôleurs avec Angular Translate initialisé dans App Config? mais ne m'a pas aidé: /

J'utilise abondamment $ httpBackend dans mes tests et cela fonctionne bien, mais dans ce cas, il est inefficace. Si je commente la ligne:

$translateProvider.preferredLanguage('en');

évidemment une erreur, si j'ajoute le runtime (dans mes contrôleurs)

$translate.uses(local);

Je me retrouve avec la même erreur?

Je passe donc à la configuration de la traduction (configTranslate.js) ou au moment de l'exécution, le résultat est le même:

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

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

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

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é d'appliquer $

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

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

Si vous avez une suggestion

40
bin

c’est un problème connu, veuillez suivre la documentation ici: unit testing angular

La solution 

Malheureusement, ce problème est dû à la conception de angulaire-traduire. Pour contourner ces erreurs, tout ce que nous pouvons faire est de écrasez la configuration de notre module dans notre suite de tests, elle ne le fait pas utiliser le chargeur asynchrone du tout. Lorsqu'il n'y a pas de chargeur asynchrone, il n'y a pas de XHR et donc pas d'erreur.

Alors, comment pouvons-nous écraser la configuration de notre module au moment de l’exécution pour notre suite de tests? Lors de l'instanciation d'un module angulaire, on peut toujours appliquer une fonction en ligne qui est exécutée en tant que 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.

En utilisant le fournisseur $ supply, nous pouvons créer une fabrique de chargeurs personnalisée, 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 lire plus dans le lien ci-dessus fourni.

28
nolimit

Nous avons adopté l’approche consistant à ignorer le chargeur de traduction dans les tests unitaires, plutôt que d’être obligés de modifier chacun des fichiers de spécification.

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 le karma.

Ainsi, par exemple, vous pouvez créer un fichier app-i18n-loader.js (toutes les autres configurations de module ont lieu dans un fichier différent):

    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 fichier karma.conf.js, excluez 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'
    ],

(Remarque: répondez modifié à une solution qui ne nécessite pas de grunt/gulp).

15
Erez Cohen

Je voulais une solution, 

  1. qui n'était pas trop hacky 
  2. qui ne m'a pas obligé à changer le code de mon application réelle, 
  3. qui ne serait pas interférer avec la possibilité de charger des modules supplémentaires 
  4. et surtout, ce qui ne nécessiterait pas que je change tous les tests simples.

C'est ce que j'ai fini avec: 

// 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’ayez pas besoin des traductions réelles pour vos tests unitaires, cela fonctionne très bien. Il suffit de placer le beforeEach à un niveau global, de préférence dans son propre fichier dans le dossier de test. Il sera alors exécuté avant tous les autres tests.

12
hugo der hungrige

J'ai rencontré ce problème avec les tests de rapporteur. Ma solution était de se moquer des traductions comme ceci:

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

Maintenant, aucun fichier de langue n'est téléchargé, aucune chaîne n'est traduite et je teste simplement les clés de chaîne dans les spécifications:

expect(element(by.css('#title')).getText()).toEqual('TITLE_TEXT');
3
AndyTheEntity

Veuillez consulter https://github.com/PascalPrecht/angular-translate/blob/master/test/unit/service/loader-static-files.spec.js en tant que référence.

En général, je recommanderais d'utiliser un chargeur de traduction standard pour les tests unitaires (sans les complications liées au chargement de http), ce qui signifie que vous pouvez fournir les étiquettes avec $translateProvider.translations(). Pourquoi? Parce que vous n'avez pas à tester la fonctionnalité de téléchargement qui fait partie du projet de traduction angulaire.

3
knalli

Essayez de tester la méthode:

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 des exemples de Documents angulaires

3
Matti Lehtinen

Aucune des solutions ne fonctionnait pour moi mais je suis venu avec ces solutions:

1) Si vous devez utiliser scope.$apply(), ou si vous devez traiter des états dans votre test (après la $apply(), la deuxième approche ne fonctionnera plus), substituez les traductions de votre application avec la méthode $translateProvider.translations(), à l'aide d'un plugin pour charger 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 charger des fichiers JSON et le combiner avec $httpBackend pour charger votre fichier de paramètres régionaux lorsque 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 cela devrait être inférieur à votre beforeEach(module('myApp')); ou vous obtiendrez une erreur $injector.

1
Bruno Peres

J'ai fait un service simulé simple pour $ translate

$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
dam1

La réponse de 2016 à cela est de prétraiter votre json dans vos tests et de tester correctement les traductions sur vos directives. 

J'utilise karma-ng-json2js-préprocesseur. Suivez toutes les étapes pour configurer votre fichier karma.conf, puis dans votre fichier de test, ajoutez le fichier correspondant en tant que module, puis définissez ces informations 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 pouvez jouer avec la fonction dans le module/lib, mais en gros, cela supprime tous les tirets, mais les soulignements KEEPS dans un camelCase. Donc, en_us devient En_us.

Vous devrez également indiquer à votre test qu'il attend ce fichier en tant que fichier GEt.

    $httpBackend.expect('GET', '/l10n/english-translation.json').respond(200);
0
efwjames

J'utilise ce motif.

  • ApplicationModule définit la configuration de traduction angulaire régulière.
  • chargement du code de test '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
nulpulum

En retard à la table avec ceci, mais je l'ai contourné en précisant que Karma sert simplement les fichiers selon cette entrée dans karma.conf.js:

files: [
    ...
    {pattern: 'scripts/i18n/*.json', included: false, served: true},
    ...
]
0
TheFoot