How to de-bootstrap an angular js application in unit tests? - javascript

In my jasmine tests I test the app initialization the following way:
(function () {
"use strict";
describe('app', function () {
beforeEach(function() {
module('app');
});
it('should load without errors', function() {
expect(function() {
angular.bootstrap(angular.element('body'), ['app']);
}).not.toThrow();
});
});
}());
My problem is that when I run Karma i often get:
Expected function not to throw an exception , but it threw [ng:btstrpd] App Already Bootstrapped with this Element '<body>'
I think the best solution would be to "un-bootstrap" the application in an afterEach but I could not find any way to do so in the docs.
Any ideas?

You're doing the wrong approach, since you can't "un-bootstrap" angular. Instead don't bootstrap it in the first place, when it's not needed: Remove the "ng-app" directive from your marup where your jasmine tests are running.

Related

Unknown proider error with karma jasmine unit tests

I have a unit test that is created with the Jasmine framework. When I put a single test specification in the describe block I get a pass in the karma console. If I copy that describe block with the test in it ( the it(); ) then I suddenly start getting problems with dependencies that the module uses.
In the console I get errors around unknown providers.
Here is my simple test:
describe('service definition tests', function () {
it('should be defined', function () {
expect(sut).toBeDefined();
});
});
and that passes okay. If I copy this block I get an error about dependencies. Which is strange as I've already proved that I can test the 'sut' is defined in the first test.
One thing to note is that I have a beforeEach block that loads the module and provides a dependency and it is this dependency that errors when I've duplicated the test. Here is the beforeEach:
var mockConfig = {};
beforeEach(function () {
module('app');
module(function ($provide) {
$provide.value('myConfig', mockConfig);
});
});
the problem has to be something to do with this beforeEach being as the error I get is about the myConfig dependency.
Here is the error:
uncaught Error: [$injecor:unpr] Unknown provider: myConfigProvider <- myConfig <- authorisation
http://errors.angularjs.org/1.4.6/$injector/unpr?p0=myConfiProvider
I managed to resolve this issue by creating a dummy implementation of myConfig factory so that the test files used this.
angular.module('app').factory('myConfig', function() {
var env = 'test';
return {
env: env
}
});
This code lives in a js file that is loaded with the rest of the tests.

protractor + Angular + requireJs

I want to test my app with protracor but test fails with this errors :
Failed: Error while waiting for Protractor to sync with the page: "root element (html) has no injector. this may mean it is not inside ng-app."
it seems that angular doesn't load completely, and browser.waitForAngular(); not working.
how can I setup protractor to continue test after RequireJs load dependencies ?
also adding this :
onPrepare:function(){
browser.manage().timeouts().pageLoadTimeout(40000);
browser.manage().timeouts().implicitlyWait(25000);
}
to ocnfig file(As mentioned here) cause this error:
Failed: Error while waiting for Protractor to sync with the page: "angular could not be found on the window"
You will need a manual way to know that Angular has bootstrapped from within your specs. Here's the basic run-down of how I have this set up with Angular, RequireJS and Protractor. This works for me with jasmine2 and old jasmine.
We want to add a class of ng-app to the element that Angular bootstraps. For example:
index.html
<html lang="en" class="ng-app">
But rather than putting this in the HTML file, we want to add the class using the same RequireJS module that is manually bootstrapping your Angular App. Eg:
ng-bootstrap.js
require(['angular'], function (angular, otherdeps) {
// Start the Angular App
angular.bootstrap(document, ['MyApp']);
// Set the ng-app class for Angular Protractor tests
var root = document.documentElement;
angular.element(root).addClass('ng-app');
});
Check that your page adds this class after bootstrapping. then set up your protractor.conf exports to run the onprepare test. This spec is executed each time Protractor is launched and we will use it to check for the class you added in the ng-bootstrap.js module.
protractor-conf.js
exports.config = {
// Every time protractor is launched:
onPrepare: 'onprepare.e2e.js',
};
In your onprepare.e2e.js spec file, you can trigger the load of the home page. Then ask Protractor to wait until the class .ng-app is found on the root element, Ie: Angular has bootstrapped and is ready to run Protractor tests.
onprepare.e2e.js
describe("On prepare", function () {
// Replace with your own URL
var baseUrl = 'http://127.0.0.1:8001/#/';
// Begin fetching the page
browser.driver.get(baseUrl);
// Wait until `.ng-app` is found on the root elem
browser.driver.wait(function () {
return browser.driver.getCurrentUrl().then(function (url) {
return browser.driver.isElementPresent(by.className('ng-app')).then(function () {
return true;
});
});
});
});
Keep in mind that if you a running lots of spec files together, your page could is being re-loaded when a new test starts. Your page also may be being reloaded if your Angular router is using a reload: true param.
This means that the app has to bootstrap again; And you will need to wait for the bootstrap class again before you can use Protractor.
Add a helper for this and include it in your protractor-conf.js.
helpers.js
module.exports = {
get: function (url) {
browser.driver.get(url);
browser.driver.wait(function () {
return browser.driver.getCurrentUrl().then(function (url) {
return browser.driver.isElementPresent(by.className('ng-app')).then(function () {
return true;
});
});
});
},
};
protractor-conf.js
helpers = require('helpers.js');
exports.config = {
onPrepare: 'onprepare.e2e.js',
specs: [
'my-spec.js'
]
};
Now your helper is globally visible to your specs and you can use your new helper.get(url) method instead of browser.driver.get(url). Example:
my-spec.js
describe("Users", function() {
it('should login', function () {
// Wait for Angular and RequireJS to finish
helpers.get('http://127.0.0.1:8001/#/login');
// ...tests here...
});
});
I had some similar problem, maybe it is because the way our app is loaded, but you can try having some custom wait:
browser.driver.wait(function() {
return browser.driver.isElementPresent(by.css('.ng-scope'));
}, 50000);// ^^or some other locator for your angular
});
inside your beforeEach() for example.
Edit:
Also for someone it helps to change browser windows size:
browser.manage().window().setSize(1280, 1024);
in onPrepare()
I can run test by adding browser.driver.sleep(3000) to beforeEach.
but this isn't the right solution.

