I am using HttpBackend to mock some Http responses for some calls that my Angular app is making.
However, I am getting the error "Unexpected Request: [object Object] undefined" when I run my tests.
I know that usually this error means you're missing or mistyped one of the $http requests that the app makes so it can't find a response. But my error is not specific like the other ones which usually say like "Unexpected Request: GET api/call" so I don't know what is wrong.
Has anyone ever encountered this specific error before?
Sample Code
angular controller:
app.controller( 'ctrl',
[ '$scope' , '$http' , '$location', function( $scope, $http, $location ) {
$http.get(
"/api/1.0/id/" + id,
{
headers: getAuthHeaders()
}
).success(function( data ){ //... })]
);
jasmine test:
it('should ...', function(){
httpBackend.whenGET('/api/1.0/id/*').respond(200,{"test":"Test"});
//...
});
I tried your code and test by myself. I add a function to set the ID, but I think you have something simular:
//PRESET ID
var id = 'deajedgwe1e213df';
//FUNCTION TO CAPSULATE API CALL
this.callApi = function () {
http.get(
"/api/1.0/id/" + id,
{
//headers: getAuthHeaders()
}
).success(function( data ){
console.log('success', data);
}).error(function (err) {
console.log('error', err);
});
};
//FUNCTION TO SET ID
this.setId = function (ID) {
id = ID;
};
then I copied your test and modified it like this:
it('should ...', function(){
ctrl.setId('test123ID');
httpBackend.expectGET('/api/1.0/id/test123ID').respond(200,{"test":"Test"});
ctrl.callApi();
httpBackend.flush();
httpBackend.verifyNoOutstandingExpectation();
httpBackend.verifyNoOutstandingRequest();
});
this works. alternative you could do this:
it('should ...', function(){
httpBackend.expectGET(/api/i).respond(200,{"test":"Test"});
ctrl.callApi();
httpBackend.flush();
httpBackend.verifyNoOutstandingExpectation();
httpBackend.verifyNoOutstandingRequest();
});
this works too, but it only verifies, that /api has been called... I don't if this is that, what you want to test.
I think the major problem is your asterisk notation at the api-call. try to modify your code like mine. if this shouldn't help, you should have a call on your header function.
Good luck.
Related
I'm applying some tests in an existing AngularJS application in order to ensure it's correct behaviour for future changes in the code.
I am pretty new with Jasmine & Karma testing, so I've decided to start with a small and basic service which performs an http request to the backend, and waits for the result with a promise, nothing new.
Here's the service method to test:
function getInformedConsent(queryParameters) {
var def = $q.defer(),
httpParameters = {
url: ENV.apiEndpoint + '/urlResource',
method: 'GET',
params: queryParameters,
paramSerializer: '$httpParamSerializerJQLike'
};
$http(httpParameters)
.then(
function (response) {
def.resolve(response);
},
function (error) {
def.reject(error);
}
);
return def.promise;
}
And here my test:
it('getInformedConsent method test', function() {
$httpBackend.expectGET(/.*\/urlResource?.*/g)
.respond(informedConsentJson.response);
var promise;
promise = InformedconsentService.getInformedConsent(informedConsentJson.queryParameters[0]);
promise
.then(function(response) {
console.log(response);
expect(response).toEqual(informedConsentJson.response);
});
$httpBackend.flush();
});
informedConsentJson as you can supose, is a fixture with input and the expected output.
Reading AngularJS documentation, I decided to use $httpBackend, because it's already a mock of $http service, so I thought it could be useful.
The problem is that somewhere in the code, someone is broadcasting a "$locationChangeStart" event and executing
$rootScope.$on('$locationChangeStart', function (event,current,old) {
/* some code here */
});
in app.js.
I'm not trying to change the URL, i'm just trying to get some data from the mocked backend.
I asume that is because I'm not using $http mock ($httpBackend) as it should be used.
Anyone can help me with $http with configuration JSON mock?
It's freaking me out.
Thank you all in advance for your time and your responses
I am building an app with Ionic and MEAN stack. My express server is running on localhost:3000 while my Ionic public code is running on localhost:8100. From my research, it seems like Ionic can run on a different IP address from the server and should just use ngResource to send $http requests.
So I have a RESTful endpoint like this in server.js
router.get('/', function(req, res){
res.json({"name":"Abdul"});
});
And on the Ionic client code I am sending in a request like this:
app.controller('mainCtrl', function($scope, $resource){
$scope.test = $resource('localhost:3000/');
$scope.test_button = function(){
console.log($scope.test);
}
});
But when I click the test_button, instead of [{"name":"Abdul"}] being logged in the console, I get the following null message:
function Resource(value) {
shallowClearAndCopy(value || {}, this);
}
Can anyone help me out on connecting the client and server?
$resource object will only create an object with having get, save, update, etc. So for calling get method of server, you need to call get method of $resource object. That method will return $promise object will provide a promise. On which you can place .then promise, in which you will get data in success function.
One more thing is, when you are returning data from the server, you are returning object in array format. So in that case you need to specify get method will return array by having isArray: true option there.
$scope.test = $resource('http://localhost:3000/', {}, {get: { isArray: true}});
$scope.test.get().$promise.then(function(data){ //success function
$scope.test = data;
},function(error){ //error function
console.log(error);
})
to make your application more better, you could move up your $resource object to service/factory to make that call reusable.
app.service('dataService', function($resource){
var resourceUrl = $resource('http://localhost:3000/', {}, {get: { isArray: true} });
this.getData = function(){
return resourceUrl.get().$promise;
};
})
Controller
app.controller('mainCtrl', function($scope, dataService){
$scope.test_button = function(){
dataService.getData().then(function(data){ //success function
$scope.test = data;
},function(error){ //error function
console.log(error);
})
}
});
I'm attempting to unit test an angular factory in jasmine but I'm having trouble understanding how to get an actual response from my database.
I have the following factory code that retrieves an object containing company information based on a company ticker value.
The factory works fine but I'd like to test it in jasmine.
app.factory('adminOps',function($http, $log, $q){
return {
getByTicker: function(ticker){
return $http.post("http://localhost:3000/ticker", {"ticker": ticker})
.then(function(response){
if (typeof response.data === 'object') {
return response.data;
} else {
return $q.reject(response.data);
}
}, function(response) {
return $q.reject(response);
});
}
};
});
To test this I have the following jasmine code based on online examples I found.
describe('adminOps', function() {
var factory, http;
beforeEach(module('myApp'));
beforeEach(inject(function(_adminOps_, $httpBackend) {
factory = _adminOps_;
http = $httpBackend;
}));
it('Should retrieve company data', function(done) {
var testCompany = function(company) {
expect(company.name).toBe("Google Inc.");
};
var failTest = function(error) {
expect(error).toBeUndefined();
}
http.expectPOST('http://localhost:3000/ticker',{ticker:"GOOG"}).respond(200, {ticker:"GOOG"});
factory.getByTicker("GOOG")
.then(testCompany)
.catch(failTest)
.finally(done);
http.flush();
});
});
I get a Expected undefined to be 'Google Inc.'.
How can I call my factory and test for the correct name value for the ticker parameter I send?
UPDATE: Koen's code works correctly. I've found if you want to test values on a server, like a rest api, then you should try something like frisby, which is built on Jasmine.
Unittests should test your local code and should not have external dependencies. Therefor your test doesn't and shouldn't actually access your database.
$httpBackend allows you to mock the http request and response.
Your code mocks the $httpBackend as follows:
http.expectPOST('http://localhost:3000/ticker',{
ticker:"GOOG"
}).respond(200,{
ticker:"GOOG"
});
meaning it will respond with a response of
{ticker:"GOOG"}
So it doesn't have the 'name' attribute you need.
A proper way to test your 'getByTicker' method is with the following $httpBackend setup:
$http.expectPOST('http://localhost:3000/ticker', {
ticker: "GOOG"
}).respond(200, {
name: "Google Inc."
});
I am playing around with the MEAN stack. I have created a rest service to delete from mongo db which works fine but When I try to use angular Factory method and call it I get the above error
myApp.factory('methodFactory', ['$http', function ($http) {
return {
//id becomes undefined over here
removeContact:function($http, id){
//TODO add URL
var url = '/contactlist/'+id;
return $http({
method: 'DELETE',
url: url
});
}
};
}]);
myApp.controller('AppControl', ['$scope','$http','methodFactory', function($scope,$http,methodFactory) {
$scope.remove = function(id) {
console.log(id); //able to print correct id
methodFactory.removeContact(id).success(function(response){
console.log("remv"+response);
refresh();
});//tthiss throws the error
//this rest service works properly.
/*$http.delete('/contactlist/'+id).success(function(response){
console.log("remv"+response);
refresh();
});*/
};
};
This what node server looks like
app.delete('/contactlist/:id',function(req,res) {
var id = req.params.id;
console.log(id);
db.contactlist.remove({_id:mongojs.ObjectId(id)},function(err,doc){
res.json(doc);
});
console.log("exiting delete")
});
I am not sure if factory could be one of the way to call a rest service. What could cause the problem ?
Error
TypeError: string is not a function
at Object.removeContact (http://localhost:3000/controllers/controller.js:10:20)
at l.$scope.remove (http://localhost:3000/controllers/controller.js:85:23)
at hb.functionCall (https://ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular.min.js:198:426)
at Cc.(anonymous function).compile.d.on.f (https://ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular.min.js:215:74)
at l.$get.l.$eval (https://ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular.min.js:126:193)
at l.$get.l.$apply (https://ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular.min.js:126:419)
at HTMLButtonElement.<anonymous> (https://ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular.min.js:215:126)
at HTMLButtonElement.c (https://ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular.min.js:32:363)
Your removeContact method needs two parameters:
removeContact:function($http, id) ...
but you call it with only one:
methodFactory.removeContact(id) ...
Id is a string, I suppose, but it it will be assigned to the first parameter of the function $http, what must be executable according the code you provided.
I have an AngularJS app. In this app, I'm trying to ping a REST API. This API returns a list of orders.
I need to be able to handle the scenario where I successfully GET the orders. I also need to handle the
scenario where the request to GET the orders fails. In an attempt to do this, I'm using the ngResource
module. My controller looks like the following:
myController.js
myApp.controller('myController',
function myController($scope, myService) {
myService.getOrders(function(data) {
$scope.orders = data;
});
}
);
The definition of myService is stored in myService.js. That file looks like this:
app.factory("myyService", function($resource, $log) {
return {
getOrders: function(onSuccess) {
var orders = $resource("http://localhost:1000/orders", { fetch:{method:'JSON'} });
orders.fetch(function (response) {
console.log(response);
onSuccess(response.data);
});
}
};
});
When I run this code, I get a runtime error. The error says:
TypeError: Object function g(b){z(b||{},this)} has no method 'fetch'
Maybe there has to be something I don't understand. In my mind, I see fetch defined.
The other question I have is how do I set this up to handle failed requests? Like a 404 or 502?
You forgot the curly braces after the URL parameter...
Change: http://localhost:1000/orders", { fetch :
To: http://localhost:1000/orders", {}, { fetch :
app.factory("myyService", function($resource, $log) {
return {
getOrders: function(onSuccess) {
var orders = $resource("http://localhost:1000/orders", {}, { fetch : {method:'JSON'} });
orders.fetch(function (response) {
console.log(response);
onSuccess(response.data);
});
}
};
});
[EDIT]
To handle errors from the server side, you need to set the second function in the resource call.
Example :
orders.fetch(function success() {...},
function error() {... this will execute in a http error, 400 or 500}
);