AngluarJS Unit Test - Undefined is not a function - javascript

I am trying to learn how to unit test within angular. Unit testing my controllers to start with then my services.
I have started off with a basic test.
Controller code:
angular.module('app')
.controller('TestCtrl', function ($rootScope,$scope,fileLoader,ngProgress) {
$scope.test= [];
$scope.init = function(){
fileLoader.getFile("test")
.then(function(res){
//Success
console.log(res);
$scope.test= res;
}, function(err){
//Error
});
ngProgress.complete();
}
$scope.init();
$scope.title = "Test";
});
Test code:
describe('Controller: TestCtrl', function () {
// load the controller's module
beforeEach(module('app'));
var TestCtrl,
scope,test;
test = {};
test.getFile = function(name) {
return [
{
"id": 0,
"name": "Test",
"imgName": "Test.png"
},
{
"id": 1,
"name": "Test1",
"imgName": "Test1.png"
}];
};
// Initialize the controller and a mock scope
beforeEach(inject(function ($controller, $rootScope) {
scope = $rootScope.$new();
TestCtrl= $controller('TestCtrl', {
$scope: scope,
fileLoader : test
});
}));
it('should have the correct title', function () {
expect(scope.title).toBe("Test");
});
});
And I can't understand why I am getting the following error:
TypeError: 'undefined' is not a function (evaluating
'fileLoader.getFile("test")
.then') undefined
Is there any way once this is resolved I can lay all my mocking out in a separate file e.g. my fileLoader mock and just inject it that way?
I don't understand how I am injecting it but yet it is undefined.
Thanks

And I can't understand why I am getting the following error:
you defined
fileLoader.getFile("test")
.then(function(res){
so getFile() should return a promise which could be resolved, but you return a simple array with two objects inside
test.getFile = function(name) {
return [
{
"id": 0,
"name": "Test",
"imgName": "Test.png"
},
{
"id": 1,
"name": "Test1",
"imgName": "Test1.png"
}]
refactor one side. Use defers or handle the array result.

Related

AngularJS - Passing a function to spyOn

SO I have this simple, watered-down app that returns the NATO alphabet and then does unit tests on it using mocks and promises.
HERE'S A LIVE DEMO
I'm trying to spyOn a function from my .service() of MainModel. In the controller, I have a deferred promise called natoAlphabet that successfully displays on the page.
At first, I was referencing getNato from the MainController, but I never set MainController.getNato to the MainModel.getNato.
So I added in the MainController:
this.getNato = MainModel.getNato;
And I get the error of: Expected spy getNato to have been called.
However, in the console log, if you do a console output of mockMainCtrl the controller being mocked inside the beforeEach near the top, you get Object {name: "Hello World!", getNato: Promise}
and then below inside the first it() test, the output is Object {name: "Hello World!"} however, if you expand that one, you get:
Object {name: "Hello World!"}
getNato: Promise
name: "Hello world!";
__proto__: Object
Whereas the one inside the beforeEach, you had getNato.
My error
My error happens when the Jasmine test runs and I get Expected spy getNato to have been called. from the line expect(mockMainCtrl.getNato).toHaveBeenCalled(); on theSpec.js.
So what am I doing wrong?
I don't think there is anything wrong with app.js because the page can successfully read the promise.
Appendix:
theSpec.js:
describe('Controller: MainCtrl', function() {
beforeEach(module('app'));
var $scope, $q, mockMainCtrl, $controller, scope, deferred;
beforeEach(inject(function($controller, _$rootScope_, _$q_, MainModel) {
$q = _$q_;
$scope = _$rootScope_.$new();
deferred = _$q_.defer();
mockMainCtrl = $controller('MainCtrl', {
$scope: $scope,
MainModel: MainModel
});
console.log(mockMainCtrl);
}));
it('spied and have been called', function() {
spyOn(mockMainCtrl, 'getNato');
console.log(mockMainCtrl);
expect(mockMainCtrl.getNato).toHaveBeenCalled();
});
it('Name from service, instantiated from controller, to be mocked correctly', inject(function() {
expect(mockMainCtrl.name)
.toEqual("Hello World!");
}));
it('Get [getNato] mocked deferred promise', function(mainCtrl) {
deferred.resolve([{ id: 1 }, { id: 2 }]);
$scope.$apply();
expect($scope.results).not.toBe(undefined);
expect($scope.results).toEqual(['Alpha', 'Bravo', 'Charlie', 'Delta', 'Echo', 'Foxtrot', 'Golf', 'Hotel', 'India']);
expect($scope.error).toBe(undefined);
});
});
app.js:
var app = angular.module('app', []);
app.service('MainModel', function($q) {
this.name = "Hello World!";
var getNato = function() {
var deferred = $q.defer();
var theNatoAlphabet = ['Alpha', 'Bravo', 'Charlie', 'Delta', 'Echo', 'Foxtrot', 'Golf', 'Hotel', 'India'];
deferred.resolve(theNatoAlphabet);
return deferred.promise;
};
this.getNato = getNato();
});
app.controller('MainCtrl', function($scope, MainModel) {
this.name = MainModel.name;
var self = this;
MainModel.getNato.then(function(data) {
self.natoAlphabet = data;
$scope.results = data;
}).catch(function() {
$scope.error = 'There has been an error!';
});
this.getNato = MainModel.getNato;
});
Take a look at - http://plnkr.co/edit/57ZA8BXscmdY6oDX5IOA?p=preview.
You'd want to 'spyOn' the dependency i.e. the 'MainModel' here and do it before the '$controller' construction as the 'promise' is getting resolved on the construction of the controller. Hope this helps.
Something like -
beforeEach(inject(function($controller, _$rootScope_, _MainModel_) {
scope = _$rootScope_.$new();
MainModel = _MainModel_;
spyOn(MainModel, 'getNato').andCallThrough();
mockMainCtrl = $controller('MainCtrl', {
$scope: scope
});
}));
it('spied and have been called', function() {
expect(MainModel.getNato).toHaveBeenCalled();
});

Jasmine unit testing with $q.all in angular js factory service

Below is the service which get the values from web api using $resource
(function () {
'use strict';
function a($resource) {
return $resource('api/values/a');
}
angular
.module('app')
.factory('a', ['$resource', a]);}());
i have a list of services like above service
below is the service which uses the above services to get the values and it will return it as single result
How can i test the below service with jasmine unit test i need code coverage for the below service
(function () {
'use strict';
function factoryService($q, a, b, c, d) {
function fetch(id) {
var data = {},
promises = [];
data.avalues = a.query();
promises.push(data.avalues.$promise);
data.bvalues = b.query();
promises.push(data.bvalues.$promise);
data.cvalues = c.query();
promises.push(data.cvalues.$promise);
data.dvalues = d.query();
promises.push(data.dvalues.$promise);
return $q.all(promises).then(function () {
return data;
});
}
return {
fetch: fetch
};
}
angular
.module('app')
.factory('factoryService', ['$q', 'a', 'b', 'c', 'd', factoryService]);}());
can anyone help me to write unit test for above factory service with code coverage
describe('getting a,b,c,d values from service', function() {
var service,result,httpBackend,rootScope,$q,scope;
beforeEach(module('app'));
beforeEach(inject(function(proposalPlanInitialData, _$httpBackend_,$rootScope, _$q_) {
$q = _$q_;
service = proposalPlanInitialData;
httpBackend = _$httpBackend_;
rootScope = $rootScope;
scope = $rootScope.$new();
deferred = _$q_.defer();
}));
it ('should be loaded', function() {
expect(service).toBeDefined();
});
it ('should return get data when calling fetch', function() {
fakeListing = {
id: 123,
price: 300000
};
var getData = { "id": '1', "name": "dummyvalue" };
httpBackend.expectGET('api/values/a').respond({ "id": '1', "name": "dummyvalue" });
httpBackend.expectGET('api/values/b').respond({ "id": '1', "name": "dummyvalue" });
httpBackend.expectGET('api/values/c').respond({ "id": '1', "name": "dummyvalue" });
httpBackend.expectGET('api/values/d').respond({ "id": '1', "name": "dummyvalue" });
var data = service.fetch(1,1);
spyOn(service, 'fetch').and.callFake(function () {
return fakeListing;
});
// httpBackend.flush();
var data = service.fetch(1);
expect(data).toBe(fakeListing);});});
but getting a data as undefined

Using non-angular external js functions inside angularjs controller

I'm still new to angularJs.
I need to access Enumerable class from linqjs inside an angularjs controller.
My ngController looks similar to this:
var app1= angular.module("app1");
app1.controller("peopleController", ['$scope', 'Restangular',
function($scope, Restangular) {
$scope.people = Restangular.all('people').getList()
$scope.selectedItem = Enumerable.From(people).FirstOrDefault(); // ERROR
}]);
The error I'm getting is:
ReferenceError: Enumerable is not defined
any help?
I think it should be:
var app1 = angular.module("app1");
app1.controller("peopleController", ['$scope', 'Restangular', function ($scope, Restangular) {
$scope.people = Restangular.all('people').getList();
$scope.selectedItem = Enumerable.From(people).FirstOrDefault(); // ERROR
}]);
So without the 'peopleController' name of the function.
Edit
The problem is that Enumerable ( which is defined in linqjs ) is not available at the moment that it is called. Therefore the injector is looking for it and for the Enumerableprovider, but it doesn't exist nor is it injected.
You want to make sure you load the linqjs sources before your application is run. If Enumerable is defined in the global scope, you should be fine to use it from your controller. I created a JsFiddle that loads angular and linqjs from a CDN and shows you that it will 'just work' from your controller: http://jsfiddle.net/ngn3Lprx/2/
var app = angular.module('myapp', []);
app.controller('myctrl', ['$scope', function ($scope) {
// Example from: http://linqjs.codeplex.com/
var jsonArray = [
{ "user": { "id": 100, "screen_name": "d_linq" }, "text": "to objects" },
{ "user": { "id": 130, "screen_name": "c_bill" }, "text": "g" },
{ "user": { "id": 155, "screen_name": "b_mskk" }, "text": "kabushiki kaisha" },
{ "user": { "id": 301, "screen_name": "a_xbox" }, "text": "halo reach" }
]
// ["b_mskk:kabushiki kaisha", "c_bill:g", "d_linq:to objects"]
$scope.queryResult = Enumerable.From(jsonArray)
.Where(function (x) { return x.user.id < 200 })
.OrderBy(function (x) { return x.user.screen_name })
.Select(function (x) { return x.user.screen_name + ':' + x.text })
.ToArray();
}]);
With view:
<div ng-app="myapp">
<div ng-controller="myctrl">
<pre>
{{ queryResult | json }}
</pre>
</div>
</div>
Gives:
[
"b_mskk:kabushiki kaisha",
"c_bill:g",
"d_linq:to objects"
]
As expected.
p.s. Dont' forget the semi-colon after .getList();

Angular controller testing with many dependencies

I am learning AngularJS by building a small Real estate app. As i am new to AngularJS, i have very little knowledge about controller testing with many dependencies. I google about that but found very few information. Any help would be appreciated.
The following test is failing:
it('should create "proterties" model with 1 property fetched from xhr', function() {
$httpBackend.flush();
scope.properties = scope.getResultsPage(1);
expect(scope.properties).toEqual(propertiesData());
});
ControllersSpecs.js:
'use strict';
/* jasmine specs for controllers go here */
describe('Realty controllers', function() {
beforeEach(module('realtyApp'));
beforeEach(module('angularUtils.directives.dirPagination'));
beforeEach(module('realtyFilters'));
beforeEach(module('realtyServices'));
describe('PropertyListCtrl', function(){
var scope, ctrl, $httpBackend;
function propertiesData() {
return {
"total": 1,
"data":[{
"id": "26",
"property_type": "apartment",
"amount": "600",
"address": "26 Marsh St, Wolli Creek",
}]
}
};
// Learn more about dependency injection for testing
// https://docs.angularjs.org/tutorial/step_05
beforeEach(inject(function(_$httpBackend_, $rootScope, $controller) {
$httpBackend = _$httpBackend_;
$httpBackend.expectGET('/api/properties?page=1').
respond(propertiesData());
scope = $rootScope.$new();
ctrl = $controller('PropertyListCtrl', {$scope: scope});
}));
it('should create "proterties" model with 1 property fetched from xhr', function() {
$httpBackend.flush();
scope.properties = scope.getResultsPage(1);
// scope.properties = propertiesData();
expect(scope.properties).toEqual(propertiesData());
});
});
});
Main app module:
'use strict';
/* App Module */
var realtyApp = angular.module('realtyApp', [
'ngRoute',
'angularUtils.directives.dirPagination',
'realtyControllers',
'realtyFilters',
'realtyServices'
]);
Property List controller
'use strict';
/* Controllers */
var realtyControllers = angular.module('realtyControllers', []);
realtyControllers.controller('PropertyListCtrl', ['$scope', 'Property', 'propertyImage', 'propertyData',
function($scope, Property, propertyImage, propertyData) {
$scope.beds = propertyData.beds();
$scope.bathrooms = propertyData.bathrooms();
$scope.garageSpaces = propertyData.garageSpaces();
// Paginate properties
$scope.totalProperties = 0;
$scope.propertiesPerPage = 10; // this should match however many results your API puts on one page
$scope.pagination = {
current: 1
};
$scope.getResultsPage = function getResultsPage(pageNumber) {
// The following will generate :
// http://realty.dev/api/properties?page=1
Property.get({page:pageNumber}, function(result) {
$scope.properties = result.data;
$scope.totalProperties = result.total;
});
}
$scope.getResultsPage(1);
$scope.pageChanged = function(newPage) {
$scope.getResultsPage(newPage);
};
$scope.isCarSpaceAvailable = function(carSpace) {
if (carSpace != 0) {
return true;
}
return false;
}
$scope.getPropertyImage = function(photo) {
return propertyImage.jpg(photo.name);
}
$scope.clearFilters = function() {
$scope.filter = {};
}
}]);
Edit 1:
I am getting the following error:
When you do
scope.properties = scope.getResultsPage(1);
you affect properties to what is return from getResultsPage and nothing is return from that function.
Can you try (I think flush should be call after the method) :
beforeEach(inject(function(_$httpBackend_, $rootScope, $controller) {
$httpBackend = _$httpBackend_;
$httpBackend.whenGET('/api/properties?page=1').
respond(propertiesData());
/**** your code *****/
}));
it('should create "proterties" model with 1 property fetched from xhr', function() {
$httpBackend.expectGET('/api/properties?page=1');
scope.getResultsPage(1);
$httpBackend.flush();
// scope.properties = propertiesData();
expect(scope.properties).toEqual(propertiesData());
});

Testing repsonse of angular service with $http get

Im pretty new to AngularJS and testing so im trying to test my service i made in AngularJS.
I have set up the service like this
var serviceModule = angular.module('App.services', []);
serviceModule.factory('subscribeService', function ($http) {
return {
getNumberOfSubscribers : function() {
return $http.get('/jsonFiles/subscribers.json')
.then(function(subscribers) {
return subscribers.length;
});
}
};
});
And the test goes like this.
describe('Subscribe service test', function() {
var httpBackend,
subscribeServiceMock,
subscribers;
subscribers = [{"email": "test1#mail.com", "subscriptions": "A,B,C"},
{"email": "test2#mail.com", "subscriptions": "A,C,D"},
{"email": "test3#mail.com", "subscriptions": "B,C,F"}];
beforeEach(module('App.services'));
beforeEach(inject(function(subscribeService){
subscribeServiceMock = subscribeService;
}));
it('Should return total numbers of subsribers', inject(function ($httpBackend) {
$httpBackend.whenGET('/jsonFiles/subscribers.json').respond(subscribers);
var numberOfSubscribers = subscribeServiceMock.getNumberOfSubscribers();
$httpBackend.flush();
expect(numberOfSubscribers).toEqual(3);
}));
The test seams to fail with this error:
"Subscribe service test Should return total numbers of subsribers FAILED"
" Expected { then : Function } to equal 3"
Im wondering where i have got this wrong?
Have you tried to inject your httpBackend? Try this on your "beforeEach" call:
beforeEach(inject(function(subscribeService, _$controller_, _$rootScope_, _$httpBackend_){
subscribeServiceMock = subscribeService;
$scope = _$rootScope_.$new();
$controller = _$controller_('MyCtrl', { $scope: $scope });
$httpBackend = _$httpBackend_;
}));
UPDATE
As I found out here: AngularJS Issues mocking httpGET request
It seems there's in fact a difference between these two "injection methods"

Categories