Test caching with Mocha and Supertest - node.js

I have an express app that implements some caching/memoization in a few endpoints (the calls can take several seconds to run, so I save results for various amounts of time depending on the nature of the endpoint). When I'm running the unit tests via Mocha using Supertest, however, each call is taking the longer period of time. How can I actually test that the caching portion of my application is functioning properly?
When I run the node server by itself, I can see the time for each return (e.g. 3979.848 ms for the first, 3.180 ms for the second), and it is working as intended, but the tests aren't behaving the same way.
What do I need to do to test the caching/memoization? Can it be done with these tools? Do I need to utilize some other module?
My code looks like this:
var supertest = require('supertest');
var base_url = 'http://localhost:3000';
var server = supertest(base_url);
describe('big section', function() {
describe('test section', function() {
it('should do a thing', function(done) {
this.timeout(10000);
server
.get('/url1')
.expect('Content-Type', /json/)
.expect(200)
.end(function(err, res) {
if (err) return done(err);
//stuff
done();
});
});
});
describe('test section', function() {
it('should do a similar thing, but faster', function(done) {
this.timeout(10000); //I should be able to reduce this a lot
server
.get('/url1')
.expect('Content-Type', /json/)
.expect(200)
.end(function(err, res) {
if (err) return done(err);
//stuff
done();
});
});
});
});

Related

Test redirection using jest in express

i am using Jest to test my code.
What i want achieve is to test redirection from http to https. (if it exists if process.env.IS_PRODUCTION).
I don't know how to test it, how to mockup this and so on...
I've tried standard get reqest but don't know how to mockup environment varible or test it in different way
it('should redirect from http to https, (done) => {
request(server)
.get('/')
.expect(301)
.end((err, res) => {
if (err) return done(err);
expect(res.text).toBe('...')
return done();
});
}, 5000);
I expect to be able to test this redirection :)
You could use the node-mocks-http libary which allows you to simulate a request and response object.
Example:
const request = httpMocks.createRequest({
method: 'POST',
url: '/',
});
const response = httpMocks.createResponse();
middlewareThatHandlesRedirect(request, response);
I never worked with jest but I believe that you can check the response.location parameter once the middleware has been called
Preface: I'm not familiar with jest or express or node. But I have found it to be much easier to test explicit configuration (instantiating objects with explicit values) vs implicit configuration (environmental variables and implementation switches on them):
I'm not sure what request or server are but explicit approach might look like:
it('should redirect from http to https, (done) => {
const server = new Server({
redirect_http_to_https: true,
});
request(server)
.get('/')
.expect(301)
.end((err, res) => {
if (err) return done(err);
expect(res.text).toBe('...')
return done();
});
}, 5000);
This allows the test to explicitly configure server to the state it needs instead of mucking with the environment.
This approach also helps to keep process configuration at the top level of your application:
const server = new Server({
redirect_http_to_https: process.env.IS_PRODUCTION,
});

Dynamically testing authentication RESTful API (NodeJS, Mocha, SuperAgent)

