How to unit test module with provider dependencies? - angularjs

I have an existing angular app. Now I want to start with some unit tests. I use jasmine and karma to test my services.
My problem is that I am not able to test a service with the $stateProvider dependency.
What I am using:
- angularjs 1.5.8
- angular mocks 1.5.8
My code:
angular.module('starter.start', []).config(function ($stateProvider) {
// ...
}).factory('MyService', function() {
// ...
});
The test:
describe('MyServiceSpec', function() {
var MyService;
beforeEach(angular.mock.module('starter.start'));
beforeEach(inject(function(_MyService_) {
MyService= _MyService_;
}));
it('should exist', function() {
expect(MyService).toBeDefined();
});
});
I get the error Unknown provider: $stateProvider.
In my research I found out that I need to do something with $provide but I am not sure how to do this.

you need to add $stateProvider in your config file
next you need to inject ui.router before your main app
beforeEach(angular.mock.module('ui.router'));
beforeEach(angular.mock.module('starter.start'));
and then you need to call service

Related

unit testing angular service, beforeEach inject doesn't execute

I'm trying to write unit tests for an angular service with jasmine/karma. I have a similar service test, which works just fine. But this one has some additional dependencies, is in a different module and just doesn't find the service with the inject.
The service looks something like this. bService is in the same module, but commonFactory and commonService are in another module, say commonModule.
(function () {
'use strict';
angular
.module('myService')
.service('aService', aService);
aService.$inject = [
'commonFactory',
'commonService'
'bService'
];
function aService (
commonFactory,
commonService,
bService
) {
};
return {
codeIWantToTest: CodeIWantToTest;
}
function CodeIWantToTest () {
console.log('surprise!');
}
})();
My jasmine test looks like:
describe('myService.aService', function () {
'use strict';
var aService;
// I tried adding beforeEach(module('commonModule')); too, but that didn't do anything
beforeEach(module('myService'));
beforeEach(function() {
inject(function(_aService_) {
console.log('getting aService');
aService = _aService_;
});
});
it('tests my service is defined', function() {
expect(myService).toBeDefined();
});
});
This test fails. myService isn't defined and the console.log in the inject function doesn't ever fire. My karma.conf.js basically lists the dependencies in the order that they're injected into the service, then adds the service then the test.
What would cause the inject to not grab the service? What am I missing? I mentioned I have a similar test for commonService and it works just fine. So I'm baffled.
Another dev on my team found the solution and I wanted to post it as an answer for the future people. I had a feeling it was a dependency problem, and it was. While we were loading all of the JS stuff correctly, the template that the component uses was loading another js dependency. So to fix this for jasmine, we had two different solutions:
at the top of the component test file, we could add:
beforeEach(function () {
module(function ($provide) {
$provide.constant('myMissingDependency', {
// do things to load the dependency here
});
});
});
In our case it was a translation library
The other solution was to add a 'shim' file into the unit test directory and load it with karma.config.js ahead of the tests. That looked like:
(function() {
angular
.module('MyService')
.constant('myMissingDependency', Object.freeze({
// things to load the dependency
}));
})();
I wasn't able to switch to Chrome because we're using Docker and I couldn't get the tests to run locally to run Chrome. So adding a second set of eyes to this was what I needed.

Is there a way to see if the angular mock modules are loaded?

I have the following code in my spec file
beforeEach(function () {
module('app');
inject(function ($injector) {
user = $injector.get('app.user');
});
});
user is undefined, and isn't being injected. So I want to make sure that the app module actually loaded.
If the module is not loaded, you get $injector:nomod error. If the module is loaded but the service cannot be found, you get $injector:unpr error. It is as easy as that. There is always a breadcrumb trail, no need to probe Angular to know if it fails silently or not.
Just make sure you're using the right module name. You can use beforeEach to load your module. Also, with $injector you can get an instance of your service or controller you're trying to test:
'use strict';
describe('MyControllerName', function () {
var MyControllerName;
beforeEach(module('myAppMomduleName'));
beforeEach(inject(function ($injector) {
MyControllerName = $injector.get('MyControllerName');
}));
it('should create an instance of the controller', function () {
expect(MyControllerName).toBeDefined();
});
});

Mock service injected into Angular module run block

In my module.run block it is calling a method on a service I have made. When running my tests I want it to reference a mock service instead of the real one which is making http requests. I am currently trying to test a controller, not the actual run block itself - how can I inject the mock service into the run function? I have tried using $provide.factory but it doesn't seem to do anything and is still loading the service as normal.
I am using Jasmine to write my tests.
app.js
angular.module("app")
.run(function(MyService) {
MyService.log("starting app");
});
test.js
describe("MyController", function() {
beforeEach(function() {
module(function ($provide) {
$provide.factory("MyService", { log: function(){} });
});
});
// I want module 'app' to execute its run function using injected value for MyService
beforeEach(module("app"));
beforeEach(inject(function($controller, $rootScope) {
MyController = $controller("MyController", { $scope: $rootScope.$new() });
}));
...........
});
In this case is important order.
You need load your app first
beforeEach(module("app"));
and then overwrite MyService definition.
beforeEach(
module({
"MyService": {
log: function(message) {
console.log("MyFakeService called: " + message);
}
}
})
);
Otherwise app service implementation is last registred and used.
working example is here - look to the console http://plnkr.co/edit/BYQpbY?p=preview

