【问题标题】:How to mock a function that runs on controller initialization?如何模拟在控制器初始化时运行的函数?
【发布时间】:2014-12-10 19:30:25
【问题描述】:

控制器:

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

【问题讨论】:

  • 你在使用任何测试框架吗,也许是 Jasmine?
  • 是的,我正在使用 Jasmine。

标签: javascript angularjs unit-testing karma-runner


【解决方案1】:

您需要:

  1. 在创建控制器之前在 $scope 上创建 firstFn 的模拟版本
  2. 防止控制器替换模拟

除非您正在测试 IE8 等旧浏览器,否则您可以通过使用 Jasmine 的 createSpy 和 ECMAScript 5 方法 Object.defineProperty() 来完成此操作。

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

创建模拟:

firstFnMock = jasmine.createSpy('firstFn').and.callFake(function() {
  console.log('firstFnMock called');
});

使用defineProperty$scope 上创建一个名为firstFn 的属性并将其值设置为模拟函数:

Object.defineProperty($scope, "firstFn", {
  enumerable: false,
  configurable: false,
  writable: false,
  value: firstFnMock
});

writable 设置为false 很重要,因为它会阻止控制器替换模拟。

像往常一样创建控制器时使用准备好的$scope(在本例中设置创建控制器的函数):

createController = function() {
  return _$controller_('MyController', {
    $scope: $scope
  });
};

规格示例:

it('Spec', function() {

  createController();

  expect(firstFnMock).toHaveBeenCalled();

  $scope.secondFn();

  expect($scope.test1).toBe(1);
  expect($scope.test2).toBe(3);
});

演示: http://plnkr.co/edit/rJuixdHx70SvMcLhGoBz?p=preview

【讨论】:

  • 哇,谢谢!我失去了希望。它完美无缺。正是我需要的:)
【解决方案2】:

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

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

spyOn(scope, 'firstFn');
scope.secondFn();
// assert $scope.test1 = 1
// assert $scope.test2 = 3

有关对间谍行为的更多控制,请参阅http://jasmine.github.io/2.0/introduction.html#section-Spies

【讨论】:

  • 这不起作用,因为该函数在控制器创建后立即执行。
  • 如果该函数是控制器私有的,并且它在控制器创建后立即执行,那么您就不走运了。将其抽象为服务,然后在创建控制器之前对其进行监视。
猜你喜欢
  • 1970-01-01
  • 2014-04-03
  • 1970-01-01
  • 1970-01-01
  • 2016-06-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多