I'm trying to test my simple controller but seems like nothing is working.
the controller:
userCtrlMod.controller('resetCtrl',
['$scope', '$ionicPopup', '$timeout','resetPwd',
function($scope, $ionicPopup, $timeout, resetPwd){
$scope.reset = function(){
$scope.resetPopUp = $ionicPopup.show({
templateUrl:'././templates/popup/reset.html',
scope: $scope
});
}}]);
my test file :
describe("resetCtrl", function () {
var $myScope, $myController, timeout;
beforeEach(module('dbooks.userCtrl'));
beforeEach(inject(function(
_$controller_,
_$rootScope_,
_$timeout_,
$ionicPopup
){
$myController = _$controller_;
$myScope = _$rootScope_;
$myController = $controller('resetCtrl' , {
$scope: $myScope,
$resetPopUp : $ionicPopup
});
}));
it("should have a $scope variable", function() {
//console.log($myScope);
expect($myScope).toBeDefined();
});});
I googled it but i could'nt find any solution, please someone tell me what I'm doing wrong.
the errors :
Uncaught Error: [$injector:unpr] Unknown provider: $ionicPopupProvider <- $ionicPopup
Uncaught Expected undefined to be defined.
at Object.
You don't provide all required dependencies when creating controller in test. You have to provide all dependencies required by the controller:
describe("resetCtrl", function () {
var $myScope, $myController, timeout;
beforeEach(module('dbooks.userCtrl'));
beforeEach(inject(function(
_$controller_,
_$rootScope_,
_$timeout_,
$ionicPopup
){
$myController = _$controller_;
$myScope = _$rootScope_;
var resetPwd = {
someResetmethod: jasmine.createSpy('rese')
};
$myController = $controller('resetCtrl' , {
$scope: $myScope,
$ionicPopup: $ionicPopup,
$timeout: _$timeout_,
resetPwd: resetPwd
});
}));
it("should have a $scope variable", function() {
//console.log($myScope);
expect($myScope).toBeDefined();
});
}
Please note that you can inject mocked objects as dependencies - in above code instead of original resetPwd mocked object with spy as method is injected. The important thing is that you have to provide all dependencies used by your controller and if you inject mocked objects those object of course have to include required methods and properties.
Please try this.
$myScope =__$rootScope_.$new();
Related
I'm getting the below error while doing karma/jasmine unit testing for both the test cases.I tried by modifying the controller by adding angular.controller in the spec file even then it is not working.Is there any way to fix?
TypeError: undefined is not a constructor (evaluating 'angular.controller('myView')')
myView.spec.js
// myView.spec.js
(function(){
describe('controller: myView', function(){
var module,myView,$q, $rootScope, $scope, uiGridConstants, overviewService, commonService, $timeout;
beforeEach(function() {
module = angular.module('app.myView');
controller= angular.controller('myView')
});
beforeEach(inject(function ($controller, _$q_, _$rootScope_, _$timeout_) {
$q= _$q_;
$rootScope = _$rootScope_;
$timeout= _$timeout_;
myView= $controller('myView', {
$q : _$q_,
$rootScope : _$rootScope_,
$timeout: _$timeout_
});
}));
describe("myViewto be defined", function() {
it("should be created successfully", function () {
expect(controller).toBeDefined();
});
it("overview should be defined", function () {
expect(myView()).toBeDefined();
});
});
});
})();
and myView.js
(function() {
'use strict';
angular
.module('app.myView')
.controller('myView', myView);
function myView($q, $rootScope, $scope, uiGridConstants, myViewService, commonService, $timeout) {
var vm = this;
vm.callFeedback = function () { };
})();
Sharing following code
// myView.spec.js
(function(){
describe('myView', function(){
var $controller, myView;
//we use angular-mocks to specify which modules we'll need within this
//test file.
beforeEach(angular.mock.module('app.myView'));
// Inject the $controller service to create instances of the controller
//(myView) we want to test
beforeEach(inject(function(_$controller_) {
$controller = _$controller_;
myView = $controller('myView', {});
}));
// Verify our controller exists
it('should be defined', function() {
expect(myView).toBeDefined();
});
});
})();
We set _$controller_to the $controller variable we created and then create an instance of our controller by calling $controller('myView', {}). The first argument is the name of the controller we want to test and the second argument is an object of the dependencies for our controller.
You should pass the injected parameters to your controller as shown:
(function() {
'use strict';
angular
.module('app.myView')
.controller($q,$rootScope,$scope,uiGridConstants,'myView', myView);
function myView($q, $rootScope, $scope, uiGridConstants, myViewService, commonService, $timeout) {
var vm = this;
vm.callFeedback = function () { };
})();
Also make sure that your module has all the necesary dependences in the angular.module('app.myView',['uiGridConstants', ...'etc']);
I'm trying to unit test $scope.$watch in controller, I don't know why $scope.$apply() in test code causes unexpected request error such as Error: Unexpected request: GET /locales/en.json. That's other part of the controller, why it's involved here?
However this error will not occur if I comment $scope.$apply, but of course $watch cannot be triggered in that case. Do I have to mock those requests like $httpBackend.whenGET('/locales/en.json').respond(''); ?
controller:
$scope.$watch(function(){
return $location.path();
}, function() {
$scope.currentPath = $location.path().match(/\/[a-z0-9A-Z_]*/)[0];
$scope.currentNav = 'menu.' + $scope.currentPath.replace('/', '');
});
jasmine:
describe('homeController', function() {
beforeEach(module('homeApp'));
var $rootScope, $scope, controller, $httpBackend, $location, $route, $window
beforeEach(inject(function($controller, _$rootScope_, _$httpBackend_, _$location_, _$route_, _$window_) {
$rootScope = _$rootScope_;
$scope = $rootScope.$new();
controller = $controller('homeController', {$scope: $scope});
$httpBackend = _$httpBackend_;
$location = _$location_;
$route = _$route_;
$window = _$window_;
}));
describe('watch path', function() {
it('should change currentPath and currentNav', function() {
$location.path('/dashboard');
$scope.$apply();
$location.path('/images');
$scope.$apply();
expect($scope.currentPath).toBe('/images')
expect($scope.currentNav).toBe('menu.images')
})
})
})
update:
It's working after mocking all the http requests required. But still want to know why it affects those requests.
In above example you are not watching anything. $watch function will be called when value of object being watched changes. It will return you both old and new value for object.
$scope.someValue = 0;
$scope.$watch(
"$scope.someValue",
function handleFooChange(newValue, oldValue) {
console.log("$scope.someValue:", newValue);
}
);
Here someValue is being watched for change. Once value of someValue will change, callback function of $watch will be called.
I am new to developing in angular, and am trying to learn how to test angular controllers. The controller I am testing uses $location.seach().something. I looked at the docs for $location, but don't quickly see how I am supposed to mock this in karma/jasmine.
The controller:
rmtg.controller('ErrorCtrl', ['Session', '$location', '$routeParams', '$scope', '$window',
function(Session, $location, $routeParams, $scope, $window) {
console.log('ErrorCtrl(%o, %o, %o)', $location.path(), $location.search(), $routeParams);
$scope.status = $location.search().status;
$scope.message = $location.search().message;
$scope.isAuthorized = (typeof(Session.auth) === 'object');
$scope.signin = function() {
$window.location = '/signin/#/' + $routeParams.origin + (Session.auth ? '?email=' + Session.auth.email : '');
};
}]);
My current spec attempt:
'user strict';
describe('Testing the errorCtrl controller', function(){
beforeEach(module("rmtg"));
var errorCtrl, scope;
beforeEach(inject(function($controller, $rootScope){
scope = $rootScope;
errorCtrl = $controller("ErrorCtrl", {
$scope: scope
});
}));
it('$scope.status should be set to 404 when location is set to 404', function(){
//set the $location.search values so that the scope is correct
$location.search('status', '404');
expect(scope.status).toBe('404');
});
});
And the current error message:
Testing the errorCtrl controller $scope.status should be set to 404 when location is set to 404 FAILED
Expected undefined to be '404'.
at Object. (/Users/adamremeeting/git/mrp-www/app/tests/example.js:20:24)
I'd also really appreciate links to resources on tdd with angular 1.5 and how I mock and stub correctly.
Edit After Answer
So I updated the test as per user2341963 suggestions, and did my best to look through his plunker example, but still don't have a passing test.
the current spec (controller has not changed from above)
'user strict';
describe('ErrorCtrl', function(){
beforeEach(module("rmtg"));
var scope, $location, $controller;
beforeEach(inject(function(_$controller_, _$rootScope_, _$location_){
scope = _$rootScope_.$new();
$location = _$location_
$controller = $_controller_;
}));
describe('$scope.status', function(){
it('should set status to 404', function(){
//set the $location.search values so that the scope is correct
$location.search('status', '404');
//init controller
$controller('ErrorCtrl', {
$scope: scope,
$location: $location
});
expect(scope.status).toBe('404');
});
});
});
But I am getting an error now that $controller is not defined.
You are getting undefined in your test because you are not setting $location anywhere.
Based on your controller, the search parameters must be set before the controller is initialised. See plunker for full example.
describe('testApp', function() {
describe('MainCtrl', function() {
var scope, $location, $controller;
beforeEach(module('myApp'));
beforeEach(inject(function(_$rootScope_, _$controller_, _$location_) {
scope = _$rootScope_.$new();
$location = _$location_;
$controller = _$controller_;
}));
it('should set status to 404', function() {
// Set the status first ...
$location.search('status', '404');
// Then initialise the controller
$controller('MainCtrl', {
$scope: scope,
$location: $location
});
expect(scope.status).toBe('404');
});
});
});
As for resources, so far I've found the angular docs are good enough.
I've been trying to get started with unit testing in angular with karma and jasmine, and i've been pulling my hair out trying to wrap my head around how to test controllers with dependencies. I tried mocking a spy with a jasmine spyObj and registering it in the beforeEach hook, but for some reason the spy isn't being recognized.
Here's the code:
angular.module('testModule', [])
.controller('TestController', [
'$scope',
'TestService',
function ($scope, TestService) {
$scope.data = TestService.load();
}])
.factory('TestService', function () {
return {
load: function(){
return "foo";
}
}
});
and here's the test
describe('TestController', function() {
var $controller, $scope, TestService;
beforeEach(module('testModule'), function($provide){
TestService = jasmine.createSpyObj("TestService", ["load"]);
TestService.load.andReturn("bar");
$provide.value("TestService", TestService)
});
beforeEach(inject(function(_$controller_, $rootScope, _TestService_) {
$scope = $rootScope.$new();
TestService = _TestService_;
$controller = _$controller_('TestController', {
$scope: $scope,
TestService: TestService
});
}));
it('should set $scope.data to bar when TestService.load is called', function() {
expect(TestService.load).toHaveBeenCalled();
expect($scope.data).toEqual("bar");
}); });
Both assertions in the test fail.
I get 'Error: Expected a spy, but got Function' when i call expect(TestService.load).toHaveBeenCalled();
and if I call expect($scope.data).toEqual("bar"), I get Expected 'foo' to equal 'bar'. "Foo" is coming from the actual service, not the spy object.
Thanks for your help.
Instead of jasmine.createSpyObj, it will be easier to use the existing service that the $injector provides and then just mock the single method. You can achieve this with spyOn instead:
describe('TestController', function() {
var $controller, $scope, TestService;
beforeEach(module('testModule'));
beforeEach(inject(function(_$controller_, $rootScope, _TestService_) {
$scope = $rootScope.$new();
TestService = _TestService_;
spyOn(TestService, 'load').and.returnValue('bar');
$controller = _$controller_('TestController', {
$scope: $scope,
TestService: TestService
});
}));
it('should set $scope.data to bar when TestService.load is called', function() {
expect(TestService.load).toHaveBeenCalled();
expect($scope.data).toEqual("bar");
});
});
In your beforeEach you are injecting in _TestService_ and then overwriting the one you declared in the previous beforeEach via:
TestService = _TestService_;
Remove that code and your test should succeed.
Also there is no need to do this:
$provide.value("TestService", TestService)
Basically you're trying to use Angular's dependency injection when you're manually injecting things which is unnecessary.
I'm getting this error when trying to test a controller in Karma:
Error: [$injector:unpr] http://errors.angularjs.org/1.2.14/$injector/unp
r?p0=%24elementProvider%20%3C-%20%24element
at c:/js/libs/angular/angular1.2.14/angular.min.js:32
at c (c:/js/libs/angular/angular1.2.14/angular.min.js:30)
at c:/js/libs/angular/angular1.2.14/angular.min.js:32
at c (c:/js/libs/angular/angular1.2.14/angular.min.js:30)
at d (c:/js/libs/angular/angular1.2.14/angular.min.js:30)
at c:/js/libs/angular/angular1.2.14/angular.min.js:31
at c:/js/libs/angular/angular1.2.14/angular.min.js:63
at c:/tests/unit/widget_tests/myTest.test.js:13
at d (c:/js/libs/angular/angular1.2.14/angular.min.js:30)
at workFn (c:/js/libs/angular/angular1.2.14/angular-mocks.js:2160)
I'm including all the angular files in karma.conf.js and compiling the controller like so:
var $scope, $http, $translate;
beforeEach(module('myApp.services'));
beforeEach(module('myApp.directives'));
beforeEach(inject(function ($rootScope, $controller, _$httpBackend_) {
$scope = $rootScope.$new();
$controller('myController', {$scope : $scope});
}));
describe('Initialization :', function(){
it('Should ', function() {
})
})
})
I needed to inject the $element into the controller, though compiling the full directive is another option.
$controller('myController', {$scope : $scope, $element :$('<div></div>')});
Posting this as the duplicate question is missing an example.
Your $scope variable is undefined so far.
By the way, the following should work:
var $scope, $httpBackend;
beforeEach(function() {
module('myApp', 'myApp.services', 'myApp.directives'));
inject(function ($rootScope, $controller, _$httpBackend_) {
$scope = $rootScope.$new();
$httpBackend = _$httpBackend_;
$controller('myController', {$scope : $scope});
}));
});
describe('Initialization :', function(){
it('Should ', function() {
})
});