web-dev-qa-db-ja.com

角度変換でユニットテストを行う方法

私はangularここから翻訳します( http://pascalprecht.github.io/angular-translate/ )を使用しています。エラーのユニットテスト:

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

なぜか分かりませんか?

私はヨーマンを使い、カルマでテストします。

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',

  ...

];

コントローラーテスト:

'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(...);

  ...

});

これをテストコントローラーに追加すると、製品と同じエラーが発生します。

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

または

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

私はこの投稿を見つけます Angular App Configで初期化された翻訳? でコントローラーをテストするにはどうすればいいですか?

私はテストで広範囲に$ httpBackendを使用し、正常に動作しますが、この場合は効果がありません。行をコメントした場合:

$translateProvider.preferredLanguage('en');

ランタイムに追加すると(コントローラーで)明らかにエラー

$translate.uses(local);

同じエラーが発生しますか?

したがって、翻訳構成(configTranslate.js)を使用するか、実行時に同じ結果になります。

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

これは、「beforeEach(inject(function(...});」のいずれかでテストした構文です。

またはテストで「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);

最後に

httpMock.flush();

私も$を適用しようとしました

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

何も起こらない、それでもこのエラーは私を夢中にさせる..

何か提案があれば

40
bin

これは既知の問題です。こちらのドキュメントに従ってください: nit testing angular

解決策

残念ながら、この問題は角度変換の設計が原因です。これらのエラーを回避するためにできることは、テストスイートのモジュール構成を上書きすることです。非同期ローダーをまったく使用しません。非同期ローダーが存在しない場合、XHRは存在しないため、エラーは発生しません。

では、テストスイートの実行時にモジュール構成をどのように上書きしますか? angularモジュールをインスタンス化するとき、構成関数として実行されるインライン関数を常に適用できます。この構成関数は、すべてのプロバイダーにアクセスできるため、モジュール構成を上書きするために使用できます。

$ provideプロバイダーを使用して、静的ファイルローダーの代わりに使用するカスタムローダーファクトリを構築できます。

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

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

  $translateProvider.useLoader('customLoader');

}));

上記のリンクの詳細をご覧ください。

28
nolimit

各仕様ファイルを強制的に変更するのではなく、単体テストで翻訳ローダーを無視するアプローチを取りました。

これを行う1つの方法は、ローダー構成を個別のファイルに分離し、カルマで除外することです。

たとえば、app-i18n-loader.jsファイルを作成できます(他のすべてのモジュール構成は別のファイルで行われます)。

    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');
}

そして、karma.conf.jsでファイルを除外します:

        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'
    ],

(注:grunt/gulpを必要としないソリューションに編集された回答)。

15
Erez Cohen

私は解決策が欲しかった、

  1. あまりハックではなかった
  2. 実際のアプリケーションコードを変更する必要はありませんでしたが、
  3. 追加のモジュールをロードする機能を妨げません
  4. そして最も重要なことは、すべてのテストを変更する必要がないことです。

これが私がやったことです:

// 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 () {
    };
}));

単体テストに実際の翻訳が必要ないと仮定すると、これはうまく機能します。 beforeEachをグローバルレベルに配置するだけで、できればテストフォルダー内の独自のファイルに配置してください。その後、他のすべてのテストの前に実行されます。

12

分度器テストでこの問題に遭遇しました。私の解決策は、このような翻訳をモックすることでした:

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

言語ファイルはダウンロードされず、文字列は翻訳されず、仕様の文字列キーに対してテストするだけです:

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

参考として https://github.com/PascalPrecht/angular-translate/blob/master/test/unit/service/loader-static-files.spec.js をご覧ください。

一般に、ユニットテストに標準の翻訳ローダーを使用することをお勧めします(httpのロードの手間なし)。つまり、ラベルに$translateProvider.translations()を指定できます。どうして?角度変換プロジェクトの一部であるリモート読み込み機能をテストする必要がないためです。

3
knalli

テストメソッドに入れてみてください:

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
}

Angular docs の例を参照してください

3
Matti Lehtinen

どのソリューションも私にとってはうまくいきませんでしたが、私はこれらのソリューションを用意しました。

1)scope.$apply()を使用する必要がある場合、またはテストの状態を処理する必要がある場合($apply()の後、2番目のアプローチは機能しません)、アプリの翻訳を$translateProvider.translations()メソッド、プラグインを使用して JSONファイルをロード

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

2)テスト済みのコントローラーが_$translate_サービスに依存している場合、プラグインを使用して JSONファイルをロード し、それを_$httpBackend_と組み合わせて、角度変換リクエスト時にロケールファイルをロードできますそれ。

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

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

これはbeforeEach(module('myApp'));の下にある必要があります。そうでないと、_$injector_エラーが発生します。

1
Bruno Peres

$ translateの簡単な模擬サービスを作成しました

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

ここの使用例: https://Gist.github.com/dam1/5858bdcabb89effca457

1
dam1

これに対する2016年の答えは、jsonをテストに前処理し、適切にテスト翻訳がディレクティブで機能するようにすることです。

Karma-ng-json2js-preprocessorを使用します。すべての手順に従ってkarma.confをセットアップし、次にテストファイルで、関連ファイルをモジュールとして追加し、その情報を$ 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');
}));

プラグインによると、ファイル名を使用してキャメルケース化されたモジュール名を生成します。モジュールの/ lib内の関数を使用できますが、基本的にはすべてのダッシュが削除されますが、キャメルケースではKEEPSがアンダースコアになります。したがって、en_usはEn_usになります。

また、そのファイルをGEtとして期待していることをテストに伝える必要があります。

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

このパターンを使用します。

  • ApplicationModuleは、通常の角度変換設定を設定します。
  • テストコードは「applicationModule」の代わりに「testModule」をロードします
// 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

これでテーブルに遅れましたが、カルマはkarma.conf.js

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