Injecting custom factory in jasmine test - angularjs

I tried to inject a factory to my controller in jasmine test like in
Unit testing AngularJS factories that have dependencies
When I $provide a factory in the test, I would expect the controller to use the provided factory. But the console.log still prints 'real value'. I don't get it.
var app = angular.module('mod', []);
app.factory('factoryA', [
function () {
return "real value";
}
]);
app.controller('myController', ['factoryA',
function (factoryA) {
console.log(factoryA);
}
]);
describe("test", function() {
var $scope, $controller, $httpBackend;
var app;
beforeEach(function() {
module(function($provide) {
$provide.factory('factoryA', function () { return "fake value"; });
});
app = module("mod");
inject(function (_$controller_, _$httpBackend_, $rootScope) {
$httpBackend = _$httpBackend_;
$scope = $rootScope.$new();
$controller = _$controller_;
});
});
it("works", function() {
$controller("myController", { '$scope': $scope });
});
});

You would need to get the $provider from the module mod so pass module name as first argument so that it overrides the factoryA definition that was created originally. Or load the module - module("mod") - before setting up mock.
module('mod', function($provide) {
$provide.factory('factoryA', function () { return "fake value"; });
});
Another way it to create mocks and pass it to the controller creation.
describe("test", function() {
var $scope, $controller, $httpBackend;
var app, factoryA;
beforeEach(function() {
module(mod);
inject(function (_$controller_, _$httpBackend_, $rootScope) {
//...Your code
//Mock factory
factoryA = jasmine.createSpy('factoryA');
factoryA.and.returnValue("fake value");
});
});
it("works", function() {
//Pass the mock factory
$controller("myController", { '$scope': $scope, factoryA:factoryA });
});
});

Related

TypeError: undefined is not a constructor (evaluating 'angular.controller('myView')')

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']);

How to check if a function is available in instantiated controller for unit testing in Jasmine

I am trying to unittest a controller in AngularJS for the first time. I want to test if the login-function in this controller calls the right service but I get the error, that the function doesn't exist in the controller. What did I do wrong? Do I have to mock more? But I shouldn't have to mock the functions in the controller to be tested because if I would do that the whole testing would be senseless, or am I wrong?
Controller-Snippet:
function LoginController($scope, $state, myService, ngToast) {
$scope.loginUser = loginUser;
activate();
function activate() {}
function loginUser(credentials) {
myService.authenticate(/*things*/);
}
}
Test-Code:
describe('Login Controller', function () {
'use strict';
//globals
var controller, scope;
var $controller, $state, myService, ngToast;
//needed modules
beforeEach(module('app.login'));
beforeEach(module('app.core'));
beforeEach(module('ui.router'));
//instanciate Controller
beforeEach(inject(function (_$controller_, _$state_, _myService_, _ngToast_) {
scope = {};
$state = _$state_;
myService = _myService_;
ngToast = _ngToast_;
$controller = _$controller_;
controller = $controller('LoginCtrl', {
$scope: scope,
$state: $state,
myService: myService,
ngToast: ngToast
});
}));
it('should have an existing controller', function () {
expect(controller).toBeDefined();
});
/*************************** unit-tests ***************************/
describe('.loginUser()', function () {
it('should exist', function () {
expect(controller.loginUser).toBeDefined();
});
});
});
The Error I am getting when running karma start:
.loginUser()
✗ should exist
TypeError: controller.loginUser is not a function
at Object.<anonymous> (src/login/login.controller.spec.js:74:31)
But it seems to me that the controller does exist, because this test doesn't fail:
Login Controller
✓ should have an existing controller
it is because loginUser is defined on $scope and not on the controller so loginUser is not bound to your controller object in your test.
To test loginUser please do the following:
Inject $rootScope in your beforeEach
Set scope = $rootScope.$new()
The controller should be created with this scope
Your test should check if loginUser is defined on the scope
describe('Login Controller', function () {
'use strict';
//globals
var controller, scope;
var $controller, $state, myService, ngToast;
//needed modules
beforeEach(module('app.login'));
beforeEach(module('app.core'));
beforeEach(module('ui.router'));
//instanciate Controller
beforeEach(inject(function (_$controller_, _$state_, _myService_, _ngToast_, _$rootScope_) {
scope = _$rootScope_.$new();
$state = _$state_;
myService = _myService_;
ngToast = _ngToast_;
$controller = _$controller_;
controller = $controller('LoginCtrl', {
$scope: scope,
$state: $state,
myService: myService,
ngToast: ngToast
});
}));
it('should have an existing controller', function () {
expect(controller).toBeDefined();
});
/*************************** unit-tests ***************************/
describe('.loginUser()', function () {
it('should exist', function () {
expect(scope.loginUser).toBeDefined();
});
});
});
If you want to test loginUser on the controller itself then make use of the controller as syntax where you would set this.loginUser = loginUser instead of $scope.loginUser = loginUser. Then you will be able to use controller.loginUser in your test.

AngularJS + Jasmine Noob: How to access $scope in spec?

