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 перед тестами и очистите его потом (или запустите его в изолированной области).
Попробуйте сделать снимок кучи до и после запуска теста и проанализировать моментальные снимки, чтобы найти, какие объекты продолжают увеличиваться в числах.
Ответ 2
Не уверен, но это может быть связано с утечкой памяти в Mocha, которая была исправлена в последнее время. Обязательно обновите локальное мокко до 2.4.5
или новее, чтобы получить исправление и попробовать запустить тест.
Ответ 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 состояний).