Angular + утечка памяти 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]);
    });
  });
});

Ниже приведены некоторые скриншоты профилирования:

введите описание изображения здесь

введите описание изображения здесь

введите описание изображения здесь

UPDATE

Мне удалось вызвать утечку памяти, просто обернув один из моих it в цикле.

например:.

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

В моих тестах я установил все объекты внутри объекта-контейнера и очистил его в afterEach (например, решение здесь), но не повезло. Распределение памяти по-прежнему увеличивается в инструменте Chrome Dev Timeline.

Спасибо!

Ответ 1

Я не вижу, чтобы вы очищали ваш издевавшийся HTTP-сервер. Я не знаю, как это работает, но я бы ожидал, что все ваши запросы и ответы будут сохранены в памяти. Это приведет к тому, что он будет постепенно увеличиваться, тест на тест.

Если бы это использовало Sinon, я бы ожидал, что вы создадите stub/spy перед тестами и очистите его потом (или запустите его в изолированной области).

Попробуйте сделать снимок кучи до и после запуска теста и проанализировать моментальные снимки, чтобы найти, какие объекты продолжают увеличиваться в числах.

Ответ 3

Похоже, что 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 состояний).