如何使用angular度平移进行unit testing
我从这里使用angular度翻译( http://pascalprecht.github.io/angular-translate/ ),它只是工作正常,但它打破了我的控制器的unit testingwhith错误:
Unexpected request: GET scripts/i18n/locale-en.json
我不明白为什么?
我使用自我testing和业力。
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', ... ];
控制器testing:
'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(...); ... });
如果我在testing控制器中添加这个,产品同样的错误:
httpMock.when('GET', 'scripts/i18n/locale-en.json').respond(200); httpMock.flush();
要么
httpMock.when('GET', 'scripts/i18n/locale-en.json').passThrough(); httpMock.flush();
我发现这篇文章我如何testing控制器angular度翻译初始化在应用程序configuration? 但没有帮助我:/
我广泛使用$ httpBackend在我的testing,它工作正常,但在这种情况下,它是无效的。 如果我评论这一行:
$translateProvider.preferredLanguage('en');
显然是一个错误,如果我添加运行时(在我的控制器)
$translate.uses(local);
我结束了同样的错误?
所以我转向翻译configuration(configTranslate.js)或在运行时是相同的结果:
Unexpected request: GET scripts/i18n/locale-en.json
这里是我testing的语法,在“beforeEach(inject(function(…});”
或者在testing中“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();
没有任何反应,但这个错误正在让我发疯。
如果您有任何build议
这是一个已知的问题,请按照这里的文档: unit testingangular度
解决scheme
不幸的是,这个问题是由angular度翻译的devise引起的。 为了解决这些错误,我们所能做的就是在我们的testing套件中覆盖我们的模块configuration,它根本不使用asynchronous加载器。 当没有asynchronous加载器时,没有XHR,因此没有错误。
那么我们如何在运行时为我们的testing套件覆盖我们的模块configuration呢? 当实例化一个angular度模块时,我们总是可以应用一个内联函数作为configuration函数来执行。 由于我们可以访问所有提供者,因此可以使用此configurationfunction覆盖模块configuration。
使用$ provide提供程序,我们可以构build一个自定义的加载程序工厂,然后使用它来代替静态文件加载程序。
beforeEach(module('myApp', function ($provide, $translateProvider) { $provide.factory('customLoader', function () { // loader logic goes here }); $translateProvider.useLoader('customLoader'); }));
请阅读上面提供的链接。
我们采取了在unit testing中忽略翻译加载器的方法,而不是被迫修改每个spec文件。
一种方法可以通过将加载程序configuration分离到单独的文件,然后在业障中排除它。
因此,例如,您可以创build一个文件app-i18n-loader.js(所有其他模块configuration发生在不同的文件中):
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' ],
(注意:编辑答案不需要咕噜/咕嘟咕嘟的解决scheme)。
我想要一个解决scheme,
- 这不是太hacky
- 这并不要求我改变我的实际应用程序代码,
- 这不会干扰加载附加模块的能力
- 最重要的是不需要我改变每一个testing。
这是我结束了:
// 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 () { }; }));
假设你不需要unit testing的实际翻译,这很好。 只需将beforeEach放在全局级别上,最好在testing文件夹内的自己的文件中。 它会在每隔一个testing之前执行。
尝试把testing方法:
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文档的示例
请参考PascalPrecht/angular-translate/blob/master/test/unit/service/loader-static-files.spec.html 。
一般来说,我build议使用一个标准的翻译加载程序进行unit testing(没有http加载的麻烦),这意味着您可以使用$translateProvider.translations()
提供标签。 为什么? 因为您不必testing作为angular度转换项目一部分的远程加载function。
我用量angular器testing遇到了这个问题。 我的解决scheme是嘲笑这样的翻译:
angular.module('app') .config(function ($translateProvider) { $translateProvider.translations('en', {}); $translateProvider.preferredLanguage('en'); })
现在没有下载语言文件,没有string被翻译,我只是在规格的string键testing:
expect(element(by.css('#title')).getText()).toEqual('TITLE_TEXT');
没有解决scheme为我工作,但我带着这些解决scheme:
1)如果你需要使用scope.$apply()
,或者应该在你的testing中处理状态(在$apply()
第二种方法不起作用),用$translateProvider.translations()
覆盖你的应用程序的翻译。方法,使用插件来加载JSON文件
beforeEach(module(function ($translateProvider) { $translateProvider.translations('en', readJSON('scripts/i18n/locale-en.json')); }));
2)如果你testing过的控制器依赖于$translate
服务,你可以使用一个插件来加载JSON文件 ,并且在$httpBackend
和angular-translate请求它时加载你的语言环境文件。
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'));
之下beforeEach(module('myApp'));
否则你会得到$injector
错误。
我为$ 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
我使用这种模式。
- ApplicationModule设置了常规的angular-translateconfiguration。
- testing代码加载'testModule'而不是'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'); } })();
在这个桌子的karma.conf.js
,但是我通过指定Karma只是按照karma.conf.js
这个条目来提供文件来解决这个karma.conf.js
:
files: [ ... {pattern: 'scripts/i18n/*.json', included: false, served: true}, ... ]
2016年的答案是预处理你的JSON到你的testing中,并正确地testing你的指令翻译工作。
我使用karma-ng-json2js-preprocessor。 按照所有步骤设置你的karma.conf,然后在你的testing文件中,将相关文件作为模块添加,然后在$ 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'); }));
注意根据插件,它使用你的文件名来生成camelcased模块名称。 你可以使用模块的/ lib中的函数,但基本上它会删除所有的破折号,但KEEPS在camelCase中强调。 所以en_us变成了En_us。
您还需要告诉您的testing,它正在期待该文件为GEt。
$httpBackend.expect('GET', '/l10n/english-translation.json').respond(200);