i have a parser service which uses a fileReader service:
app.factory( 'parserService', [ '$q', 'fileReader', function ( $q, fileReader ) {
var parser = {};
parser.addASTtoFilesInTree = function ( fileTree ) {
var promises = [];
traverse(fileTree);
return $q.all(promises);
}
return parser;
}]);
and FileReader is:
app.factory( 'fileReader', [ '$q', function ($q) {
var fileReader = {};
fileReader.readFile = function( file ) {
var defer = $q.defer();
var reader = new FileReader();
reader.onload = function(e) {
defer.resolve(e.target.result);
};
reader.onerror = function(msg){
console.log(msg);
//add defer on error
};
reader.readAsBinaryString(file.ref);
return defer.promise;
};
return fileReader;
}]);
i am currently trying to create a test using karma - jasmine to test the addASTtoFilesInTree function in the parseService.
describe( "parser tests" , function() {
var parserService, $rootScope;
var mockFileReader = {
readFile: function( file ) {
var defer = $q.defer();
defer.resolve(file.ref.content);
console.log("in mockFileReader"); // this never gets called
return defer.promise;
}
};
var mockFileContent = "..."
var mockAST = { ... }
var itemOne = {...};
var parsedItemOne = { ... }
var mockFileTree = { ... };
beforeEach( function() {
//load the module
module('FYP');
//using mock service
module( function($provide) {
$provide.value = ('fileReader', mockFileReader);
});
//inject service for testing
inject( function( _$rootScope_, _parserService_) {
$rootScope = _$rootScope_;
parserService = _parserService_;
});
});
describe( "addASTtoFilesInTree", function() {
it('should add AST to all JS file', function() {
parserService.addASTtoFilesInTree(mockFileTree).then(function() {
console.log("in THEN"); //this never gets called
});
$rootScope.$digest();
expect(mockFileTree['root'].files['itemOne']).toEqual(parsedItemOne);//fails since it compares the two objects before promise is returned
});
});
});
The test is failing because its comparing the two objects before the promise returns. Am i missing something?
Try using a decorator:
//load the module
module('FYP');
//using mock service
module( function($provide) {
$provide.decorator('fileReader', function() {
return mockFileReader;
});
});
//inject service for testing
inject( function( _$rootScope_, _parserService_, _$q_ ) {
$rootScope = _$rootScope_;
parserService = _parserService_;
$q = _$q_;
});
or just a value service:
beforeEach(module('FYP'));
beforeEach(function () {
module( function($provide) {
$provide.value('fileReader', mockFileReader);
});
});
beforeEach(inject( function( _$rootScope_, _parserService_, _$q_) {
$rootScope = _$rootScope_;
parserService = _parserService_;
$q = _$q_;
}));
Related
I am trying to write unit test case with jasmine spy. Below is my service code.
function reset(someParam) {
var deferred = $q.defer();
svcTypes.getTasksWithRoles().then(function (types) {
if (types.HadError) return;
// do some stuff..
deferred.resolve(results);
}, function (errorResponse) {
deferred.reject(errorResponse);
});
return deferred.promise; }
Below is my unit test case :
describe('tests for svcWorkordertypes', function () {
beforeEach(angular.mock.module('workorders.service'));
beforeEach(angular.mock.module('workordertypes.service'));
var svcWorkordertypes;
var svcTypes;
var $q, $httpBackend;
beforeEach(angular.mock.inject(function (_svcWorkordertypes_, _svcTypes_, _$q_, _$httpBackend_) {
svcWorkordertypes = _svcWorkordertypes_;
svcTypes = _svcTypes_;
$q = _$q_;
$httpBackend = _$httpBackend_;
}));
//This is working fine.
it('all controls should be set', function () {
expect(svcWorkordertypes).toBeDefined();
expect(svcTypes).toBeDefined();
expect($q).toBeDefined();
expect($httpBackend).toBeDefined();
});
it('test reset method', function () {
//Arrange
var someParam = 'test';
var types = [{ RoleList: 'newRoleList', isHeader: true, Roles: 'testRole' }];
$httpBackend.when("GET", 'null/tasks/types?$select=IncludeRoles').respond(200, JSON.stringify(types));
spyOn(svcTypes, 'getTasksWithRoles').and.returnValue(function () {
var deferred = $q.defer();
deferred.resolve(JSON.stringify(types));
return deferred.promise;
});
//Act
var result = svcWorkordertypes.reset(selectedRoleName);
//Assert
result.then(function (res) {
console.log(res + ' res output...');
//expect(JSON.stringify(res)).toEqual(JSON.stringify(types));
});
//expect(svcTypes).toBeDefined();
}) });
Below is the error :
I have tried many ways with callFake as well. I found a similar question on stackoverflow but it didn't help me.
getTasksWithRoles should return a promise, and it is mocked to return a function that returns a promise.
It should be:
spyOn(svcTypes, 'getTasksWithRoles').and.returnValue(
$q.resolve(JSON.stringify(types))
);
Given the following test.
The $provided service is not being injected. If I debug the test in karma I can see that the service being provided is the real one, and not the mock.
The really weird thing, is that if I remove the $provide.service... I get an error Error: [$injector:unpr] Unknown provider: ficaServiceProvider <- ficaService. This clearly means that the service is getting registered, just not replaced?
describe("component: FicaStatusComponent",
function () {
var fs;
beforeEach(function () {
module("aureus",
function ($provide) {
$provide.service("ficaService", function () {
this.status = function () {
return $q(function (resolve, reject) {
resolve([{ documentType: { id: 1 } }]);
});
}
})
});
});
beforeEach(inject(function (_$componentController_, _ficaService_) {
$componentController = _$componentController_;
fs = _ficaService_;
}));
it("should expose a `fica` object", function () {
console.log('should expose');
var bindings = {};
var ctrl = $componentController("ficaStatus", null, bindings);
expect(ctrl.fica).toBeDefined();
});
it("compliant with no documents should not be compliant",
function () {
var ctrl = $componentController("ficaStatus");
expect(ctrl.fica.length).toEqual(1);
});
}
);
The second test compliant with no documents... is failing.
Chrome 56.0.2924 (Windows 10 0.0.0) component: FicaStatusComponent compliant with no documents should not be compliant FAILED
Error: Unexpected request: GET api/fica/status/
I have also tried this, expecting to have an empty object injected, but the "real" service is there nevertheless?
module("aureus", function($provide) {
$provide.value("ficaService", function() { return {}; });
$provide.service("ficaService", function() { return {}; });
});
Here is the implementation of the controller for the component:
var FicaStatusController = (function () {
function FicaStatusController($log, $loc, ficaService) {
var _this = this;
this.$log = $log;
this.$loc = $loc;
this.ficaService = ficaService;
this.fica = [];
this.ficaService.status(1234).then(function (_) { return _this.fica = _; });
}
FicaStatusController.$inject = ["$log", "$location", "IFicaStatusService"];
module("aureus").component("ficaStatus", new FicaStatusComponent());
module("aureus").service("IFicaStatusService", FicaStatusService);
The service is as follows:
var FicaStatusService = (function () {
function FicaStatusService($log, $http) {
this.$log = $log;
this.$http = $http;
}
FicaStatusService.prototype.status = function (accountNumber) {
var url = "api/fica/status/" + accountNumber;
this.$log.log("status: " + url);
return this.$http
.get(url)
.then(function (_) { return _.data; });
};
return FicaStatusService;
}());
...
You have added your service in your module like this:
module("aureus").service("IFicaStatusService", FicaStatusService);
That means that you will need to provide IFicaStatusService instead of ficaService with $provide.
I'm lost. I try to test if state.go is called after a ApiServiceMock has resolved a promise after a fake login.
For this test I get:
Expected spy go to have been called.
If I test another function which is directly triggering state.go it works. So I guess the promise isn't being resolved in the first place.
Using Angular Components and testing is quite new to me. Would be great if someone could give me a hint what I'm doing wrong or let me know if the whole approach is wrong in the first place.
login.component.ctrl.js
module.exports = function LoginComponentCtrl($state, $log, apiService) {
var vm = this;
vm.submit = function() {
apiService
.login(vm.username, vm.password)
.then(function(response) {
$state.go('storeDetail');
})
.catch(function(errData) {
$log.error(errData);
});
};
};
login.spec.js
var app = require('../../app.js');
var login = require('./login.module.js');
describe('login', function() {
var controller;
var element;
var scope;
var state;
var ApiServiceMock;
var StateMock;
var deferred;
beforeEach(angular.mock.module(app));
beforeEach(angular.mock.module(login));
describe('Component: login', function () {
beforeEach(inject(function($rootScope, $compile, $componentController, $q){
deferred = $q.defer();
ApiServiceMock = {
login: function () {
return deferred.promise;
}
};
StateMock = {
go: function () {
return true;
}
};
scope = $rootScope.$new();
controller = $componentController('login', {
$scope: scope,
apiService: ApiServiceMock,
$state: StateMock
});
element = angular.element('<login></login>');
element = $compile(element)(scope);
scope.$apply();
}));
it('on successful login', function() {
controller.username = 'Michael Jackson';
controller.password = 'dangerous123';
spyOn(StateMock, 'go').and.callThrough();
controller.submit();
deferred.resolve();
scope.$digest();
expect(StateMock.go).toHaveBeenCalled();
}););
});
});
Let me know if I can add further information to make this clearer.
Just directly return resolved promise from your mock service.
Try this
beforeEach(inject(function($rootScope, $compile, $componentController, $q){
ApiServiceMock = {
login: function () {
return $q.resolve({}); // if angular version is 1.4+
//return $q.when({}); // if angular less than 1.4
}
};
StateMock = {
go: function () {
return true;
}
};
scope = $rootScope.$new();
controller = $componentController('login', {
$scope: scope,
apiService: ApiServiceMock,
$state: StateMock
});
element = angular.element('<login></login>');
element = $compile(element)(scope);
scope.$apply();
}));
it('on successful login', function() {
controller.username = 'Michael Jackson';
controller.password = 'dangerous123';
spyOn(StateMock, 'go').and.callThrough();
controller.submit();
scope.$digest();
expect(StateMock.go).toHaveBeenCalled();
});
);
i try to test an angular controller but he returns a promise and i cant really resolve it...what's wrong with my code?
the Angular Controller:
kpi.AboutController = function ($scope, Version) {
function loadVersion() {
//$scope.version = Version.getVersion();
$scope.version = '1.1.0';
}
loadVersion();
};
and the jasmine test:
describe('dashboard: AboutController', function() {
beforeEach(module('dashboard'));
var $controller;
beforeEach(inject(function(_$controller_){
// The injector unwraps the underscores (_) from around the parameter names when matching
$controller = _$controller_;
}));
it('testing the version number', function() {
var $scope = {};
var controller = $controller('AboutController', { $scope: $scope });
var version = 0;
console.log($scope.version);
expect($scope.version).toContain("1.");
});
});
this is working, but when i change the line $scope.version = '1.1.0'; to $scope.version = Version.getVersion(); i receive a promise and i can not rly check it....i tried to resolve it with "then()" function via $scope.version.then(...)but that did not work...what to do?
edit:
the following error occures:
Expected e({ $promise: Object({ $$state: Object({ status: 0 }) }), $resolved: false }) to contain '1.'.
at Object.<anonymous>
and the Service:
kpi.VersionFactory = function ($resource, appConfig) {
var Version = $resource(thePath, {}, {
"getVersion": {
method: "GET",
url: thePath,
isArray: false
}
});
return Version;
};
you need to pass a callback into your test case .
kpi.AboutKPIController = function ($scope, Version) {
$scope.loadVersion= function () {
Version.getVersion().then(function(res){
$scope.version = res.data;
})
}
$scope.loadVersion();
};
describe('dashboard: AboutKPIController', function() {
beforeEach(module('dashboard'));
var $controller;
beforeEach(inject(function(_$controller_){
// The injector unwraps the underscores (_) from around the parameter names when matching
$controller = _$controller_;
}));
it('testing the version number', function(done) {
var $scope = {};
var controller = $controller('AboutKPIController', { $scope: $scope });
var version = 0;
console.log($scope.version);
$scope.loadVersion().then(function(res)){
//can test here
expect($scope.version).toContain("1.");
done();
});
});
});
I expect Version.getVersion(); returns a promise. Your controller implemnentation should look like
function loadVersion() {
Version.getVersion().then(function(version) {
$scope.version = version;
});
}
In your test code use $scope.$apply to resolve promises before you perform except.
beforeEach(inject(function(_$controller_, _$rootScope_){
$controller = _$controller_;
$rootScope = _$rootScope_;
}));
it('testing the version number', function() {
var $scope = $rootScope.$new();
var controller = $controller('AboutKPIController', { $scope: $scope });
controller.loadVersion();
$scope.$apply();
expect($scope.version).toContain("1.");
});
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);
});
});