unit-testing - unitarias - unit test angular 4
¿Cómo funciona la unidad de prueba con angular-translate (11)
Tengo usos de traducción angular desde aquí ( http://pascalprecht.github.io/angular-translate/ ) y funciona bien, pero rompe la prueba unitaria de mi controlador con Error:
Unexpected request: GET scripts/i18n/locale-en.json
No entiendo por qué?
Yo uso yeoman y pruebo con 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'',
...
];
prueba de controlador:
''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 agrego esto en el controlador de prueba, producto el mismo error:
httpMock.when(''GET'', ''scripts/i18n/locale-en.json'').respond(200);
httpMock.flush();
o
httpMock.when(''GET'', ''scripts/i18n/locale-en.json'').passThrough();
httpMock.flush();
encuentro esta publicación ¿Cómo pruebo los controladores con Angular Translate inicializado en App Config? pero no me ayudó: /
Uso extensivamente $ httpBackend en mis pruebas y funciona bien, pero en este caso no es efectivo. Si comento la línea:
$translateProvider.preferredLanguage(''en'');
obviamente un error, si agrego el tiempo de ejecución (en mis controladores)
$translate.uses(local);
Termino con el mismo error?
Así que me dirijo a la configuración de traducción (configTranslate.js) o en el tiempo de ejecución es el mismo resultado:
Unexpected request: GET scripts/i18n/locale-en.json
Aquí está la sintaxis que probé, ya sea en "beforeEach (inject (function (...});"
o en una prueba "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);
con al final
httpMock.flush();
También probé $ aplicar
httpMock.expectGET(''scripts/i18n/locale-fr.json'');
scope.$apply(function(){
$translate.uses(''fr'');
});
httpMock.flush();
no pasa nada, aún este error me está volviendo loco ...
Si tienes alguna sugerencia
Adoptamos el enfoque de ignorar el cargador de traducción en pruebas unitarias, en lugar de tener que modificar cada uno de los archivos de especificaciones.
Una forma de hacerlo podría ser separar la configuración del cargador en un archivo separado y luego excluirlo en karma.
Entonces, por ejemplo, puede crear una aplicación de archivo-i18n-loader.js (todas las demás configuraciones de módulos tienen lugar en un archivo diferente):
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'');
}
Y en su karma.conf.js excluye el archivo:
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''
],
(Nota: Respuesta editada a una solución que no requiere gruñido / trago).
Encontré este problema con las pruebas de transportador. Mi solución fue simular traducciones como esta:
angular.module(''app'')
.config(function ($translateProvider) {
$translateProvider.translations(''en'', {});
$translateProvider.preferredLanguage(''en'');
})
Ahora no se descargan archivos de idioma, no se traducen cadenas y solo pruebo contra las claves de cadena en las especificaciones:
expect(element(by.css(''#title'')).getText()).toEqual(''TITLE_TEXT'');
Hice un simple simulacro de servicio para $ translate
$translate=function (translation) {
return {
then: function (callback) {
var translated={};
translation.map(function (transl) {
translated[transl]=transl;
});
return callback(translated);
}
}
};
Ejemplo de uso aquí: https://gist.github.com/dam1/5858bdcabb89effca457
Intenta poner a prueba el método:
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
}
Ver ejemplos de documentos angulares
La respuesta de 2016 para esto es preprocesar su JSON en sus pruebas y probar adecuadamente las traducciones en sus directivas.
Yo uso karma-ng-json2js-preprocessor. Siga todos los pasos para configurar su karma.conf luego en su archivo de prueba, anteponga el archivo relevante como un módulo, luego configure esa información en $ 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'');
}));
Tenga en cuenta de acuerdo con el complemento, utiliza su nombre de archivo para generar un nombre de módulo camelcased. Puedes jugar con la función dentro de la / lib del módulo, pero básicamente elimina todos los guiones pero mantiene los guiones bajos en un camelCase. Entonces en_us se convierte en En_us.
También deberá indicarle a su prueba que está esperando ese archivo como un GE.
$httpBackend.expect(''GET'', ''/l10n/english-translation.json'').respond(200);
Llegó tarde a la mesa con esto, pero lo karma.conf.js
especificando que Karma simplemente sirve los archivos según esta entrada en karma.conf.js
:
files: [
...
{pattern: ''scripts/i18n/*.json'', included: false, served: true},
...
]
Ninguna de las soluciones funcionó para mí, pero vine con estas soluciones:
1) Si necesita usar scope.$apply()
, o debería tratar con estados en su prueba (después de que $apply()
el segundo enfoque no funcionará), anule las traducciones de su aplicación con las $translateProvider.translations()
método, usando un complemento para cargar archivos JSON
beforeEach(module(function ($translateProvider) {
$translateProvider.translations(''en'', readJSON(''scripts/i18n/locale-en.json''));
}));
2) Si su controlador probado depende del servicio $translate
, puede usar un complemento para cargar archivos JSON y combinarlo con $httpBackend
para cargar su archivo local cuando angular-translate lo solicite.
beforeEach(inject(function (_$httpBackend_) {
$httpBackend = _$httpBackend_;
$httpBackend.whenGET(''scripts/i18n/locale-en.json'').respond(readJSON(''scripts/i18n/locale-en.json''));
$httpBackend.flush();
})));
Tenga en cuenta que esto debe estar debajo de su beforeEach(module(''myApp''));
o recibirá un error de $injector
.
Por favor, eche un vistazo a https://github.com/PascalPrecht/angular-translate/blob/master/test/unit/service/loader-static-files.spec.js como referencia.
En general, recomendaría usar un cargador de traducción estándar para las pruebas unitarias (sin la molestia de las cargas http), lo que significa que puede proporcionar las etiquetas con $translateProvider.translations()
. ¿Por qué? Porque no tiene que probar la funcionalidad de carga remota que es parte del proyecto de traducción angular.
Yo quería una solución,
- que no era muy hacky
- que no requería que cambiara el código de mi aplicación actual,
- que no interferiría con la capacidad de cargar módulos adicionales
- y lo más importante, que no me obligaría a cambiar todas las pruebas.
Esto es con lo que terminé:
// 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 () {
};
}));
Suponiendo que no necesita las traducciones reales para las pruebas de su unidad, esto funciona muy bien. Simplemente coloque el beforeEach a nivel global, preferiblemente en su propio archivo dentro de la carpeta de prueba. Se ejecutará antes de cada otra prueba en ese momento.
Yo uso este patrón
- ApplicationModule establece regular angular-translate config.
- el código de prueba carga ''testModule'' en lugar 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");
})
})
es un problema conocido, por favor, siga la documentación aquí: unidad de prueba angular
La solución
Desafortunadamente, este problema es causado por el diseño de angular-translate. Para evitar estos errores, todo lo que podemos hacer es sobrescribir la configuración de nuestro módulo en nuestro banco de pruebas, que no utiliza ningún cargador asíncrono. Cuando no hay cargador asíncrono, no hay XHR y, por lo tanto, no hay error.
Entonces, ¿cómo sobrescribir la configuración de nuestro módulo en tiempo de ejecución para nuestro banco de pruebas? Al crear instancias de un módulo angular, siempre podemos aplicar una función en línea que se ejecuta como función de configuración. Esta función de configuración se puede utilizar para sobrescribir la configuración de los módulos ya que tenemos acceso a todos los proveedores.
Usando el proveedor $ provide, podemos construir una fábrica de carga personalizada, que luego se debe usar en lugar del cargador de archivos estáticos.
beforeEach(module(''myApp'', function ($provide, $translateProvider) {
$provide.factory(''customLoader'', function () {
// loader logic goes here
});
$translateProvider.useLoader(''customLoader'');
}));
Por favor, lea más en el enlace anterior proporcionado.