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());
});
Related
Lets see, we have this according to:https://docs.angularjs.org/guide/unit-testing
describe('PasswordController', function() {
beforeEach(module('app'));
var $controller;
beforeEach(inject(function(_$controller_){
// The injector unwraps the underscores (_) from around the parameter names when matching
$controller = _$controller_;
}));
describe('$scope.grade', function() {
it('sets the strength to "strong" if the password length is >8 chars', function() {
var $scope = {};
var controller = $controller('PasswordController', { $scope: $scope });
$scope.password = 'longerthaneightchars';
$scope.grade();
expect($scope.strength).toEqual('strong');
});
});
});
now i am making service and factory, is there any equivalent to ____$controller____ for service and factory? so i can inject it with something else like:
var controller = $controller('PasswordController', { $scope: $scope });
and change the inner functions of the dependency so i can test it, or is there any better approach?
Edit: to make question more clear
here is the example of the question:
i have this:
var app = angular.module("app").service("MyService",["$scope","$http",function($scope,$http){
this.myFunction = function(){
$http.get("/myApi/1");
}
}]);
how do i use the equivalent of
var controller = $controller('PasswordController', { $scope: $scope });
so i can inject $scope and $http with something else to myService?
You can't inject dependencies to factories or services on the go, but you can mock the dependencies with your custom objects and have angular substitute them automatically. You can use $provide for that. Here is an example:
angular.module('app').service('some', function(dependencyService) {
});
When testing:
beforeEach(module(function($provide) {
$provide.value('dependencyService', {
});
}));
After doing a workarround, i found out from https://www.sitepoint.com/unit-testing-angularjs-services-controllers-providers/ about the service. i tested out the tutorial here and here is the test script:
(function () {
angular.module('services', [])
.service('sampleSvc', ['$window', 'modalSvc', function ($window, modalSvc) {
this.showDialog = function (message, title) {
if (title) {
modalSvc.showModalDialog({
title: title,
message: message
});
} else {
$window.alert(message);
}
};
}]);
describe("Testing service", function () {
var mockWindow, mockModalSvc, sampleSvcObj;
beforeEach(module(function ($provide) {
$provide.service('$window', function () {
this.alert = jasmine.createSpy('alert');
});
$provide.service('modalSvc', function () {
this.showModalDialog = jasmine.createSpy('showModalDialog');
});
}, 'services'));
beforeEach(inject(function ($window, modalSvc, sampleSvc) {
mockWindow = $window;
mockModalSvc = modalSvc;
sampleSvcObj = sampleSvc;
}));
it('should show alert when title is not passed into showDialog', function () {
var message = "Some message";
sampleSvcObj.showDialog(message);
expect(mockWindow.alert).toHaveBeenCalledWith(message);
expect(mockModalSvc.showModalDialog).not.toHaveBeenCalled();
});
it('should show modal when title is passed into showDialog', function () {
var message = "Some message";
var title = "Some title";
sampleSvcObj.showDialog(message, title);
expect(mockModalSvc.showModalDialog).toHaveBeenCalledWith({
message: message,
title: title
});
expect(mockWindow.alert).not.toHaveBeenCalled();
});
});
})();
and i try my own test script:
(function () {
describe("Testing service", function () {
var mockHttp, mockCookies, mockApi;
beforeEach(function () {
module(function ($provide) {
$provide.service('$http', function () {
this.defaults = {
headers: {
common: {
}
}
};
});
$provide.service('$cookies', function () {
});
});
module('timesheet');
});
beforeEach(inject(function ($http, $cookies, APIService) {
mockHttp = $http;
mockCookies = $cookies;
mockApi = APIService;
}));
it('Test Service', function () {
});
});
})();
apparently in somewhere in my code, there is an app.run which inside do the
$http.defaults.headers.common.Authorization = 'Bearer ' + $cookies.get('sessionToken');
and causes the error the moment i inject the $http with something else because headers not defined, i thought it was from my own test script because they are using same name, but apparently this is the one causing problem.
So, actually the moment we load in testing mode, the angularjs still do the whole running of application, in which i forgot about this one.
Here is the function in the Controller:
angular.module("MyMod")
.controller("MyController", function(UserData, mtcLogService, $state,
BroadcastService, $scope, $rootScope, ChartsService, PaxCountSummaryService) {
self.waveCountSummary = function() {
if (self.program.programID !== undefined) {
PaxCountSummaryService.getWaveCountChartSummary(self.program.programID)
.then(function(data) {
self.totalWaveCount = data[data.length - 1].count;
data.pop();
return (data || []).map(_.bind(ChartsService.tasksCountToChartData, this, _, "unknown"));
})
.then(function(chart) {
return self.replaceContentsOf(self.waveCountChartData, chart);
});
}
};
});
Here is my working test:
fit("My test", inject(function ($rootScope, $controller, $q) {
var results = [{
pop: sinon.spy(),
count: 1
}];
mockPaxCountSummaryService.getWaveCountChartSummary
.returns($q.resolve(results));
testController = $controller("PaxCountSummaryController", {
$state: state,
$scope: scope,
$rootScope: $rootScope,
PaxCountSummaryService: mockPaxCountSummaryService
});
testController.program = testProgram;
testController.totalWaveCount = null;
expect(testController.totalWaveCount).toBe(null);
testController.waveCountSummary();
scope.$apply();
expect(testController.totalWaveCount).toBe(1);
}));
Ok, this test works. But how would I get to the second THEN in the promise chain so I can test if REPLACECONTENTSOF is called?
Any help on this?
Well, this is one of those times where I got turned around with multiple issues but it does work as expected. I just needed to create a spy for REPLACECONTENTSOF.
Here is working final test:
fit("MyTest.", inject(function($controller, $q) {
var results = [{
pop: sinon.spy(),
count: 1
}];
mockPaxCountSummaryService.getWaveCountChartSummary
.returns($q.resolve(results));
testController = $controller("PaxCountSummaryController", {
$state: state,
$scope: scope,
PaxCountSummaryService: mockPaxCountSummaryService
});
testController.program = testProgram;
testController.replaceContentsOf = sinon.spy();
testController.totalWaveCount = null;
expect(testController.replaceContentsOf.callCount).toBe(0);
testController.waveCountSummary();
scope.$apply();
expect(testController.totalWaveCount).toBe(1);
expect(testController.replaceContentsOf.callCount).toBe(1);
}));
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
I'm having an issue testing an AngularJS controller. The controller uses a
service to fetch some data and put it on the $scope.
// src/controllers/posts.js
module.exports = ['$scope', 'posts', function($scope, posts) {
posts.refresh(function(err, data) {
$scope.posts = data;
});
}]
// src/services/posts.js
module.exports = ['$http', function($http) {
this.refresh = function(callback) {
$http.get('/posts')
.success(function(data, status, headers, config) {
callback(null, data);
})
.error(function(data, status, headers, config) {
});
};
}]
// src/app.js
var angular = require('angular');
var PostsController = require('./controllers/posts');
var PostsService = require('./services/posts');
var simpleApp = angular.module('simple-app', []);
simpleApp.controller('PostsController', PostsController);
simpleApp.service('posts', PostsService);
This all works when I run it in the browser. I have a functioning test for the service that works with Karma and Jasmine. The problem I'm having with the controller test is that the result is that $scope is undefined.
Here is the test:
// test/controllers/posts.test.js
var fixtures = require('../fixtures/posts');
describe('PostsController', function() {
var PostsController, scope;
beforeEach(angular.mock.module('simple-app'));
beforeEach(angular.mock.inject(function($rootScope, $controller, _posts_) {
scope = $rootScope.$new();
PostsController = function() {
return $controller('PostsController', {
$scope: scope,
posts: _posts_
});
};
}));
it('should set the posts on the scope', function() {
debugger; // shows me that the scope's posts property isn't set
var controller = PostsController();
expect(scope.posts).toEqual(fixtures);
});
});
From the documentation, $controller returns a new instance of the specified controller. So when I'm calling PostsController() it should return the new instance with the dependencies injected right? When the instance is instantiated it should run the controller function, manipulating the scope that was injected in the test. Is there something I'm missing?
EDIT: I've added $httpBackend to the test but now the test says httpBackend is undefined. Here's the code (only the functions that changed):
beforeEach(angular.mock.inject(
function($rootScope, $controller, _posts_, $httpBackend) {
scope = $rootScope.$new();
PostsController = function() {
return $controller('PostsController', {
$scope: scope,
posts: _posts_
});
httpBackend = $httpBackend;
};
}));
it('should set the posts on the scope', function() {
debugger; // checking httpBackend here says httpBackend is undefined
httpBackend.whenGET('/posts').respond(fixtures);
var controller = PostsController();
expect(scope.posts).toEqual(fixtures);
});
I am having a bit of trouble testing a HTTP POST in AngularJs with Jasmine.
I have a controller that looks like so:-
appControllers.controller("TaskAddController", function ($scope, $http) {
$scope.task = {};
$scope.messages = {};
$scope.actions = {
save : function() {
$http.post("/ajax/tasks/save", $scope.task)
.then(function() {
$scope.messages.success = true;
$scope.task = {};
});
}
};
});
I am testing it like so:-
describe("TaskAddController", function() {
var createController, scope, $httpBackend;
beforeEach(function () {
module('appControllers');
scope = {};
inject(function ($injector) {
$httpBackend = $injector.get("$httpBackend");
});
inject(function ($controller) {
createController = function () {
return $controller("TaskAddController", { $scope: scope });
};
});
});
afterEach(function () {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it("when actions.save is called then should call service", function () {
var task = {
title: "Title",
description: "Description"
};
$httpBackend.expectPOST("/ajax/tasks/save", task);
createController();
scope.task = task;
scope.actions.save();
$httpBackend.flush();
});
});
This causes me to get the following error Error: No pending request to flush !
What am I doing wrong?
Thanks.
What version of AngularJS are you using?
When I run the code I get: Error: No response defined !
When I add a response the test passes:
$httpBackend.expectPOST("/ajax/tasks/save", task).respond({});