I would like to test my simple API that has /groups URL.
I want to make an API request to that URL (using Axios) before all tests begin and make the response visible to all test functions.
I am trying to make the response visible but not able to make it work. I followed a similar case with filling out the DB upfront but no luck with my case.
My simple test file below:
var expect = require('chai').expect
var axios = require('axios')
var response = {};
describe('Categories', function() {
describe('Groups', function() {
before(function() {
axios.get(config.hostname + '/groups').then(function (response) {
return response;
})
});
it('returns a not empty set of results', function(done) {
expect(response).to.have.length.greaterThan(0);
done();
})
});
});
I tried also a sligh modification of before function:
before(function(done) {
axios.get(config.hostname + '/groups')
.then(function (response) {
return response;
}).then(function() {
done();
})
});
but no luck too.
The error I am getting is simply that response isn't changing nor is visible within it. AssertionError: expected {} to have property 'length'
Summarising: How can I pass response from axios inside to in()?
Your first form is incorrect, because you're not returning the chained promise. As such, mocha has no way of knowing when your before is finished, or even that it's async at all. Your second form will solve this problem, but since axios.get already returns a promise, it's kind of a waste not to use mocha's built-in promise support.
As for making the response visible in the it, you need to assign it to a variable in a scope that will be visible within the it.
var expect = require('chai').expect
var axios = require('axios')
var response;
describe('Categories', function() {
describe('Groups', function() {
before(function() {
// Note that I'm returning the chained promise here, as discussed.
return axios.get(config.hostname + '/groups').then(function (res) {
// Here's the assignment you need.
response = res;
})
});
// This test does not need the `done` because it is not asynchronous.
// It will not run until the promise returned in `before` resolves.
it('returns a not empty set of results', function() {
expect(response).to.have.length.greaterThan(0);
})
});
});
Related
I'm trying to write some tests using Lab and Sinon for various HTTP requests that are called in a file of mine. I followed the Fake XMLHttpRequest example at http://sinonjs.org/ but when I run my tests it appears to not actually capture any requests.
Here is the (relevant) testing code:
context('when provided a valid payload', function () {
let xhr;
let requests;
before(function (done) {
xhr = sinon.useFakeXMLHttpRequest();
requests = [];
xhr.onCreate = function (req) { requests.push(req); };
done();
});
after(function (done) {
// clean up globals
xhr.restore();
done();
});
it('responds with the ticket id', (done) => {
create(internals.validOptions, sinon.spy());
console.log(requests); // Logs empty array []
done();
});
});
create is the function I imported from the other file, here:
internals.create = async function (request, reply) {
const engineeringTicket = request.payload.type === 'engineering';
const urgentTicket = request.payload.urgency === 'urgent';
if (validation.isValid(request.payload)) {
const attachmentPaths = formatUploads(request.payload.attachments);
const ticketData = await getTicket(request.payload, attachmentPaths);
if (engineeringTicket) {
const issueData = getIssue(request.payload);
const response = await jira.createIssue(issueData);
jira.addAttachment(response.id, attachmentPaths);
if (urgentTicket) {
const message = slack.getMessage(response);
slack.postToSlack(message);
}
}
zendesk.submitTicket(ticketData, function (error, statusCode, result) {
if (!error) {
reply(result).code(statusCode);
} else {
console.log(error);
}
});
} else {
reply({ errors: validation.errors }).code(400); // wrap in Boom
}
};
as you can see it calls jira.createIssue and zendesk.submitTicket, both of which use an HTTP request to post some payload to an API. However, after running the test, the requests variable is still empty and seems to have captured no requests. It is definitely not actually submitting the requests as no tickets/issues have been created, what do I need to fix to actually capture the requests?
Your problem is apparent from the tags: you are running the code in NodeJS, but the networking stubs in Sinon is for XMLHttpRequest, which is a browser specific API. It does not exist in Node, and as such, the setup will never work.
That means if this should have worked you would have needed to run the tests in a browser. The Karma test runner can help you with this if you need to automate it.
To make this work in Node you can either go for an approach where you try to stub out at a higher level - meaning stubbing the methods of zendesk and jira, or you can continue with the approach of stubbing network responses (which makes the tests a bit more brittle).
To continue stubbing out HTTP calls, you can do this in Node using Nock. Saving the requests like you did above is done like this:
var requests = [];
var scope = nock('http://www.google.com')
.get('/cat-poems')
.reply(function(uri, requestBody) {
requests.push( {uri, requestBody} );
});
To get some insights on how you can stub out at a higher level, I wrote this answer on using dependency injection and Sinon, while this article by Morgan Roderick gives an intro to link seams.
I have some mocha tests I run with Nodejs to test a web server.
Many of the tests should cause the server to return an error, e.g. 400 Bad Request.
Currently the tests are peppered with many copies of the following code:
it('should respond with 400 (Bad Request)', function (){
expect(httpResponse.statusCode).to.equal(httpstatus.BAD_REQUEST);
});
Here's a simplified pseudocode example:
describe('When passing bad JSON data', function(){
var response
before(function(done){
callUrlToInsert(url, badJson, function(err, resp){
response = resp
done()
}
}
it('should respond with 400 (Bad Request)', function (){
expect(httpResponse.statusCode).to.equal(httpstatus.BAD_REQUEST)
})
}
This bugs me because as a programmer I avoid duplicate code wherever possible.
However, putting this into a function does not work:
function verifyItReturnedBadRequest400(httpResponse)
{
it('should respond with 400 (Bad Request)', function (){
expect(httpResponse.statusCode).to.equal(httpstatus.BAD_REQUEST);
});
}
because the call to it() doesn't test the assertion right then; my [limited] understanding is that it() adds the closure to the list of tests. So by the time that check is done, the httpResponse variable has gone out of scope. (I don't understand why that is the case, because in both cases there is a call to it(); why would it matter that in one case it's inside another level of function call? I'm probably missing something with regard to Javascript scoping.)
Is there a common way to avoid all this duplicate code? Or is everyone out there duplicating all their assertion code everywhere? This is my first foray into Mocha so I am probably missing something obvious.
Also, bonus points for explaining why doesn't the function approach work?
Thanks!
There is an article on wiki about this.
https://github.com/mochajs/mocha/wiki/Shared-Behaviours
I guess you have some bugs in your test. Placing it() into wrapper function works fine. Here's a small working demo.
'use strict';
const assert = require('assert');
const xEqualsOne = () => {
it('should be equal 1', () => {
assert.equal(this.x, 1);
});
};
describe('async number', () => {
this.x = 0;
before(done => {
this.x++
setTimeout(done, 100);
});
xEqualsOne();
});
I guess your code looks something like this:
describe('When passing bad JSON data', function(){
var response
before(function(done){
callUrlToInsert(url, badJson, function(err, resp){
response = resp
done()
}
}
verifyItReturnedBadRequest400(httpResponse)
}
Think about it like this:
it() creates a test.
All the calls to it happen before any tests are actually run (you have to create tests before you run them)
The function passed to `before' is run after the tests have been created, but before they are run.
verifyItReturnedBadRequest400 calls it, to create a test, but you're passing in httpResponse right then before any tests have run, so before hasn't run yet either.
You could continue to use that sort of pattern, but you'll need to put the httpresponse in a container so you can pass a reference to it:
describe('When passing bad JSON data', function(){
var data = {};
before(function(done){
callUrlToInsert(url, badJson, function(err, resp){
data.response = resp
done()
}
}
verifyItReturnedBadRequest400(data)
}
and then your verifyItReturnedBadRequest400 becomes:
function verifyItReturnedBadRequest400(data) {
it('should respond with 400 (Bad Request)', function (){
expect(data.response.statusCode).to.equal(httpstatus.BAD_REQUEST);
});
}
I've got a MEAN app and I'm trying to get tests to work on the node side. Async events are wrapped in promises, which are consumed in the controller. I failed at testing the controller :(
The controller I'm trying to test:
ProjectController.prototype.getAll = function(req, res, next) {
req.dic.subjectRepository
.getById(req.params.subjectId)
.then(function(subject) {
res.json(subject.projects);
}, function(err) {
return res.status(404).send('Subject does not exist.' + err);
});
};
The subjectRepository is our data source, which returns a promise (mpromise because under the hood we're using mongoose, but it shouldn't matter):
So in our test we tried mocking the request (we're injecting our dependency injection container from a middleware into the req) and response (the test succeeds if response.json() has been called with the subjects we tried to fetch) and our subjectRepository. We used bluebird (although I tried others out of frustration) to create fake promises for our mocked subjectRepository:
describe('SubjectController', function() {
'use strict';
var Promise = require('bluebird');
it('gets all existing subjects', function() {
// -------------------------------------
// subjectRepository Mock
var subjectRepository = {
getAll: function() {},
};
var subjectPromise = Promise.resolve([
{name: 'test'},
{name: 'test2'},
]);
spyOn(subjectRepository, 'getAll').andReturn(subjectPromise);
// -------------------------------------
// request mock
var req = {
dic: {
subjectRepository: subjectRepository,
},
};
// -------------------------------------
// response mock
var res = {
json: function() {},
send: function() {},
};
spyOn(res, 'json');
// -------------------------------------
// actual test
var subjectController = new (require('../../../private/controllers/SubjectController'))();
subjectController.getAll(req, res);
// this succeeds
expect(subjectRepository.getAll).toHaveBeenCalled();
// this fails
// expect(res.json).toHaveBeenCalled();
});
});
Question: How do I make the test run the expect() AFTER the promise succeeded?
Node v0.12
The code is on GitHub for anyone who's interested: https://github.com/mihaeu/fair-projects
And maybe I should mention that the controller is called from the router:
// router handles only routing
// and controller handles data between view and model (=MVC)
subjectRouter.get('/:subjectId', subjectController.get);
I got this to work by changing our controllers to hand down the promises, but I'm not sure this is what we want. Isn't there a way to get my approach to work?
it('gets all existing subjects', function(done) {
// ...
var subjectController = new (require('../../../private/controllers/SubjectController'))();
subjectController.getAll(req, res).then(function() {
expect(res.json).toHaveBeenCalledWith(testSubjects); // success
}).finally(done);
expect(subjectRepository.getAll).toHaveBeenCalled(); // success
}
Your code makes the mistake of mixing business logic with front facing routing.
If your getAll did not touch the request and response object, it would look something like this:
ProjectController.prototype.getAll = function(subjectId) {
return req.dic.subjectRepository.getById(subjectId).then(function(subject){
return subject.projects;
});
};
Now, it is no longer related to the request response life cycle or in charge of logic, testing it is trivial by:
it("does foo", function(){
// resolve to pass the test, reject otherwise, mocha or jasmine-as-promised
return controller.getAll(152).then(...)
});
That would make your actual handler look like:
app.get("/projects", function(req, res){
controller.getAll(req.params.subjectId).then(function(result){
res.json(result);
}, function(){
res.status(404).send("...");
});
});
In zapier I use an action of Code By Zapier. It's based on node.js. I need to use fetch for implementing REST-API of my CRM.
Here is the code I wrote, which runs well when I tried it with VS Code (outside Zapier):
// the code by zapier includes already the require('fetch')
var api_token = "..."; // my api
var deal_name = "Example"; // a string
fetch("https://api.pipedrive.com/v1/deals/find?term="+deal_name+"&api_token=" + api_token)
.then(function(res) {
return res.json();
}).then(function(json) {
var deal_id = json.data[0].id;
console.log("deal_id="+deal_id);
}).catch(function(error) {
console.log("error");
});
output = {id: 1, hello: "world"}; // must include output...
The error I got from Zapier is:
If you are doing async (with fetch library) you need to use a
callback!
Please help me with solving it.
Zapier knows, that fetch is a async function. You have to use the callback-function and not the output variable.
// bad code
fetch(url)
.then(function(res) {
return res.json();
}).then(function(json) {
// when i run this in my node repl it works perfect!
// the problem is this doesn't return the data to zapier
// it just prints it to the system output
console.log(json);
});
// good code
fetch(url)
.then(function(res) {
return res.json();
}).then(function(json) {
// but if i swap this to callback, this works perfect in zapier
callback(null, json);
});
I have the following route (express) for which I'm writing an integration test.
Here's the code:
var q = require("q"),
request = require("request");
/*
Example of service wrapper that makes HTTP request.
*/
function getProducts() {
var deferred = q.defer();
request.get({uri : "http://localhost/some-service" }, function (e, r, body) {
deferred.resolve(JSON.parse(body));
});
return deferred.promise;
}
/*
The route
*/
exports.getProducts = function (request, response) {
getProducts()
.then(function (data) {
response.write(JSON.stringify(data));
response.end();
});
};
I want to test that all the components work together but with a fake HTTP response, so I am creating a stub for the request/http interactions.
I am using Chai, Sinon and Sinon-Chai and Mocha as the test runner.
Here's the test code:
var chai = require("chai"),
should = chai.should(),
sinon = require("sinon"),
sinonChai = require("sinon-chai"),
route = require("../routes"),
request = require("request");
chai.use(sinonChai);
describe("product service", function () {
before(function(done){
sinon
.stub(request, "get")
// change the text of product name to cause test failure.
.yields(null, null, JSON.stringify({ products: [{ name : "product name" }] }));
done();
});
after(function(done){
request.get.restore();
done();
});
it("should call product route and return expected resonse", function (done) {
var writeSpy = {},
response = {
write : function () {
writeSpy.should.have.been.calledWith("{\"products\":[{\"name\":\"product name\"}]}");
done();
}
};
writeSpy = sinon.spy(response, "write");
route.getProducts(null, response);
});
});
If the argument written to the response (response.write) matches the test passes ok. The issue is that when the test fails the failure message is:
"Error: timeout of 2000ms exceeded"
I've referenced this answer, however it doesn't resolve the problem.
How can I get this code to display the correct test name and the reason for failure?
NB A secondary question may be, could the way the response object is being asserted be improved upon?
The problem looks like an exception is getting swallowed somewhere. The first thing that comes to my mind is adding done at the end of your promise chain:
exports.getProducts = function (request, response) {
getProducts()
.then(function (data) {
response.write(JSON.stringify(data));
response.end();
})
.done(); /// <<< Add this!
};
It is typically the case when working with promises that you want to end your chain by calling a method like this. Some implementations call it done, some call it end.
How can I get this code to display the correct test name and the reason for failure?
If Mocha never sees the exception, there is nothing it can do to give you a nice error message. One way to diagnose a possible swallowed exception is to add a try... catch block around the offending code and dump something to the console.