Jasmine Test in Angular for controller - angularjs

I get the following error: TypeError: undefined is not a function
The problem is that the common is module and a factory and the problem is on my line
var ctrl = $controllerConstructor("resetPasswordSentScreen", { $scope: scope, common: common});
Here is the full test:
describe('resetPasswordSentScreen', function () {
var scope, $controllerConstructor;
beforeEach(module('common', 'app'));
beforeEach(inject(function($controller, $rootScope) {
scope = $rootScope.$new();
$controllerConstructor = $controller;
}));
it('it should navigate to the correct url when backToLogin is called ', function (common) {
var ctrl = $controllerConstructor("resetPasswordSentScreen", { $scope: scope, common: common });
var mocklocation = sinon.stub({ url: function () {}});
expect(scope.backToLogin()).toBe(mocklocation.url);
});
});

That is not the problem, the problem is that you can't inject stuff into your functions like you do in your code. To inject you need to call inject as you did in the beforeEach. So, if you want to inject that factory, you need this:
it("message", inject(function(common) {
...
}));
There is how you inject. That should work.

Related

AngularJSUnitTesting : Expected spy function() to have been called

Below is my code , I dont want the service function to be invoked so I am using spy, but its giving error.I am not able to figure it out.
'use strict';
describe('Testing DetailCtrl\n\n\n', function() {
beforeEach(module("safe-repository"));
var $controller, $scope, controller;
var services = {
documentService:null
};
// Initialization before tests
beforeEach(inject(function(_$controller_, _documentService_){
$controller = _$controller_;
$scope = {};
controller = $controller('DetailCtrl', { $scope: $scope });
services.documentService=_documentService_;
spyOn(services.documentService, 'deleteDocument').and.callFake(function(){
console.log("inside delete function");
});
}));
describe('Testing self.deleteFile() function for different test cases\n\n', function() {
it(' When user has access permission to delete file/doc', function(done) {
expect(services.documentService.deleteDocument).toHaveBeenCalled();
// Inform jasmine that the test finish here
done();
});
});
});
Any help is appreciated.
you don't need ...
var services = {
documentService:null
};
that code is just confusing matters.
you should simplify this to ...
// services.documentService=_documentService_; // WHY DO THIS??
documentService=_documentService_;
then ...
spyOn(documentService, 'deleteDocument').and.callFake ... etc
then ...
expect(documentService.deleteDocument).toHaveBeenCalled();
you might also want to try ...
spyOn(loginService, 'isSuperAdmin').and.returnValue("something");
INSTEAD OF callFake (your expect statement would remain unchanged)
ALSO ...
I assume your controller is making the expected call to this method during construction? e.g the following line makes the expected call during construction?
controller = $controller('DetailCtrl', { $scope: $scope });
In other words your controller should look something like ...
app.controller("DetailCtrl", function($scope, documentService) {
// some other code
documentService.deleteDocument(); // MAKE SURE THIS CODE IS ACTUALLY BEING HIT IF ITS WRAPPED IN A CONDITIONAL STATEMENT
// some other code
});
Try this, it might help you :)
'use strict';
describe('Testing DetailCtrl\n\n\n', function() {
var $controller, scope, ctrl, mockService;
beforeEach(module("safe-repository"));
beforeEach(inject(function($rootScope, _$controller_){
scope = $rootScope.$new();
function del() {
//your return value
}
mockService = {
deleteDocument: del
}
$controller = _$controller_;
}));
function initController(){
ctrl = $controller('DetailCtrl', {
$scope: scope,
documentService: mockService
});
}
it(' When user has access permission to delete file/doc', function() {
spyOn(documentService,'deleteDocument').and.callThrough();
initController();
expect(mockService.deleteDocument).toHaveBeenCalled();
});
});
});

Defined service method is not called, instead real service's is called

