单元测试中的bindToController

ern*_*ejo 30 unit-testing angularjs

我在一个指令中使用bindToController让隔离的作用域直接连接到控制器,如下所示:

app.directive('xx', function () {
  return {
    bindToController: true,
    controller: 'xxCtrl',
    scope: {
      label: '@',
    },
  };
});
Run Code Online (Sandbox Code Playgroud)

然后在控制器中我有一个默认情况下,HTML中没有指定标签:

app.controller('xxCtrl', function () {
  var ctrl = this;

  ctrl.label = ctrl.label || 'default value';
});
Run Code Online (Sandbox Code Playgroud)

如何在Jasmine单元测试中实例化xxCtrl,以便我可以测试ctrl.label?

describe('buttons.RemoveButtonCtrl', function () {
  var ctrl;

  beforeEach(inject(function ($controller) {
    // What do I do here to set ctrl.label BEFORE the controller runs?
    ctrl = $controller('xxCtrl');
  }));

  it('should have a label', function () {
    expect(ctrl.label).toBe('foo');
  });
});
Run Code Online (Sandbox Code Playgroud)

选中此项以测试问题

mey*_*tee 47

在Angular 1.3中(见下面的1.4+)

深入研究AngularJS源代码,我发现了一个未记录的$controller服务的第三个参数later(参见$ controller source).

如果为true,则$controller()返回具有可instance在其上设置属性的属性的Function .
当您准备好实例化控制器时,调用该函数并使用构造函数中可用的属性实例化控制器.

你的例子会像这样工作:

describe('buttons.RemoveButtonCtrl', function () {

  var ctrlFn, ctrl, $scope;

  beforeEach(inject(function ($rootScope, $controller) {
    scope = $rootScope.$new();

    ctrlFn = $controller('xxCtrl', {
      $scope: scope,
    }, true);
  }));

  it('should have a label', function () {
    ctrlFn.instance.label = 'foo'; // set the value

    // create controller instance
    ctrl = ctrlFn();

    // test
    expect(ctrl.label).toBe('foo');
  });

});
Run Code Online (Sandbox Code Playgroud)

这是一个更新的Plunker(必须升级Angular使其工作,现在是1.3.0-rc.4):http://plnkr.co/edit/tnLIyzZHKqPO6Tekd804?p = preview

请注意,可能不建议使用它,引用Angular源代码:

稍后实例化控制器:此机制用于在调用控制器的构造函数本身之前创建对象的实例.

这允许在调用构造函数之前将属性添加到控制器.主要是,它用于$ compile中的隔离范围绑定.

此功能不适用于应用程序,因此不会公开记录.

然而,缺乏一种测试控制器的机制bindToController: true让我使用它.也许Angular家伙应该考虑将该旗帜公之于众.

在引擎盖下它使用临时构造函数,我们也可以自己编写它.
您的解决方案的优点是构造函数不会被调用两次,如果属性没有示例中的默认值,则可能会导致问题.

Angular 1.4+(2015-12-06更新):
Angular团队在版本1.4.0中添加了对此的直接支持.(参见#9425)
您可以将对象传递给$controller函数:

describe('buttons.RemoveButtonCtrl', function () {

  var ctrl, $scope;

  beforeEach(inject(function ($rootScope, $controller) {
    scope = $rootScope.$new();

    ctrl = $controller('xxCtrl', {
      $scope: scope,
    }, {
      label: 'foo'
    });
  }));

  it('should have a label', function () {
    expect(ctrl.label).toBe('foo');
  });
});
Run Code Online (Sandbox Code Playgroud)

另请参阅此博客文章.

  • 看起来他们现在增加了支持.我写了一个例子:http://www.syntaxsuccess.com/viewarticle/5520197d61d7e9d80a9f52db (9认同)