Angular + Mocha内存泄漏

时间:2015-12-29 11:28:43

标签: angularjs memory-leaks mocha

我在运行整套测试时遇到以下错误:

timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.

经过一番调查,我发现是一个内存泄漏问题。查看一些堆分析快照,仍然可以引用对象并且不会收集垃圾。

任何人都知道可以防止它发生的解决方案吗?有一些选项,比如浏览我的1000个规格中的每一个,并添加afterEach来做一些清理,但这看起来很多工作。

这是我的大多数测试看起来像

的示例布局
describe('MyClassCtrl', function() {

  var $httpBackend, $rootScope, ctrl;
  ctrl = $rootScope = $httpBackend = null;

  beforeEach(function() {
    module('myApp');
    inject(function($controller, $rootScope, _$httpBackend_, $stateParams) {
      var $scope;
      $stateParams.id = 1;
      $httpBackend = _$httpBackend_;
      $scope = $rootScope.$new();
      ctrl = $controller('MyClassCtrl', {
        $scope: $scope
      });
    });
  });

  describe('#_getMyList', function() {
    beforeEach(function() {
      $httpBackend.expectGET("/my/app/url").respond({
        my_list: [1, 2, 3]
      });
      ctrl._getMyList();
      $httpBackend.flush();
    });

    it('does this', function() {
      expect(ctrl.my_list).to.eql([1, 2, 3]);
    });
  });
});

以下是一些分析截图:

enter image description here

enter image description here

enter image description here

更新

我能够通过简单地将一个it包裹在一个循环中来触发内存泄漏。

e.g:

for (i = 0; i < 200; i++) {
  it('does this', function() {
    expect(ctrl.my_list).to.eql([1, 2, 3]);
  });
}

在我的测试中,我在容器对象中设置所有对象并在afterEach中清理它(如解决方案here)但没有运气。 Chrome的Dev Timeline工具的内存分配仍在不断增加。

谢谢!

3 个答案:

答案 0 :(得分:0)

我没有看到你清理你嘲笑的http后端。我不知道它是如何运作的,但我希望你的所有请求和响应都存储在内存中。这会导致它逐渐增加,通过测试来测试。

如果这是使用Sinon我会希望你在测试之前创建一个存根/间谍并在之后清理它(或者在沙盒中运行它)。

尝试在运行测试之前和之后获取堆快照并对快照进行区分以查找哪些对象的数量不断增加。

答案 1 :(得分:0)

不确定,但这可能与memory leak in Mocha that has been fixed recently有关。确保将本地摩卡更新为2.4.5或更新以获得修复并尝试运行测试。

答案 2 :(得分:0)

看起来ui-router在单元测试中泄漏了内存。我在我的应用程序的运行块中添加了以下代码:

$rootScope.$on('$destroy', function() {
    var clearAllMembers = function(obj, depth) { // prevent infinite recursion because of circular references
        depth = depth || 0;
        if (depth > 10) {
            return;
        }
        for (var i in obj) {
            if (obj.hasOwnProperty(i)) {
                if (typeof(obj[i]) == 'object') {
                    clearAllMembers(obj[i], ++depth);
                }
                if (obj) {
                    if (obj[i] && (obj[i] instanceof Array) && obj[i].length) {
                        obj[i].splice(0);
                    }
                    delete obj[i];
                }
            }
        }
    }

    setTimeout(function() {
        clearAllMembers($stateRegistry); // ui-router 1.0+
        clearAllMembers($urlRouter);
        clearAllMembers($urlService); // ui-router 1.0+
        clearAllMembers($state);
    });
});

并且单元测试中的内存消耗小7-8倍(我有大约350个状态)。