Unit testing directives without unnecessary dependencies in module AngularJS - angularjs

I want to write a test for the directive.
In App
requiresDependencies = ["someModule1","someModule2"];
var app = angular.module('app', requiresDependencies);
In test
describe("Logon Hours Editor", function ()
{
var compile, rootScope;
var element;
beforeEach(module('app'));
beforeEach(inject(['$compile', '$rootScope', function ($compile, $rootScope)
{
compile = $compile;
rootScope = $rootScope;
element = $compile('some html')($rootScope);
} ]
)); ....
My directive is relative to the main unit, and I do not want to connect other test modules (someModule1,someModule2) described in "requiresDependencies" ,because later their numbers and names can be changed.
How do I connect only without his dependencies?

You have to mock them. For example, you can create a module called mocks with a d with the services or directives you want to mock. You then load it with beforeEach(module('mocks')); and your calls from the test suite to whatever service you have in mocks will be using the dummy implementation.
You can put this mock module in your test folder, like /test/lib/my-mocks.js (next to angular-mocks.js, if you're using angular-seed). Lastly, you include it in karma.conf.js:
files = [
'app/lib/jquery/jquery-1.9.1.js',
JASMINE,
JASMINE_ADAPTER,
'test/lib/jasmine-jquery.js',
'app/lib/angular/angular.js',
...
'test/lib/myproject/my-mocks.js',
...
];
Another approach is using jasmine spies. For example:
spyOn($location, 'absUrl').andCallFake(function (p) {
return 'http://localhost/app/index.html#/this/is/my/route';
});

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.

Testing controllers with services

I'm new to angular and unit-testing.
I have an application module MyApp including basic things an services, that are needed in all other modules, like service for logging loggingService
I also have an module for handling everything about map&geo-positon, called MapModule and I have an main module for application logic, called MainModule
The MainModule contains a controller, that I like to test: messageSendCtrl
The controller has some dependencies, like services from MapModule.
And: MainModule and MapModule has dependencies to the MyApp, because the loggingServiceis needed everywhere.
The code looks like that (pseudo-code):
MyApp
var MyApp = angular
.module('MyApp', ['ngRoute','MainModule','MapModule']);
MyApp.service('loggingService', function (one, two) {
[..] /*logging data somewhere for debugging application*/
});
MainModule
var MainModule = angular
.module('MainModule', []);
MainModule.controller('messageSendCtrl',
function($scope,$http, $location, locationService, loggingService) {
[...]
});
MapModule
var MapModule = angular
.module('MapModule', ['uiGmapgoogle-maps']);
MapModule.service('locationService', function (loggingService) {
[...]
What I like to test is the messageSendCtrl from the MainModule. (probably) I was able to inject the location service into the test environment. But injecting the locationService was not successful.
Probably because locationService also uses the loggingService.
Running the test results in
Error: [$injector:unpr] Unknown provider: loggingServiceProvider <- loggingService <- locationService
My test looks like that:
describe('saving a document', function() {
beforeEach(module('MainModule'));
beforeEach(module('MyApp'));
beforeEach(module('MapModule'));
describe ('messageSendCtrl', function () {
var scope,ctrl,locationService,loggingService;
beforeEach(inject(function($rootScope, $controller,_locationService_,_loggingService_) {
scope = $rootScope.$new();
ctrl = $controller('messageSendCtrl',
{$scope: scope,
locationService: _locationService_,
loggingService : _loggingService_ });
}));
it('should actual not saved', function(){
expect(scope.result).to.equal('unsaved');
});
})
});
So who can I solve the dependencies? Or is there an design problem at my application?
there are multiple things going on, let's check it one by one:
at your test, you don't need do load all your modules, load just that module, that you want to test, your ctrl is in your MainModule, so use just
beforeEach(module('MainModule'));
every module should declare its dependencies, so your MainModule declaration should look like this: var MainModule = angular.module('MainModule', ['MyApp']); because one of your controller in your MainModule dependent on a service that is in an other module (MyApp)
it is easier to test if one module do just one thing, so if you have a logging service, make a logging service module for that, and include that module where you want to use logging.
So don't make modules that is responsible for several different things, because if an other module need logging, that module will get every other service that your "godmodule" contains, and that makes difficult to test, and find bugs.

Angular Unit testing with Karma

I have an angular app that i am developing tests for. In the app.js file i have something like this:
angular.module('app',
[
'app.config',
'app.factories',
'app.directives',
'app.controllers'
]
);
For each controller i want to be in that controller module i essentially define them like this:
angular.module('app.controllers').controller("controller1" ,[],function(){
bleh bleh bleh
code code code
})
The goal here is to write some unit tests with karma but unfortunately the most i have been able to figure out how to do is make sure the dependencies of my main modules load.
What I need to figure out is using the structure i have, how do I (a) create a test to make sure that my controller is actually there, and (b) test things inside the controller
I have tried multiple ways but cannot seem to instantiate the controller within my test framework.
You can test for the existence of your controller like this:
describe("SomeControllerTest", function () {
var scope, ctrl;
beforeEach(module('myApp'));
beforeEach(inject(function($rootScope, $controller) {
scope = $rootScope.$new();
ctrl = $controller('SomeController', {
$scope: scope
});
}));
it("should be defined", function () {
expect(ctrl).toBeDefined();
});
});
Careful with your controller syntax. The second param is an array of strings ending with the function, the function is not a 3rd param.
app.controller('MyController', [ '$log', function MyController($log) {} ]);

Mocking collaborators in Angular controller and service Jasmine tests

I have been writing some Jasmine unit tests in Angular. In the first example I'm testing a controller.
myApp.controller('MyCtrl', function($scope, Config){
...
});
I have a configuration service (Config) that keeps configuration from the database and is injected into my controller. As this is a unit test, I want to mock out that configuration service altogether, rather than allowing execution to pass through it and using $httpBackend. Examples I found taught me about a $controller function I can use like this, in order to get an instance of my controller with my mocks injected in place of the usual collaborator:
beforeEach(inject(function($controller, $rootScope){
var scope = $rootScope.$new();
var configMock = {
theOnlyPropertyMyControllerNeeds: 'value'
};
ctrl = $controller('MyCtrl', {
$scope:scope,
Config: configMock
});
}));
But I also have other services that use the Config service. To help unit test them, I assumed there would be a similar $service function I could use to instantiate a service with whatever mocks I want to provide. There isn't. I tried $injector.get, but it doesn't seem to let me pass in my mocks. After searching for a while, the best I could come up with in order to instantiate a service in isolation (avoid instantiating its collaborators) is this:
beforeEach(function() {
mockConfig = {
thePropertyMyServiceUses: 'value'
};
module(function($provide) {
$provide.value('Config', mockConfig);
});
inject(function($injector) {
myService = $injector.get('MyService');
});
});
Is this the right way? It seems to be overriding the entire application's definition of the Config service, which seems maybe like overkill.
Is it the only way? Why is there no $service helper method?
For unit testing, it is common that you override a service for the sake of testing. However, you can use $provide to override an existing service instead of using inject, as long as you load the application before hand.
Assuming that you created Config using something like:
angular.moduel('...', [...]).factory('Config', function (...) {...});
If so, try this:
...
beforeEach(module("<Name of you App>"));
beforeEach(
module(function ($provide) {
$provide.factory('Config', function (...) {...});
});
);
...
After that, when you initialise your controller, it will get the mocked Config.

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