I'm trying to test a Backbone collection's fetch. I'm using Sinon's fake server to set up a fake REST endpoint. The problem is that it seems like the request isn't sending.
I'm using Jasmine with Karma and running it through PhantomJS.
The problem is that the request is apparently not being sent. There aren't any errors but nothing is being logged to the console.
Here's the code:
describe("The Posts collection", function() {
var posts;
var server;
beforeEach(function() {
server = sinon.fakeServer.create();
posts = new PostCollection();
});
afterEach(function() {
server.restore();
});
it("should fetch the posts from the api", function() {
server.respondWith("GET", "/posts",
[200, { "Content-Type": "application/json" },
'{ "stuff": "is", "awesome": "in here" }']);
posts.fetch({
success: function(model, response, options) {
console.log("REQUEST SENT");
}
});
});
});
So as it turns out, I didn't read the docs carefully enough. With the fake server, you need to tell it to respond. I added the following after the call to posts.fetch():
server.respond();
It works perfectly now.
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
UPDATE: I tried another API: http://www.omdbapi.com/?t=Sherlock
And the GET works. However, I'm still unsure why my node.js server API does not work with this method.
I am currently working on the backend of an app of which I created an API similar to the tutorial: https://codeforgeek.com/2015/03/restful-api-node-and-express-4/.
However, I cannot seem to get my ionic angular app to retrieve my JSON result. When I put my get command on Postman, I get :
[{"floor":3}]
My controller is:
$http({url:"http://localhost:3000/api/fl",method:'GET'})
.then(function(response) {
$scope.status = response.status;
$scope.data = response.data;
$log.log("success");
$log.log("res:"+ response);
}, function(response) {
$scope.data = response.data || "Request failed";
$scope.status = response.status;
$log.log("failed");
$log.log("res:"+ response);
});
$log.log("status: "+$scope.status);
$log.log("data: "+$scope.data);
(my /fr references the JSON which has a GET method on my node server)
And always gives the result of "failed", "status: undefined" as well as "data: undefined". When I recurse this method, it gives me a "status:0".
I have also tried using $resource with no success.
Any guidance would be appreciated. My API does not have any auth and I don't think it has anything to do with CORS(unless you guys think it does).
EDIT:
this is the get method on my server.js:
//GET Floor
router.get("/fl",function(req,res){
var query = "SELECT floor FROM stor1 WHERE id=0";
connection.query(query,function(err,rows){
if(err) {
res.json({"Error" : true, "Message" : "Error executing MySQL query"});
} else {
res.json(rows);
}
});
});
EDIT2:
Well, i added more logs to display results and now i get an [object Object] from response. The object is:
{"data":null,"status":0,"config":{"method":"GET","transformRequest":[null],"transformResponse":[null],"url":"http://localhost:3000/api/fl","headers":{"Accept":"application/json, text/plain,*/*"}},"statusText":""}
Your postman seems to get the response for the GET request which means your server side code is fine with routing. But your Angular App is unable to get any response with out throwing any error in the server side. Which seems to be the case of CORS
var cors = require('cors');
app.use(cors());
Note: Use this middleware before your first route.
There is one more way to make Access-Control-Allow-Origin
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With");
next();
});
Try adding names for the success and error callback functions, like so:
$http({url:"http://localhost:3000/api/fl",method:'GET'})
.then(function successCallBack(response) { // Add 'successCallBack' function name
$scope.status = response.status;
$scope.data = response.data;
$log.log("success");
$log.log("res:"+ response);
}, function errorCallBack(response) { // Add 'errorCallBack' function name
$scope.data = response.data || "Request failed";
$scope.status = response.status;
$log.log("failed");
$log.log("res:"+ response);
});
$log.log("status: "+$scope.status);
$log.log("data: "+$scope.data);
I'm trying to write a test in Protractor/Jasmine that depends upon my being able to see the headers sent in an HTTP request. To that end I'm trying to create a mock endpoint with $httpBackend that will respond to a call with the headers themselves, allowing me to look into them in my test. My code is as follows:
describe('Headers', function () {
it('should include X-XSRF-TOKEN on HTTP calls', function () {
browser.addMockModule('httpBackendMock', function () {
angular.module('httpBackendMock', ['CorsApp', 'ngMockE2E'])
.run(function ($httpBackend) {
$httpBackend.whenGET('/xsrftest')
.respond(function (method, url, data, headers) {
return headers;
});
})
});
loginPage.get();
browser.executeAsyncScript(function (callback) {
var $http = angular.injector(['ng']).get('$http');
$http.get('/xsrftest')
.then(function (response) {
callback(response);
});
})
.then(function (response) {
console.log(response);
});
});
});
I've tried to follow the patterns set out in many resources for utilizing $httpBackend in protractor testing. However, when I run this test, I get a Protractor timeout. It seems as though the $http.get call never receives a response, hence the callback is never called and so the executeAsyncScript call times out. If I put in a dummy call to the callback that's not dependent on the $http.get, it works as expected.
What am I doing wrong in setting up $httpBackend? How can I get it to respond to my $http request?
Thanks!
For hours I've been trying to test my NewPostController with $httpBackend. The problem is whenever I set non-2xx status code in the response, the test fails.
NewPostController has the following method:
$scope.submit = function () {
var newPost = $scope.newPost;
PostService.insertPost(newPost).then(function (post) {
$location.path("/my-posts");
}, function (status) {
$scope.form.$setPristine();
$scope.error = status;
});
};
I have a problem testing the failure path:
it(...) {
...
$scope.post.text = "";
$httpBackend.expectPOST("/create-post", {"post":$scope.post}).respond(400);
$scope.submit();
$httpBackend.flush();
expect($scope.error).toBeDefined();
$scope.post.text = "This is a valid text.";
$httpBackend.expectPOST("/create-post", {"post": $scope.post}).respond(200);
$scope.submit();
$httpBackend.flush();
expect($location.path()).toBe("/my-posts");
});
The test fails with a message "400 thrown" (no callstack). I tried to change the order of subtests, use whenPOST instead of expectPOST and combine the methods as they do in Angular docs (https://docs.angularjs.org/api/ngMock/service/$httpBackend) but without success.
Please help.
EDIT:
Now when I look at PostService, it makes sense where the "400 thrown" comes from but I expected the error to be handled by angular. I threw it because of the section "Handling problems in nested service calls" of this article. It is supposed to be a shorter version of deferred.resolve/reject mechanism.
this.insertPost = function (newPost) {
return $http({
method: "post",
url: "/create-post",
data: {
post: newPost
}
}).then(function (res) {
return (res.data);
}, function (res) {
throw res.status;
});
};
This is indeed strange, and is perhaps something the angular team didn't consider.
When a promise is rejected by throwing (as you're doing), the angular $exceptionHandler service is called with the thrown exception. By default, this service just logs the exception in the browser console.
But when using ngMocks, this service is replaced by a mock implementation that can either log or rethrow the exception. The default mode is to rethrow, in order to make a test fail when an exception is thrown.
My advice would be to avoid using throw to simply reject a promise, and thus replace
function (res) {
throw res.status;
}
by
function (res) {
return $q.reject(res.status);
}
But if you really want to keep using throw, you can also configure the mock exceptionHandler to log instead of rethrowing:
beforeEach(module(function($exceptionHandlerProvider) {
$exceptionHandlerProvider.mode('log');
}));
Given the following BackboneJS 1.1.0 model / MarionetteJS 1.0.4 module:
MyApp.module('Product', function(Product, App, Backbone, Marionette, $, _) {
Product.Model = Backbone.Model.extend({
destroy: function() {
console.log("Product.destroy()");
return Backbone.Model.prototype.destroy.apply(this, arguments);
}
});
});
How would you simulate that the destroy function fails so you can test the associated behavior (such as a user notification alert message)? I use Jasmine 1.3.0 for testing in this project.
if you wanna test the error callback...you can mock the server response in jasmine by using http://sinonjs.org/
define your server in a before block:
var server, aProductInstance;
beforeEach(function() {
server = sinon.fakeServer.create();
aProductInstance = new Product.Model({id: 999});
});
restore it after each test:
afterEach(function() {
server.restore();
});
in your test, use respondWith method to return a non-200 response
server.respondWith(method, url, response);
like so
describe("fail to destroy", function() {
it("calls the error callback", function() {
server.respondWith("DELETE", "/products/destroy", [500, { "Content-Type": "application/json" }, '{ "error": "bad request" }']);
//call the method
aProductInstance.destroy();
//send the response
server.respond();
//now write your tests to see if error callback is called.
});
});