Тестирование Angular 1.X Jasmine: использование переводчика в скомпилированном шаблоне, директиве или компоненте

Ожидание: при тестировании template или выводимых directive / component переводы, такие как <h1>{{home.title | translate}}</h1>, должны быть переведены, чтобы отображался фактический текст <h1>Home Page</h1>.

Теперь, после долгих поисков, я смог заставить его работать, вручную помещая нужные мне переводы в свой тест.

Пример. Текущий тест использует ручную настройку перевода в тесте. Здесь ключ $translateProvider.translations.

(function() {
'use strict';

describe('home component', function() {
  var rootscope, compile, directiveElement;
  beforeEach(module('Templates'));
  beforeEach(module('myApp'));

  beforeEach(module('tmh.dynamicLocale'), function () {
    tmhDynamicLocaleProvider.localeLocationPattern('base/angular/i18n/angular-locale_{{locale}}.js');
  });

  beforeEach(module('pascalprecht.translate', function ($translateProvider) { 
    $translateProvider.translations('en', {
      "home":{
         "title": "Home page"
      }
    });
  }));

  beforeEach(inject(function(_$rootScope_, _$compile_) {
    rootscope = _$rootScope_.$new();
    compile = _$compile_;
  }));

  function getCompiledElement(){
    var element = angular.element('<home-component></home-component');
    var compiledElement = compile(element)(rootscope);
    rootscope.$digest();
    return compiledElement;
  }

  describe('home', function () {
    it('should have template defined', function () {
     directiveElement = getCompiledElement();
     console.log('my component compiled', directiveElement);
    });
  });
 });
 })();

Output generated is correct:

Домашняя страница

Теперь это выше компилирует мой компонент и показывает правильно переведенный текст вместо того, чтобы видеть фигурные скобки и ключи. Теперь в реалистичном приложении не очень удобно вручную брать нужные вам переводы и вставлять их, так как переводы могут измениться, и вы можете забыть обновить свой тест.

Я хотел бы, чтобы в моих тестах использовались фактические статические файлы перевода json

resources
  | locale-en_US.json

Я попытался использовать приведенный ниже код, однако, поскольку он асинхронный, он не загружается к тому времени, когда проходят тесты. Мне нужен способ либо дождаться загрузки файлов, либо другой способ загрузить файлы в папку $translateProvider.

$translateProvider.useStaticFilesLoader({
  prefix: 'app/resources/locale-', // path to translations files
  suffix: '.json'
});   

Я также попытался загрузить языковые файлы json через karma.conf.js, как показано ниже.

Files[
   ...
   { pattern: 'app/resources/angular-i18n/*.js', included: true, served: true },
   {pattern: 'app/resources/*.json', included: true, served: true},
 ]

Я знаю, что должен быть способ, которым это может работать, но я еще не нашел решения. Некоторые говорят, что их решение работает, однако я пробовал использовать разные плагины и, похоже, все еще не работает.

ОБНОВЛЕНИЕ: Пользовательский загрузчик

Я читаю о создании собственного загрузчика для $translateProvider. Я не уверен, как построить его, чтобы он работал так, как я хочу, чтобы его можно было правильно использовать для тестирования, но если другие изучают это, то это может быть местом для просмотра.

$provide.factory('customLoader', function ($q) {
    return function () {
      var deferred = $q.defer();
      deferred.resolve({});
      return deferred.promise;
    };
  });

  $translateProvider.useLoader('customLoader');

