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´
Related
I'm using Angular 1.5.5 and Jasmine as test framework. Currently I have to do something like this so that the test passes:
function createController(bindings) {
return $componentController('myController', null, bindings);
}
beforeEach(inject(function (_$componentController_) {
$componentController = _$componentController_;
}));
describe('on pages updated', function () {
beforeEach(function () {
controller = createController({prop1: 0, prop2: 0});
controller.$onInit(); // you see I have to explitcitly call this $onInit function
});
it('should update isSelected and currentPage', function () {
expect(controller.prop1).toBe(0);
expect(controller.prop2).toBe(0);
controller.prop1= 1;
controller.prop2= 2;
controller.$onChanges(controller); // and $onChanges here as well
expect(controller.prop1).toBe(1);
expect(controller.prop2).toBe(2);
});
});
There is an issue in github regarding this: https://github.com/angular/angular.js/issues/14129
Basically it is working as intended, not calling $onInit or $onChanges automatically.
it makes no sense (or low sense) to execute $onInit, I explain it: $componentController is to instance controllers a kind of replacement for $controller, but instead of creating instances of controllers registered by the controllerProvider it creates instances of controllers registered through directives (the ones that satisfies a component definition). So, once you have the instance of the controller, you can call manually $onInit, and all the lifecycle of your controller, the idea is that you are testing a controller, not a directive (and its relationships).
I don't know if this will help, but for testing components I do the following
beforeEach(module('templates'));
var element;
var scope;
beforeEach(inject(function ($rootScope, $compile) {
scope = $rootScope.$new();
scope.draw = new ol.source.Vector({ wrapX: false });
element = angular.element('<custom-element draw="draw"></custom-element>');
element = $compile(element)(scope);
}));
var controller;
beforeEach(inject(function ($rootScope, $componentController) {
scope = $rootScope.$new();
scope.draw = new ol.source.Vector({ wrapX: false });
controller = $componentController('customElement', {draw: new ol.source.Vector({ wrapX: false })}, { $scope: scope });
}));
and $onInit() and $onChanges() get triggered when they should be, by themselves
You would require to take reference of controller from compiled version of element. As shown below:
describe('Component: Test Method', function () {
beforeEach(inject(function (_$rootScope_) {
scope = _$rootScope_.$new();
}));
it('should set value of selectedPackage to null after $onChanges event', inject(function ($compile) {
// Trigger 1st $onChanges
scope.selectedPackage = 'packageCode';
var element = angular.element('<select-my-tv selected-package="selectedPackage"></select-my-tv>');
element = $compile(element)(scope);
scope.$digest();
//Extract the Controller reference from compiled element
var elementController = element.isolateScope().$ctrl;
// Assert
expect(elementController.selectedPackage).toBeNull();
}));
});
});
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;
})
});
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 });
}));
I have been doing angularJS for a while now (without tests) but I want to do it properly! I have a controller defined like so
(function () {
'use strict';
angular.module('app')
.controller('CarehomeListCtrl', ['$scope', 'carehomesDataService', carehomeListCtrl]);
function carehomeListCtrl($scope, carehomesDataService) {
var vm = this;
vm.carehomeCollection = [];
vm.activate = activate;
function activate() {
vm.carehomeCollection = carehomesDataService.getAllCarehomes();
}
activate();
}
})();
and then my spec
describe("Carehomes tests", function () {
var $scopeConstructor, $controllerConstructor;
beforeEach(module('app'));
beforeEach(inject(function ($controller, $rootScope) {
$controllerConstructor = $controller;
$scopeConstructor = $rootScope;
}));
describe("CarehomeListCtrl", function () {
var ctrl, dataService, scope;
function createController() {
return $controllerConstructor('CarehomeListCtrl', {
$scope: scope,
carehomesDataService: dataService
});
}
beforeEach(inject(function ($injector) {
scope = $scopeConstructor.$new();
dataService =$injector.get('carehomesDataService') ;
}));
it("should have a carehomesCollection array", function () {
ctrl = createController();
expect(ctrl.carehomesCollection).not.toBeNull();
});
it("should have 3 items in carehomesCollection array when load is called", function () {
ctrl = createController();
expect(ctrl.carehomeCollection.length).toBe(3);
});
});
});
The problem here is that the call to instantiate my controller fails with error whenever I call it with any arguments whether an empty object {} or just $scope : scope} so I know the problem is not carehomesDataService.
Result StackTrace: Error: [ng:areq] Argument 'CarehomeListCtrl' is not
a function, got undefined
http://errors.angularjs.org/1.2.26/ng/areq?p0=CarehomeListCtrl&p1=not%20a%20function%2C%20got%20undefined
However, if I instantiate that controller like this $controllerConstructor('CarehomeListCtrl'); without arguments, it gets instantiated. I'm stumped!
carehomesDataService is a custom service I have written but it's own tests pass and it is correctly injected into the controller in the application.
Any help would be massively appreciated.
Note: I do not quite agree with defining properties on the controller as the view model instead of on $scope but I am following Jesse Liberty's pluralsight course and that's how he does it....plus injecting scope isn't quite working right now which is annoying. Thanks in advance.
My Jasmine unit test is as follows
describe('controllers', function () {
'use strict';
beforeEach(module('myapp.controllers'));
angular.mock.module('myapp.controllers', function ($provide) {
$provide.value('$localStorage', $localStorage);
});
it('should have a creationController', inject(function ($controller, _$rootScope_, localize) {
var scope = _$rootScope_.$new();
var localize = localize;
var myCtrl1 = $controller('creationController', {
$scope: scope,
localize: localize
});
expect(myCtrl1).toBeDefined();
}));
});
When I try to execute the test I'm getting the following error
Error: [$injector:unpr] http://errors.angularjs.org/1.2.20/$injector/unpr?p0=localizeProvider%20%3C-%20localize
There is a dependency called as "localize" being used in the controller. However I'm unable to inject that in to this unit test.
Any idea how I can solve this problem ?
injector look for _componenrToBeInjected_ for injection . If you have a service name localized (Are you sure you have one ?) then for injection use
_ localize_ so your it block should look like this .
it('should have a creationController', inject(function ($controller, _$rootScope_, _localize_) {
var scope = _$rootScope_.$new();
var localize = _localize_;
var myCtrl1 = $controller('creationController', {
$scope: scope,
localize: localize
});
expect(myCtrl1).toBeDefined();
}));