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);
}));
});
Related
I am working on an angular js app with karma/Jasmine testing framework, I need to test a factory that returns a http promise but it always return undefined
here is my factory
angular.module('GithubUsers').factory('Users',['$http','$q',function($http,$q){
return{
getAllUsers:function(){
var defered= $q.defer();
$http({
url:'https://api.github.com/users',
method:'GET'
}).then(function(users){
defered.resolve(users.data);
},function(err){
defered.reject(err);
})
return defered.promise;
}
}
}])
here is my tests
Update thanks to your answers I modified my code to the following but no I got this error
Possibly unhandled rejection: {"status":0,"config":{"method":"GET","transformRequest":[null],"transformResponse":[null],"jsonpCallbackParam":"callback","url":"https://api.github.com/users?since=1","headers":{"Accept":"application/json, text/plain, /"},"cached":false},"statusText":""} thrown
describe('Test Users Factory',function(){
var $controller,
Users,
$rootScope,
$httpBackend,
$q;
beforeEach(module('GithubUsers'));
beforeEach(inject(function(_$controller_,_Users_,_$rootScope_,_$httpBackend_,_$q_){
$controller = _$controller_;
Users = _Users_;
$rootScope= _$rootScope_;
$httpBackend=_$httpBackend_;
}))
it('should get users',function(){
var result;
$httpBackend.whenGET('https://api.github.com/users?since=1').respond(function(){
return {data:[{id:2}],status:200};
})
Users.getAllUsers().then(function(res){
result = res;
});
$httpBackend.flush();
$rootScope.$digest()
expect(result).toBeTruthy();
})
})
Thanks in advance!
I think you need to pass a function that returns a array with 3 items in it, to whenGET().respond().
Maybe, you can try something like this:
beforeEach(angular.mock.inject(function (User, $httpBackend, $http) {
...
this.withOKUsers = function() {
var i1 = new User();
i1.id = 10;
return [200, JSON.stringify([ i1]), {}];
} ...
}));
...
it('should get users',function(){
$httpBackend
.whenGET('https://api.github.com/users')
.respond(this.withOKUsers);
Users.getAllUsers().then(function(res){
result = res;
});
$httpBackend.flush();
expect(result).not.toBeNull();
...
(I prefer to arrange spec outside of it() clause for better readability)
You're missing a $httpBackend.flush(); call after your test method call. It will invoke a success/error or then part and resolve a $q's promise properly. For more tests I would move a $httpBackend.whenGET to each test case separately so I can later verify it per use case but it's just my personal opinion.
I find it a little suspicious that you mix a $controller and a factory in one test. I would suggest to split them, and in controller test just check the calls to service methods and in a facotry test itself do a $httpBackend stuff.
Below I paste your test with my corrections. It works now for me:
describe('Test Users Factory', function () {
var Users,
$rootScope,
$httpBackend,
$q;
beforeEach(module('app.utils'));
beforeEach(inject(function (_Users_, _$rootScope_, _$httpBackend_, _$q_) {
Users = _Users_;
$rootScope = _$rootScope_;
$httpBackend = _$httpBackend_;
}));
afterEach(function () {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it('should get users', function () {
var result;
$httpBackend.when('GET', "https://api.github.com/users").respond({ data: [{ id: 2 }], status: 200 });
Users.getAllUsers().then(function (res) {
result = res;
expect(result).toBeTruthy();
});
$httpBackend.flush();
$rootScope.$digest();
});
Important notices:
1)afterEach - check if no pending requests remain after your call
2) your url differ with a parameter ?since=1. But you do not give it as a parameter in your code so i do not understand why you added this parameter.
Maybe consider string concatenation with url and parameter ?
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'd like to test that a (component's) controller is sending a GET request to some URL (without caring about the response). I was expecting that
httpBackend.expectGET('/some/random/url');
would spy on the http backend and fail if it did not get the GET request, so I was expecting the following spec to fail:
describe('myApp', function() {
var httpBackend;
beforeEach(module('myApp'));
beforeEach(inject(function($httpBackend) {
httpBackend = $httpBackend;
}));
it('sends a GET request to /some/random/url', function() {
httpBackend.expectGET('/some/random/url');
httpBackend.expect('GET', '/some/random/url');
});
});
But this seems to pass trivially
Starting the Teaspoon server...
Teaspoon running default suite at http://127.0.0.1:56255/teaspoon/default
..
Finished in 0.01200 seconds
2 examples, 0 failures
with this:
angular.module('myApp', []);
So I suppose I am misunderstanding what expectGET is doing and this is not the way the way to check what I am trying to check.
I usually add the following code to any spec (test) files deal with http mocking. This makes sure that the call is flushed and that there are no outstanding expectations / requests.
afterEach(() => {
try {
$httpBackend.flush();
} catch (e) {
}
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
This would change your code like so
describe('myApp', function() {
var httpBackend;
beforeEach(module('myApp'));
beforeEach(inject(function($httpBackend) {
httpBackend = $httpBackend;
}));
afterEach(() => {
try {
httpBackend.flush();
} catch (e) { // entering here is a sign your unit test failed
}
httpBackend.verifyNoOutstandingExpectation();
httpBackend.verifyNoOutstandingRequest();
});
it('sends a GET request to /some/random/url', function() {
httpBackend.expectGET('/some/random/url');
httpBackend.expect('GET', '/some/random/url');
});
});
You forgot to call flush() on httpBackend. That's when it will check that all the expected requests have been received.
I was reading posts related for don't repeat the question.
I have the next unit testing code:
describe('service', function() {
var questionApiService;
beforeEach(module('myApp'));
beforeEach(inject(function (_questionApiService_) {
questionApiService = _questionApiService_;
}));
// Test service availability
it('check the existence of get field question service', inject(function(questionApiService) {
//expect(1).toEqual(100);
questionApiService.getField()
.then(function(data) {
//console.log(data);
expect(1).toEqual(100);
});
}));
});
If I run the code expect(1).toEqual(100); outside the service, the result is Error, but if I write the same code expect(1).toEqual(100); inside the service, the result is Success, which makes me think that the validator is not entering the service.
Whats wrong?
EDIT 1:
Hello Asta, I think ur idea is very good and i'm trying to implement it. I have an error in my code and i don't know how do debugging:
defer = $q.defer();
spyOn(questionApiService, 'getField').andReturn(defer.promise);
defer.resolve(data);
expect(data.nextQ).toEqual(1);
My unit testing always fails. If promise is successful, the "data" object must have nextQ attribute.
EDIT 2:
Hi Asta, your code is amazing. I'm trying to execute your code in my system and still with error. The ut fails:
Error: Unexpected request: GET http://mi.url.com/api/thefield No more request expected
Do u know what's wrong? Clarify that the code works fine on my application but ut is the problem.
Question Api Service code:
angular.module('myApp.services')
.factory('questionApiService', function($http, $q) {
var myService = {
getField: function() {
var defer = $q.defer();
$http.get('http://mi.url.com/api/thefield')
.success( function(data) {
defer.resolve(data);
})
.error( function(data) {
defer.reject(data);
});
return defer.promise;
};
return myService;
});
Your test:
describe('myApp', function () {
beforeEach(function () {
module('myApp');
});
describe('questionApiService', function () {
it('should check the existence of get field question service', inject(function($rootScope, questionApiService) {
var response = null;
var promise = questionApiService.getField();
promise.then(function(data) {
response = data;
});
$rootScope.$apply();
var expectedResponse = { "nextQ": 1 };
console.log(response);
//expect(JSON.parse(response.nextQ)).toEqual(expectedResponse.nextQ);
}));
});
});
I think you just need to move your expectation outside the then and do a $rootScope.$apply().
it('should check the existence of the get field question service', inject(function($rootScope, questionApiService) {
response = null;
promise = questionApiService.getField()
promise.then(function(data) {
response = data;
});
$rootScope.$apply();
expectedResponse = { "nextQ": "value" }
expect(JSON.parse(response)).toEqual(expectedResponse);
}));
I created a jsFiddle you can use to play around with. It sets up a service that returns JSON via a promise which I used to test http://jsfiddle.net/neridum/9uumwfzc/
Alternatively if you want to test this service from another service you can mock it out using spies. Here you would mock the response as a promise and then resolve it
defer = $q.defer();
spyOn(questionApiService, 'getField').andReturn(defer.promise);
defer.resolve(data);
expect(data).toEqual('100');
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