在单元测试期间,AngularJS 控制器中的 Leaflet 插件不可用

Leaflet plugin not available in AngularJS controller during unit tests

我正在使用 Karma + Mocha 来测试 Angular 中的控制器。下面的代码是控制器和测试规范的简化示例。 L.Control.LocateLeafletJS plugin.

问题

在测试期间 运行 L.Control.Locate 应该在控制器被实例化时存在,但它不存在。它抛出:TypeError: L.Control.Locate is not a constructor.

该应用程序在浏览器中按预期运行。

PhantomJS 和 Chrome 都出现错误。

在测试中,我用调试器确认 before MapCtrl2 已实例化,L.Control.Locate 已声明并附加到 window.L.Control应该是这样,但是在实例化控制器之前它就丢失了。

L 的其他标准属性如预期的那样存在。

当我将 L.Control.Locate.js 粘贴到控制器声明之前的文件中时,错误消失了。

我也试过将 $window 注入控制器并查看 $window.L.Control.Locate,但它仍然未定义。

感谢您提供任何线索。

代码:

angular.module('app')
.controller('MapCtrl2', function ($scope) {

  // Throws error here.
  var locateControl = new L.Control.Locate();

  // Do something with locateControl.

});

describe('MapCtrl2', function () {

  var controller, scope;
  beforeEach(module('app'));
  beforeEach(inject(function ($controller, $rootScope) {
    scope = $rootScope.$new();
    controller = $controller('MapCtrl2', {
      $scope: scope
    });
  }));

  it('has L.Control.Locate', function() {
    expect(window.L.Control.Locate).to.be.an.instanceOf(Object)
  });

});

更新:

关于全局使用 L 的问题,我意识到 L 可以通过 $window.L 获得。虽然这对许多 AngularJS 开发人员来说可能是显而易见的,但我忽略了它。

要模拟您的库对象,您需要

beforeEach(
    function() {
        window.L = {
            Control: {
                Locate: function() {
                    self = this;
                    //populate based on the API you're mocking
                    self.someMethod = angular.noop;
                    self.someProp = 'foo';
                }
            }
        }
    }
}

然后您想监视这些方法以确保您的控制器按预期调用它们,或者读取属性以确保您的控制器按预期设置它们。

请注意,我继承了将自身附加到 window 对象的代码(yuk!)。我尽可能地隐藏这种依赖性。我永远不会让控制器直接触摸它。