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)
Related
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.
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.
I generated my web application with gulp angular. Then I created a service and want to test it.
Module file is app.module.js
angular.module('text', []);
Service file is text.service.js
(function () {
'use strict';
angular.module('text')
.factory('description',TextService);
TextService.$inject = ['$http']
function TextService($http) {
return {
QueryStaticText: queryStaticText
};
function queryStaticText(link) {
return link;
}
}
})();
Test file is text.service.spec.js
'use strict';
describe('TextService', function () {
var description;
beforeEach(function () {
module('text');
inject(function (_description_) {
description = _description_;
});
});
it('should return text', function () {
expect(description.QueryStaticText("Hello")).toEqual("Hello anu");
});
});
In the console I execute gulp test and I've got the error message
[22:02:44] Using gulpfile /Volumes/Developer/angularjs/project/gulpfile.js
[22:02:44] Starting 'test'...
[22:02:45] Starting Karma server...
INFO [karma]: Karma v0.12.31 server started at http://localhost:9876/
INFO [launcher]: Starting browser PhantomJS
INFO [PhantomJS 1.9.8 (Mac OS X)]: Connected on socket 1KW_uYCk45moVKwfgAd2 with id 19804685
PhantomJS 1.9.8 (Mac OS X) ERROR
Error: [$injector:nomod] Module 'text' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
http://errors.angularjs.org/1.3.12/$injector/nomod?p0=text
at /Volumes/Developer/angularjs/project/bower_components/angular/angular.js:1769
/Volumes/Developer/angularjs/project/gulp/unit-tests.js:30
throw err;
The text module is not loaded, how can I load it?
It looks like you are not loading your files in order in Karma. Inside of you karma.conf.js there should be a file list. Load the app module then the rest of your javascript. Here is a post that had the same problem.
For Example:
files: [
'bower_components/angular/angular.js',
'bower_components/angular-mocks/angular-mocks.js',
'bower_components/angular-animate/angular-animate.js',
'app/**/*.module.js', // Loads all files with .module.js
'app/**/*.js', // Load the rest of your .js angular files
'test/**/*.js' // Load your tests
],
Use angularFilesort to order files by dependency:
// gulp/unit-tests.js
...
// around line 36 inserted the following line:
.pipe($.angularFilesort())
So should look like:
gulp.src(srcFiles)
.pipe($.angularFilesort())
.pipe(concat(function(files) {
callback(bowerDeps.js
.concat(_.pluck(files, 'path'))
.concat(htmlFiles)
.concat(specFiles));
}))
beforeEach(module('text'));
beforeEach(inject(function (_description_) {
description = _description_;
}));
Basically, the return value of your module and injection need to be the argument for beforeEach. See https://docs.angularjs.org/guide/module#unit-testing
When running grunt karma, a test on one of the directive fails when it tries to fetch the template. I am using ng-html2js as a preprocessor. Here is some of my karma.conf.js
plugins: ['karma-chrome-launcher',
'karma-jasmine',
'ng-html2js',
'karma-ng-html2js-preprocessor'],
preprocessors: {
'app/scripts/directives/**/*.html': 'ng-html2js'
},
ngHtml2JsPreprocessor: {
moduleName: 'templates'
}
In my test, I have the following:
'use strict';
describe('Directive: myDirective', function () {
// load the directive's module
beforeEach(module('myApp'));
beforeEach(module('templates'));
var element,
scope;
beforeEach(inject(function ($rootScope) {
scope = $rootScope.$new();
}));
it('should not show search area initially', inject(function ($compile) {
element = angular.element('<navbar></navbar>');
element = $compile(element)(scope);
scope.$digest();
expect(element.find('.myClass').hasClass('myClass')).toBe(true);
}));
});
When I run the test, I get
Error: Unexpected request: GET /scripts/directives/myDirective/myDirective.html
It seems like the preprocessor is not properly injecting the javascript version of the template.
I have also tried using the path of the template in the beforeEach(module('')); but that causes an error that reads:
Error: [$injector:modulerr] Failed to instantiate module...
How can I fix this?
I had kind of the same problem. Be sure you have the exact file match. Open the Google chrome console and check the file path is exactly the same.
In the upper exemple, I had to add a "/" string in ngHtml2JsPreprocessor.stripPrefix and it worked.
So I guess with Yeoman, you should use
ngHtml2JsPreprocessor: {
moduleName: 'templates',
stripPrefix: 'app/' //add a slash
}
Since I was using the Yeoman tool to scaffold my project, I needed to add a stripPrefix to the ngHtml2JsPreprocessor option in my karma.conf.js file:
ngHtml2JsPreprocessor: {
moduleName: 'templates',
stripPrefix: 'app'
}
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.