I am following this video tutorial and its source is here.
I am trying to apply this test
Here is my test
describe("InStudentController", function () {
beforeEach(module("eucngts"));
var inStudentsController;
var MyInStudentsService;
var $scope;
var $q;
var deferred;
beforeEach(function () {
MyInStudentsService =
{
getInStudents: function () {
deferred = $q.defer();
return deferred.promise;
}
};
});
beforeEach(inject(function ($controller, $rootScope, _$q_) {
$q = _$q_;
$scope = $rootScope.$new();
inStudentsController = $controller('InStudentsController', {
service: MyInStudentsService
});
}));
it("should request list of inStudents", function () {
spyOn(MyInStudentsService, "getInStudents").and.callThrough();
inStudentsController.getPage(); // <-- HERE
//deferred.resolve();
$scope.$root.$digest();
expect(MyInStudentsService.getInStudents).toHaveBeenCalled();
});
});
Here is relevant controller code:
InStudentsController.prototype.getPage = function (criteria) {
var self = this;
self.showGrid = true;
self.service.getInStudents();
};
When I call getPage() on test it calls real service method instead of defined in test.
What am I doing wrong?
EDIT
I don't use scope in my controller here is generated code(I use typescript):
function InStudentsController (service) {
var self = this;
self.service = service;
}
InStudentsController.$inject = ['InStudentsService'];
angular.module("eucngts").controller("InStudentsController", InStudentsController);
According to your latest update it is clear that the name of dependency is used wrong in the test. It must be InStudentsService instead of service. When using $inject property of controller constructor only that name matters, not the formal parameter name in function. That makes minification possible
inStudentsController = $controller('InStudentsController', {
InStudentsService: MyInStudentsService
});
Right now you're not injecting a scope into the controller. I think this:
$scope = $rootScope.$new();
inStudentsController = $controller('InStudentsController', {
service: MyInStudentsService
});
Should be this:
$scope = $rootScope.$new();
$scope.service = MyInStudentsService
inStudentsController = $controller('InStudentsController', {
$scope: $scope
});
But it seems odd passing the service in on the scope. Instead, you should be declaring the controller something like this:
angular.module('myApp')
.controller('InStudentsController', function ($scope, InStudentsService) {
...
});
And then the service would be injected like so:
$scope = $rootScope.$new();
inStudentsController = $controller('InStudentsController', {
$scope: $scope,
InStudentsService: MyInStudentsService
});

AngularJS Jasmine error 'Controller is not a function' when instantiated with arguments

I have been doing angularJS for a while now (without tests) but I want to do it properly! I have a controller defined like so
(function () {
'use strict';
angular.module('app')
.controller('CarehomeListCtrl', ['$scope', 'carehomesDataService', carehomeListCtrl]);
function carehomeListCtrl($scope, carehomesDataService) {
var vm = this;
vm.carehomeCollection = [];
vm.activate = activate;
function activate() {
vm.carehomeCollection = carehomesDataService.getAllCarehomes();
}
activate();
}
})();
and then my spec
describe("Carehomes tests", function () {
var $scopeConstructor, $controllerConstructor;
beforeEach(module('app'));
beforeEach(inject(function ($controller, $rootScope) {
$controllerConstructor = $controller;
$scopeConstructor = $rootScope;
}));
describe("CarehomeListCtrl", function () {
var ctrl, dataService, scope;
function createController() {
return $controllerConstructor('CarehomeListCtrl', {
$scope: scope,
carehomesDataService: dataService
});
}
beforeEach(inject(function ($injector) {
scope = $scopeConstructor.$new();
dataService =$injector.get('carehomesDataService') ;
}));
it("should have a carehomesCollection array", function () {
ctrl = createController();
expect(ctrl.carehomesCollection).not.toBeNull();
});
it("should have 3 items in carehomesCollection array when load is called", function () {
ctrl = createController();
expect(ctrl.carehomeCollection.length).toBe(3);
});
});
});
The problem here is that the call to instantiate my controller fails with error whenever I call it with any arguments whether an empty object {} or just $scope : scope} so I know the problem is not carehomesDataService.
Result StackTrace: Error: [ng:areq] Argument 'CarehomeListCtrl' is not
a function, got undefined
http://errors.angularjs.org/1.2.26/ng/areq?p0=CarehomeListCtrl&p1=not%20a%20function%2C%20got%20undefined
However, if I instantiate that controller like this $controllerConstructor('CarehomeListCtrl'); without arguments, it gets instantiated. I'm stumped!
carehomesDataService is a custom service I have written but it's own tests pass and it is correctly injected into the controller in the application.
Any help would be massively appreciated.
Note: I do not quite agree with defining properties on the controller as the view model instead of on $scope but I am following Jesse Liberty's pluralsight course and that's how he does it....plus injecting scope isn't quite working right now which is annoying. Thanks in advance.

Unit test AngularJS controller that inherits from a base controller via $controller

