I'm trying to test an angular controller that queries the DOM for a select box value.
$scope.getValue = function(){
document.getElementById('select_box_value').value;
}
How can I stub this function so it returns a simple integer value in a controller test, like so? Every time I try it this way, the original function runs instead of my stub.
describe('MyCtrl', function () {
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller('MyCtrl', {
'$scope': scope,
'$scope.getValue':sinon.stub().returns(20)
});
}));
it('gets the select box value', function () {
expect(scope.getValue).toBe(20);
});
});
Related
I wrote a test for my directive using jasmine testcase framework with karma testcase runner.
In my project ,I am already having one directive called
<parent-directive></parent-directive>
and i tried to include that parent directive into another one called
<child-directive></child-directive>.
Parent directive elements are converted as components called SampleComponents and included in the child directive
Sample.js
'use strict'
angular.module('Sample')
.directive('SampleHeader', SampleHeader)
function SampleHeader () {
return {
restrict: 'A',
templateUrl: 'header/header.html',
scope: {},
controller: function ($scope) {
$scope.logoutHeader = function () {
console.log('Logout call back')
require('electron').remote.app.quit()
}
}
}
}
SampleSpec.js
describe('SampleHeader', function () {
var $compile, $rootScope, elements, scope, controller
beforeEach(module('Sample'))
beforeEach(module('SampleComponenets'))
beforeEach(module('ngAnimate'))
beforeEach(module('ngRoute'))
beforeEach(module('ngMaterial'))
beforeEach(module('ngCookies'))
beforeEach(module('datatables'))
beforeEach(inject(function (_$compile_, _$rootScope_, _$q_,_$controller_) {
deferred = _$q_.defer()
$compile = _$compile_
$rootScope = _$rootScope_
controller = _$controller_
scope = $rootScope.$new()
elements = angular.element('<sample-header></sample-header>')
$compile(elements)($rootScope.$new())
$rootScope.$digest()
controller = elements.controller('SampleHeader')
scope = elements.isolateScope() || elements.scope()
scope.$digest()
}))
it('should check logoutHeader is called', function () {scope.logoutHeader()
})
})
restrict: 'A' -> it seems you created a attribute directive so you should compile the directive as a attribute (like, elements = angular.element('<div sample-header></div>')).
describe('SampleHeader', function () {
var $compile, $rootScope, elements, scope, controller
beforeEach(module('Sample'))
beforeEach(module('SampleComponenets'))
beforeEach(module('ngAnimate'))
beforeEach(module('ngRoute'))
beforeEach(module('ngMaterial'))
beforeEach(module('ngCookies'))
beforeEach(module('datatables'))
beforeEach(inject(function (_$compile_, _$rootScope_, _$q_,_$controller_) {
deferred = _$q_.defer()
$compile = _$compile_
$rootScope = _$rootScope_
controller = _$controller_
scope = $rootScope.$new()
elements = angular.element('<div sample-header></div>')
$compile(elements)($rootScope.$new())
$rootScope.$digest()
controller = elements.controller('SampleHeader')
scope = elements.isolateScope() || elements.scope()
scope.$digest()
}))
it('should check logoutHeader is called', function () {scope.logoutHeader()
})
})
I'm trying to test one of my directives and I can't work out why my scope object is undefined.
define(["require", "exports", 'angular', 'directives', "angularMocks", "chartApp"], function (require, exports, angular, Directives) {
"use strict";
describe('board controls', function () {
describe('task filter', function () {
var $compile;
var $rootScope;
var scope;
var element;
beforeEach(angular.mock.module('chartApp'));
beforeEach(angular.mock.module('partials/directives/board-controls.html'));
beforeEach(inject(function (_$compile_, _$rootScope_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
scope = $rootScope.$new();
expect(scope).toBeDefined();
element = $compile('<board-controls></board-controls>')(scope);
scope.$digest();
}));
it('displays modal', function () {
scope.showChildFilters();
});
});
});
});
In the it('displays modal')... part Karma outputs:
TypeError: undefined is not an object (evaluating 'scope.showChildFilters')
But in the beforeEach(...) part it seems to be working. I just can't see why this doesn't work.
You need to change to this
it('displays modal', function () {
//scope.showChildFilters();
var isolateScope = element.isolateScope(); //I prefer to name isolateScope
isolateScope.$apply() //cause scope to digest and watch and all that
isolateScope.showChildFilters();
});
This is a very detailed answer to your question as well.
Testing element directive - can't access isolated scope methods during tests
Thie question is somewhat related to How do I inject a mock dependency into an angular directive with Jasmine on Karma. But I cant figure it out. Heres the thing:
I have a simple angular directive for rendering a head-part of my apllication with several parameters. One is passed, two came from the URL vie $location and $routeParam. The directive looks like this:
'use strict';
myApp.directive('appHeader', ['$routeParams', '$location', function ($routeParams, $location) {
return {
restrict: 'E',
templateUrl: 'path/to/partials/template.html',
scope: {
icon: '#icon'
},
link: function (scope, element, attributes) {
var lastUrlPart = $location.path().split('/').pop();
scope.project = $routeParams.itemName;
scope.context = lastUrlPart === scope.project ? '' : lastUrlPart;
}
};
}]);
This is called via <app-header icon="bullhorn"></app-header>.
Now I want to add some tests. As for the template rendering I'm done. The following works like expected. The test passes.
describe('appHeader', function () {
var element, scope;
beforeEach(module('myApp'));
beforeEach(module('myAppPartials'));
beforeEach(inject(function ($rootScope, $compile) {
element = angular.element('<app-header icon="foo"></app-header>');
scope = $rootScope;
$compile(element)(scope);
scope.$digest();
}));
it('should contain the glyphicon passed to the directive', function () {
expect(element.find('h1').find('.glyphicon').hasClass('glyphicon-foo')).toBeTruthy();
});
});
Now I want to test that scope.context and scope.project are set accordingly to the dependencies $location and $routeParams, which I want to mock of course. How can I acieve this.
I tried for instance the answer from the question linked above:
beforeEach(module(function ($provide) {
$provide.provider('$routeParams', function () {
this.$get = function () {
return {
itemName: 'foo'
};
};
});
}));
But In my test
it('should set scope.project to itemName from $routeParams', function () {
expect(scope.project).toEqual('foo');
});
scope.project is undefined:
Running "karma:unit:run" (karma) task
Chrome 35.0.1916 (Mac OS X 10.9.3) appHeader should set scope.project to itemName from routeParams FAILED
Expected undefined to equal 'foo'.
Error: Expected undefined to equal 'foo'.
As for the location dependency I tried to setUp a Mock mysel like this:
var LocationMock = function (initialPath) {
var pathStr = initialPath || '/project/bar';
this.path = function (pathArg) {
return pathArg ? pathStr = pathArg : pathStr;
};
};
Then injection $location in the before each and set a spyOn to the calling of path() like this:
spyOn(location, 'path').andCallFake(new LocationMock().path);
But then, scope.context is undefined, too.
it('should set scope.context to last part of URL', function () {
expect(scope.context).toEqual('bar');
});
Can someone please point out what I am doing wrong here?
Provider's mock works fine, but the problem is in scopes. Your directive has isolated scope. Thus this directive's scope is the child of the scope defined in test. Quick but not recomended fix is:
it('should set scope.project to itemName from $routeParams', function () {
expect(scope.$$childHead.project).toEqual('foo'); });
Try to avoid use scope when testing directives. Better approach will be to mock template and check data in it. For your case it will be something like this:
var viewTemplate = '<div>' +
'<div id="project">{{project}}</div>' +
'</div>';
beforeEach(inject(function ($templateCache) {
$templateCache.put('path/to/partials/template.html', viewTemplate);
}));
and test:
it('should set scope.project to itemName from $routeParams', function () {
expect(element.find('#project').text()).toEqual('foo');
});
for the context it will be the same.
I'm trying to test a directive.
This directive use the $compile provider.
I would try to expect if $compile will been called but providing it in the test I'm getting this error:
TypeError: 'undefined' is not a function (evaluating '$compile(angular.element(html))(scope)')
I know why is that happening (I'm overriding with a fake mock the actual $compile provider) but I don't really know how can I fix that problem.
This is the actual test code:
describe('directive', function () {
var scope, mockCompile;
beforeEach(function () {
mockCompile = jasmine.createSpy();
module('directive', function ($provide) {
$provide.value('$compile', mockCompile);
});
var html = '<div directive="foo"></div>';
// The problem is there. I'm injecting the mocked compile service
// Not the real one
inject(function ($compile, $rootScope) {
scope = $rootScope.$new();
$compile(angular.element(html))(scope);
scope.$digest();
});
});
it("should test the directive", function () {
//Act.
scope.$apply();
//Assert.
expect(mockCompile).toHaveBeenCalled();
});
});
First, make sure you have included angular-mocks.js for testing.
Then, each service you mock needs to begin and end with an underscore;
inject(function ($compile, $rootScope)
Should really by;
inject(function (_$compile_, _$rootScope_)
I have some JavaScript written in the context of AngularJS. My relevant JavaScript looks like the following:
.factory('$myFactory', function ($myLibrary, $interpolate) {
return {
myFunction: function ($scope, $attrs, p) {
if (p !== null) {
$attrs.set('myProperty', p);
}
}
};
I am trying to unit test this code. In an attempt to unit test the code, I'm using Jasmine.
it('should set myProperty', inject(function ($scope, $myFactory) {
// $myFactory.myFunction($scope
}));
I can't figure out how to inject some $attrs from my unit test. How do I do that? I can successfully get my $scope setup. However, I don't understand how to inject $attrs. Is there something special about it that I'm not aware of? I'm having a similar issue with $element, though that one is out of the context of this specific test.
Thank you!
Here is a plunker: http://plnkr.co/edit/k1cxSjpAXhhJUEOcx9PG
Maybe there is a better solution but That's what I got.
$scope is easy to get, you can inject $rootScope everywhere
$attrs on the other hand is only available through the $compile variable (it lives in compile.js)
My solution is to create a fake controller , to compile it and to hijack it's $attrs.
So that's how it looks like:
var injected = null
function fakeController($scope, $attrs, $element){
injected = {}
injected.$scope = $scope;
injected.$attrs = $attrs;
injected.$element = $element;
}
describe('Testing a Hello World controller', function() {
var $scope = null;
var $attrs = null;
var $element = null;
var ctrl = null;
//you need to indicate your module in a test
beforeEach(module('plunker'));
beforeEach(inject(function($compile, $rootScope, $controller) {
$compile('<span ng-controller="fakeController"></span>')($rootScope);
$scope = injected.$scope;
$attrs = injected.$attrs;
$element = injected.$element;
ctrl = $controller('MainCtrl', {
$scope: $scope,
$attrs: $attrs,
$element: $element
});
}));
it('should say hallo to the World', function() {
expect($scope.name).toEqual('World');
});
});
seems that you can just instantiate the controller with empty object, as well...
ctrl = $controller('MyCtrl', {
$scope: $scope,
$attrs: {}
});
In latest versions of AngularJS a controller needs to be provided to get full $attrs.
See $controllerProvider documentation.
Here's a snippet
describe('angular-component-controller', function() {
// save injected parameters of controller
var injected = {};
var controller;
// #see $https://docs.angularjs.org/api/ngMock/service/$componentController
// we need to extract those from $compile instead of use as locals
angular.module('locals', []).controller('controller',
['$attrs', function($attrs) {
injected.$attrs = $attrs;
}]
);
beforeEach(module('locals'));
beforeEach(inject(function($rootScope, $compile, $componentController) {
// invoke dummy component to get $attrs
$compile('<span ng-controller="controller">')($rootScope);
var locals = {};
locals.$scope = $rootScope.$new();
locals.$attrs = injected.$attrs;
var bindings = {};
controller = $componentController('component', locals, bindings);
}));
});
See this gist AngularJS $componentController unit test