I'm trying to unit test a (child) controller of an AngularJS 1.5 (with Webpack) component that requires a parent component and a controller from another module.
Child controller structure:
function ChildController () {
var vm = this;
vm.searchText = '';
vm.submit = function() {
var data = {};
data['srch'] = vm.searchText;
vm.parentCtrl.submitTextSearch(data);
};
}
module.exports = ChildController;
Child component:
var template = require('./child.html');
var controller = require('./child.controller');
var childComponent = {
require: {
parentCtrl: '^parent'
},
template: template,
controller: controller,
controllerAs: 'vm'
};
module.exports = childComponent;
So what I would like to do is to mock out the parentCtrl that's required in the childController's submit()-function. I've been unable to find how to actually do this. I've found some similar child-parent directive solutions and tried those, e.g. injecting the parent controller through fake HTML-element as described in this child-parent directive example and basically the same stackoverflow solutions with no results. My problems differ at least in the fact that the child and parent controller are in different modules. And I suppose scope-tricks are not that much Angular 1.5-style?
The skeleton of my Jasmine test without my failed mock attempts:
describe('child component', function() {
describe('child controller', function() {
var controller;
beforeEach(angular.mock.module('child'));
beforeEach(inject(function(_$componentController_) {
controller = _$componentController_('child');
}))
it('should work', function() {
controller.searchText = "test";
controller.submit();
})
})
})
That results in TypeError: Cannot read property 'submitTextSearch' of undefined
. What exactly should I do to mock the parent controller out? With my limited experience in Angular, I'm out of ideas.
In your case you're add parentCtrl
as dependency of your component so in order to test it you have to mock parent component as well and assign it to controller. So you'd need to do something like:
beforeEach(inject(function(_$componentController_) {
controller = _$componentController_('child');
parentCtrl = _$componentController_('parent');
controller.parentCtrl = parentCtrl;
}))
Using the below code will get it initialized and please check the working Jasmine unit test Plunker
var ctrP = $componentController('parentComp');
var ctrl = $componentController('childComp', {}, {
parentCtrl: ctrP
});
And your test case should be as shown below:
'use strict';
describe('component: heroDetail', function() {
var $componentController, $compile, $rootScope;
beforeEach(module('plunker'));
beforeEach(inject(function(_$componentController_) {
$componentController = _$componentController_;
}));
it('should expose a `hero` object', function() {
var ctrP = $componentController('parentComp');
console.log(ctrP);
var ctrl = $componentController('childComp', {}, {
parentCtrl: ctrP
});
console.log(ctrl);
ctrl.submit('some data');
expect(ctrl.parentCtrl.searchText).toEqual('some data');
});
});
1. Solution
In your test instantiate the parent controller with a new scope:
mainScope = $rootScope.$new();
$controller('ParentController', {$scope: mainScope});
and in your child controller, instantiate a new scope using the previously instantiated scope:
childScope = mainScope.$new();
$controller('ChildController', {$scope: childScope});
Example from the AngularJS documentation:
describe('state', function() {
var mainScope, childScope, grandChildScope;
beforeEach(module('myApp'));
beforeEach(inject(function($rootScope, $controller) {
mainScope = $rootScope.$new();
$controller('MainController', {$scope: mainScope});
childScope = mainScope.$new();
$controller('ChildController', {$scope: childScope});
grandChildScope = childScope.$new();
$controller('GrandChildController', {$scope: grandChildScope});
}));
it('should work', function() {
grandChildScope.searchText = "test";
grandChildScope.submit();
});
});
2. Solution
Child controller structure:
function ChildController () {
var vm = this;
vm.searchText = '';
vm.submit = function() {
var data = {};
data['srch'] = vm.searchText;
vm.parentCtrl.submitTextSearch(data);
};
}
module.exports = ChildController;
Child component:
var template = require('./child.html');
var controller = require('./child.controller');
var childComponent = {
bindings: {
searchText: 'test'
},
template: template,
controller: controller,
controllerAs: 'vm'
};
module.exports = childComponent;
var ChildController = $componentController('childComponent', null, {...});
ChildController.$onInit();
expect(ChildController.searchText).to.equal('test');
expect(ChildController.submit()).to.equal('*expected result value should come here*');
REFRENCES :
AngularJS documentation - Testing Controllers
AngularJS documentation - $componentController
Unit Testing Angular Components with $componentController
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With