person L1ghtk3ira    schedule 13.02.2018    source источник
comment
Причина, по которой вы не нашли, скорее всего, в том, что это не обычный сценарий. Обычно это делается в тестах e2e на реальном DOM и реальных данных. Нет смысла тестировать компонентный блок таким образом.   -  person Estus Flask    schedule 13.02.2018
comment
Наш проект проходит много этапов тестирования. Во-первых, покрытие тестами должно постоянно увеличиваться, иначе наши сборки провалятся из-за решения наших менеджеров. Смысл этих тестов в том, чтобы с переданными данными убедиться, что компоненты и директивы строятся и ведут себя правильно. Требуются тесты HTML с нашей стороны, и это единственный способ для достойного теста в его скомпилированном виде. Наши файлы переводов все статичны, им нужны отключенные компоненты, чтобы они могли проверить в сценариях правильность переводов в рамках проверки авторов контента. Не мое решение.   -  person L1ghtk3ira    schedule 14.02.2018
comment
Является ли это также обычным, не имеет значения, поскольку оно меняется от проекта к проекту и требованиям.   -  person L1ghtk3ira    schedule 14.02.2018
comment
То, что вы описываете, - это то, для чего нужны тесты Protractor e2e. Выполнение этого в Karma и модульных тестах сделает их более хакерскими, медленными и менее эффективными. Более чистым подходом было бы имитировать канал translate и создавать выходные значения из файлов JSON, аналогично тому, как это делает сторонний переводчик, в то время как настоящий pascalprecht.translate никогда не включается.   -  person Estus Flask    schedule 14.02.2018
comment
Транспортир хорош, да, однако они хотят, чтобы тесты usit проверяли отдельные компоненты, а также их функциональность. Можете ли вы привести пример вашей идеи?   -  person L1ghtk3ira    schedule 14.02.2018
comment
Надеюсь это поможет. Что касается Protractor, вы не ограничены реальным приложением для тестирования. Нередко настраиваются дополнительные HTML-страницы и модули приложений для e2e/интеграционных тестов, чтобы вы могли протестировать все, что вам нужно, в производственной среде (даже если вы тестировали переводы с помощью Karma, нет гарантии, что файлы JSON будут загружаться в реальной жизни). .   -  person Estus Flask    schedule 14.02.2018
comment
Что касается того, как делать реальные запросы с помощью Karma и ngMock, см. тесты" title="выполнение реальных запросов к http-серверу в модульных интеграционных тестах angularjs"> stackoverflow.com/questions/42331583/   -  person Estus Flask    schedule 14.02.2018


Ответы (2)


Чтобы модульные тесты были наиболее эффективными для решения проблем, модуль должен быть изолирован от других модулей. Таким образом, если тест становится красным, можно однозначно определить неисправный блок. По этой причине предпочтительнее явно имитировать все в модульных тестах и ​​предоставлять фикстуры вместо того, чтобы полагаться на реальные данные.

Использование сторонних модулей нарушает изоляцию, потому что их проблемы (баги, версии пакетов) невозможно отличить от проблем в самих модулях. Это делает отладку более дорогостоящей.

Зависимость от pascalprecht.translate может быть устранена в модульных тестах путем предоставления ложного фильтра, который будет выполнять проверенные функции упрощенным и контролируемым способом. Чтобы протестировать реальные данные, Karma должна быть настроена для поддержки модулей, например. karma-commonjs, таким образом данные JSON могут быть загружены напрямую, а не через запрос XHR.

