Javascript 如何模拟在控制器初始化时运行的函数?

Javascript 如何模拟在控制器初始化时运行的函数?,javascript,angularjs,unit-testing,karma-runner,Javascript,Angularjs,Unit Testing,Karma Runner,控制器: function myCtrl($scope) { $scope.firstFn = function() { console.log('firstFn called'); $scope.test1 = 2; }; $scope.secondFn = function() { console.log('secondFn called'); $scope.test2 = 3; };

控制器:

function myCtrl($scope) {
    $scope.firstFn = function() {
        console.log('firstFn called');
        $scope.test1 = 2;
    };

    $scope.secondFn = function() {
        console.log('secondFn called');
        $scope.test2 = 3;
    };

    (function constructor() {
        console.log('Controller init.');
        $scope.test1 = 1;
        $scope.test2 = 2;
        $scope.firstFn();
    })();
};
是否可以在不执行“firstFn”的情况下为名为“secondFn”的函数编写单元测试? 有可能嘲笑它吗

scope = $rootScope.$new();
ctrl = $controller('myCtrl', {$scope: scope});
// Mock firstFn ?
scope.secondFn();
// assert $scope.test1 = 1
// assert $scope.test2 = 3

只需监视
firstFn
。默认情况下,对spied on函数的任何调用都将是对Jasmine spy对象的调用,而不是对实际函数的调用

scope = $rootScope.$new();
ctrl = $controller('myCtrl', {$scope: scope});

spyOn(scope, 'firstFn');
scope.secondFn();
// assert $scope.test1 = 1
// assert $scope.test2 = 3
有关对间谍行为的更多控制,请参见你需要:

  • 在创建控制器之前,在
    $scope
    上创建模拟版本的
    firstFn
  • 防止控制器更换模拟控制器
  • 除非您正在测试像IE8这样的旧浏览器,否则您可以通过使用Jasmine的
    createSpy
    和ECMAScript 5方法
    Object.defineProperty()
    来实现这一点

    请注意,以下示例使用的是Jasmine 2.0

    创建模拟:

    firstFnMock = jasmine.createSpy('firstFn').and.callFake(function() {
      console.log('firstFnMock called');
    });
    
    使用
    defineProperty
    在名为
    firstFn
    $scope
    上创建属性,并将其值设置为模拟函数:

    Object.defineProperty($scope, "firstFn", {
      enumerable: false,
      configurable: false,
      writable: false,
      value: firstFnMock
    });
    
    writable
    设置为
    false
    非常重要,因为这将阻止控制器更换模拟

    在正常情况下创建控制器时,使用准备好的
    $scope
    (在本例中设置创建控制器的函数):

    规范示例:

    it('Spec', function() {
    
      createController();
    
      expect(firstFnMock).toHaveBeenCalled();
    
      $scope.secondFn();
    
      expect($scope.test1).toBe(1);
      expect($scope.test2).toBe(3);
    });
    

    演示:

    你在使用任何测试框架吗,也许是Jasmine?是的,我在使用Jasmine。这不起作用,因为函数是在控制器创建后立即执行的。如果函数是控制器专用的,并且它在控制器创建后立即执行,那么你就倒霉了。将其抽象为服务,然后在创建控制器之前对其进行监视。哇,谢谢!我失去了希望。它工作完美无瑕。正是我所需要的:)
    it('Spec', function() {
    
      createController();
    
      expect(firstFnMock).toHaveBeenCalled();
    
      $scope.secondFn();
    
      expect($scope.test1).toBe(1);
      expect($scope.test2).toBe(3);
    });