Is it possible to have angular bootstrap the app at the beginning of each test file? Instead of the current behavior, which has the app bootstrap at the beginning of the series of tests, and then the same instance is used throughout all the test files (we have about 600 tests throughout ~60 files right now)?
We have beforeEach statements to handle clean-up and that doesn't help. In fact, seems sometimes the beforeEach statements are altogether skipped for no apparent reason (possible memory leak with test runner).
So the route I would like to take this is to have each test file bootstrap the angular app, so that the state is completely reset, instead of reusing dependency injection (ie. services) that had state set by a different test.
You don't need to bootstrap the app for the tests. That's why we have angular-mock
. With angular.module('app.module')
we load the module we need for our test and that module contains the component we want to test. Since angular-mock
is not the cause for the memory leaks, there can be several reasons for them. One of the most common reasons for memory leaks is jasmine itself and the way we usually write the tests. The variables we use for the dependencies we inject in our test and are defined on the describe
scope and cannot be collected by the GC when the tests finish. This is because there are references of those variables in the it
blocks that cannot be garbage collected because the variables still live in some other scope of the tests tree. Another problem can be the compiled elements that should also be cleaned up after each test. So you probably need to clean-up the following things:
- compiled element when using $compile for testing directives
- all variables in the describe functions scope
You can do something like this:
describe('testSuite', function () {
var suite = {};
beforeEach(module('app'));
beforeEach(inject(function ($rootScope, $compile, heavyLoad) {
suite.$rootScope = $rootScope;
suite.$compile = $compile;
suite.heavyLoad = heavyLoad;
suite.$scope = $rootScope.$new();
spyOn(suite.heavyLoad, 'getHeavyString').and.callThrough();
spyOn(suite.heavyLoad, 'getHeavyObject').and.callThrough();
spyOn(suite.heavyLoad, 'getHeavyList').and.callThrough();
}));
// NOTE: cleanup
afterEach(function () {
// NOTE: prevents DOM elements leak
suite.element.remove();
});
afterAll(function () {
// NOTE: prevents memory leaks because of JavaScript closures created for
// jasmine syntax (beforeEach, afterEach, beforeAll, afterAll, it..).
suite = null;
});
suite.compileDirective = function (template) {
suite.element = suite.$compile(template)(suite.$scope);
suite.directiveScope = suite.element.isolateScope();
suite.directiveController = suite.element.controller('heavyLoad');
};
it('should compile correctly', function () {
// given
var givenTemplate = '<div heavy-load></div>';
// when
suite.compileDirective(givenTemplate);
// then
expect(suite.directiveScope.title).toBeDefined();
expect(suite.directiveScope.items).toBeDefined();
expect(suite.heavyLoad.getHeavyString).toHaveBeenCalled();
expect(suite.heavyLoad.getHeavyList).toHaveBeenCalled();
});
});
Taken from here.
This should reduce your memory leaks significantly. You should also take a look at your module structure and the dependency graph your modules have, cause you might have some modules that are not needed for some tests but they are loaded anyway. They can be heavy on memory or contain memory leaks in them and can make you additional problems. You can also take a look at this github project.
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments