Test statement containing parent scope variable using Karma - angularjs

I am testing my controller which has a parent scope variable reference. But it is giving variable undefined error.
subscription (controller)
var vm = this;
var userId=$scope.$parent.vm.userId;
var simId=$scope.$parent.vm.simId;
subscriptionSpec (spec file)
describe('subscription controller',function (){
var SubscriptionListCtrl,scope;
beforeEach(module('app'));
beforeEach(inject(function($controller,$compile,SubscriptionService,dataTableConfigService,commonUtilityService){
scope={};
scope.vm={};
SubscriptionListCtrl=$controller("SubscriptionListCtrl",{$scope:scope,$compile:$compile,SubscriptionService:SubscriptionService,dataTableConfigService:dataTableConfigService,commonUtilityService:commonUtilityService});
}));
});
Karma Jasmine Error
TypeError: Cannot read property 'vm' of undefined
This comes because of the controller statement
var userId=$scope.$parent.vm.userId;
Also if I replace the $scope.$parent.vm.userId with actual value then it will not give any error.
How do I write the test case for this line?

Try with mocking the parent scope like
describe('subscription controller',function (){
var SubscriptionListCtrl,scope;
beforeEach(module('app'));
beforeEach(inject(function($controller,$rootScope,$compile,SubscriptionService,dataTableConfigService,commonUtilityService){
scope = $rootScope.$new();
scope.$parent = {vm: {userId: 1, simId: 2}};
scope.vm={};
SubscriptionListCtrl=$controller("SubscriptionListCtrl",{$scope:scope,$compile:$compile,SubscriptionService:SubscriptionService,dataTableConfigService:dataTableConfigService,commonUtilityService:commonUtilityService});
}));
});

To extend vpsingh016 answer what should be done to give wanted result is define parent controller and parent scope and when initializing define $scope.$parent = $parentScope. For example :
describe('Controller: TestController', function () {
beforeEach(module('App'));
var $controller, $scope, $parentController, $parentScope;
beforeEach(inject(function (_$controller_, _$rootScope_) {
$scope = _$rootScope_.$new();
$parentScope = _$rootScope_.$new();
$scope.$parent = $parentScope;
$parentController = _$controller_('ParentController', {'$scope': $parentScope});
$controller = _$controller_('TestController', {'$scope': $scope});
}));
it('should get $parent variable', function () {
var userId=$scope.$parent.vm.userId;
var simId=$scope.$parent.vm.simId;
})
});

Related

Cant access Model (function) in Directive while unit testing

Unit testing a Directive which uses ngModel that has several function including getAll(). Model gets injected perfectly (when I output it, it shows the accessible getters/setters/etc). I pass it to the element. Do a compile and digest.
Getting the error 'TypeError: Cannot read property 'getAll' of undefined' though.
'console.log('vehiclesModel', vehiclesModel.get('vehicles'));'
Outputs the stubbedData!
'use strict';
describe('Directive: selectBox', function () {
beforeEach(module('sytacApp'));
beforeEach(module('syt.templates'));
var scope,
httpBackend,
$rootScope,
$compile,
element,
vehiclesModel,
stubbedData;
beforeEach(function () {
inject(function ($injector) {
$compile = $injector.get('$compile');
});
});
beforeEach(inject(function (_$rootScope_, _$httpBackend_, _vehiclesModel_, _stubbedData_) {
httpBackend = _$httpBackend_;
$rootScope = _$rootScope_;
vehiclesModel = _vehiclesModel_;
stubbedData = _stubbedData_;
vehiclesModel.set('vehicles', {data: stubbedData.container});
console.log('vehiclesModel', vehiclesModel.get('vehicles'));
}));
it('should process model data accordingly', function () {
var element = angular.element('<select-box identifier="type" selectedidentifier="selectedType" model="vehiclesTypesModel" data-ng-model="vehiclesModel"></select-box>');
element = $compile(element)(scope);
scope.$digest();
//......
});
});
Question. Am I overlooking something?
had to put ´vehiclesModel´ on ´scope scope.vehiclesModel´ before ´$compile´

Defined service method is not called, instead real service's is called

I am following this video tutorial and its source is here.
I am trying to apply this test
Here is my test
describe("InStudentController", function () {
beforeEach(module("eucngts"));
var inStudentsController;
var MyInStudentsService;
var $scope;
var $q;
var deferred;
beforeEach(function () {
MyInStudentsService =
{
getInStudents: function () {
deferred = $q.defer();
return deferred.promise;
}
};
});
beforeEach(inject(function ($controller, $rootScope, _$q_) {
$q = _$q_;
$scope = $rootScope.$new();
inStudentsController = $controller('InStudentsController', {
service: MyInStudentsService
});
}));
it("should request list of inStudents", function () {
spyOn(MyInStudentsService, "getInStudents").and.callThrough();
inStudentsController.getPage(); // <-- HERE
//deferred.resolve();
$scope.$root.$digest();
expect(MyInStudentsService.getInStudents).toHaveBeenCalled();
});
});
Here is relevant controller code:
InStudentsController.prototype.getPage = function (criteria) {
var self = this;
self.showGrid = true;
self.service.getInStudents();
};
When I call getPage() on test it calls real service method instead of defined in test.
What am I doing wrong?
EDIT
I don't use scope in my controller here is generated code(I use typescript):
function InStudentsController (service) {
var self = this;
self.service = service;
}
InStudentsController.$inject = ['InStudentsService'];
angular.module("eucngts").controller("InStudentsController", InStudentsController);
According to your latest update it is clear that the name of dependency is used wrong in the test. It must be InStudentsService instead of service. When using $inject property of controller constructor only that name matters, not the formal parameter name in function. That makes minification possible
inStudentsController = $controller('InStudentsController', {
InStudentsService: MyInStudentsService
});
Right now you're not injecting a scope into the controller. I think this:
$scope = $rootScope.$new();
inStudentsController = $controller('InStudentsController', {
service: MyInStudentsService
});
Should be this:
$scope = $rootScope.$new();
$scope.service = MyInStudentsService
inStudentsController = $controller('InStudentsController', {
$scope: $scope
});
But it seems odd passing the service in on the scope. Instead, you should be declaring the controller something like this:
angular.module('myApp')
.controller('InStudentsController', function ($scope, InStudentsService) {
...
});
And then the service would be injected like so:
$scope = $rootScope.$new();
inStudentsController = $controller('InStudentsController', {
$scope: $scope,
InStudentsService: MyInStudentsService
});

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 });
}));

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();
});
});

Jasmine Test in Angular for controller

I get the following error: TypeError: undefined is not a function
The problem is that the common is module and a factory and the problem is on my line
var ctrl = $controllerConstructor("resetPasswordSentScreen", { $scope: scope, common: common});
Here is the full test:
describe('resetPasswordSentScreen', function () {
var scope, $controllerConstructor;
beforeEach(module('common', 'app'));
beforeEach(inject(function($controller, $rootScope) {
scope = $rootScope.$new();
$controllerConstructor = $controller;
}));
it('it should navigate to the correct url when backToLogin is called ', function (common) {
var ctrl = $controllerConstructor("resetPasswordSentScreen", { $scope: scope, common: common });
var mocklocation = sinon.stub({ url: function () {}});
expect(scope.backToLogin()).toBe(mocklocation.url);
});
});
That is not the problem, the problem is that you can't inject stuff into your functions like you do in your code. To inject you need to call inject as you did in the beforeEach. So, if you want to inject that factory, you need this:
it("message", inject(function(common) {
...
}));
There is how you inject. That should work.

Resources