javascript - tutorial - Vistas de prueba unitarias: mejores prácticas
unit test angular 4 (1)
Creo que lo que estás haciendo es una gran manera de ver las pruebas unitarias. El código en su pregunta es una buena receta para alguien que busca vistas de pruebas unitarias.
1. ng-repeat $$ hashKey
No te preocupes por los datos. En cambio, prueba el resultado de varias operaciones, porque eso es lo que realmente te importa al final del día. Por lo tanto, use jasmine-jquery para verificar el estado del DOM después de la creación del controlador, y después de click()
simulado click()
s, etc.
2. $ scope = $ rootScope. $ New () no es ningún problema
$rootScope
es una instancia de Scope , mientras que $rootScope.$new()
crea una instancia de ChildScope . Las pruebas con una instancia de ChildScope son técnicamente más correctas porque, en producción, los ámbitos del controlador también son ejemplos de ChildScope .
Por cierto, lo mismo ocurre con las directivas de pruebas unitarias que crean ámbitos aislados. Cuando $compile
su directiva con una instancia de ChildScope , se creará automáticamente un ámbito aislado (que es una instancia de Scope ). Puede acceder a ese alcance aislado con element.isolateScope()
// decalare these variable here so we have access to them inside our tests
var element, $scope, isolateScope;
beforeEach(inject(function($rootScope, $compile) {
var html = ''<div my-directive></div>'';
// this scope is an instance of ChildScope
$scope = $rootScope.$new();
element = angular.element(html);
$compile(element)($scope);
$scope.$digest();
// this scope is an instance of Scope
isolateScope = element.isolateScope();
}));
3. +1 vistas de prueba
Algunas personas dicen vistas de prueba con Protractor. El transportador de impulsos es excelente cuando se quiere probar toda la pila: de la parte frontal a la parte de atrás. Sin embargo, el transportador es lento y las pruebas unitarias son rápidas. Es por eso que tiene sentido probar sus puntos de vista y directivas con pruebas unitarias al burlarse de cualquier parte de la aplicación que dependa del back-end.
Las directivas son altamente comprobables por unidad. Controladores menos. Los controladores pueden tener muchas partes móviles y esto puede hacer que sean más difíciles de probar. Por esta razón, estoy a favor de crear directivas a menudo. El resultado es un código más modular que es más fácil de probar.
¿Alguien puede compartir experiencia con vistas de prueba de unidad? Leí muchos tutoriales sobre cómo hacer pruebas unitarias con vistas, pero todo tiene algunos inconvenientes.
Llegué con el siguiente enfoque. Funciona, pero me pregunto si hay una mejor manera de hacerlo. También hay algunos inconvenientes, que explicaré más adelante. También estoy haciendo pruebas E2E con transportador, pero siempre son lentas y, por lo tanto, las limito al mínimo.
Este es mi controlador. Tiene dos variables vinculadas a su $scope
que se usan en la vista:
// test_ctrl.js
angular.module(''app'', [])
.controller(''TestCtrl'', ["$rootScope", "$scope", function ($rootScope, $scope) {
$scope.bar = "TEST";
$scope.jobs = [
{name: "cook"}
];
}]);
La vista lleva $scope.bar
a una <span>
y la matriz $scope.jobs
a una directiva ng-repeat
:
<!-- test.html the view for this controller -->
<span>
Bar is {{bar || "NOT SET"}}
</span>
<ul>
<li ng-repeat="job in jobs">{{job.name}}</li>
</ul>
Y esta es la prueba:
describe(''Controller: TestCtrl'', function () {
beforeEach(module(''templates''));
beforeEach(module(''app''));
var TestCtrl, $rootScope, $compile, createController, view, $scope;
beforeEach(inject(function($controller, $templateCache, _$rootScope_, _$compile_, _$httpBackend_) {
$rootScope = _$rootScope_;
$scope = $rootScope.$new();
$compile = _$compile_;
createController = function() {
var html = $templateCache.get(''views/test.html'');
TestCtrl = $controller(''TestCtrl'', { $scope: $scope, $rootScope: $rootScope });
view = $compile(angular.element(html))($scope);
$scope.$digest();
};
}));
it(''should test the view'', function() {
createController();
expect(view.find("li").length).toEqual(1)
console.log($scope.jobs)
});
});
En la función beforeEach
, configuraré el controlador. La función createController
(que se llama desde las pruebas en sí) saca una vista de $templateCache
, crea un controlador con su propio $scope
, luego compila la plantilla y desencadena un $digest
.
La memoria caché de la plantilla está precargada con el preprocesador karmas ng-html2js
// karma.conf.js
...
preprocessors: {
''app/views/*.html'': ''ng-html2js''
}
...
Con este enfoque, tengo un pequeño problema y algunas preguntas:
1. Teclas adicionales hashKey $$ en mis objetos desde ng-repeat
The expect($scope.jobs).toEqual([{name: "cook"}]);
en mi prueba arroja un error:
Expected [ { name : ''cook'', $$hashKey : ''009'' } ] to equal [ { name : ''cook'' } ]
Sé que ng-repeat
agrega estas teclas, pero esto es una tontería de prueba. La única forma en que puedo pensar es en separar las pruebas del controlador y las pruebas de vista. Pero cuando $$hashKey
la matriz de jobs
dentro de mi controlador, $$hashKey
no está presente. Alguna idea, por que esto esta pasando?
2. Problema de $ alcance
Cuando probé esto por primera vez, solo tenía mi alcance local definido como $scope={}
y no $scope = $rootScope.$new()
, como lo hice en mis otras pruebas de controlador. Pero con un simple objeto como ámbito local, no pude compilarlo ( $compile(angular.element(html))($scope);
arrojó un error).
También pensé que si era una buena idea pasar el $rootScope
como el alcance local actual para el controlador. ¿Es este un buen enfoque? ¿O hay algún inconveniente, todavía no he visto?
3. Mejores prácticas
Me gustaría saber cómo todos hacen pruebas unitarias en AngularJS. Creo que los puntos de vista tienen que ser probados, porque con todas las directivas angulares, hay mucha lógica en ellos, que me complacería ver a prueba de agua;)