Why is Mocha passing this https.get request? - node.js

Brand new to testing. Trying to figure out why mocha is passing this test when it should be failing.
var assert = require('assert');
var nock = require('nock');
var https = require('https');
describe('thing', function() {
describe('foo', function () {
it('makes the correct https call to API', function () {
nock('https://example.com')
.get('/foo')
.reply(404);
https.get('https://example.com/foo', function (response) {
console.log(response.statusCode); // returns 404
assert.equal(response.statusCode, 200); //passes
});
});
});
});

Mocha, just like any other [properly-written] Node.js module/app, runs asynchronously out of the box. Because your https call takes longer to execute than the entire Mocha test, Mocha never has a chance to perform its assertions before the process completes.
That said, Mocha tests also supports a callback that let you execute long-running activities before performing your assertions:
var assert = require('assert');
var nock = require('nock');
var https = require('https');
describe('thing', function() {
describe('foo', function () {
it('makes the correct https call to API', function (done) {
nock('https://example.com')
.get('/foo')
.reply(404);
https.get('https://example.com/foo', function (response) {
console.log(response.statusCode); // returns 404
assert.equal(response.statusCode, 200); //passes
done();
});
});
});
});

Related

Making a request before all tests start in Mocha

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);
})
});
});

http.request callback not called in mocha and chai unit test