This is my first time testing using Jasmine. I'm having trouble accessing the $scope variables in the spec. I have a failing test:
mysite ProductsDetailCtrl sets hey
Expected undefined to be 1.
Error: Expected undefined to be 1.
spec:
//= require helpers/load-angular-mysite-module
//= require products/controllers/products_detail_controller
describe('mysite', function() {
var $rootScope, $scope, $controller;
beforeEach(function() {
module('mysite');
});
describe('ProductsDetailCtrl', function() {
beforeEach(inject(function(_$rootScope_, _$controller_) {
$rootScope = _$rootScope_; // don't really
$scope = $rootScope.$new(); // understand what's
$controller = _$controller_; // going on in this function
controller = $controller('ProductsDetailCtrl', {
'$rootScope': $rootScope,
'$scope': $scope
});
}));
it('sets hey', function() {
expect($rootScope.hey).toBe(1);
});
});
});
controller:
app.controller('ProductsDetailCtrl', ['$scope', '$resource', function($scope, $resource) {
$scope.hey = 1;
....
Could someone explain to me how I would access the scope?
You just have to check for the property heyin your $scope not in the $rootScope:
describe('mysite', function() {
var scope, ProductsDetailCtrl;
beforeEach(function() {
module('mysite');
});
describe('ProductsDetailCtrl', function() {
beforeEach(inject(function($controller, $rootScope) {
// Create a mock scope for your controller.
scope = $rootScope.$new();
// Initialize the controller with the mocked scope.
ProductsDetailCtrl = $controller('ProductsDetailCtrl', {
$scope: scope
});
}));
it('sets hey', function() {
expect(scope.hey).toBe(1);
});
});
});

Jasmine Testing Angular Controller Method

So I have my blank tests passing with this setup.
describe('loginController', function() {
var scope, createController;
beforeEach(module('souply'));
beforeEach(inject(function ($rootScope, $controller, _$location_) {
$location = _$location_;
scope = $rootScope.$new();
createController = function() {
return $controller('loginController', {
'$scope': scope
});
};
}));
And here are the tests...
describe('processGoogleLogin', function(){
describe('successful', function(){
beforeEach(function() {
});
it('should connect to google plus', function () {
expect(true).toBe(true);
});
This one passes no problem.
QUESTION: How can I test a method on the login controller?
Here is the method I want to test on the login controller:
$scope.processGoogleLogin = function(){
console.log('process login was clicked');
window.location.replace('/#/dashboard');
};
The test I have so far is:
it('should sign you into the dashboard', function () {
scope.processGoogleLogin();
//$controller.processGoogleLogin();
//expect(window.location).toBe('/#/dashboard');
});
This test throws an error of:
'undefined' is not a function (evaluating 'scope.processGoogleLogin()')
This needed this line.
var ctrl = $controllerConstructor('myController', {$scope: scope, myResolve: {}, state: state});

Unit test of controller angularjs

I have this simple controller, UserService is a service which return JSON
"use strict";
angular.module("controllers").controller('profileCtrl', ["$scope", "UserService",
function ($scope, UserService) {
$scope.current_user = UserService.details(0);
}
]);
I can not make the test. However this is my try
'use strict';
describe('profileCtrl', function () {
var scope, ctrl;
beforeEach(angular.mock.module('controllers'), function($provide){
$provide.value("UserService", {
details: function(num) { return "sdfsdf"; }
});
});
it('should have a LoginCtrl controller', function() {
expect(controllers.profileCtrl).toBeDefined();
});
beforeEach(angular.mock.inject(function($rootScope, $controller){
scope = $rootScope.$new();
$controller('profileCtrl', {$scope: scope});
}));
it('should fetch list of users', function(){
expect(controllers.scope.current_user.length).toBe(6);
expect(controllers.scope.current_user).toBe('sdfsdf');
});
});
The usage of $controller is correct, that's the way to instantiate a controller for a unit test. You can mock the UserService instance it gets directly in the $controller invocation.
You should be using its return value - this is the instance of your controller you're going to test.
You're trying to read stuff from controllers but its not defined anywhere in the test, I guess you're referring to the module.
This is how I would go about it + fiddle
//--- CODE --------------------------
angular.module('controllers', []).controller('profileCtrl', ["$scope", "UserService",
function ($scope, UserService) {
$scope.current_user = UserService.details(0);
}]);
// --- SPECS -------------------------
describe('profileCtrl', function () {
var scope, ctrl, userServiceMock;
beforeEach(function () {
userServiceMock = jasmine.createSpyObj('UserService', ['details']);
userServiceMock.details.andReturn('sdfsdf');
angular.mock.module('controllers');
angular.mock.inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
ctrl = $controller('profileCtrl', {
$scope: scope,
UserService: userServiceMock
});
});
});
it('should have a LoginCtrl controller', function () {
expect(ctrl).toBeDefined();
});
it('should fetch list of users', function () {
expect(scope.current_user).toBe('sdfsdf');
});
});
You're welcome to change the fiddle online to see how it affects testing results.

Resources