AngularJS Test a Factory with Mocha

I am new to Mocha and AngularJS Unit Testing but want to test my application using Mocha. I have basic language tests working, but I cannot run tests against my applications Factory or Controller.
I have the following basic files.
apps.js
aangular.module('MyApp', []);
file1.js
angular.module('MyApp').factory('Factory1' ...);
file2.js
angular.module('MyApp').factory('Factory2' ...);
angular.module('MyApp').factory('Controller' ...);
describe('Main Test', function() {
var FactoryToTest;
beforeEach(module('MyApp'));
beforeEach(inject(function (_Factory_) {
FactoryToTest = _Factory_;
}));
describe('Factory2', function () {
it('should return "unknown"', function () {
Game = {};
expect(new Factory2(Game)).to.equal('unknown');
});
});
});
When I run the test, it generates an error, and I am not sure what to fix to get this to work.
Error:
Message:
object is not a function
Stack:
TypeError: object is not a function
at Suite.<anonymous> (b:\app\test.js:5:16)
You're getting an error because the beforeEach function should take a callback function instead of an object. According to the Angular guide on module unit testing (scroll to bottom of the page) :
Each module can only be loaded once per injector. Usually an Angular app has only one injector and modules are only loaded once. Each test has its own injector and modules are loaded multiple times.

JavaScript unit tests with Jasmine and RequireJS

In my visual studio I have two projects one of them provide functionality for SharePoint page and another one for testing purposes.
In the first project I have few view modules that use RequireJS as a dependency injector.
I need to write js tests in another project:
/// <reference path="Scripts/require.js" />
describe('directly', function () {
beforeEach(function() {
require.config({
baseUrl: '../CopmanyName/Layouts/1033/Scripts/CopmanyName/ProjectName/app',
});
});
it('should see booking', function (done) {
require(['Booking'],
function (booking) {
expect(booking).toBeDefined();
done();
});
});
});
The test failed. It appears that requireJS is not able find the Booking module, which is defined like that:
define(['knockout','moment'], function(ko, moment) {
return function Booking(data) {// some functionality}
The error message is: "Expected undefined to be defined." in expect(booking).toBeDefined();

Loading AngularJS module in Jasmine test via Grunt

I am trying to write some unit tests for an AngularJS service. I want to run the unit tests from the command-line via Grunt. In an attempt to do that, I've written the following:
gruntfile.js
'use strict';
module.exports = function (grunt) {
grunt.initConfig({
jasmine: {
service: {
src: 'dist/myService.js',
options: {
specs: 'test/*.js',
vendor: [
'bower_components/angularjs/angular.min.js',
'bower_components/angular-mocks/angular-mocks.js'
]
}
}
}
});
// load all grunt task details
require('load-grunt-tasks')(grunt);
grunt.registerTask('default', ['jasmine:service']);
};
dist/myService.js
'use strict';
angular.module('myModule')
.factory('$myService', function () {
return {
getResult: function () {
return 3;
}
};
})
;
test/serviceTests.spec.js
describe('myModule', function() {
beforeEach(function() {
console.log('loading module...');
module('myModule');
});
describe('$myService', function () {
it('should work', function () {
console.log('testing');
expect(1 + 2).toEqual(3);
});
});
})
When I try to run this, I get the following error:
Running "jasmine:service" (jasmine) task
Testing jasmine specs via PhantomJS
>> Error: [$injector:nomod] http://errors.angularjs.org/1.2.22/$injector/nomod?p0=myModule at
>> ..\..\..\C:\source\myModule\bower_components\angularjs\angular.min.js:20
>> ..\..\..\C:\source\myModule\bower_components\angularjs\angular.min.js:21
>> ..\..\..\C:\source\myModule\dist\myService.js
myModule
$myService
- should work...
log: loading module...
log: testing
√ should work
I know that in order to test my service, I need to inject it. However, at this time, I'm getting an error loading the module itself. For that reason, I know that I cannot inject my service. However, I do not know why the module won't load. I've confirmed that I have the correct src value.
Can anybody tell me what I'm doing wrong? Or, perhaps point me to the smallest possible example of testing a service in AngularJS (complete with Grunt, etc.)?
I just don't understand what is wrong with my approach. Thank you for your help.
When you call angular.module('myModule') (without second parameter) Angular tries to reference already existing module and cannot find it.
To declare a new module you should call angular.module('myModule', []) (with two parameters)

Categories