I want to make a test of a service in AngularJS but there is always a problem with the injector.
Here is the service:
var projectApp = angular.module("projectApp", []);
projectApp.service("workTimeService", function(){
this.workHours = function(workHours){
return workHours;
}
}
Here is the test:
describe("Unit: Testing services", function(){
var workTimeService;
beforeEach(function(){
module("projectApp");
});
beforeEach(inject(function(_workTimeService_){
workTimeService = _workTimeService_;
}));
it('Should have funtion', function(){
expect(angular.isFunction(workTimeService.workHours)).toBe(true);
});
});
I have made it just as in the tutorials but jasmine says that workTimeService is undefined.
Related
I have to unit test my controller. First I have to create mock for my services.
Here is my service:
angular.module("demo-app")
.factory("empService",function($http){
var empService={};
empService.getAllEmployees=function(){
return $http.get("http://localhost:3000/api/employees");
}
empService.postEmployee=function(emp){
return $http.post("http://localhost:3000/api/employees",emp);
}
empService.getEmployee=function(id){
return $http.get("http://localhost:3000/api/employees/"+id)
}
empService.putEmployee=function(emp){
return $http.put("http://localhost:3000/api/employees/"+emp._id,emp)
}
empService.deleteEmployee=function(id){
return $http.delete("http://localhost:3000/api/employees/"+id);
}
empService.findEmployee=function(emp){
return $http.post("http://localhost:3000/api/employees/search",emp);
}
return empService;
})
Here is findData() method in my controller, which I am going to test:
$scope.findData=function(){
$scope.loadingEmployee=true;
var emp={};
listProp=Object.getOwnPropertyNames($scope.searchEmployee);
for(index in listProp){
if($scope.searchEmployee[listProp[index]]!=""){
emp[listProp[index]]=$scope.searchEmployee[listProp[index]];
}
}
console.log(emp);
empService.findEmployee(emp).then(function(data){
$scope.allEmployees=data.data;
console.log(data.data);
$scope.loadingEmployee=false;
});
}
How can I mock my empService.findEmployee(emp) method, so that I can test the findData() method.
My spec.js test file with mocking my service method. Here it is:
beforeEach(function(){
var emp={"name":"sanjit"};
fakeService={
getAllEmployees:function(emp){
def=q.defer();
def.resolve({data:[{"name":"sanjit"},{'name':'ssss'}]});
return def.promise;
},
findEmployee:function(emp){
var def=q.defer();
def.resolve({data:[{"name":"sanjit"}]});
console.log("working");
return def.promise;
}
};
spyOn(fakeService,'findEmployee').and.callThrough();
fakeService.findEmployee(emp);
});
beforeEach(angular.mock.inject(function($rootScope,$controller,$injector,$q){
httpBackend=$injector.get('$httpBackend');
scope=$rootScope.$new();
q=$q;
ctrl=$controller('adminEmployeeCtrl',{$scope:scope,empService:fakeService});
}));
it('findData test',function(){
scope.$apply();
scope.findData();
expect(scope.loadingEmployee).toEqual(false);
})
But I got another error:
Error: Unexpected request: GET dashboard/views/dashboard-new.html
No more request expected
But I didn't call it. Please help me
You may not have manually called GET dashboard/views/dashboard-new.html but $scope.$apply() might be triggering it somehow and you can't do anything but handle it.
You can do something like this to handle it: (after injecting it using _$httpBackend_ and assigning to $httpBackend in beforeEach)
$httpBackend.when('GET', 'dashboard/views/dashboard-new.html').respond(200);
scope.$digest();
$httpBackend.flush();
One of the most important rules when testing controllers in angularjs is you do not need to create reall http requests, just mock the functions in that service that are used by your controller. So you need to spyOn them and call fake function to return the proper value. Let's spy on one of them
/**
* #description Tests for adminEmployeeCtrl controller
*/
(function () {
"use strict";
describe('Controller: adminEmployeeCtrl ', function () {
/* jshint -W109 */
var $q, $scope, $controller;
var empService;
var errorResponse = 'Not found';
var employeesResponse = [
{id:1,name:'mohammed' },
{id:2,name:'ramadan' }
];
beforeEach(module(
'loadRequiredModules'
));
beforeEach(inject(function (_$q_,
_$controller_,
_$rootScope_,
_empService_) {
$q = _$q_;
$controller = _$controller_;
$scope = _$rootScope_.$new();
empService = _empService_;
}));
function successSpies(){
spyOn(empService, 'findEmployee').and.callFake(function () {
var deferred = $q.defer();
deferred.resolve(employeesResponse);
return deferred.promise;
// shortcut can be one line
// return $q.resolve(employeesResponse);
});
}
function rejectedSpies(){
spyOn(empService, 'findEmployee').and.callFake(function () {
var deferred = $q.defer();
deferred.reject(errorResponse);
return deferred.promise;
// shortcut can be one line
// return $q.reject(errorResponse);
});
}
function initController(){
$controller('adminEmployeeCtrl', {
$scope: $scope,
empService: empService
});
}
describe('Success controller initialization', function(){
beforeEach(function(){
successSpies();
initController();
});
it('should findData by calling findEmployee',function(){
$scope.findData();
// calling $apply to resolve deferred promises we made in the spies
$scope.$apply();
expect($scope.loadingEmployee).toEqual(false);
expect($scope.allEmployees).toEqual(employeesResponse);
});
});
describe('handle controller initialization errors', function(){
beforeEach(function(){
rejectedSpies();
initController();
});
it('should handle error when calling findEmployee', function(){
$scope.findData();
$scope.$apply();
// your error expectations
});
});
});
}());
I am trying to write the test cass for the factory which is returing a JSON response.
But I am getting the error:
Error: [$injector:unpr] http://errors.angularjs.org/1.4.1/$injector/unpr?p0=serviceProvider%20%3C-%20service
at Error (native)
Here is my code:
(function () {
angular.module('uspDeviceService',[]).factory('getDevice', GetDevice);
GetDevice.$inject = ['$http'];
function GetDevice($http) {
getDeviceList = function() {
return $http.get("static/test-json/devices/device-list.json");
}
return {
getDeviceList: getDeviceList
}
}
}());
Code for Test case:
describe('Get Product test', function() {
beforeEach(module('uspDeviceService'));
var service, httpBackend, getDevice ;
beforeEach(function () {
angular.mock.inject(function ($injector) {
//Injecting $http dependencies
httpBackend = $injector.get('$httpBackend');
service = $injector.get('service');
getDevice = $injector.get('getDevice');
})
});
console.log('Injection Dependencies is done');
describe('get Device List', function () {
it("should return a list of devices", inject(function () {
httpBackend.expectGET("static/test-json/devices/device-list.json").respond("Response found!");
httpBackend.flush();
}))
})
});
I am new to Angular Unit testing, can anyone please help me, where I am going wrong..
Two things that jump out at me:
Your angular.module declaration is defining a module, not getting the module. I would encourage you to split that up so that it's a fair bit more clear what your intent is.
angular.module('uspDeviceService', []);
angular.module('uspDeviceService').factory('getDevice', GetDevice);
It likely works as-is, but clarity is important.
What is...service? It's not defined anywhere in your code, and Angular can't find it either, hence the error message. You may be looking to get getDevice instead. Also, name your test variable with respect to what it actually is, so you don't confuse yourself.
// defined above
var getDevice;
// while injecting
getDevice = $injector.get('getDevice');
Supposing that you have an angularjs controller myController defined in myModule. The controller do some action when the api call is success and shows a flash message when api returns success = false. The your controller code would be something like
angular.module('myModule')
.controller( 'myController', function ( $scope,flashService, Api ) {
Api.get_list().$promise.then(function(data){
if(data.success) {
$scope.data = data.response
}
else{
flashService.createFlash(data.message, "danger");
}
});
});
Now to test both success = true and success = false we
describe('myController', function(){
var $rootScope, $httpBackend, controller, flashService;
var apilink = 'http://apilink';
beforeEach(module('myModule'));
beforeEach(inject(function(_$httpBackend_,_$rootScope_, _$controller_, _flashService_) {
$rootScope = _$rootScope_;
$httpBackend = _$httpBackend_;
flashService = _flashService_;
controller = _$controller_("myController", {$scope: $rootScope});
}));
it('init $scope.data when success = true', function(){
$httpBackend.whenGET(apilink)
.respond(
{
success: true,
response: {}
});
$httpBackend.flush();
expect($rootScope.data).toBeDefined();
});
it('show flash when api request failure', function(){
spyOn(flashService, 'createFlash');
$httpBackend.whenGET(apilink)
.respond(
{
success: false
});
$httpBackend.flush();
expect(flashService.createFlash).toHaveBeenCalled();
});
});
You are always going to mock the response because here we are testing the javascript code behaviour and we are not concerned with the Api. You can see when success the data is initialized and when success is false createFlash is called.
As far as test for factory is concerned you can do
describe('Get Product test', function() {
beforeEach(module('uspDeviceService'));
var service, httpBackend, getDevice ;
beforeEach(function () {
inject(function ($injector) {
httpBackend = $injector.get('$httpBackend');
service = $injector.get('service');
getDevice = $injector.get('getDevice');
});
});
describe('get Device List', function () {
it("should return a list of devices", inject(function () {
httpBackend.expectGET("static/test-json/devices/device- list.json").respond("Response found!");
var result = getDevice.getDeviceList();
httpBackend.flush();
expect(result).toEqual('Response found!');
}));
});
});
I can't get these two spec files to play well with each other. I didn't think spec files would effect other spec files but in this case it seem like they do, it makes no sense to me.
I'm using Jasmine and Karma the tests are automated with Gulp
The error I'm getting is "Unknown provider: ProductServiceProvider <- ProductService"
I have changed the tests to troubleshoot the issue here is the simple versions.
If I comment out the following line in file 2 both files pass.
angular.module('eu.product.service', []);
It has something to do with mocking the module but I can't figure out what I'm doing wrong here.
spec file 1
describe('Testing euProduct', function(){
var $factory;
var $httpBackend;
beforeEach(function () {
//modules
module('eu.product.service');
//injections
inject(function($injector){
$factory = $injector.get('ProductService');
$httpBackend = $injector.get('$httpBackend');
});
//mock data
$httpBackend.when('GET', '/Mercury/product/list/0/0?PrimaryCategoryID=0&pageSize=20&startPage=1').respond({
"data":
[{
"recallid":45,
}]
});
});
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
//-----Tests----
it('Should be able to get data from the server on default parameters.', function(){
$factory.list({},function(data){
expect(data.data[0].recallid).toBe(45);
});
$httpBackend.flush();
});
});
Spec file 2
'use strict';
describe('Testing euProduct Logic', function(){
//variables in closure scope so they can be used in tested but set with injection in beforeEach
var $factory;
//mocking a module :: http://www.sitepoint.com/mocking-dependencies-angularjs-tests/
beforeEach(function () {
angular.module('eu.product.service',[]);
module(function($provide) {
$provide.factory('ProductService', function() {
// Mocking utilSvc
return {
list : function(para, callback){
callback({
data : {
product : 'The product Name'
}
})
}
};
});
$provide.service('storageSvc', function() {
// Mocking storageSvc
});
});
//modules
module('eu.product.logic');
//injections
inject(function($injector){
$factory = $injector.get('ProductLogic');
});
});
//-----Tests----
it('Should be able to run tests', function(){
expect(2).toBe(2);
});
});
Both module and inject from angular-mocks return functions which need to be called.
In the following example I made these changes:
Refactor to a basic working example
Don't define custom $-prefixed variables. These are reserved by angular.
Use inject to inject instead of $injector.
Add some comments for further explanation.
describe('ProductService', function() {
var ProductService;
var $httpBackend;
// Start module config phase.
beforeEach(module('eu.produce.service', function($provide) {
// Inject providers / override constants here.
// If this function is empty, it may be left out.
}))
// Kickstart the app and inject services.
beforeEach(inject(function(_ProductService_, _$httpBackend_){
ProductService = _ProductService_;
$httpBackend = _$httpBackend_;
});
beforeEach(function() {
// Optionally use another beforeEach block to setup services, register spies, etc.
// This can be moved inside of the inject function as well if you prefer.
//mock data
$httpBackend.when('GET', '/Mercury/product/list/0/0?PrimaryCategoryID=0&pageSize=20&startPage=1').respond({
"data":
[{
"recallid":45,
}]
});
});
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
//-----Tests----
it('Should be able to get data from the server on default parameters.', function(){
ProductService.list({},function(data){
expect(data.data[0].recallid).toBe(45);
});
$httpBackend.flush();
});
});
This is my first question here. I'll try to do my best. I searched a lot before posting
I'm developing an angularJS application relying on the Javascript ParseSDK.
I have convinced myself to dive in testing recently, so I am a beginner.
I have this factory UserFactory that wraps around the SDK so everything is clean and modular the Angular way. ie: SDK is only used through factories (not controller nor directives).
It goes like this:
myModule.factory('UserFactory', ['$q', function($q){
var User = Parse.User.extend({
// Instance methods
},{
// static/class methods
// Overrides Parse.User.current() to wrap current User in promise
current: function(){
var deferred = $q.defer();
var currentUser = Parse.User.current();
if(currentUser)
deferred.resolve(currentUser);
else
deferred.reject("No current user");
return deferred.promise;
}
});
return User;
}]);
My question: How to test for UserFactory.current() knowing it uses an external service?
I've looked into mocking the Parse SDK but don't know how to do it since it's not angular related (ie: can't use httpBackend).
My current test file:
describe("Unit: UserFactory", function(){
var UserFactory;
beforeEach(function(){
module("myModule");
inject(function(_UserFactory_){
UserFactory = _UserFactory_;
});
});
it("should return current User", function(){
// What to expect ?
});
});
Thank you in advance
describe("Unit: UserFactory", function(){
var UserFactory;
beforeEach(function(){
module("myModule");
inject(function(_UserFactory_){
UserFactory = _UserFactory_;
$rootScope = _$rootScope_;
});
});
describe('current()', function() {
var successCallback, errorCallback;
beforeEach(function() {
successCallback = jasmine.createSpy('success');
errorCallback = jasmine.createSpy('error');
});
it("promise should resolve if Parse.User.current is truthy", function(){
spyOn(Parse.User, 'current').and.returnValue(true);
UserFactory.current().then(successCallback, errorCallback);
$rootScope.$digest();
expect(successCallback.calls.count()).toBe(1);
expect(errorCallback.calls.count()).toBe(0);
expect(Parse.User.current).toHaveCalledOnce();
});
it("promise should reject if Parse.User.current is falsy", function(){
spyOn(Parse.User, 'current').and.returnValue(false);
UserFactory.current().then(successCallback, errorCallback);
$rootScope.$digest();
expect(errorCallback.calls.count()).toBe(1);
expect(successCallback.calls.count()).toBe(0);
expect(Parse.User.current).toHaveCalledOnce();
});
});
});
I am developing angularjs app and i have defined app configuration data. When i unit test my controller i am getting error if i remove app configuration unit tests are running with out errors.
My code is here
app.js
var signup = angular.module('Signup', []);
signup.config("ajaxURL",{"signupSubmit": "/signup/submit","signupCheckEmailAvailability": "/signup/checkemail"});
signup.factory('SignupService',['$http','ajaxURL',function($http,URL){
return {
submit: function(signupData){
console.log("in submit service--");
console.log(signupData);
var promise = $http.post((URL.signupSubmit).toString(), signupData).then(function(response){
return response;
});
return promise;
},
checkEmailAvailability: function(emailData){
var promise = $http.post((URL.signupCheckEmailAvailability).toString(),emailData).then(function(response){
return response;
});
return promise;
}
};
}]);
signup.controller('SignupCtrl',['SignupService', '$scope',function(Signup, $scope){
$scope.signupPromise;
$scope.submitSignupForm = function(signupData){
$scope.signupPromise = Signup.submit(signupData);
signupSubmitEvent();
}
function signupSubmitEvent(){
$scope.signupPromise.then(function(response){
console.log("http response");
});
}
}]);
appSpec.js
'use strict';
describe('signup unit tests', function() {
var signup, scope, $httpBackend, ctrl;
var userData = {"userid":"2","email":"xxxx#sss.com","clientId":"123456789","clientSecret":"a1b2c3d4e5f6","accessToken":"AP16MD3217"};
beforeEach(module('Signup'));
beforeEach(inject(function($injector, _$rootScope_, $controller, _SignupService_){
$httpBackend = $injector.get('$httpBackend');
scope = _$rootScope_.$new();
var Signup = _SignupService_;
ctrl = $controller('SignupCtrl', {Signup: Signup, $scope: scope});
}));
it("simple testing", function(){
console.log("in simple test");
$httpBackend.expectPOST("/signup/submit/", userData).respond([{name: 'Nexus S'}, {name: 'Motorola DROID'}]);
$httpBackend.flush();
scope.submitSignupForm(userData);
});
});
Can any on help me how to add configuration data to the unit test?
If you want test your services/controllers without running the config section of your app you need to put the controllers and services into a separate module. e.g.:
var signup = angular.module('Signup', ['Signup.services', 'Signup.ctrls']);
signup.config("ajaxURL",{"signupSubmit": "/signup/submit","signupCheckEmailAvailability": "/signup/checkemail"});
var signupServices = angular.module('Signup.services', []);
signupServices.factory('SignupService',['$http','ajaxURL',function($http,URL){
return {
...
};
}]);
var signupControllers = angular.module('Signup.ctrls', ['Signup.services']);
signupControllers.controller('SignupCtrl',['SignupService', '$scope',function(Signup, $scope){
...
}]);
Then in your test you can just load up the module you want to test. e.g.:
beforeEach(module('Signup.services'));