I've defined the following service in my angular app :
services.factory('MyService', ['Restangular', function (Restangular) {
return {
events : { loading : true },
retrieveQuotes : function() {
return Restangular.all('quotes').getList().then(function() {
return { hello: 'World' };
});
}
};
}]);
and I'm writing the following spec to test it :
describe("MyService", function () {
beforeEach(module('MyApp'));
beforeEach(module("restangular"));
var $httpBackend, Restangular, ms;
beforeEach(inject(function (_$httpBackend_, _Restangular_, MyService) {
ms = MyService;
$httpBackend = _$httpBackend_;
Restangular = _Restangular_;
}));
it("retrieveQuotes should be defined", function () {
expect(ms.retrieveQuotes).toBeDefined();
});
it("retrieveQuotes should return array of quotes", function () {
$httpBackend.whenGET("internalapi/quotes").respond({ hello: 'World' });
ms.retrieveQuotes();
$httpBackend.flush();
});
});
Whenever I run the tests, the first test passes but the second test produces the error :
Error: Unexpected request: GET /internalapi/quotes
What am I doing wrong?
EDIT:
It turned out I'd configured Restangular like so ... RestangularProvider.setBaseUrl("/internalapi");. But I was faking calls to internalapi/quotes. Notice the lack of the "/". Once I added the slash /internalapi/quotes all was good :)
You need to tell $httpBackend to expect a GET request.
describe("MyService", function () {
beforeEach(module('MyApp'));
beforeEach(module("restangular"));
var Restangular, ms;
beforeEach(inject(function (_Restangular_, MyService) {
ms = MyService;
Restangular = _Restangular_;
}));
it("retrieveQuotes should be defined", function () {
expect(ms.retrieveQuotes).toBeDefined();
});
it("retrieveQuotes should return array of quotes", inject(function ($httpBackend) {
$httpBackend.whenGET("internalapi/quotes").respond({ hello: 'World' });
//expect a get request to "internalapi/quotes"
$httpBackend.expectGET("internalapi/quotes");
ms.retrieveQuotes();
$httpBackend.flush();
}));
});
Alternatively you can put your respond() on your expectGET(). I prefer to put my whenGET() statements in a beforeEach() that way I do not have to define the response within every test.
//expect a get request to "internalapi/quotes"
$httpBackend.expectGET("internalapi/quotes").respond({ hello: 'World' });
ms.retrieveQuotes();
$httpBackend.flush();
I had the same problem as you guys. My solution was to add a '/' at the start of the URL-parameter of the .expectGET. Using your example:
$httpBackend.expectGET("/internalapi/quotes").respond({ hello: 'world'})
Best of luck
Related
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 am new to angularjs testing and have been trying to run this test but it fails with same error again and again. I have viewed questions here and read the docs but haven't got to the cause of this error.
A help will be greatly appreciated. Thanks in advance.
my service.js
'use strict';
var services = angular.module('services',['ngResource'])
services.factory('callAppsList',['$resource',function($resource){
return $resource('/api/apps/:appId', {}, {
query: {method:'GET', isArray:false},
get: {method:'GET', isArray:false},
});
}])
serviceSpec.js
//serviceSpec testing
describe("Testing service", function() {
beforeEach(module('services'));
var service, $httpBackend, response;
var url = 'http://0.0.0.0:5000/api/apps/a365cc3520c7a70a553e95ee354670264'
beforeEach(inject(function( _$httpBackend_, callAppsList) {
$httpBackend = _$httpBackend_;
res = { msg :'name'};
service = callAppsList;
}));
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
//tests
it("callAppsList should be defined", function () {
expect(service).toBeDefined();
});
it('should send a ping and return the response',(function () {
res = service.get({appId: 'a365cc3520c7a70a553e95ee354670264'});
$httpBackend.whenGET(url).respond(res);
$httpBackend.expectGET(url)
$httpBackend.flush();
//expect(res.msg).toEqual('name');
}));
});
the first test (when I am testing if it is defined passes) but the next one fails.
error :
Error: Unexpected request: GET /api/apps/a365cc3520c7a70a553e95ee354670264
Expected GET http://0.0.0.0:5000/api/apps/a365cc3520c7a70a553e95ee354670264 in /home/anurag/anurag/projects/betablide/applunge/glide/static/test/lib/angular-mocks.js (line 1179)
A flask server is running in another terminal.
Please let me know what I am doing wrong here and how to proceed further.
As mentioned in the comments, changing the url worked out. I have also changed new lines in the spec file. Hope this may help others.
//serviceSpec testing
describe("Testing service", function() {
beforeEach(module('services'));
var service, $httpBackend, response;
var url = '/api/apps/a365cc3520c7a70a553e95ee354670264'
beforeEach(inject(function( _$httpBackend_, callAppsList) {
$httpBackend = _$httpBackend_;
service = callAppsList;
}));
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
//tests
it("callAppsList should be defined", function () {
expect(service).toBeDefined();
});
it('should send a ping and return the response',(function () {
res = service.get({appId: 'a365cc3520c7a70a553e95ee354670264'});
$httpBackend.whenGET(url).respond({status: 200});
//explicitly flushes pending requests
$httpBackend.flush();
expect(res.status).toEqual(200);
}));
});
I'm new to AngularJS and running into some problems with unit testing. I've seen countless examples of mocking $httpBackend calls, but when I do that it won't work unless I also include $rootScope.$apply().
My service:
angular.module('myApp.services', ['ngResource'])
.factory('TestingService', ['$resource', function($resource) {
return $resource('/api/v1/values', {}, {
getValues: {
method: 'GET'
}
});
}]);
My unit test:
describe('Testing services', function() {
beforeEach(module('myApp.services'));
afterEach(function() {
inject(function($httpBackend) {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
});
describe('TestingService', function() {
it('would be nice to get an explanation for this',
inject(['$rootScope', '$httpBackend', 'TestingService',
function ($rootScope, $httpBackend, testingService) {
$httpBackend.expectGET('/api/v1/values').respond(100);
var result = testingService.getValues();
//$rootScope.$apply();
$httpBackend.flush();
expect(result).toBe(100);
alert(result);
}])
);
});
});
When Karma runs the test like this I get:
Error: No pending request to flush !
Error: Unsatisfied requests: GET /api/v1/values
And if I include the $rootScope.$apply(); I'll get this (and the alert of course also prints out a $promise):
Expected { $promise : { then : Function, catch : Function, finally : Function }, $resolved : true } to be 100.
Can anyone explain why I need "$rootScope.$apply();" to pass the expectGET?
And why the response is a promise instead of the mock response I've specified?
Found the problem after some sleep. Simple one fortunately.
I'm using Angular version 1.3.0-beta.2, but had an older version for angular-mocks. Updating the versions removes the need for "$root.$apply();".
The updated working test:
describe('Testing services', function() {
beforeEach(function(){
module('myApp.services');
this.addMatchers({
toEqualData: function(expected) {
return angular.equals(this.actual, expected);
}
});
});
afterEach(function() {
inject(function($httpBackend) {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
});
describe('TestingService', function() {
it('should work',
inject(['$rootScope', '$httpBackend', 'TestingService',
function ($rootScope, $httpBackend, testingService) {
$httpBackend.expectGET('/api/v1/values').respond( { key: 'value' } );
var result = testingService.getValues();
$httpBackend.flush();
expect(result).toEqualData( { key: 'value' } );
alert(angular.toJson(result, true));
}])
);
});
});
I would like to unit test the following AngularJs service:
.factory('httpResponseInterceptor', ['$q', '$location', '$window', 'CONTEXT_PATH', function($q, $location, $window, contextPath){
return {
response : function (response) {
//Will only be called for HTTP up to 300
return response;
},
responseError: function (rejection) {
if(rejection.status === 405 || rejection.status === 401) {
$window.location.href = contextPath + '/signin';
}
return $q.reject(rejection);
}
};
}]);
I have tried with the following suite:
describe('Controllers', function () {
var $scope, ctrl;
beforeEach(module('curriculumModule'));
beforeEach(module('curriculumControllerModule'));
beforeEach(module('curriculumServiceModule'));
beforeEach(module(function($provide) {
$provide.constant('CONTEXT_PATH', 'bignibou'); // override contextPath here
}));
describe('CreateCurriculumCtrl', function () {
var mockBackend, location, _window;
beforeEach(inject(function ($rootScope, $controller, $httpBackend, $location, $window) {
mockBackend = $httpBackend;
location = $location;
_window = $window;
$scope = $rootScope.$new();
ctrl = $controller('CreateCurriculumCtrl', {
$scope: $scope
});
}));
it('should redirect to /signin if 401 or 405', function () {
mockBackend.whenGET('bignibou/utils/findLanguagesByLanguageStartingWith.json?language=fran').respond([{"description":"Français","id":46,"version":0}]);
mockBackend.whenPOST('bignibou/curriculum/new').respond(function(method, url, data, headers){
return [401];
});
$scope.saveCurriculum();
mockBackend.flush();
expect(_window.location.href).toEqual("/bignibou/signin");
});
});
});
However, it fails with the following error message:
PhantomJS 1.9.2 (Linux) Controllers CreateCurriculumCtrl should redirect to /signin if 401 or 405 FAILED
Expected 'http://localhost:9876/context.html' to equal '/bignibou/signin'.
PhantomJS 1.9.2 (Linux) ERROR
Some of your tests did a full page reload!
I am not sure what is going wrong and why. Can anyone please help?
I just want to ensure the $window.location.href is equal to '/bignibou/signin'.
edit 1:
I managed to get it to work as follows (thanks to "dskh"):
beforeEach(module('config', function($provide){
$provide.value('$window', {location:{href:'dummy'}});
}));
You can inject stub dependencies when you load in your module:
angular.mock.module('curriculumModule', function($provide){
$provide.value('$window', {location:{href:'dummy'}});
});
To get this to work for me I had to make a minor adjustment. It would error out and say:
TypeError: 'undefined' is not an object (evaluating '$window.navigator.userAgent')
So I added the navigator.userAgent object to get it to work for me.
$provide.value('$window', {
location:{
href:'dummy'
},
navigator:{
userAgent:{}
}
});
I faced the same problem, and went a step further in my solution. I didn't just want a mock, I wanted to replace $window.location.href with a Jasmine spy for the better ability to track changes made to it. So, I learned from apsiller's example for spying on getters/setters and after creating my mock, I was able to spy on the property I wanted.
First, here's a suite that shows how I mocked $window, with a test to demonstrate that the spy works as expected:
describe("The Thing", function() {
var $window;
beforeEach(function() {
module("app", function ($provide) {
$provide.value("$window", {
//this creates a copy that we can edit later
location: angular.extend({}, window.location)
});
});
inject(function (_$window_) {
$window = _$window_;
});
});
it("should track calls to $window.location.href", function() {
var hrefSpy = spyOnProperty($window.location, 'href', 'set');
console.log($window.location.href);
$window.location.href = "https://www.google.com/";
console.log($window.location.href);
expect(hrefSpy).toHaveBeenCalled();
expect(hrefSpy).toHaveBeenCalledWith("https://www.google.com/");
});
});
As you can see above, the spy is generated by calling the below function: (it works for both get and set)
function spyOnProperty(obj, propertyName, accessType) {
var desc = Object.getOwnPropertyDescriptor(obj, propertyName);
if (desc.hasOwnProperty("value")) {
//property is a value, not a getter/setter - convert it
var value = desc.value;
desc = {
get: function() { return value; },
set: function(input) { value = input; }
}
}
var spy = jasmine.createSpy(propertyName, desc[accessType]).and.callThrough();
desc[accessType] = spy;
Object.defineProperty(obj, propertyName, desc);
return spy;
}
Lastly, here's a fiddle demonstrating this in action. I've tested this against Angular 1.4, and Jasmine 2.3 and 2.4.
I am trying to begin writing unit tests for my angular application and hit a stopping block pretty quick as I am unsure of how exactly to mock my service in a testable way.
Is there a way to mock the REST call otherwise it would seem like I need to mirror everything within my service in my tests which doesn't seem right to me, but I am rather new to test writing so maybe this is how it is supposed to be accomplished. Any help would be greatly appreciated.
My service is as follows:
angular.module('resources.users', ['ngResource'])
.factory('User', function($resource) {
var resource = $resource('/api/index.php/users/:username', {}, {
'update': {method: 'PUT'}
});
resource.getUser = function(username, successCb) {
return resource.query({username: username}, successCb);
};
return resource;
});
My test consists thus far of:
describe('User', function() {
var mockUserResource;
beforeEach(module('resources.users'));
beforeEach(function() {
mockUserResource = sinon.stub({
getUser: function(username) {
mockUserResource.query({username: username});
},
query: function() {}
});
module(function($provide) {
$provide.value('User', mockUserResource);
})
});
describe('getUser', function() {
it('should call getUser with username', inject(function(User) {
User.getUser('test');
expect(mockUserResource.query.args[0][0]).toEqual({username: 'test'});
}));
})
});
You can mock the requests made by ngResource like this:
describe('User', function () {
var mockUserResource, $httpBackend;
beforeEach(angular.mock.module('myApp'));
beforeEach(function () {
angular.mock.inject(function ($injector) {
$httpBackend = $injector.get('$httpBackend');
mockUserResource = $injector.get('User');
})
});
describe('getUser', function () {
it('should call getUser with username', inject(function (User) {
$httpBackend.expectGET('/api/index.php/users/test')
.respond([{
username: 'test'
}]);
var result = mockUserResource.getUser('test');
$httpBackend.flush();
expect(result[0].username).toEqual('test');
}));
});
});
Demo
zsong's answer greatly helped me understand this, but I would like to expand on how it works. In case it gets edited, I list the code again here:
describe('User', function () {
var mockUserResource, $httpBackend;
beforeEach(angular.mock.module('myApp'));
beforeEach(function () {
angular.mock.inject(function ($injector) {
$httpBackend = $injector.get('$httpBackend');
mockUserResource = $injector.get('User');
})
});
describe('getUser', function () {
it('should call getUser with username', inject(function (User) {
$httpBackend.expectGET('/api/index.php/users/test')
.respond([{
username: 'test'
}]);
var result = mockUserResource.getUser('test');
$httpBackend.flush();
expect(result[0].username).toEqual('test');
}));
});
});
What's going on here?
1
beforeEach(angular.mock.module('myApp'));
We tell the Angular injector ($injector and angular.mock.inject) to inject things defined in the myApp module. You can think of it as defining a module dependency without a dependent module. Compare with how things defined in the myApp module can be injected in, say, a controller in a angular.module('myOtherApp', ['myApp']) module.
2
beforeEach(function () {
angular.mock.inject(function ($injector) {
$httpBackend = $injector.get('$httpBackend');
mockUserResource = $injector.get('User');
})
});
Before each spec, run the function ($injector) function with dependencies injected. In this case, the dependency ($injector) is resolved implicitly from the parameter name. A functionally equivalent variant of this snippet is
beforeEach(function () {
angular.mock.inject(['$httpBackend', 'User', function ($httpB, User) {
$httpBackend = $httpB;
mockUserResource = User;
}]);
});
Here we have instead declared the dependencies explicitly, and are free to use any parameter names we wish.
3
it('should call getUser with username', inject(function (User) {
Again, the test function is injected with the implicitly resolved User service as a parameter, though it isn't actually used.
Notice that this time there is no wrapper function around the inject call. inject invokes the passed function immediately if a spec is currently running, but otherwise it returns a wrapper function (see the inject docs and source code), so we don't actually need the wrapper function. Thus, we could have written the beforeEach snippet above like this:
beforeEach(angular.mock.inject(function ($injector) {
$httpBackend = $injector.get('$httpBackend');
mockUserResource = $injector.get('User');
}));