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
});
});
});
}());
Related
I would like to test my then and catch function from my $scope.customerinfo. The problem is i dont know how exactly.
var app = angular.module('shop', ['ngRoute','ngResource'])
.factory('Customerservice', function ($resource) {
return $resource('http://localhost:8080/Shop/:customer',{customer: "#customer"});
})
.controller('customerController', function ($scope,Customerservice) {
$scope.customerinfo = CustomerService.get({customer: "Mark"});
$scope.customerinfo.$promise.then(function(info) {
return info;
}).catch(function(errorResponse) {
throw errorResponse;
});
});
Im not done yet but this is my jasmine code
describe('Testing the customerinfo', function () {
var $scope;
var $q;
var deferred;
beforeEach(module('shop'));
beforeEach(inject(function($controller, _$rootScope_, _$q_) {
$q = _$q_;
$scope = _$rootScope_.$new();
deferred = _$q_.defer();
$controller('userController', {
$scope: $scope
});
}));
it('should reject promise', function () {
// I want to check if the catch option is working
});
});
So how exactly can i do this, or do i need to refactor the code?
The jasmine 'it' method takes a done parameter that you can call for async testing
it('Should reject', function(done) {
someAsyncFunction().catch(function(result) {
expect(result.status).toBe(401);
done();
});
});
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 want to test the following method in my controller class:
// getIds() {
// this.api.getIds()
// .then((response)=> {
// this.ids = response.data;
// this.doSomethingElse();
// });
// }
I'm not sure how to handle the promise using jasmine and karma. The project is written in ES6. api.getIds() returns a $http.get().
beforeEach(function() {
inject(function($controller, $rootScope, _api_) {
vm = $controller('MainController', {
api: _api_,
$scope:$rootScope.$new()
});
});
});
beforeEach(function () {
vm.getIds();
});
it('should set the ids', function () {
expect(vm.ids).toBeDefined(); //error
});
How do I wait for the promise to complete before running the expect() ?
First of all, you should use the done callback provided by the jasmine; see async support in Jasmine.
Then, you should mock your getIds on the api so that it returns a resolved promise with an expected value. The asserts should be done after the then promise is called - se bellow the full example.
beforeEach(function () {
var $q, vm, api, $controller, $rootScope;
inject(function (_$controller_, _$rootScope_, _$q_) {
$q = _$q_;
$controller = _$controller_;
$rootScope = _$rootScope_;
api = jasmine.createSpyObj('api', ['getIds']);
api.getIds.and.returnValue($q.when([]));
vm = $controller('MainController', {
api: api,
$scope: $rootScope.$new()
});
});
});
it('should set the ids', function (done) {
vm
.getIds()
.then(function (ids) {
expect(ids).toBeDefined();
// add more asserts
done();
});
});
As a side note, if the this.doSomethingElse(); is a promise too, you have to return it in the first then so that you can test the final result.
I'd like to know which would be the best way to test functions that returns nothing(just changes a field value) and contains an async call.
This the AngularJS controller I want to test, the service I call returns a promise(always returns {name:"John"}):
app.controller('MyCtrl', function($scope, AsyncService) {
$scope.greeting = "";
$scope.error =
$scope.sayHello = function() {
AsyncService.getName().then(
function(data){
$scope.saludo = "Hello " + data.name;
},
function(data){
$scope.error = data;
}
);
};
});
This would be the spec if the sayHello function did not contain an async call, but it always fails because scope.greeting is always empty.
describe('Test My Controller', function() {
var scope, $httpBackend;
// Load the module with MainController
//mock Application to allow us to inject our own dependencies
beforeEach(angular.mock.module('app'));
//mock the controller for the same reason and include $rootScope and $controller
beforeEach(angular.mock.inject(function($rootScope, $controller,_$httpBackend_){
//Mock the service to always return "John"
$httpBackend = _$httpBackend_;
$httpBackend.when('POST', 'http://localhost:8080/greeting').respond({name: "John"});
//create an empty scope
scope = $rootScope.$new();
//declare the controller and inject our empty scope
$controller('MyCtrl', {$scope: scope});
}));
it('$scope.greeting should get filled after sayHello', function() {
expect(scope.greeting).toEqual("");
scope.sayHello();
expect(scope.greeting).toEqual("Hello John");
});*/
});
How would I make this spec to handle the async call? I don't really understand how and where to use the "done" flag of Jasmine 2.0.
Use $q.defer() to return a promise from the getName function in a mock of your service. Then pass the mocked into the dependancies when your controller is created:
beforeEach(inject(function($controller, _$rootScope_, $q) {
$rootScope = _$rootScope_;
deferred = $q.defer();
asyncService = {
getName: function () {
}
};
spyOn(asyncService, 'getName').and.returnValue(deferred.promise);
$scope = $rootScope.$new();
createController = function() {
return $controller('MyCtrl', { $scope: $scope, AsyncService: asyncService } );
};
}));
Then after you call $scope.hello() call deferred.resolve(data)l where data is the data that you want returned from your service in the promise. Then call $rootScope.$digest();
it('$scope.saludo should get filled after sayHello', function() {
//Arrange
var controller = createController();
var data = {
name: 'John'
};
//Act
$scope.sayHello();
deferred.resolve(data);
$rootScope.$digest();
//Assert
expect($scope.saludo).toEqual('Hello ' + data.name);
});
Plunkr
I have the following function in my controller:
$scope.submitNote = function(){
myService.addNote($scope.note).then(function(data){
if(data.success === true){
$scope.note = null;
}
else{
// API call failed
}
}, function(){
// Promise call failed
});
};
I set up my testing environment with:
// Mock out fake service
beforeEach(function(){
myService = {
addNote: function(){
deferred = q.defer();
deferred.resolve({
success: true
});
return deferred.promise;
}
};
spyOn(myService, 'addNote').and.callThrough();
});
// Assign controller scope
beforeEach(inject(function($controller, $rootScope, $q){
q = $q;
scope = $rootScope.$new();
$controller('myController', {
$scope: scope,
myService: myService
});
}));
Then test out my submitNote() function with:
describe('submitNote Test', function(){
it('should set scope.note to null after successful service call', function(){
scope.submitNote();
expect(myService.addNote).toHaveBeenCalled();
expect(scope.note).toBe(null);
});
});
The first expect passes, but the second expect does not. It looks like the then() callback from my submitNote() function isn't being called in the test.
How do I make sure the promise callback in the original function is called?
To give you cleaner tests that you have more control over the ngMock module extends various core services so they can be inspected and controlled in a synchronous manner.
Promise callbacks are executed during the digest loop, which in your testing environment you need to start manually.
For example:
describe('submitNote Test', function () {
it('should set scope.note to null after successful service call', function () {
scope.submitNote();
scope.$digest();
expect(myService.addNote).toHaveBeenCalled();
expect(scope.note).toBe(null);
});
});