The scenario is I have a ChildCtrl controller that inherits from BaseCtrl following this inheritance pattern:
angular.module('my-module', [])
.controller('BaseCtrl', function ($scope, frobnicate) {
console.log('BaseCtrl instantiated');
$scope.foo = frobnicate();
// do a bunch of stuff
})
.controller('ChildCtrl', function ($controller, $scope) {
$controller('BaseCtrl', {
$scope: $scope,
frobnicate: function () {
return 123;
}
});
});
Assuming BaseCtrl does a bunch of stuff and is already well tested, I want to test that ChildCtrl instantiates BaseCtrl with certain arguments. My initial thought was something along these lines:
describe("ChildCtrl", function () {
var BaseCtrl;
beforeEach(module('my-module'));
beforeEach(module(function($provide) {
BaseCtrl = jasmine.createSpy();
$provide.value('BaseCtrl', BaseCtrl);
}));
it("inherits from BaseCtrl", inject(function ($controller, $rootScope) {
$controller('ChildCtrl', { $scope: $rootScope.$new() });
expect(BaseCtrl).toHaveBeenCalled();
}));
});
However when I run the test the spy is never called and the console shows "BaseCtrl instantiated", indicating that $controller is using the actual controller instead of the instance I am providing with $provide.value().
What's the best way to test this?
So it looks like $controller doesn't search for controllers by name in the $provide.value() namespace. Instead you have to use the $controllerProvider.register() method, which is only accessible from the module.config() block. Fortunately it looks like there's a hook we can use to get access to $controllerProvider on the module under test.
The updated test code looks like:
describe("ChildCtrl", function () {
var BaseCtrl;
beforeEach(module('my-module', function ($controllerProvider) {
BaseCtrl = jasmine.createSpy();
BaseCtrl.$inject = ['$scope', 'frobnicate'];
$controllerProvider.register('BaseCtrl', BaseCtrl);
}));
beforeEach(inject(function ($controller, $rootScope) {
$controller('ChildCtrl', { $scope: $rootScope.$new() });
}));
it("inherits from BaseCtrl", inject(function ($controller, $rootScope) {
expect(BaseCtrl).toHaveBeenCalled();
}));
it("passes frobnicate() function to BaseCtrl that returns 123", function () {
var args = BaseCtrl.calls.argsFor(0);
var frobnicate = args[1];
expect(frobnicate()).toEqual(123);
});
});

Injecting a mock service for an angularjs controller test

I'm trying to test a controller that depends on a service I built myself. I'd like to mock this service since the service talks to the DOM.
Here's my current test:
describe('Player Controllers', function () {
beforeEach(function () {
this.addMatchers({
toEqualData: function (expected) {
return angular.equals(this.actual, expected);
}
});
});
describe('TestPSPlayerModule', function () {
var $httpBackend, scope, ctrl;
beforeEach(module('PSPlayerModule'));
beforeEach(inject(function (_$httpBackend_, $rootScope, $controller) {
$httpBackend = _$httpBackend_;
scope = $rootScope.$new();
ctrl = $controller(PlayerController, { $scope: scope });
}));
it('should request a clip url from the server when clipClicked is called', function () {
expect(1).toBe(1);
});
});
});
My controller looks like this:
w.PlayerController = function ($scope, $http, $window, speedSlider, $location) {
...
}
so it's the speedSlider I want to mock.
I had the idea to use a module I created in my test code that could provide a faked implementation of the speed slider, so I added the following to the top of the test.js file:
module('TestPSPlayerModule', []).factory('speedSlider', function () {
return = {
...
};
});
and then list that module in the beforeEach() call instead of the concrete one, but if I do that I get the following error:
Injector already created, can not register a module!
So I figure there must be a better way for me to provide a mock implementation of one of my services. Something I can perhaps use sinon.js for....
Also be sure you're not trying to do this inside an inject function call:
This will throw the error:
beforeEach(inject(function(someOtherService) {
module('theApp', function($provide) {
myMock = {foo: 'bar'};
$provide.value('myService', myServiceMock);
someOtherService.doSomething();
});
}));
This will not:
beforeEach(function() {
module('theApp', function($provide) {
myMock = {foo: 'bar'};
$provide.value('myService', myServiceMock);
});
inject(function(someOtherService) {
someOtherService.doSomething();
});
});
Make sure when you use module after its definition that you don't have the extra brackets.
So module('TestPSPlayer') instead of module('TestPSPlayer',[]).
In my case this didn't worked:
beforeEach(module('user'));
beforeEach(inject(function ($http) {
}));
beforeEach(module('community'));
beforeEach(inject(function ($controller, $rootScope) {
}));
I've changed to this to make it to work:
beforeEach(module('user'));
beforeEach(module('community'));
beforeEach(inject(function ($http) {
}));
beforeEach(inject(function ($controller, $rootScope) {
}));
If your provider does not use global init you can use the original injected provider and mock it.
in the example below the testedProvider is your controller.
var injectedProviderMock;
beforeEach(function () {
module('myModule');
});
beforeEach(inject(function (_injected_) {
injectedProviderMock = mock(_injected_);
}));
var testedProvider;
beforeEach(inject(function (_testedProvider_) {
testedProvider = _testedProvider_;
}));
it("return value from injected provider", function () {
injectedProviderMock.myFunc.andReturn('testvalue');
var res = testedProvider.executeMyFuncFromInjected();
expect(res).toBe('testvalue');
});
//mock all provider's methods
function mock(angularProviderToMock) {
for (var i = 0; i < Object.getOwnPropertyNames(angularProviderToMock).length; i++) {
spyOn(angularProviderToMock,Object.getOwnPropertyNames(angularProviderToMock)[i]);
}
return angularProviderToMock;
}

Resources