I have a function inside a controller, which calls a method of an object "mapping".
$scope.createParameter = function (parameter) {
var apiObj = {};
mapping.createApiService(apiObj)
}
that object structure is as follows, and this method 'createApiService' calls a 'create' method of apiService.
var mapping = {
createApiService : apiService.create
}
I have injected that apiService into my Testsuite. and a spyon is also been added as follows.
spyOn(apiService, "create").and.callFake(function () {
return {
then: function (callback) {
callback(createResponse);
return {
catch: function () {
}
}
}
}
but when I execute my it block with following expectation it gives an error "expected spy create to have been called.
expect(apiService.create).toHaveBeenCalled();
can some one help me with this?
EDIT: Added testing code
describe("Controller", function () {
beforeEach(angular.mock.module("ui.router"));
beforeEach(angular.mock.module("PpmApp"));
var $controller, $scope, $state, $httpBackend, parameterDetails, apiService, constantsProvider;
beforeEach(inject(function (_$controller_, _$rootScope_, _$state_, _$httpBackend_, _apiService_, _constantsProvider_) {
$controller = _$controller_;
$scope = _$rootScope_.$new();
$state = _$state_;
$scope.header = {};
$state.current = {};
$httpBackend = _$httpBackend_;
apiService = _apiService_;
constantsProvider = _constantsProvider_;
$controller = $controller("Controller", { "$scope": $scope, "parameterDetails": parameterDetails });
}));
describe("create", function () {
it("should succeed with valid Input", function () {
var parameter = {
ParameterType: 1,
ParameterId: 2,
LookupCurveStructures: [{
Name: "Parameter name",
XAxisName: "XAxisName",
YAxisName: "YAxisName",
XAxisDataType: 1,
YAxisDataType: 2
}]
};
var createResponse = {
Data: [],
IsSuccess: true,
Message: "Parameter added successfully"
};
spyOn(apiService, "create").and.callFake(function () {
return {
then: function (callback) {
callback(createResponse);
return {
catch: function () {
}
}
}
}
});
$scope.createParameter(parameter);
expect($scope.createPopupInfo.validationErrors.isError).toEqual(false);
expect(apiService.create).toHaveBeenCalled();
expect($scope.parameterDetails.Parameters).toEqual([]);
});
});
});
Related
I have the following services:
DataService.js
app.service("DataService", [
function () {
this.getData = function () { return "original value" }
}
]);
LocationService.js
app.service("LocationService", ["DataService",
function(dataSvc) {
this.getLocationData = function () {
return dataSvc.getData();
}
}
]);
Now for testing LocationService.js, how do mock DataService inside of LocationService?
This is what I currently have for LocationService_tests.js:
describe("LocationService", function () {
var locationSvc;
beforeEach(module('myModule'));
beforeEach(inject(function (LocationService) {
locationSvc = LocationService;
}));
describe("getLocationData", function () {
it("returns the location data", function () {
var mockLocationDataValue = "mocked value";
// ???
// I want to mock dataSvc.getData within getLocationData
// so it returns "mocked value" instead of "original value"
expect(locationSvc.getLocationData()).toBe(mockLocationDataValue);
});
});
});
You could mock whole service using $provide API's service method, and change your service to returned mock data.
describe("LocationService", function () {
var locationSvc;
beforeEach(module('myModule'));
beforeEach(module(function($provide) {
$provide.service('LocationService', function() {
this.getData = function() {
return "mock value";
}
});
});
beforeEach(inject(function (LocationService) {
locationSvc = LocationService;
}));
...
});
this is my controller:
angular
.module('studentsApp')
.controller('StudentsController', StudentsController);
function StudentsController($scope, StudentsFactory) {
$scope.students = [];
$scope.specificStudent= {};
var getStudents = function() {
StudentsFactory.getStudents().then(function(response) {
if($scope.students.length > 0){
$scope.students = [];
}
$scope.students.push(response.data);
});
};
}
This is my factory:
angular.module('studentsApp')
.factory('StudentsFactory', function($http) {
var base_url = 'http://localhost:3000';
var studentsURI = '/students';
var studentURI = '/student';
var config = {
headers: {
'Content-Type': 'application/json'
}
};
return {
getStudents: function() {
return $http.get(base_url + studentsURI);
}
};
});
And here is how I'm trying to unit test the controller:
describe('Controller: Students', function() {
var StudentsController, scope, StudentsFactory;
beforeEach(function() {
module('studentsApp');
inject(function($rootScope, $controller, $httpBackend, $injector) {
scope = $rootScope.$new();
httpBackend = $injector.get('$httpBackend');
StudentsFactory = $injector.get('StudentsFactory');
StudentsController = $controller('StudentsController', {
$scope : scope,
'StudentsFactory' : StudentsFactory
});
students = [{
name: 'Pedro',
age: 10
}, {
name: 'João',
age: 11
}, {
name: 'Thiago',
age: 9
}];
spyOn(StudentsFactory, 'getStudents').and.returnValue(students);
});
});
it('Should get all students', function() {
scope.students = [];
StudentsController.getStudents();
$scope.$apply();
expect(scope.students.length).toBe(3);
});
});
The problem is when I run the test, the following message is displayed:
undefined is not a constructor (evaluating
'StudentsController.getStudents()')
I looked at the whole internet trying to find a tutorial that can help me on that, but I didn't find anything, could someone help me here?
It's link to the fact that the function getStudent() is private (declared by var). Thus your test can't access it. You have to attach it to the $scope or this to be able to test it.
I generally use this in controller:
var $this = this;
$this.getStudents = function() {
...
};
There's no StudentsController.getStudents method. It should be
this.getStudents = function () { ... };
Mocked StudentsFactory.getStudents returns a plain object, while it is expected to return a promise.
$controller shouldn't be provided with real StudentsFactory service as local dependency (it is already provided with it by default):
var mockedStudentsFactory = {
getStudents: jasmine.createSpy().and.returnValue($q.resolve(students))
};
StudentsController = $controller('StudentsController', {
$scope : scope,
StudentsFactory : mockedStudentsFactory
});
I am having a controller like below
(function () {
var mockController = function ($scope, MockService) {
$scope.message = "This is a text message";
$scope.getCities = function () {
return MockService.getCities();
};
};
var mockService = function ($http) {
this.getCities = function () {
return $http.get("../rest/url", {
headers: {
'Accept': 'application/yang.data+json'
}
});
};
};
angular.module("MockApp", [])
.service("MockService", mockService)
.controller("MockController", mockController);
}())
I am trying to write a UT mocking the service like below
describe("MockController", function () {
var $scope;
beforeEach(function () {
module("MockApp");
inject(function (_$controller_, _$rootScope_, MockService) {
$scope = _$rootScope_.$new();
spyOn(MockService, "getCities").and.callFake(function () {
return [{
city: "Bangalore"
, country: "India"
}];
});
controller = _$controller_("MockController", {
$scope: $scope
});
});
});
describe("Test", function () {
it("Should be Bangalore", function () {
$scope.getCities()
.then(function (data) {
console.log("got it");
})
});
});
});
Its throwing an error saying
TypeError: $scope.getCities(...).then is not a function
Please help me.
I think:
$scope.getCities = function () {
return MockService.getCities();
};
should be:
$scope.getCities = function () {
return MockService(getCities());
};
I am having a controller like below
(function () {
var newPlaceController = function ($scope, PlacesService, $mdDialog) {
$scope.newPlace = {
"city": "",
"country": ""
};
$scope.addCity = function () {
$scope.places.push($scope.newPlace);
PlacesService.addCity($scope.newPlace);
$mdDialog.hide();
};
$scope.cancel = function(){
$mdDialog.hide();
};
};
angular.module("module.place")
.controller('NewPlaceController', newPlaceController);
}());
Also I have a test file like below which tests whether the initialization value of newPlace is correct.The code is like below
describe("NewPlaceController", function () {
var $scope, ctrl;
beforeEach(module("module.place"));
beforeEach(
inject(function (_$controller_) {
$scope = {};
controller = _$controller_('NewPlaceController', {
$scope: $scope
});
}));
describe("Initialization", function () {
it("asdasd", function () {
expect($scope.newPlace).toEqual({
city: ''
, country: ''
});
})
});
});
It's working fine. My doubt is how can I test the addCity and cancel functions in the controller.
You can use it like this
it("Should call addCity", function () {
var spy = spyOn(PlacesService, 'addCity');
controller.addCity();
expect(spy).toHaveBeenCalled();
})
it("Should call mdDialog hide", function () {
var spy = spyOn($mdDialog, 'hide');
controller.cancel();
expect(spy).toHaveBeenCalled();
})
I am having a bit of trouble getting set up to test an Angular controller that contains promises.
The controller code it this:
angular.module('jhApp')
.controller('adminPagesCtrl', function(resourceCache) {
var adminPages = this;
adminPages.items = resourceCache.query('page');
adminPages.delete = function(page) {
resourceCache.delete('page', {id:page._id})
.then(function(responceData) {
if(responceData.deleted === true) {
adminPages.items = resourceCache.query('page');
}
});
};
});
my test looks like this:
describe('adminPagesCtrl', function() {
var defferred,
$rootScope,
controller,
resourceCache,
scope,
page,
defferred,
promise;
beforeEach(function() {
module('jhApp');
});
beforeEach(inject(function ($rootScope, $controller, $q) {
scope = $rootScope.$new();
controller = $controller('adminPagesCtrl as adminPages', {$scope: scope});
deffered = $q.defer();
promise = deffered.promise;
resourceCache = {
delete: promise
};
page = {_id: 1};
spyOn(resourceCache, 'delete');
}));
it('deletes a page', function() {
expect(controller).toBeDefined();
scope.adminPages.delete(page);
console.log(resourceCache.delete) //outputs: function{}
console.log($rootScope) //outputs: undefined
resourceCache.delete.resolve({deleted: true});
$rootScope.$apply();
expect(resourceCache.delete).toHaveBeenCalled();
});
});
I am trying to mock the resourceCache promise so it returns some fake data and so I can just test that something got returned and the adminPages.delete calls the the resourceCache.delete.
I think I am doing something fundamentally wrong though as the current error is:
undefined is not a fuction
This i am sure is because if I try to log out resourceCache.delete it just shows and empty function. The first expect
resourceCache.delete.resolve();
passes ok.
You need to setup resourceCache.delete as a function that returns a promise rather than just set to a promise. You should also be mocking resourceCache.query. To resolve the promise you need to use deffered.resolve(response); after controller.delete is called. Then $rootScope.$digest();.
describe('adminPagesCtrl', function() {
var createController, $rootscope, deferred, resourceCache;
beforeEach(module('jhApp'));
beforeEach(inject(function($controller, _$rootScope_, $q) {
$rootScope = _$rootScope_;
deferred = $q.defer();
resourceCache = {
delete: function () {
},
query: function (page) {
}
};
spyOn(resourceCache, 'delete').and.returnValue(deferred.promise);
createController = function() {
return $controller('adminPagesCtrl', { resourceCache: resourceCache } );
};
}));
it('deletes a page', function() {
//Arrange
var controller = createController();
var page = {
_id: 1
};
var response = {
deleted: true
};
var items = [{
test: 'test'
}];
var expectedDeleteParam = {
id: page._id
};
spyOn(resourceCache, 'query').and.returnValue(items);
//Act
controller.delete(page);
deferred.resolve(response);
$rootScope.$digest();
//Assert
expect(resourceCache.delete).toHaveBeenCalledWith('page', expectedDeleteParam);
expect(resourceCache.query).toHaveBeenCalledWith('page');
expect(controller.items).toEqual(items);
});
});
Plunkr
I finally got this working so in case anyone else has a similar issue here is the amended version. Calling $rootScope.digest() was causing an error with Karma.
Error: Unexpected request: GET views/projects.html
No more request expected
I swapped that for scope = $rootScope.$new(); and now tests are passing.
describe('adminPagesCtrl', function() {
var createController, $rootScope, deferred, resourceCache, scope;
beforeEach(module('jhApp'));
beforeEach(inject(function($controller, $rootScope, $q) {
scope = $rootScope.$new();
deferred = $q.defer();
resourceCache = {
delete: function () {
},
query: function (page) {
}
};
spyOn(resourceCache, 'delete').and.returnValue(deferred.promise);
createController = function() {
return $controller('adminPagesCtrl', { resourceCache: resourceCache } );
};
}));
it('deletes a page', function() {
//Arrange
var controller = createController();
var page = {
_id: 1
};
var response = {
deleted: true
};
var items = [{
test: 'test'
}];
var expectedDeleteParam = {
id: page._id
};
spyOn(resourceCache, 'query').and.returnValue(items);
//Act
controller.delete(page);
deferred.resolve(response);
scope.$digest();
//Assert
expect(resourceCache.delete).toHaveBeenCalledWith('page', expectedDeleteParam);
expect(resourceCache.query).toHaveBeenCalledWith('page');
expect(controller.items).toEqual(items);
});
});