Writing Unit Testing With Jasmine , Require JS in Karma for Angular JS?

I have Done a Library Management App with Angular JS and Mongo Lab will Act as a DB Part, I am facing issuse with Require JS Dependency While Crating Unit test Case
Error: [$injector:modulerr] Failed to instantiate module lmaApp due to:
Error: [$injector:nomod] Module 'lmaApp' is not available!
The Above error I am facing,kindly help me to get out from this.
MyCode:
Controller.js
define(
['modules'],
function(lmaApp) {
lmaApp.controller('gridControl',['$scope' , '$http' , 'internal' , 'commonValues', function($scope , $http , internalFn , commonValues){
jQuery('ul.navbar-nav li').removeClass('active');
jQuery('ul.navbar-nav li:nth-child(1)').addClass('active');
$('.loaderImg').removeClass('no-loading');
var lmaTableData = internalFn.getTableData(commonValues.mongoAPIurl,commonValues.bookTableName,'');
var promise = lmaTableData
.success(function(tbData) {
if(tbData.length > 0){
$scope.nobook=0;
$scope.books =tbData;
}
else{
$scope.nobook =1;
}
}).then(function (response) {$('.loaderImg').addClass('no-loading');});
}]);
});
modules.js
define(
['app_config','angularRoute'],
function() {
var lmaApp =angular.module('lmaApp',['ngRoute'])
return lmaApp;
});
app_config.js
define(
['angular','angularRoute','modules'],
function() {
angular.element(document).ready(function() {
angular.bootstrap(document, ['lmaApp'], {});
});
});
My spec File
controllerSpec.js
define(['angular', 'angular-mocks'], function() {
describe('gridControl', function(){
beforeEach(module('lmaApp'));
it('should get the book table Datas', inject(function($controller) {
var scope = {},
ctrl = $controller('gridControl', {$scope:scope});
expect(scope.phones.length).not.toEqual(0);
}));
});
});
Here i have a doubt with require js dependecy like i have to mention modules.js as a dependency in Spec file too.
Your controller-spec.js is wrong, you need to call its dependencies which is your module.js, app_config.js, Controller.js also 'angularRoute'. In other ot make your app to able to boostrap.
try it like this
define(['angular', 'angular-mocks','modules', 'app_config', 'Controller', 'angularRoute'], function() { ... }
Still I have nothing to be sure that it will work for you. Because if any of your configuration is wrong. It will be broken. Using requireJS with AngularJS in unit testing is very tricky/complex in the configuration.
You should read more about requireJS config, and using shim for define dependencies for your script. It will be lots clearer and eaiser to handle when your app getting bigger. For example you can do something like this in your config file:
shim: {
'modules': ['angular', 'app_config', 'angular-mocks'],
'app_config': ['angular', 'angularRoute', 'modules'],
'Controller': ['modules']
}
and your controller-spec.js will be like this
define(['modules', 'app_config', 'Controller'], function(){ ... });
Also Bootstraping angular on element ready is not a good idea for testing. It may cause some conflict in loading simulating of karma. I am not sure but don't feel right about it
Sencond Your app_config.js and modules.js are dependency of each other. So what do you think if there's something require an order in loading. Which will need to load first? since Both required the other to be loaded before it got to be injeacted by requireJS.
Still I don't think my solution will work. Because your config seem so wrong.

AngularJS + Jasmine test scripts failing fn is not function

I'm using Yeoman to create an angular project and have modules defined as:
angular.module('angularApp')<br />
.controller('LogOutCtrl', function ($scope) {//do stuff});
Test scripts via Yeoman are as follows:
describe('Controller: LogOutCtrl', function () {
// load the controller's module
beforeEach(module('angularApp', ['ui', 'appSettings']));
var LogOutCtrl,
scope;
// Initialize the controller and a mock scope
beforeEach(inject(function ($controller) {
scope = {};
LogOutCtrl = $controller('LogOutCtrl', {
$scope: scope
});
}));
it('should pass', function () {
expect(2+2).toBe(4); //aka some test
});
});
This is returning as an error via grunt/karma:
Error: Argument 'fn' is not a function, got string
I have looked at a few other ways of writing these as well:
How do I test an AngularJS service with Jasmine?
Any help would be appreciated, as I am new to Angular and Jasmine testing. I believe this is probably an issue with Yeoman's templates for test scripts.
Thanks!
This was an issue with the templates that Yeoman was using. They have been resolved after an update from 1.0 beta.

Resources