AngularJS - Unit Test - Test Controller With Custom Services - angularjs

I have been searching for a couple of hours now for a solution for this but I just can't make it work.
I have a Controller defined as:
(function () {
'use strict';
angular.module('spaSkeleton.parCCP')
.controller('ParCCPCtrl', function ($scope, $mdToast, AnosLetivosService, UnidadesOrganicasService, CursosService, RelatoriosService, PareceresService) {
//my code
and I want to test this controller, but i have all this Services that I have to inject.
One of the Services looks like this:
var app = angular.module('sigq.anosLetivos', []);
app.service('AnosLetivosService', function (Restangular) {
this.getAnosLetivos = function () {
return Restangular.all("anos-letivos").getList({"sort": "ano_inicio"});
};
});
and in my test file I have this:
describe('Parecer Controllers', function(){
beforeEach(module('spaSkeleton.parCCP'));
beforeEach(function() {
module('namespace.anosLetivos');
module('namespace.unidadesOrganicas');
module('namespace.cursos');
module('namespace.relatorios');
module('namespace.pareceres');
module('namespace.landingPage');
});
describe('Parecer Ctrl', function(){
var scope, ctrl, $httpBackend;
beforeEach(inject(function(_$httpBackend_, $rootScope, $controller) {
$httpBackend = _$httpBackend_;
$httpBackend.expectGET(...).respond(...);
scope = $rootScope.$new();
ctrl = $controller('ParCtrl', {$scope: scope});
}));
});
});
I would like some help on how to inject these services into the controller so i can test it. I already tried a lot of stuff.
https://docs.angularjs.org/tutorial/step_11 this looks easy but does not work, he doesn't even inject stuff or does he? I know in the tutorial works but I don't know how and why and I can't make it work on my project.
Any help is welcome :D

so I solved my problem, the problem was that the services had a module that i had to inject in the test that i wasn't seeing.
describe('Parecer Controllers', function(){
beforeEach(module('spaSkeleton.parCCP'));
beforeEach(function() {
module('sigq.anosLetivos');
module('sigq.unidadesOrganicas');
module('sigq.cursos');
module('sigq.relatorios');
module('sigq.pareceres');
module('restangular');
module('ngMaterial');
});
var $scope;
var $controller;
var $mdToast, AnosLetivosService, UnidadesOrganicasService, CursosService, RelatoriosService, PareceresService, Restangular;
beforeEach(inject(function(_$controller_, _$q_, _AnosLetivosService_, _UnidadesOrganicasService_, _CursosService_,
_RelatoriosService_, _PareceresService_, _Restangular_, _$mdToast_) {
$scope = {};
$mdToast = _$mdToast_;
Restangular = _Restangular_;
$controller = _$controller_;
AnosLetivosService = _AnosLetivosService_;
UnidadesOrganicasService = _UnidadesOrganicasService_;
CursosService = _CursosService_;
RelatoriosService = _RelatoriosService_;
PareceresService = _PareceresService_;
$controller('ParCCPCtrl',
{
'$scope': $scope,
'AnosLetivosService': AnosLetivosService,
'UnidadesOrganicasService': UnidadesOrganicasService,
'CursosService': CursosService,
'RelatoriosService': RelatoriosService,
'PareceresService': PareceresService,
'$mdToast': $mdToast
});
}));
it('should make Blog menu item active.', function() {
expect(1).toEqual(1);
});
});
so i need all this code to test my controller xD

Related

How to Unit Test Angular with Jasmine Controller inside a Directive

So I am trying to learn how to unit test with Jasmine with Angular. I have got a number of unit tests working but this one has stumped me. I took out the alerts array in my test you can make it any array.. But how to mock this and getting this to work has really stumped me. I would think that the object would exist.
(function () {
var app = angular.module("TabDirectives", ["Communication"]);
app.directive("sentAlerts", ["DirectiveProvider", "DataProvider", function (DirectiveProvider, DataProvider) {
var dir = DirectiveProvider("SentAlerts");
dir.controller = function () {
var ctrl = this;
DataProvider("AlertsByDate").then(function (Result) {
ctrl.AlertList = Result.data;
});
};
dir.controllerAs = "Alerts"
return dir;
}]);
})()
I have a test that looks like
describe("Tab Directive Unit Tests", function () {
var controller;
describe("Tab Directive Default Values", function () {
beforeEach(inject(function ($rootScope, $compile) {
element = angular.element("<sent-alerts></sent-alerts>");
$compile(element)($rootScope.$new());
$rootScope.$digest();
controller = element.controller;
}));
it("Ctrl should be this", function () {
expect(controller.ctrl).toBe(controller.this);
});
it("AlertList should have Alerts", function () {
expect(controller.ctrl.AlertList).toBe(alerts);
});
});
});
The error I'm getting looks like
TypeError: Cannot read property 'AlertList' of undefined
You have to initialize and inject your controller as well. Something like this:
var $controller;
var $rootScope;
var scope;
var controller;
beforeEach(inject(function (_$controller_, _$rootScope_) {
$controller = _$controller_;
$rootScope = _$rootScope_;
scope = $rootScope.$new();
controller = $controller('ScopeController', { '$scope': scope });
}));

Unit test AngularJS controller that inherits from a base controller via $controller

The scenario is I have a ChildCtrl controller that inherits from BaseCtrl following this inheritance pattern:
angular.module('my-module', [])
.controller('BaseCtrl', function ($scope, frobnicate) {
console.log('BaseCtrl instantiated');
$scope.foo = frobnicate();
// do a bunch of stuff
})
.controller('ChildCtrl', function ($controller, $scope) {
$controller('BaseCtrl', {
$scope: $scope,
frobnicate: function () {
return 123;
}
});
});
Assuming BaseCtrl does a bunch of stuff and is already well tested, I want to test that ChildCtrl instantiates BaseCtrl with certain arguments. My initial thought was something along these lines:
describe("ChildCtrl", function () {
var BaseCtrl;
beforeEach(module('my-module'));
beforeEach(module(function($provide) {
BaseCtrl = jasmine.createSpy();
$provide.value('BaseCtrl', BaseCtrl);
}));
it("inherits from BaseCtrl", inject(function ($controller, $rootScope) {
$controller('ChildCtrl', { $scope: $rootScope.$new() });
expect(BaseCtrl).toHaveBeenCalled();
}));
});
However when I run the test the spy is never called and the console shows "BaseCtrl instantiated", indicating that $controller is using the actual controller instead of the instance I am providing with $provide.value().
What's the best way to test this?
So it looks like $controller doesn't search for controllers by name in the $provide.value() namespace. Instead you have to use the $controllerProvider.register() method, which is only accessible from the module.config() block. Fortunately it looks like there's a hook we can use to get access to $controllerProvider on the module under test.
The updated test code looks like:
describe("ChildCtrl", function () {
var BaseCtrl;
beforeEach(module('my-module', function ($controllerProvider) {
BaseCtrl = jasmine.createSpy();
BaseCtrl.$inject = ['$scope', 'frobnicate'];
$controllerProvider.register('BaseCtrl', BaseCtrl);
}));
beforeEach(inject(function ($controller, $rootScope) {
$controller('ChildCtrl', { $scope: $rootScope.$new() });
}));
it("inherits from BaseCtrl", inject(function ($controller, $rootScope) {
expect(BaseCtrl).toHaveBeenCalled();
}));
it("passes frobnicate() function to BaseCtrl that returns 123", function () {
var args = BaseCtrl.calls.argsFor(0);
var frobnicate = args[1];
expect(frobnicate()).toEqual(123);
});
});

Getting error in controller while unit testing using karma jasmine

I'm getting this error while I'm running unit test using Karma-Jasmine
ReferenceError: myModule is not defined
My sample test case is as follows..
describe("Unit Testing", function() {
beforeEach(angular.mock.module('myModule.common'));
var scope, ngTableParams, filter ,testTableParam;
it('should have a commonController controller', function () {
expect(myModule .common.controller('commonController ', function (commonController ) {
$scope:scope;
ngTableParams:ngTableParams;
$filter: filter;
tableParams: testTableParam
}
)).toBeDefined();
});});
I have injected the module name as myModule.common.
Can you please suggest a solution?
Try following code snippet it might help
describe('testing myModule.common', function() {
var $rootScope, $scope, $filter, $controller, ngTableParams, testTableParam;
beforeEach(module('myModule.common'));
beforeEach(function() {
inject(function($injector) {
$rootScope = $injector.get('$rootScope');
$scope = $rootScope.$new();
$filter = $injector.get('$filter');
testTableParam = $injector.get('testTableParam');
ngTableParams = $injector.get('ngTableParams');
$controller = $injector.get('$controller')('commonController ', {
$scope: $scope
});
});
});
it('testing commonController ', function() {
expect('commonController ').toBeDefined();
});
});
It will solve your problem

Using $location.absUrl in controller breaks tests, how would I mock it?

I've read a couple of threads on this but can't figure out a solution to my problem. In my controller I have a function:
this.dName = function() {
var url = $location.absUrl();
var matches = url.match(/\/dName\/(.*)$/);
return matches[1];
};
This causes my tests to fail as there is no absolute url to grab. How would I go about mocking $location.absUrl so that my tests can pass?
For example you could spy on the $location service, in jasmine that would be:
describe('MyController', function(){
beforeEach(module('myApp'));
var MyController, scope, $location;
beforeEach(inject(function($injector) {
var $controller = $injector.get('$controller');
var $rootScope = $injector.get('$rootScope');
$location = $injector.get('$location');
scope = $rootScope.$new();
MyController = $controller('MyController', {
$scope: scope,
});
}));
it('Check dName', function () {
spyOn($location, 'absUrl').and.returnValue(["match0", "match1"]);
var match = scope.dName();
expect(match).toEqual("match1");
expect($location.absUrl).toHaveBeenCalled();
});
});

How to test services in an AngularJS Controller?

My controller is:
angularMoonApp.controller('SourceController', ['$scope', '$rootScope', '$routeParams', 'fileService', function ($scope, $rootScope, $routeParams, fileService) {
$scope.init = function() {
$rootScope.currentItem = 'source';
fileService.getContents($routeParams.path).then(function(response) {
$scope.contents = response.data;
$scope.fileContents = null;
if(_.isArray($scope.contents)) {
// We have a listing of files
$scope.breadcrumbPath = response.data[0].path.split('/');
} else {
// We have one file
$scope.breadcrumbPath = response.data.path.split('/');
$scope.breadcrumbPath.push('');
$scope.fileContents = atob(response.data.content);
fileService.getCommits(response.data.path).then(function(response) {
$scope.commits = response.data;
});
}
});
}
$scope.init();
}]);
My test is pretty simple:
(function() {
describe('SourceController', function() {
var $scope, $rootScope, $httpBackend, createController;
beforeEach(module('angularMoon'));
beforeEach(inject(function($injector) {
$httpBackend = $injector.get('$httpBackend');
$rootScope = $injector.get('$rootScope');
$scope = $rootScope.$new();
var $controller = $injector.get('$controller');
createController = function() {
return $controller('SourceController', {
'$scope': $scope
});
};
}));
it("should set the current menu item to 'source'", function() {
createController();
$scope.init();
expect($rootScope.currentItem).toBe('source');
});
it("should get the contents of the root folder", function() {
createController();
$scope.init();
// NOT SURE WHAT TO DO HERE!
});
});
})();
I want to test that the fileService had it's getContents function called and mock a response so that I can test the two scenarios (if is array and if isn't`)
I would recommend using Jasmine spies for this.
Here is an example that might help. I usually put the spyOn call in the beforeEach.
var mockedResponse = {};
spyOn(fileService, "getContents").andReturn(mockedResponse);
In the 'it' part:
expect(fileService.getContents).toHaveBeenCalled();
To get the response, just call the method in your controller that calls the fileService method. You may need to manually run a digest cycle too. Snippet from one of my tests:
var testOrgs = [];
beforeEach(inject(function(coresvc) {
deferred.resolve(testOrgs);
spyOn(coresvc, 'getOrganizations').andReturn(deferred.promise);
scope.getAllOrganizations();
scope.$digest();
}));
it("getOrganizations() test the spy call", inject(function(coresvc) {
expect(coresvc.getOrganizations).toHaveBeenCalled();
}));
it("$scope.organizations should be populated", function() {
expect(scope.allOrganizations).toEqual(testOrgs);
expect(scope.allOrganizations.length).toEqual(0);
});
deferred in this case is a promise created with $q.defer();
You can create a spy and verify only that fileService.getContents is called, or either verify extra calls (like promise resolution) by making the spy call through. Probably you should also interact with httpBackend since you may need to flush the http service (even though you use the mock service).
(function() {
describe('SourceController', function() {
var $scope, $rootScope, $httpBackend, createController, fileService;
beforeEach(module('angularMoon'));
beforeEach(inject(function($injector) {
$httpBackend = $injector.get('$httpBackend');
$rootScope = $injector.get('$rootScope');
$scope = $rootScope.$new();
// See here
fileService = $injector.get('fileService');
spyOn(fileService, 'getContents').andCallThrough();
var $controller = $injector.get('$controller');
createController = function() {
return $controller('SourceController', {
'$scope': $scope
'fileService': fileService
});
};
}));
it("should get the contents of the root folder", function() {
createController();
$scope.init();
expect(fileService.getContents).toHaveBeenCalled();
});
});
})();
You can also add expectations to what happens inside the callback but you should issue a httpBackend.flush() before.

Resources