I am trying to test a controller for angularjs using karma, the controller inject a $route to get the current path but when I try to run karma test on it I get.
TypeError: 'undefined' is not an object( evaluating '$route.current')
Here is my controller:
angular.module('myApp').controller('EditController',['$scope', '$http', '$route', function($scope,
$http,$route){
var myId = $route.current.params.myId;
$scope.var1 = 'var1';
console.log(myId);
}]);
Here is my Karma file:
'use strict';
describe('Controller: EditController', function(){
beforeEach(module('myApp'));
var EditCtrl,scope,route;
beforeEach(inject(function($controller,$rootScope,$route,$http){
scope=$rootScope.$new();
EditCtrl = $controller('EditCtrl',{
$scope:scope,
$route:route
});
}));
it('should have var1 equal to "var1"',function(){
expect(scope.var1).toEqual('var1');
});
});
Your beforeEach hook isn't injecting the $route service. Change it to this.
beforeEach(inject(function($controller,$rootScope,$route,$http){
scope=$rootScope.$new();
route = $route;
EditCtrl = $controller('EditCtrl',{
$scope:scope,
$route:route
});
}));
You may also want to mock the $route.current object in case it isn't instantiated properly, since there's no routing going on in your test. In which case you could just add
$route.current = { params: { myId: 'test' } };
in the hook as well.
Related
Main module being injected with everything;
require('./dashboard');
module.exports = angular.module('college', ['college.dashboard'])
.config(function ($stateProvider) {
$stateProvider
.state('college.list', {
url: '/college',
templateUrl: '/dashboard/dashboard.html',
controller: 'DashboardCtrl',
authenticate: true
});
})
.factory('ProjectFactory', require('./services/college.service'));
College Index, which makes the dashboard controller available;
module.exports = angular.module('college.dashboard',
[])
.controller('DashboardCtrl', require('./dashboard.controller.js'));
The college controller exposes the following method;
module.exports = function($scope, $rootScope, $state) {
$scope.openCollege = function(id) {
$rootScope.currentCollege = id;
$state.go('college.main', {currentCollege: id});
};
};
The following error is thrown when the unit test calls
scope.openCollege (2);
Error:
Error: Could not resolve 'college.main' from state ''
Creating state;
beforeEach(inject(function ($rootScope, $state, $location, $controller) {
scope = $rootScope.$new();
location = $location;
rootScope = $rootScope;
$rootScope.currentCollege = {};// Empty by default
state = $state;
$controller('DashboardCtrl', {
$scope: scope,
$state: state,
$location: location
});
}));
Some of the spec test code;
expect(state.current.name).to.equal('');
scope.openCollege(2);
I need to figure out how to handle/mock $state.go during the Karma unit testing so that state knows about college.main.
Any help is appreciated.
J
Here is how I got it working;
I added the following to the spec test;
// Globally defined
var stateSpy;
// within the beforeEach
stateSpy = sinon.stub($state, 'go');
// In the unit test
scope.openCollege (2);
assert(stateSpy.withArgs('college.main', '{currentCollege: 2}').calledOnce);
Note: the $state was not passed to the the controller.
I now have green tests!
Thanks for your help, gave me the idea of how to make this work.
J
You should use
it('should be able to go to person edit state', function () {
DashboardCtrl();
scope.openProject('12345');
scope.$digest();
expect(state.go).toHaveBeenCalledWith('college.main', { id : '12345' });
});
I have a very simple controller that looks like this.
timeInOut.controller('timeInOutController', function($scope, $filter, $ionicScrollDelegate){
...
});
Whenever I try to create a unit test for it like so...
(function() {
'use strict';
var scope, controller, filter;
describe('timeInOutController', function () {
beforeEach(module('common.directives.kmDateToday'));
beforeEach(inject(function ($rootScope, $controller, $filter) {
scope = $rootScope.$new();
filter = $filter;
controller = $controller('timeInOutController', {
$scope: scope
});
}));
describe('#date setting', function(){
...
});
});
})();
I get the error:
[$injector:unpr] Unknown provider: $ionicScrollDelegateProvider <- $ionicScrollDelegate
Obviously in my example here I'm not trying to inject the $ionicScrollDelegate into the test, that's just because I've tried it any number of ways with no success and don't know which failed attempt to include.
Also in my karma.conf.js file I am including the ionic.bundle.js and angular-mocks.js libraries/files.
I can successfully unit test anything that doesn't use anything $ionic in it, so I know my testing framework is set up correctly, the issue is injecting anything ionic related.
You need to pass in all the parameters if you're going to instantiate your controller via angular. By adding the parameters you are telling angular that any time you create one of these controllers I need these things too because I am dependent upon them.
So my suggestion is to mock up some representation of these dependencies and inject them in when you are creating the controller. They do not have to be (and should not be) the actual services for your unit tests. Jasmine gives you the ability to create spy objects that you can inject so you can verify the the behavior of this unit.
(function() {
'use strict';
var scope, controller, filter, ionicScrollDelegate;
describe('timeInOutController', function () {
beforeEach(module('common.directives.kmDateToday'));
beforeEach(inject(function ($rootScope, $controller, $filter) {
scope = $rootScope.$new();
filter = $filter;
// func1 and func2 are functions that will be created as spies on ionicScrollDelegate
ionicScrollDelegate = jasmine.createSpyObj('ionicScrollDelegate', ['func1', 'func2']
controller = $controller('timeInOutController', {
$scope: scope,
$filter: filter,
$ionicScrollDelegate: ionicScrollDelegate
});
}));
describe('#date setting', function(){
...
});
});
})();
You can find more about spies via jasmine's documentation
You need to create mock objects for all dependencies your controller is using.
Take this controller as an example:
angular.module('app.module', [])
.controller('Ctrl', function($scope, $ionicLoading) {
$ionicLoading.show();
});
Here you are using the $ionicLoading service, so if you want to test this controller, you have to mock that object specifying the methods you're using in the controller:
describe('Test', function() {
// Mocks
var $scope, ionicLoadingMock;
var ctrl;
beforeEach(module('app.module'));
beforeEach(function() {
// Create $ionicLoading mock with `show` method
ionicLoadingMock = jasmine.createSpyObj('ionicLoading', ['show']);
inject(function($rootScope, $controller) {
$scope = $rootScope.$new();
ctrl = $controller('Ctrl', {
$scope: $scope,
$ionicLoading: ionicLoadingMock
});
});
});
// Your test goes here
it('should init controller for testing', function() {
expect(true).toBe(true);
});
});
I'm trying to test my application with Karma but I get following Errors:
minErr/<#/home/usr/webui-ng/src/client/app/bower_components/angular/angular.js:78:5
loadModules/<#/home/usr/webui-ng/src/client/app/bower_components/angular/angular.js:3859:1
forEach#/home/usr/webui-ng/src/client/app/bower_components/angular/angular.js:325:7
loadModules#/home/usr/webui-ng/src/client/app/bower_components/angular/angular.js:3824:5
createInjector#/home/usr/webui-ng/src/client/app/bower_components/angular/angular.js:3764:3
workFn#/home/usr/webui-ng/src/client/app/bower_components/angular-mocks/angular-mocks.js:2150:9
These are my files:
hello.js
angular.module('myApp', [])
.controller('MainController', function($scope) {
$scope.name = "Ari";
$scope.sayHello = function() {
$scope.greeting = "Hello " + $scope.name;
}
})
hello.js - test file :
describe('Unit: MainController', function() {
// Load the module with MainController
beforeEach(module('myApp'));
var ctrl, scope;
// inject the $controller and $rootScope services
// in the beforeEach block
beforeEach(inject(function($controller, $rootScope) {
// Create a new scope that's a child of the $rootScope
scope = $rootScope.$new();
// Create the controller
ctrl = $controller('MainController', {
$scope: scope
});
}));
it('should create $scope.greeting when calling sayHello',
function() {
expect(scope.greeting).toBeUndefined();
scope.sayHello();
expect(scope.greeting).toEqual("Hello Ari");
});
});
As you can see, I've already loaded the module, as the solution was in Controller undeclared in Jasmine test.
What else could it be? I haven't found much about these errors on the web. I would be very happy to finally find an answer for that.
As runTarm mentioned, the path in the karma.conf.js was not set to the actual path of the script file (hello.js).
I have a service, DataContext which returns me a set of data that I want to use in my controller. This data is used by an ng-grid directive. The options for the grid are supplied by GridOptionsService.
All of this works fine, but I'm trying to write a unit test to check and see if everything's working.
describe('Grid display test', function() {
var $scope, elm, oCtrl;
beforeEach(module('ngGrid'));
beforeEach(inject(function ($rootScope, $compile, $controller,DataContext,GridOptionsService) {
$scope = $rootScope.$new();
$scope.gridOptions = GridOptionsService.getGridOptions('documents');
$scope = $rootScope;
elm = angular.element('<div ng-grid="gridOptions"></div>');
oCtrl = $controller('Repository',{$scope: $scope});
$compile(elm)($scope)
DataContext.getDocuments().then(function(data){
$scope.myData = data;
console.log('here are the grid options: ')
console.log($scope.gridOptions);
})
}));
it('should display rows',function(done){
inject(function($rootScope, $compile, $controller, DataContext,GridOptionsService){
$rootScope.$apply(function(){
DataContext.getDocuments().then(function(data){
expect(data.length).toBe(1000);
done();
})
})
})
})
});
DataContext.getDocuments returns a promise and I use that to set the myData variable of the controller. This data is the data for the grid.
$scope.gridOptions = GridOptionsService.getGridOptions('documents');
gridOptions is simply a JS object returned from a service. When I run the test I get the error: Error: [$injector:unpr] Unknown provider: DataContextProvider <- DataContext
All of the scripts that should be included in the spec runner are, and the code definitely works, but I just don't know how to test it.
How can I test AJAX code that changes the appearance of my DOM with Jasmine?
Try mocking out your custom dependencies for your Jasmine test. The [$injector:unpr] results from the $injector being unable to resolve a required dependency. You can use $provide to register components with the injector. Try to add something like...
beforeEach(function () {
mockDependency = {
getDataSet: function () {
return 'mockDataSet';
}
};
module(function ($provide) {
$provide.value('DataContext', mockDependency);
});
});
Here's the documentation for $provide...https://docs.angularjs.org/api/auto/object/$provide
Hope that helps
I am trying to write a simple test using Jasmine. The test checks if $scope.testFlag is set to false.
Here is my test code
describe('Abc Controller', function() {
var $scope = null;
var ctrl = null;
//you need to indicate your module in a test
beforeEach(module('myApp'));
beforeEach(inject(function($rootScope, $controller) {
$scope = $rootScope.$new();
ctrl = $controller('abcController', {
$scope: $scope
});
}));
// test 1
it('testFlag should be set to False', function() {
expect( $scope.testFlag).toEqual(false);
});
});
But for some reason I get this error:
Error: Unknown provider: ConfigProvider <- Config <- collectionMetaFactory
Here is how my application's app.js looks like which I am including in testRunner.html
var app = angular.module('myApp')
app.constant('Config',
{
baseURL : serviceURL,
httpTimeout : 3600000 // 1 minute
});
app.config(function($logProvider) {
$logProvider.debugEnabled(true);
});
What am I missing?
Here is controller's snippet
app.controller('abcController', function ($scope, $log,abcFactory, Config) {
$scope.testFlag = false;
// more code follows
});
Let me know if you need see more of the application's code (like factory, services and controller)
You haven't provided your test with Config, so it doesn't recognize what Config is and that's why you are getting this error.
A solution would be to inject Config into your test by using the $provide module. Here's how:
describe('Abc Controller', function() {
var $scope = null;
var ctrl = null;
var Config = {
baseURL : 'someURL',
httpTimeout : 3600000 // 1 minute
};
beforeEach(function(){
beforeEach(module('myApp'));
module(function (_$provide_) {
$provide = _$provide_;
$provide.value('Config', Config);
});
});
//you need to indicate your module in a test
beforeEach(inject(function($rootScope, $controller) {
$scope = $rootScope.$new();
ctrl = $controller('abcController', {
$scope: $scope
});
}));
// test 1
it('testFlag should be set to False', function() {
expect( $scope.testFlag).toEqual(false);
});
});
This should run the tests successfully.
Hope this helps others facing a similar problem.