Lodash get хорошо подходит для анализа путей, разделенных точками, аналогично тому, как это делает служба перевода:

  var translationsEn = require('.../locale-en_US.json');
  ...
  beforeEach(angular.mock.module({ translateFilter: function (path) {
    return _.get(translationsEn, path);
  }));

Это будет имитировать фильтр translate (который является внутренней службой translateFilter) с простым обходным решением. Это будет работать до тех пор, пока значения не используют расширенные функции перевода, такие как множественное число.

В качестве альтернативы, реальную службу перевода можно исправить аналогично тому, как руководство предлагает использовать CommonJS для загрузки файлов JSON:

beforeEach(angular.mock.module(function ($translateProvider) {
  $translateProvider.translations('en', translationsEn);
}));

Вместо module следует использовать angular.mock.module, чтобы избежать конфликтов имен при использовании модулей CommonJS.

person Estus Flask    schedule 13.02.2018

Хорошо, копнув глубже, я думаю, что нашел решение, которое не слишком хакерское и довольно чистое. Сначала я установил и следовал инструкциям для этого плагина Karma-fixture https://www.npmjs.com/package/karma-fixture

После настройки фикстуры я вызываю глобальную переменную, созданную karma-fixture, и использую ее в своем тесте, чтобы получить нужный файл json, а также аккуратно загрузить его в $translateProvider.translations, как показано ниже.

Примечание. Шаблоны и приспособления, а также файлы важны:

beforeEach(module('pascalprecht.translate', function ($translateProvider) {

   $translateProvider.translations('en', 
     fixture.load('app/resources/locale-en_US.json')
   );

   $translateProvider.preferredLanguage('en');
}));

Полный рабочий пример модульного теста Jasmine, который компилирует компонент с переводами ниже.

Karma.conf.js: файл

module.exports = function(config) {
  'use strict';

   config.set({
     autoWatch: true,
     basePath: '../',

     frameworks: ['jasmine', 'fixture'],

     files: [
       // bower:js
       ... <-Bower files Here ->
       // endbower
       {pattern: 'app/resources/angular-i18n/*.js', included: true, served: true },
       {pattern: 'app/resources/*.json', included: true, served: true},
       'app/**/*.app.js',     // First load main module
       'app/**/*.module.js',  // Then Load all modules
       'app/**/*.tpl.html',   // Then Load all html pages
       'app/**/!(*.spec).js', // Then load all javascript files that are not tests
       'app/**/*.spec.js'     // Finally load all tests
     ],

     exclude: [
       'app/css/**/*.js',
       'app/js/**/*.js',
       'app/styles/vendor/**/*.js'
     ],

     port: 8080,

     browsers: ['PhantomJS'],

     plugins: [
       'karma-phantomjs-launcher',
       'karma-chrome-launcher', 
       'karma-jasmine',
       'karma-coverage',
       'karma-ng-html2js-preprocessor',
       'karma-json-fixtures-preprocessor',
       'karma-fixture'
     ],

     preprocessors: {
       'app/**/*.js': 'coverage',
       'app/**/*.html': 'ng-html2js',
       'app/resources/*.json'   : ['json_fixtures']
     },

     ngHtml2JsPreprocessor: {
       'moduleName': 'Templates',
       'stripPrefix': 'app/'
     },

     jsonFixturesPreprocessor: {variableName: '__json__'},

     reporters: ['progress', 'coverage'],
     singleRun: false,
     colors: true,
     logLevel: config.LOG_INFO,
   });
 };

Jasmine Unit Test: базовые компоненты с использованием компонентов и переводов.

(function() {
  'use strict';

  describe('Home component', function() {
    var rootscope, compile, componentElement;

    beforeEach(module('Templates'));
    beforeEach(module('app'));

    beforeEach(module('pascalprecht.translate', function ($translateProvider) {
      $translateProvider.translations('en', 
        fixture.load('app/resources/locale-en_US.json')
      );

      $translateProvider.preferredLanguage('en');
    }));

    beforeEach(inject(function(_$rootScope_, _$compile_) {
      rootscope = _$rootScope_.$new();
      compile = _$compile_;
    }));

    function getCompiledElement(){
      var element = angular.element('<home-component></home-component>');
      var compiledElement = compile(element)(rootscope);
      rootscope.$digest();
      return compiledElement;
    }

    describe('Home tests', function () {

      it('should have component defined', function () {
        componentElement = getCompiledElement();
        console.log('Compiled component with translations', componentElement);
        expect(componentElement).toBeDefined()
      });
    });
  });
})();

Приведенная выше реализация скомпилирует ваш компонент (может передавать привязки и т. д.), а также использует переводчик и покажет ваши переводы в вашем компоненте в консоли.

Вместо <h1>{{home.title | translate}}</h1> теперь вы увидите <h1>Home Page</h1>

person L1ghtk3ira    schedule 13.02.2018