I have created a scope method inside my controller which is executing when a button is pressed. I am writing unit test cases for the same. I have injected my module in beforeEach block and created spyon my scope function and then using it in 'it' method and checking whether it is called or not. But getting an error as a method not found.
Controller
'use strict';
angular.module('myApp.view1', ['ngRoute'])
.config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/view1', {
templateUrl: 'view1/view1.html',
controller: 'View1Ctrl'
});
}])
.controller('View1Ctrl', ['$scope',View1Ctrl])
function View1Ctrl($scope) {
$scope.user = {
name: '',
last: ''
}
$scope.showFormData = function() {
$scope.formData = $scope.user.name + $scope.user.last;
}
}
spec.js
'use strict';
describe('myApp.view1 module', function () {
var $controller, $rootScope;
beforeEach(module('myApp.view1'));
beforeEach(inject(function (_$controller_, _$rootScope_) {
$controller = _$controller_;
$rootScope = _$rootScope_;
}));
describe('view1 controller', function () {
var $scope, controller, formData;
beforeEach(function () {
$scope = $rootScope.$new();
controller = $controller('View1Ctrl', {
$scope: $scope
});
spyOn(controller, 'showFormData');
});
it('should check for the show form details', function () {
$scope.user.name = "Sandeep";
$scope.user.last = "Gupta";
expect($scope.showFormData).toHaveBeenCalled();
expect($scope.user.name + $scope.user.last).toEqual(firstname);
});
});
});
Need help to resolve this issue.
It looks like you're trying to spy on the showFormData method of the controller:
spyOn(controller, 'showFormData');
However, the showFormData doesn't exist on the controller, it's a method of the controller's scope.
You'll need to use:
spyOn($scope, 'showFormData');
It's also important to know that you need to use the same object to both spyOn and expect(...).toHaveBeenCalled(). In your case you where spying on controller.showFormData(), yet expecting $scope.showFormData() to have been called.
I'm trying to test my app, which has each controller defined on it's own module i.e., not as a controller of the app module, and then loaded as a dependency of the main app module. When I try to run a test that just checks that the loginController is defined, using Karma/Jasmine, I get the following output:
'Expected undefined to be defined.'
edit
I updated login.controller.spec and switched the karma browser to chrome, which gave me more useful debug info. Now I'm getting an error related to a factory that is added to $httpProvider.interceptors in the main app file:
Unknown provider: authFactoryProvider <- authFactory <- $http <- $translateStaticFilesLoader <- $translate
I found similar issues related to this which were resolved by including angular-translate-loader-static-files.js, which is being loaded when karma runs:
DEBUG [web-server]: serving (cached): /path/to/my/app/bower_components/angular-translate-loader-static-files/angular-translate-loader-static-files.js
How do I resolve all these dependency issues with karma?
index.js
'use strict';
angular.module('app',
[
//'mockBackend', //uncomment when loading mock backend
'ngAnimate',
'ngCookies',
'ngTouch',
'ngSanitize',
'ngResource',
'ui.bootstrap',
'ui.router',
'ui.router.stateHelper',
'pascalprecht.translate',
'utilsModule',
'loginModule',
'vsmsPackageModule',
'vsmsCampaignModule',
'vdmsCampaignModule',
'vdmsDashboardModule',
'daterangepicker',
'ui.event',
'idmAdminModule',
'helpdeskModule',
'ncy-angular-breadcrumb',
'rzModule',
'vsmsDashboardModule',
'highcharts-ng',
'permission',
'dndLists'
])
.config(function ($stateProvider, $urlRouterProvider, $httpProvider, $locationProvider, $translateProvider, $breadcrumbProvider, $compileProvider) {
$urlRouterProvider.otherwise('/');
//initialize get if not there
if (!$httpProvider.defaults.headers.get) {
$httpProvider.defaults.headers.get = {};
}
//disable IE ajax request caching
$httpProvider.defaults.headers.get['If-Modified-Since'] = 'Mon, 26 Jul 1997 05:00:00 GMT';
// extra
$httpProvider.defaults.headers.get['Cache-Control'] = 'no-cache';
$httpProvider.defaults.headers.get['Pragma'] = 'no-cache';
$locationProvider.html5Mode({
enabled: false,
requireBase: false
});
$translateProvider.useSanitizeValueStrategy('sanitize');
$translateProvider.useStaticFilesLoader({
prefix: '/locales/',
suffix: '.json'
});
$translateProvider
.preferredLanguage('en_us')
.fallbackLanguage('en_us');
$breadcrumbProvider.setOptions({
templateUrl: 'components/templates/breadcrumb.tpl.html'
});
$compileProvider.debugInfoEnabled(false);
// $compileProvider.aHrefSanitizationWhitelist(/^\s*(|blob|):/);
$httpProvider.interceptors.push('authFactory');
$httpProvider.interceptors.push('headersFactory');
})
login.module.js
angular.module('loginModule', []);
login.controller.js
angular.module('loginModule')
.controller('loginController', login);
login.$inject = [
'$log',
'$uibModal',
'$rootScope',
'storageFactory',
'loginFactory',
'$state',
'RoleStore',
'PermissionStore'
];
function login($log, $uibModal, $rootScope, storageFactory, loginFactory, $state, RoleStore, PermissionStore) {
/* jshint validthis: true */
var vm = this;
vm.loginUser = loginUser;
vm.forgotPassword = forgotPassword;
vm.errorCode = null;
PermissionStore.clearStore();
function loginUser() {
...
I'm just trying to test if the controller exists and I can't get past the error:
Expected undefined to be defined.
login.controller.spec.js
describe('loginController', function() {
beforeEach(module('app'));
var $controller,
$scope,
$log,
$uibModal,
$rootScope,
storageFactory,
loginFactory,
$state,
RoleStore,
PermissionStore,
vsmsCoreFactory;
beforeEach(inject(function(_$controller_, _$log_, _$uibModal_, _$rootScope_, _storageFactory_, _loginFactory_, _$state_, _RoleStore_, _PermissionStore_, _vsmsCoreFactory_){
$controller = _$controller_;
$scope = $rootScope.new();
$log = _$log_;
$uibModal = _$uibModal_;
$rootScope = _$rootScope_;
storageFactory = _storageFactory_;
loginFactory = _loginFactory_;
$state = _$state_;
RoleStore = _RoleStore_;
PermissionStore = _PermissionStore_;
vsmsCoreFactory = _vsmsCoreFactory_;
}));
describe('vm.loginUser', function() {
it('should be defined', function() {
var loginController = $controller('loginController', {
$log: $log,
$uibModal: $uibModal,
$rootScope: $rootScope,
storageFactory: storageFactory,
loginFactory: loginFactory,
$state: $state,
RoleStore: RoleStore,
PermissionStore: PermissionStore,
vsmsCoreFactory: vsmsCoreFactory
});
expect(loginController).toBeDefined();
// expect(testController.model.name).toEqual("controllerAs vm test");
});
});
});
unit-tests.js
'use strict';
var gulp = require('gulp');
var $ = require('gulp-load-plugins')();
var wiredep = require('wiredep');
var paths = gulp.paths;
function runTests (singleRun, done) {
var bowerDeps = wiredep({
directory: 'bower_components',
exclude: ['bootstrap-sass-official'],
dependencies: true,
devDependencies: true
});
var testFiles = bowerDeps.js.concat([
'./src/app/index.js',
'./src/components/scripts/ui-bootstrap-custom-tpls-2.1.3.js',
'./src/{app,components}/**/*.module.js',
'./src/{app,components}/**/*.factory.js',
'./src/{app,components}/**/*.controller.js',
'./src/{app,components}/**/*.spec.js'
]);
gulp.src(testFiles)
.pipe($.karma({
configFile: 'karma.conf.js',
action: (singleRun)? 'run': 'watch'
}))
.on('error', function (err) {
// Make sure failed tests cause gulp to exit non-zero
throw err;
});
}
gulp.task('test', function (done) { runTests(true /* singleRun */, done) });
gulp.task('test:auto', function (done) { runTests(false /* singleRun */, done) });
Please refer the the below link it is already answered.
How to inject controller dependencies in Jasmine tests?
loginController dependencies are not passed in your unit test.
The second parameter of $controller is for controller dependencies.
Empty dependency is passed.
$controller('loginController', {});
Make sure all the dependent modules are loaded before testing loginColtroller.
I have modified your code. I hope it will work.
'use strict';
describe('loginController', function() {
beforeEach(module('loginModule'));
var loginController;
beforeEach(inject(function($controller, _$log_, _$uibModal_, _$rootScope_, _storageFactory_, _loginFactory_, _$state_, _RoleStore_, _PermissionStore_ ){
scope = _$rootScope_.$new();
loginController = $controller('loginController',
{ // all dependencies has to be passed in order
'$log' : _$log_,
'$uibModal' : _$uibModal_,
'$rootScope' : _$rootScope_,
'storageFactory' : _storageFactory_,
'loginFactory': _loginFactory_,
'$state': _$state_,
'RoleStore': _RoleStore_,
'PermissionStore': _PermissionStore_
},
{});
}));
it('should be defined', function() {
expect(loginController).toBeDefined();
});
});
The issue is with your login.module.js file.
You'll have to inject ui.bootstrap and ui.router as dependencies while defining the loginModule. Otherwise it will not be able to get $uibModal and $state while defining the loginController.
This should be the definition of your loginModule
angular.module('loginModule', ['ui.bootstrap', 'ui.router']);
PS: I'm assuming here that storageFactory, loginFactory, RoleStore and PermissionStore are defined on loginModule itself.
Hope this helps!
I have the following NewPageCtrl.js
angular.module('NewPageCtrl', []).controller('NewPageController', function($scope, $document) {
$scope.showMe = false;
});
And the following test.js
describe('NewPageCtrl', function() {
var scope, $document, createController;
beforeEach(inject(function ($rootScope, $controller _$document_) {
$document = _$document_;
scope = $rootScope.$new();
createController = function() {
return $controller('NewPageCtrl', {
'$scope': scope
});
};
}));
it('should check showMe', function() {
});
});
I will write the test case later, but for now Jasmine is giving me the error:
Chrome 48.0.2564 (Mac OS X 10.10.5) ERROR
Uncaught SyntaxError: Unexpected identifier
at /Users/me/myProject/test/test.js:23
Line 23 is the beforeEach(... line
I got the example from http://nathanleclaire.com/blog/2013/12/13/how-to-unit-test-controllers-in-angularjs-without-setting-your-hair-on-fire/
You missed a comma.
Change
$rootScope, $controller _$document_
to
$rootScope, $controller, _$document_
I'm going through the AngularJS PhoneCat tutorial and while the application seems to behave correctly when I click through it manually, one of the unit tests are failing for step 8.
Using Karma, I'm getting the following console output:
Chrome 42.0.2311 (Windows 7) PhoneCat controllers PhoneDetailCtrl
should fetch phone detail FAILED Error: [$injector:unpr] Unknown
provider: $routeParamsProvider <- $routeParams
The relevant part of the unit test file (controllersSpec.js) looks like this:
describe("PhoneCat controllers", function () {
describe('PhoneDetailCtrl', function () {
var scope, $httpBackend, ctrl;
beforeEach(inject(function (_$httpBackend_, $rootScope, $routeParams, $controller) {
$httpBackend = _$httpBackend_;
$httpBackend.expectGET('phones/xyz').respond({ name: 'phone xyz' });
$routeParams.phoneId = 'xyz';
scope = $rootScope.$new();
ctrl = $controller('PhoneDetailCtrl', { $scope: scope });
}));
});
});
The problem seems to be related to the declaration of the function parameter of the call to inject(). If I take $routeParams out then the script will execute.
From reading some related StackOverflow questions it seems I might be missing a dependency somewhere. The relevant parts of the karma.conf.js file look like this:
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine'],
files: [
'../lib/angular/angular.js',
'../lib/angular/angular-mocks.js',
'../lib/angular/angular-route.js',
'../app/*.js',
'unit/*.js'
],
browsers: ['Chrome'],
singleRun: false
});
};
My app.js looks like this:
var phonecatApp = angular.module("phonecatApp", [
"ngRoute",
"phonecatControllers"]);
phonecatApp.config([
"$routeProvider", function($routeProvider) {
$routeProvider
.when("/phones", {
templateUrl: "Scripts/app/partials/phone-list.html",
controller: "PhoneListCtrl"
})
.when("/phones/:phoneId", {
templateUrl: "Scripts/app/partials/phone-detail.html",
controller: "PhoneDetailCtrl"
})
.otherwise({
redirectTo: "/phones"
});
}
]);
And controllers.js looks like this:
var phonecatControllers = angular.module("phonecatControllers", []);
phonecatControllers.controller("PhoneDetailCtrl", ["$scope", "$routeParams", "$http", function ($scope, $routeParams, $http) {
$http.get("api/Phones/" + $routeParams.phoneId).success(function (data) {
$scope.phone = data;
});
}]);
As mentioned at the top, the app seems to work fine when I click through it in the browser so I'm quite confused as to where things are breaking down for the unit tests.
As is often the case with these kind of errors, the fix is pretty obvious when you see it! Basically there are two functions in controllersSpec.js, one for a list of phones and one for an individual phone's details. The phone list tests were working fine because they had
beforeEach(module("phonecatApp")); before the call to beforeEach(inject(function ());
The phone detail tests on the other hand were missing this line. Once I moved the line to the outer function the tests passed as expected.
The working controllersSpec.js looks like this:
describe("PhoneCat controllers", function () {
// This is the line that was moved.
// It existed for PhoneListCtrl but not PhoneDetailCtrl.
beforeEach(module("phonecatApp"));
describe("PhoneListCtrl", function() {
var scope, ctrl, $httpBackend;
beforeEach(module("phonecatApp"));
beforeEach(inject(function(_$httpBackend_, $rootScope, $controller) {
// Setup
}));
// Various tests
});
describe('PhoneDetailCtrl', function () {
var scope, $httpBackend, ctrl;
beforeEach(inject(function (_$httpBackend_, $rootScope, $routeParams, $controller) {
// Setup
}));
// Various tests
});
});
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' });
});