jasmine 是否可以访问 angular 指令控制器中定义的范围方法?

Is it possible for jasmine to access the scope methods defined in an angular directive controller?

我正在尝试对如下指令进行单元测试。我希望能够调用指令控制器 ($scope.save) 中定义的函数,但我的测试似乎根本无法访问该范围。

我也尝试了 isolateScope(),但是没有像我预期的那样 return 内部 $scope 对象。也许我做错了。

如何在下面的示例中获取 myDir 的 $scope? (或者如果您愿意,可以在 fiddle 处:http://jsfiddle.net/lalanl/8fkdsme3/

    angular.module('app', []).directive([
      function(myService) {

        return {
          restrict: 'E',
          templateUrl: 'path/to/template.html',
          scope: {
            info: "=info"
          },
          controller: function($scope) {
            $scope.someVal = 'porcupine';
            $scope.save = function() { /* ... */ };
          }
        }
      }
    ]);

    describe('myDir', function() {

      var $compile, $http, $httpBackend, $scope, $rootScope, $q;
      var element;

      beforeEach(module('app'));
      beforeEach(function() {
        inject(function(_$compile_, _$http_, _$httpBackend_, _$rootScope_, _$q_) {
          $compile = _$compile_;
          $http = _$http_;
          $httpBackend = _$httpBackend_;
          $rootScope = _$rootScope_;
          $scope = $rootScope.$new();
          $q = _$q_;
        });

        $scope.data = "some data";
        element = $compile(angular.element('<my-dir info="data"></my-dir>'), $scope);
        $rootScope.$digest();
        $httpBackend.whenGET('path/to/template.html').respond('<div>test:{{someVal}}</div>');
      });

      it('should let me see its guts', function() {
        expect($scope.save).toBeTruthy();
        expect(typeof $scope.save).toBe("function");
      });

    });
<html>

<head>
  <style type="text/css">
    @charset "UTF-8";
    [ng\:cloak],
    [ng-cloak],
    [data-ng-cloak],
    [x-ng-cloak],
    .ng-cloak,
    .x-ng-cloak,
    .ng-hide:not(.ng-hide-animate) {
      display: none !important;
    }
    ng\:form {
      display: block;
    }
  </style>
  <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  <title>SO question - jsFiddle demo</title>

  <script type="text/javascript" src="/js/lib/dummy.js"></script>




  <link rel="stylesheet" type="text/css" href="/css/result-light.css">


  <script type="text/javascript" src="http://jasmine.github.io/2.0/lib/jasmine.js"></script>



  <script type="text/javascript" src="http://jasmine.github.io/2.0/lib/jasmine-html.js"></script>



  <script type="text/javascript" src="http://jasmine.github.io/2.0/lib/boot.js"></script>



  <link rel="stylesheet" type="text/css" href="http://jasmine.github.io/2.0/lib/jasmine.css">



  <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.4/angular.js"></script>



  <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.4/angular-mocks.js"></script>


  <style type="text/css">
  </style>



  <script type="text/javascript">
    //<![CDATA[ 

    angular.module('app', []).directive([
      function(myService) {

        return {
          restrict: 'E',
          templateUrl: 'path/to/template.html',
          scope: {
            info: "=info"
          },
          controller: function($scope) {
            $scope.someVal = 'porcupine';
            $scope.save = function() { /* ... */ };
          }
        }
      }
    ]);

    describe('myDir', function() {

      var $compile, $http, $httpBackend, $scope, $rootScope, $q;
      var element;

      beforeEach(module('app'));
      beforeEach(function() {
        inject(function(_$compile_, _$http_, _$httpBackend_, _$rootScope_, _$q_) {
          $compile = _$compile_;
          $http = _$http_;
          $httpBackend = _$httpBackend_;
          $rootScope = _$rootScope_;
          $scope = $rootScope.$new();
          $q = _$q_;
        });

        $scope.data = "some data";
        element = $compile(angular.element('<my-dir info="data"></my-dir>'), $scope);
        $httpBackend.whenGET('path/to/template.html').respond('<div>test:{{someVal}}</div>');
      });

      it('should let me see its guts', function() {
        expect($scope.save).toBeTruthy();
        expect(typeof $scope.save).toBe("function");
      });

    });
     //]]>
  </script>


</head>

<body>

</body>

</html>

这是更新后的 jsfiddle,您的测试使用内联指令模板。

编辑:设法让它与外部模板一起工作。我忘了刷新我的 http 请求。我已经更新了完整的答案,你可以看到更新后的 jsfiddle here

您的测试存在一些问题:-

  1. 在指令定义中,您没有给出指令名称。服务 myService 没有注入到你的指令中,但由于你没有正确定义你的指令,你没有得到那个错误。
  2. 此外,您在尝试使用之前删除的 element.isolateScope() 来获取元素的范围时是正确的。这是因为该指令将创建一个新的隔离范围。
  3. 您需要执行 $httpBackEnd.flush() 来刷新外部模板的待处理请求。由于您没有这样做,您测试中的 $compile 没有看到模板并且没有正确编译它。

我还做了一些其他小改动以使其正常工作。

P.S。如果您在项目中使用 karma,我会使用 karma-ng-html2js-preprocessor 来使用外部模板测试指令。看看here.

angular.module('app', []).directive('myDir', function () {

    return {
        restrict: 'E',
        //template: '<div>test:{{someVal}}</div>',
        templateUrl: 'path/to/template.html',
        scope: {
            info: "=info"
        },
        controller: function ($scope) {
            $scope.someVal = 'porcupine';
            $scope.save = function () { /* ... */
            };
        }
    }
});

describe('myDir', function () {

    var $compile, $scope, $rootScope;
    var element, isolatedScope;

    beforeEach(module('app'));
    beforeEach(inject(function (_$compile_, _$rootScope_, $httpBackend) {
        $httpBackend.whenGET('path/to/template.html').respond('<div>test:{{someVal}}</div>');

        $compile = _$compile_;
        $rootScope = _$rootScope_;
        $scope = $rootScope.$new();

        element = $compile('<my-dir info="data"></my-dir>')($scope);
        $httpBackend.flush();
        isolatedScope = element.isolateScope();
    }));

    it('should let me see its guts', function () {
        expect(isolatedScope.save).toBeTruthy();
        expect(typeof isolatedScope.save).toBe("function");
    });

});