Goal:
I am trying to test my authentication RESTful API. The tools i am using are NodeJS with the modules: Mocha, Supertest(.agent) and chai(.assert).
What i tried:
var users = [ new User("admin", "secretPass"), new User("guest", "Pass")];
describe('request some protected data', function(){
users.forEach(function(user){
before(function(){
agent.post('/login').send({ user.username, user.key })
.end(function(err, res) {
}
}
it('get data', function(done){
agent.get('/data').send({ user.username, user.key })
.end(function(err, res) {
// assertions on data
}
}
}
}
The problem:
Using something similar to the snippet above results in multiple before functions being executed at once and after that all the tests(it).
So agent will always be logged in as the last user in users.
I tried to replace before with beforeEach and place it above users.forEach, but then user will be out of scope.
Can anyone provide me with a small snippet of code that will explain a suitable solution for my problem?
You need to tell Mocha that your before function is asynchronous, by accepting a done parameter and calling it when logging in has completed, e.g.
before(function(done){
agent.post('/login').send({ user.username, user.key })
.end(function(err, res) {
// I assume you're logged in here...
done();
}
}
Of course, you'll also want to add the required error handling etc. You'll probably want to create a new describe block for each user too, otherwise all of the login calls will run before any user tests happen, e.g.
users.forEach(function(user){
describe("user X tests", function() { // <-- NEW
before(function(){
agent.post('/login').send({ user.username, user.key })
.end(function(err, res) {
}
}
it('get data', function(done){
agent.get('/data').send({ user.username, user.key })
.end(function(err, res) {
// assertions on data
}
}
}); // <- NEW
}

Keeping client state when an api with mocha

My api can be asked to create an object, then later asked to update that same object, so my test file does this...
var foobarId; // this might be a mistake
it("should create a single foobar", function(done) {
server
.post("/foobar?key=value")
.expect("Content-type", /json/)
.expect(200)
.end(function(err, res) {
res.status.should.equal(200);
res.body.should.have.property('key', 'value');
foobarId = res.body._id;
done();
});
});
it("should return a foobar when I get one", function(done) {
server
.get("/foobar/" + foobarId)
.expect("Content-type", /json/)
.expect(200)
.end(function(err, res) {
res.status.should.equal(200);
res.body.should.have.property('key', 'value');
done();
});
});
So is this wrong what I'm trying here? (1) as I get more logic into the test, I risk creating logic errors in the test and needing a test for my test. (2) when the create fails, my console output gets ugly... first, the assertion error that I expect and need telling me the test failed, but then ugly traceback stuff, I think, because foobarId is undefined.
The ugliness makes me think that the good people at mocha (or supertest or wherever) didn't expect me to do what I'm doing.
Is there a right way to write this kind of test?

send authenticated request via supertest with mocha on a passport managed API

It's been 24 hours since I first had this issue. I know there plenty of similar ones, specially on SO but I can't figure out how to solve it.
I have specific requirement :
I have some routes to test without authentication.
I have some routes to test with specific users.
I use a passport local strategy.
So I decided to test my API like :
var request = require('supertest');
...
describe('Routes', function () {
var anAgent;
var anotherAgent;
before(function (done) {
anAgent = request.agent(app);
anotherAgent = request.agent(app);
async.parallel([
function (callback) {
anAgent
.post('/api/users/login')
.send({user:user, password:password})
.expect(200)
.end(callback);
},
function (callback) {
userAgent
.post('/api/users/login')
.send({user:anotherUser, password:anotherPassword})
.expect(200)
.end(callback);
}
], done);
});
describe('/superroute', function () {
it('should return the user', function (done) {
anAgent
.post('/api/superroute')
.send(params)
.expect(200)
.end(function (err, res) {
should.not.exist(err);
res.body.err.should.equal('Super route');
done();
});
});
...
});
...
});
The route /superroute is describe like
express.Router().post('/superroute', auth.ensureAuthenticated, superCtrl.superroute);
Where ensureAuthenticated middleware call the req.isAuthenticated() of passport.
This API works fine when I use it from a simple angular front but when I run the test with mocha, the passport.deserializeUser method is not called and isAuthenticated return false. With the fact that the user is correctly logged and retrieved from the /login call, it's all I know.
So the call returns 401 instead of 200.
What can I've possibly missed ?

Trying to test a Node.js Server process using Mocha

Fairly new to Node.js
Made an app that runs a server process and serve files (does not use express or any frameworks), Now I'm trying to unit test it.
I'm trying to use a mocha test for that... I intended to start my server process and then run requests against it to test the expected results (stats code, body content and the likes)
However it's not working properly, all the request fail to connect to the server... I'm pretty sure that the issue is because node is juts running one process loop, the server is not running "in the background" while the queries run or possibly the server is not running yet (started ASYNC) while the request are being made ?
Anyway I was wondering what was the proper way to test this, I assume that either I need to have the server run in the background (like a forked process) and/or maybe I need to find a way to wait for the server process to be "up" first but not sure how.
Or at least recommendations on testing such server process (with Mocha or other).
Thanks.
Here is example test code (Updated since original question)
var server = new Server302('./fixture/');
var instance;
describe('Tests', function() {
before(function(done) {
instance = http.createServer(function(request, response) {
console.log(request.url);
server.serve(request, response);
}).listen(8000);
instance.on("listening", function() {
console.log("started");
done();
});
});
after(function(done){
instance.close();
console.log("stopped");
done();
});
it("Should fetch test.html", function(done) {
console.log("test1");
http.get("http://localhost:8000/", function(res) {
res.on('data', function(body) {
console.log(body)
expect(body).toEqual("test");
done();
});
})
});
It seem to Execute in order but still fails with a connection error, whereas it works when testing manually with the browser:
started
test1
․․․stopped
✖ 1 of 1 tests failed:
1) Tests Should fetch test.html:
Error: connect ECONNREFUSED
at errnoException (net.js:670:11)
at Object.afterConnect [as oncomplete] (net.js:661:19)
In your before don't call done until you get the "listening" event fired by the server.
before(function(done) {
instance = http.createServer(function(request, response) {
console.log(request.url);
server.serve(request, response);
}).listen(8000);
instance.on("listening", function() {
console.log("started");
done();
});
});
That should ensure your test connections don't start before the server is ready.
See also the documentation for server.listen
Also had to deal with the body coming in chunks, here is the final thing that works, in case that helps somebody else:
var Server302 = require('../lib/server302.js'),
http = require('http'),
assert = require("assert");
var server = new Server302('./fixture/');
var instance;
describe('Tests', function() {
before(function(done) {
instance = http.createServer(function(request, response) {
server.serve(request, response);
}).listen(8100);
instance.on("listening", function() {
done();
});
});
after(function(done) {
instance.close();
done();
});
it("Should fetch test.html", function(done) {
console.log("test1");
var body = "";
http.get({host: "localhost", port:8100, path: "/"}, function(res) {
res.on('data', function(chunk) {
// Note: it might be chunked, so need to read the whole thing.
body += chunk;
});
res.on('end', function() {
assert.ok(body.toString().indexOf("<a href='/dummy.txt'>") !== -1);
assert.equal(res.statusCode, 200);
done();
});
})
});
it("Should fetch dummy.txt", function(done) {
http.get({host: "localhost", port:8100, path: "/dummy.txt"}, function(res) {
res.on('data', function(body) {
assert.equal(res.statusCode, 200);
assert.ok(body.toString().indexOf("test") === 0);
done();
});
});
});
it("Should get 404", function(done) {
http.get({host: "localhost", port:8100, path: "/qwerty"}, function(res) {
assert.equal(res.statusCode, 404);
done();
});
});
});
Using SuperTest
Here is a full and straightforward example using SuperTest and Mocha:
var server = new Server302('./fixture/');
var request = require('supertest');
describe('Tests', function() {
it('Should fetch test.html', function(done) {
request(server)
.get('/')
.expect('test', done);
});
});
SuperTest allows you to:
Request your server using SuperAgent (much easier to use than the low level http agent).
Bound your server to an ephemeral port so there is no need to keep track of ports (you can still do it manually if needed).
Use sugary expect methods that works with Mocha (or any other test framework).

Resources