我有一个我想要进行单元测试的指令,但我遇到的问题是我无法访问我的隔离范围。这是指令:
<my-directive></my-directive>
背后的代码:
angular.module('demoApp.directives').directive('myDirective', function($log) {
return {
restrict: 'E',
templateUrl: 'views/directives/my-directive.html',
scope: {},
link: function($scope, iElement, iAttrs) {
$scope.save = function() {
$log.log('Save data');
};
}
};
});
这是我的单位测试:
describe('Directive: myDirective', function() {
var $compile, $scope, $log;
beforeEach(function() {
// Load template using a Karma preprocessor (http://tylerhenkel.com/how-to-test-directives-that-use-templateurl/)
module('views/directives/my-directive.html');
module('demoApp.directives');
inject(function(_$compile_, _$rootScope_, _$log_) {
$compile = _$compile_;
$scope = _$rootScope_.$new();
$log = _$log_;
spyOn($log, 'log');
});
});
it('should work', function() {
var el = $compile('<my-directive></my-directive>')($scope);
console.log('Isolated scope:', el.isolateScope());
el.isolateScope().save();
expect($log.log).toHaveBeenCalled();
});
});
但是当我打印出孤立的范围时,会产生undefined
。令我感到困惑的是,如果不是templateUrl
我只是在我的指令中使用template
,那么一切正常:isolateScope()
有一个完整的scope
对象作为其返回值,一切都很棒。然而,不知何故,当使用templateUrl
时,它会中断。这是ng-mocks
还是Karma预处理器中的错误?
提前致谢。
答案 0 :(得分:17)
我遇到了同样的问题。似乎在使用$compile(element)($scope)
调用templateUrl
时,摘要周期不会自动启动。因此,您需要手动设置它:
it('should work', function() {
var el = $compile('<my-directive></my-directive>')($scope);
$scope.$digest(); // Ensure changes are propagated
console.log('Isolated scope:', el.isolateScope());
el.isolateScope().save();
expect($log.log).toHaveBeenCalled();
});
我不确定$compile
功能为什么不为你做这件事,但它必须与templateUrl
的工作方式有一些特殊性,因为你没有&#39 ;如果您使用内嵌模板,则需要调用$scope.$digest()
。
答案 1 :(得分:12)
我必须在$httpBackend
被定义之前模拟并刷新isolateScope()
。请注意,$scope.$digest()
没有任何区别。
指令:
app.directive('deliverableList', function () {
return {
templateUrl: 'app/directives/deliverable-list-directive.tpl.html',
controller: 'deliverableListDirectiveController',
restrict = 'E',
scope = {
deliverables: '=',
label: '@'
}
}
})
试验:
it('should be defined', inject(function ($rootScope, $compile, $httpBackend) {
var scope = $rootScope.$new();
$httpBackend.expectGET('app/directives/deliverable-list-directive.tpl.html').respond();
var $element = $compile('<deliverable-list label="test" deliverables="[{id: 123}]"></deliverable-list>')(scope);
$httpBackend.flush();
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
expect($element).toBeDefined();
expect($element.controller).toBeDefined();
scope = $element.isolateScope();
expect(scope).toBeDefined();
expect(scope.label).toEqual('test');
expect(scope.deliverables instanceof Array).toEqual(true);
expect(scope.deliverables.length).toEqual(1);
expect(scope.deliverables[0]).toEqual({id: 123});
}));
我正在使用Angular 1.3。
答案 2 :(得分:1)
您可以配置karma-ng-html2js-preprocessor插件。它会将HTML模板转换为javascript字符串并将其放入Angular的$templateCache
服务中。
在配置中设置moduleName
后,您可以在测试中声明模块,然后所有生产模板都可用,而无需在任何地方使用$httpBackend
进行模拟。
beforeEach(module('partials'));
您可以在此处找到如何设置插件:http://untangled.io/how-to-unit-test-a-directive-with-templateurl/
答案 3 :(得分:0)
就我而言,在我试图在没有隔离范围属性的指令上隔离范围的情况下,我一直遇到这种情况。
function testDirective() {
return {
restrict:'EA',
template:'<span>{{ message }}</span>'
scope:{} // <-- Removing this made an obvious difference
};
}
function testWithoutIsolateScopeDirective() {
return {
restrict:'EA',
template:'<span>{{ message }}</span>'
};
}
describe('tests pass', function(){
var compiledElement, isolatedScope, $scope;
beforeEach(module('test'));
beforeEach(inject(function ($compile, $rootScope){
$scope = $rootScope.$new();
compiledElement = $compile(angular.element('<div test-directive></div>'))($scope);
isolatedScope = compiledElement.isolateScope();
}));
it('element should compile', function () {
expect(compiledElement).toBeDefined();
});
it('scope should isolate', function () {
expect(isolatedScope).toBeDefined();
});
});
describe('last test fails', function(){
var compiledElement, isolatedScope, $scope;
beforeEach(module('test'));
beforeEach(inject(function ($compile, $rootScope){
$scope = $rootScope.$new();
compiledElement = $compile(angular.element('<div test-without-isolate-scope-directive></div>'))($scope);
isolatedScope = compiledElement.isolateScope();
}));
it('element should compile', function () {
expect(compiledElement).toBeDefined();
});
it('scope should isolate', function () {
expect(isolatedScope).toBeDefined();
});
});