mis*_*iso 3 unit-testing jasmine angularjs
我正在尝试测试下一个角度指令.它监听ui-route $ stateChange事件以显示进度指示器.
angular.module('sm.components.progress', ['ui.router'])
.directive('smProgress', function($rootScope) {
return {
restrict: 'E',
replace: true,
template: '<div class="in-progress" ng-if="isRouteLoading">' +
'<span>.</span><span>.</span><span>.</span>' +
'<div/>',
controller: function($scope) {
$scope.isRouteLoading = false;
$rootScope.$on('$stateChangeStart',
function() {
$scope.isRouteLoading = true;
});
$rootScope.$on('$stateChangeSuccess',
function() {
$scope.isRouteLoading = false;
});
}
};
});
Run Code Online (Sandbox Code Playgroud)
这是我的测试代码:
describe('smProgress', function() {
var $compile, $rootScope, element, scope;
beforeEach(module('sm.components.progress'));
beforeEach(inject(function(_$compile_, _$rootScope_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
scope = $rootScope.$new();
}));
afterEach(function() {
element.remove();
});
it('$rootScope.isRouteLoading should be false on start',
function() {
element = $compile('<sm-progress></sm-progress>')(scope);
scope.$digest();
expect(scope.isRouteLoading).toBe(false);
$rootScope.$emit('$stateChangeStart');
//The directive listen to the $stateChangeStart and
//scope.isRouteLoading become true as expected
expect(scope.isRouteLoading).toBe(true);
scope.$digest();
// checks if the template is rendered when ng-if evaluates true
// how?
}));
});
Run Code Online (Sandbox Code Playgroud)
模板中的ng-if开始计算false,因此该元素将从DOM中删除.当我手动发出$stateChangeStart事件时,指令监听器工作,但我找不到DOM中的元素.
当ng-iftrue再次评估时,如何检查模板是否添加到DOM ?
嗯,这是我找到的解决方案.
重要的是要注意两件事:
true.这是发生的事情:
在测试中,当编译指令时:
element = $compile('<sm-progress></sm-progress>')(scope);
Run Code Online (Sandbox Code Playgroud)
<sm-progress></sm-progress> 被替换为:
<div class="in-progress" ng-if="isRouteLoading">
<span>.</span><span>.</span><span>.</span>
<div/>
Run Code Online (Sandbox Code Playgroud)
同时,ng-if为false,因此最终呈现的代码只是一个注释.这就是ng-if如何自然地运作.
<!-- ngIf: isRouteLoading -->
Run Code Online (Sandbox Code Playgroud)
当$ stateChangeStart被触发时,scope.isRouteLoading转到true,这使得ng-if将模板重新插入DOM.但在这一点上元素等于:
<!-- ngIf: isRouteLoading -->
Run Code Online (Sandbox Code Playgroud)
是不可能在其中找到指令模板.
// THE TEST FAILS
var progressEl = angular.element(element).find('in-progress');
expect(progressEl.length).toBeGreaterThan(0);
Run Code Online (Sandbox Code Playgroud)
解决方案是将html文本包装在div或任何其他元素中.因此ng-if将在其中运行,我们可以检查当时发生的事情.
it('scope.isRouteLoading should be false on start', function() {
// wrap sm-progress with a div tag
element = $compile('<div><sm-progress></sm-progress></div>')(scope);
scope.$digest();
expect(scope.isRouteLoading).toBe(false);
// In this point element compiles to:
// <div class="ng-scope"><!-- ngIf: isRouteLoading --></div>
$rootScope.$emit('$stateChangeStart');
expect(scope.isRouteLoading).toBe(true);
scope.$digest();
// Now element compiles to:
// <div class="ng-scope">
// <!-- ngIf: isRouteLoading -->
// <div class="sm-progress ng-scope" ng-if="isRouteLoading">
// <span>.</span><span>.</span><span>.</span>
// </div>
// <!-- end ngIf: isRouteLoading -->
// </div>
// Now the test pass.
var progressEl = angular.element(element).find('.in-progress');
expect(progressEl.length).toBeGreaterThan(0); //element is present
});
Run Code Online (Sandbox Code Playgroud)
一个jsfiddle.net,代码工作http://jsfiddle.net/fwxgyjwc/13/
我希望我已经解释得很好.我的英文不是很好.