I'm trying to unit test a simple http REST client using mocha and chai libraries for Node.js with this code:
var chai = require('chai');
var asrt = require('chai').assert;
var client = require('../index');
describe('#Do successful', function () {
it('should pass when schema, host and port are provided', function () {
client.do('http:', 'localhost', '8080', '', function (result) {
console.log("starting assertions");
asrt.isAbove(result.items.length,0);
// ... other assertions
});
});
});
when I run the test with npm test, the test "passes" but the line that logs "starting assertions" is never printed, because the client.do function callback is never called, but I see that the server properly received the request and responded.
I'm obviously missing something, but I can't understand what in particular. Please notice that:
1) a very similar piece of code used in a non-test file produces the expected outcome (which is: the callback is called and the result data if filled with the response data).
2) Again, I'm testing a client, not a server, so I suppose I shuldn't use the done() function (but I tried as well, and didn't work).
Have you any hint on how to fix this? Thanks
Assuming that client.do is asynchronous because what you described is inline with the test executing, firing of asynchronous request, and not waiting for the response.
In this case the solution IS to use an asynchronous test with the done parameter:
var chai = require('chai');
var asrt = require('chai').assert;
var client = require('../index');
describe('#Do successful', function () {
it('should pass when schema, host and port are provided', function (done) {
client.do('http:', 'localhost', '8080', '', function (result) {
console.log("starting assertions");
asrt.isAbove(result.items.length,0);
// ... other assertions
// TEST WAITS FOR TIMEOUT OR UNTIL done() IS CALLED
done();
});
});
});

Sinon NodeJS stub only for module under test

I have a module under test which uses https to PUT data to a response URL. Before doing so, it makes calls to the AWS SDK. I do not want to stub the calls that AWS SDK makes using https, but I do want to stub the call to https.post that my module under test uses (it's an AWS Lambda unit test if that matters).
Consider the following test code
describe('app', function () {
beforeEach(function () {
this.handler = require('../app').handler;
this.request = sinon.stub(https, 'request');
});
afterEach(function () {
https.request.restore();
});
describe('#handler()', function () {
it('should do something', function (done) {
var request = new PassThrough();
var write = sinon.spy(request, 'write');
this.request.returns(request);
var event = {...};
var context = {
done: function () {
assert(write.withArgs({...}).calledOnce);
done();
}
}
this.handler(event, context);
});
});
});
And my module under test (app.js)
var aws = require("aws-sdk");
var promise = require("promise");
exports.handler = function (event, context) {
var iam = new aws.IAM();
promise.denodeify(iam.getUser.bind(iam))().then(function (result) {
....
sendResponse(...);
}, function (err) {
...
});
};
// I only want to stub the use of https in THIS function, not the use of https by the AWS SDK itself
function sendResponse(event, context, responseStatus, responseData) {
var https = require("https");
var url = require("url");
var parsedUrl = url.parse(event.ResponseURL);
var options = {
...
};
var request = https.request(options, function (response) {
...
context.done();
});
request.on("error", function (error) {
...
context.done();
});
// write data to request body
request.write(...);
request.end();
}
How can I accomplish this?
You could use nock to mock specific HTTP/S requests, rather than function calls.
With nock, you can setup URL and request matchers that will allow requests through that don't match what you've defined.
Eg:
nock('https://www.something.com')
.post('/the-post-path-to-mock')
.reply(200, 'Mocked response!');
This would only intercept POST calls to https://www.something.com/the-post-path-to-mock, responding with a 200, and ignore other requests.
Nock also provides many options for mocking responses or accessing the original request data.

How do I launch server for multiple mocha chai-http test files?

I am starting my node server in my before block on my mocha chai-http tests.
I have it working perfect for single test files. However when I attempt to run multiple tests in a single command NODE_ENV=test mocha test/**/*.js I am getting an error.
I tried to have the node servers launch on different ports per test file. This didn't work, got node server start errors.
I'm now thinking it would be great if I can have a single mocha file that runs before my other test files to start the server and then a single file that runs after the other test files to kill/stop the server.
How would I go about this.
Below is some of my code:
Here is one of my test files for reference:
var chai = require('chai');
var chaiHttp = require('chai-http');
chai.use(chaiHttp);
var expect = chai.expect;
var Sails = require('sails');
describe('REST User API', function() {
var app; // for access to the http app
var sails; // for starting and stopping the sails server
before(function (done) {
Sails.lift({
port: 3001,
log: {
level: 'error'
}
}, function (_err, _sails) {
if(_err){
console.log("Error!", _err);
done();
}
else {
app = _sails.hooks.http.app;
sails = _sails;
done();
}
});
});
describe("user session", function () {
var res; // http response
var authenticatedUser;
before(function (done) {
chai.request(app)
.post('/users/signin')
.set('Accept', 'application/json')
.set('Content-Type', 'application/json')
.send({ email: 'admin#test.com', password: 'secret'})
.end(function (_res) {
res = _res; // Record the response for the tests.
authenticatedUser = JSON.parse(_res.text); // Save the response user for authenticated tests
done();
});
});
it("should connect with a 200 status", function () {
expect(res).to.have.status(200);
});
it("should have a complete user session", function () {
var userSession = authenticatedUser;
expect(userSession).to.have.property('firstName');
expect(userSession).to.have.property('lastName');
expect(userSession).to.have.property('gender');
expect(userSession).to.have.property('locale');
expect(userSession).to.have.property('timezone');
expect(userSession).to.have.property('picture');
expect(userSession).to.have.property('phone');
expect(userSession).to.have.property('email');
expect(userSession).to.have.property('username');
expect(userSession).to.have.property('confirmed');
expect(userSession).to.have.property('status');
expect(userSession).to.have.property('authToken');
});
});
after(function (done) {
sails.lower(function() {
done()
});
});
});
From mocha v8.2.0, you can use GLOBAL FIXTURES to setup and teardown your web server for all test suites. Global fixtures are guaranteed to execute once and only once.

Asynchronous http calls with nodeJS

I would like to launch asynchronous http calls on my server node, i saw the async node module and i guess the async.parallel enables us to do that.
The documented example is pretty clear, but i don't know how i could manage multiple http calls.
I tried the example bellow but it doesn't even launch the http calls:
var http = require('http');
var Calls = [];
Calls.push(function(callback) {
// First call
http.get('http://127.0.0.1:3002/first' callback);
});
Calls.push(function(callback) {
// Second call
http.get('http://127.0.0.1:3002/second' callback);
});
var async = require('async');
async.parallel(Calls, function(err, results) {
console.log('async callback: '+JSON.stringify(results));
res.render('view', results);
});
If i launch the http requests separately, i do have a result, but but calling the async callback i get async callback: [null,null]
Have a look at the documentation:
With http.request() one must always call req.end() to signify that
you're done with the request - even if there is no data being written
to the request body.
You are creating a request, but you are not finalizing it. In your calls you should do:
var req = http.request(options, function(page) {
// some code
});
req.end();
This is assuming you are doing a normal GET request without body.
You should also consider using http.get which is a nice shortcut:
http.get("http://127.0.0.1:3002/first", function(res) {
// do something with result
});
Update The other thing is that callbacks in async have to be of the form
function(err, res) { ... }
The way you are doing it now won't work, because callback to http.get accepts only one argument res. What you need to do is the following:
http.get('http://127.0.0.1:3002/second', function(res) {
callback(null, res);
});
Ok the thing is to call the callback this way callback(null, res); instead callback(res);, i think the first parameter is interpreted as an error and the second one is the real result.
dont use capital names for other purpouses than types/classes
below is your code with corrected obvious mistakes
var http = require('http');
var calls = [];
calls.push(function(callback) {
// First call
http.get('http://127.0.0.1:3002/first', function (resource) {
resource.setEncoding('utf8');
resource.on('data', function (data) {
console.log('first received', data);
callback();
});
});
});
calls.push(function(callback) {
// Second call
http.get('http://127.0.0.1:3002/second', function (resource) {
resource.setEncoding('utf8');
resource.on('data', function (data) {
console.log('second received', data);
callback();
});
});
});
var async = require('async');
async.parallel(calls, function(err, results) {
console.log('async callback ', results);
res.render('view', results);
});

Resources