使用templateUrl时,isolateScope()返回undefined

时间:2014-02-05 13:30:45

标签: angularjs unit-testing angularjs-directive karma-runner

我有一个我想要进行单元测试的指令,但我遇到的问题是我无法访问我的隔离范围。这是指令:

<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预处理器中的错误?

提前致谢。

4 个答案:

答案 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();
  });
});