Node js supertest is async? - node.js

I want to write some tests for some routes and I want to do something like this:
var should = require('should');
var app = require('../../app');
var request = require('supertest');
describe('Create and check that the create was successfull', function() {
var user_id = '';
it('should add a new case and return a JSON array', function(done) {
var newUser = {nume : 'Test', prenume: 'test', varsta : 23};
request(app)
.post('/api/new_user')
.send(newUser)
.expect(201)
.expect('Content-Type', /json/)
.end(function(err, res) {
if (err) return done(err);
res.body.should.be.instanceOf(Array);
res.body.should.have.property('_id');
user_id = res.body._id;
done();
});
});
it('should return the new user ', function(done) {
request(app)
.get('/api/new_user/' + user_id)
.expect(200)
.expect('Content-Type', /json/)
.end(function(err, res) {
if (err) return done(err);
res.body.should.be.instanceOf(Object);
res.body._id.should.be.exactly(user_id);
done();
});
});
});
I am not sure if the two it statements are executed one after the other or each one is done async and when I get to the second it the first one is not executed so it will fail because the insert was not done in the server.
Should I use async.series?

In your example supertest is only responsible for the chain from request(app) down, so it's actually the provider of the describe() and it() calls that determines the order, or lack thereof, in which your tests are executed, which I guess is mocha, right?
If so, Mocha will run your testcases in order (as in, the second one will be called once the first one has finished).

Supertest request can't persist session therefore your second test case seems failing. Basically the second case running as it is not aware at all about the first case.
You can persist your session with request.agent.
Below is a quick example:
var should = require('should');
var app = require('../../app');
var request = require('supertest');
describe('Create and check that the create was successfull', function() {
var session;
var new_user = {name:'Test',presume:'test',vast:'23'};
var user_id;
before (function(done){
session = request.agent(app);
session
.post('/api/new_user')
.send('new_user')
.expect(201)
.expect('Content-Type', /json/)
.end(function(err, res) {
if (err) return done(err);
res.body.should.be.instanceOf(Array);
res.body.should.have.property('_id');
user_id = res.body._id;
done();
});
});
it('should return the new user ', function(done){
session
.get('/api/new_user/' + user_id)
.expect(200)
.expect('Content-Type', /json/)
.end(function(err, res) {
if (err) return done(err);
res.body.should.be.instanceOf(Object);
res.body._id.should.be.exactly(user_id);
done();
});
});
...
});

Related

Chai assertion for expressjs token-based app

I'm writting tests for an expressjs token-based app, however I read the chai doc and find only cookie based auth. With an workaround the test passed with the code above, however I need the token to be used in others it statements. Any tips are welcome. I'm using mochajs to run tests.
var chai = require('chai');
var chaiHttp = require('chai-http');
var mongoose = require('mongoose');
var server = require('../app');
var Dish = require('../models/dishes');
var should = chai.should();
chai.use(chaiHttp);
describe('Users', function() {
it('should return success on /login POST', function(done) {
var agent = chai.request.agent(server)
agent
.post('/users/login')
.send({"username": "admin", "password": "password"})
.then(function(res) {
res.should.have.cookie('token');
return agent.get('/users')
.then(function(res) {
res.should.have.status(200);
});
});
done();
});
it('should return success on /users GET', function(done) {
chai.request(server)
.get('/users')
.end(function(err, res) {
res.should.have.status(200);
done();
});
});
});
describe('Dishes', function() {
it('should list ALL dishes on /dishes GET', function(done) {
chai.request(server)
.get('/dishes')
.end(function(err, res) {
res.should.have.status(200);
done();
});
});
it('should list a SINGLE dish on /dish/<id> GET');
it('should add a SINGLE dish on /dishes POST');
it('should update a SINGLE dish on /dish/<id> PUT');
it('should delete a SINGLE dish on /dish/<id> DELETE');
});
You can start your test by before, and then get you token
let token = '';
describe('Users', () => {
before((done) => {
const userCredentials = {
username: 'admin',
password: 'password'
};
chai.request(server)
.post('/api/v1/auth')
.send(userCredentials)
.end((err, res) => {
console.log('res', res.body.data.customer.token);
token = `Bearer ${res.body.data.customer.token}`;
done();
});
})
// Your test
})

Tests fail when 8th endpoint is created using generator angular-fullstack

The problem is as weird as the title. I have a project which I created using the generator angular-fullstack, which I am connecting to a MSSQL server using Sequelize (who the f uses MSSQL... client's demands) and everything has been working really well until I had to create the 8th endpoint (using angular-fullstack:endpoint).
Every time I created an endpoint all the test (automatically created and executed using mocha) would work except for the PATCH verb integration test, which I would just eliminate as I am not using PATCH at all.
After I created the 8th endpoint (doing the same I did for every other one) the integration tests created by the generator itself (the unit tests work perfectly) started to fail (not just the endpoint's test, but other tests that used to work before), and they fail randomly (sometimes 3 of them fail, sometimes 4, and sometimes they all work), which makes me think of some kind of race condition (which I wasn't able to find).
Findings:
The POST integration test "works" but the result doesn't show up in the database. The log shows a correct SQL command sent to the database:
INSERT INTO [Findings] ([name],[info],[createdAt],[updatedAt]) OUTPUT INSERTED.* VALUES (N'New Finding',N'This is the brand new finding!!!',N'2018-03-05 22:30:24.000',N'2018-03-05 22:30:24.000');, and it returns 201 as status.
When the status code returned is 500, the error is usually name: 'SequelizeDatabaseError',
message: 'Invalid object name \'Findings\'.', as if the Table didn't exist, but it does!
If you have any idea on what can I try, I will be more than grateful! (I have already searched everywhere I could think of, but it's even hard to search for this problem)
This is the file containing the last-endpoint-created's tests. I can add any other file that might help to find a possible solution!
'use strict';
/* globals describe, expect, it, beforeEach, afterEach */
var app = require('../..');
import request from 'supertest';
var newFinding;
describe('Finding API:', function() {
describe('GET /api/findings', function() {
var findings;
beforeEach(function(done) {
request(app)
.get('/api/findings')
.expect(200)
.expect('Content-Type', /json/)
.end((err, res) => {
if(err) {
return done(err);
}
findings = res.body;
done();
});
});
it('should respond with JSON array', function() {
expect(findings).to.be.instanceOf(Array);
});
});
describe('POST /api/findings', function() {
beforeEach(function(done) {
request(app)
.post('/api/findings')
.send({
name: 'New Finding',
info: 'This is the brand new finding!!!'
})
.expect(201)
.expect('Content-Type', /json/)
.end((err, res) => {
if(err) {
return done(err);
}
newFinding = res.body;
done();
});
});
it('should respond with the newly created finding', function() {
expect(newFinding.name).to.equal('New Finding');
expect(newFinding.info).to.equal('This is the brand new finding!!!');
});
});
describe('GET /api/findings/:id', function() {
var finding;
beforeEach(function(done) {
request(app)
.get(`/api/findings/${newFinding._id}`)
.expect(200)
.expect('Content-Type', /json/)
.end((err, res) => {
if(err) {
return done(err);
}
finding = res.body;
done();
});
});
afterEach(function() {
finding = {};
});
it('should respond with the requested finding', function() {
expect(finding.name).to.equal('New Finding');
expect(finding.info).to.equal('This is the brand new finding!!!');
});
});
describe('PUT /api/findings/:id', function() {
var updatedFinding;
beforeEach(function(done) {
request(app)
.put(`/api/findings/${newFinding._id}`)
.send({
name: 'Updated Finding',
info: 'This is the updated finding!!!'
})
.expect(200)
.expect('Content-Type', /json/)
.end(function(err, res) {
if(err) {
return done(err);
}
updatedFinding = res.body[0];
done();
});
});
afterEach(function() {
updatedFinding = {};
});
it('should respond with the updated finding', function() {
expect(updatedFinding).to.equal(1);
});
it('should respond with the updated finding on a subsequent GET', function(done) {
request(app)
.get(`/api/findings/${newFinding._id}`)
.expect(200)
.expect('Content-Type', /json/)
.end((err, res) => {
if(err) {
return done(err);
}
let finding = res.body;
expect(finding.name).to.equal('Updated Finding');
expect(finding.info).to.equal('This is the updated finding!!!');
done();
});
});
});
});
I found a workaround and I think I have found the problem, but I don't understand why it is happening now, and not before.
Some tests were being executed before the db connection was established, so I have added
before(function(done) {
app.on('appStarted', function() {
done();
});
});
to the new test file, and now it works without any problem!

Can't made authenticated requests with Mocha and Supertest

I'm trying to test an API with Mocha and Supertest without lucky to make it work.
I have the following code:
var supertest = require('supertest');
describe('Routing', function() {
var url = 'http://example.com';
var server = supertest.agent(url);
var credentials = {
user: 'username',
pass: 'password'
};
describe('Login', function() {
it('should login ok given valid credentials', function(done) {
server
.post('/login.php')
.send(credentials)
.end(function(err, res) {
if (err) {
throw err;
}
server.saveCookies(res);
done();
});
});
it('should correctly make an authenticated request', function(done){
server
.get('/api/me/accounts?_=1449865354112')
.end(function(err,res) {
if (err) {
throw err;
}
res.status.should.be.equal(200);
done();
});
});
});
});
The login request works fine, I get authenticated. The second call throws a 401 status.
I read the documentation but I can't make it work.
What is wrong?
thanks!
UPDATE:
I finally get authenticated by sending the params using .field('user', 'myUsername') and .field('pass', 'myPassword').
Also I have to persist the cookie between calls:
cookie = res.headers['set-cookie']; when I get authenticated, and .set('cookie', cookie) in the next requests.
.send() is for your data. .auth() is for your credentials. Try:
it('should login ok given valid credentials', function(done) {
server
.post('/login.php')
.auth(credentials)
.send({"some": "value"})
.expect(200)
.end(function(err, res) {
if (err) {
done(error);
}
server.saveCookies(res);
done();
});
});
See http://visionmedia.github.io/superagent/docs/test.html for a bit more information on supertest.
That is because the session (cookie) is not persisted between your two tests.
First you should do the two calls inside the same test.
Second i remember that i have used superagent to persist the session between two calls to the same server. But it seems that supertest now expose the agent to persist the session.
var supertest = require('supertest');
var app = express();
var agent = supertest.agent(app);
// then you can persist cookie
agent
.post('/login.php')
.auth(credentials)
...
edit :
here is an example of how i have used superagent for tests :
var request = require('superagent');
var postData= {
email: 'john#test.com',
password: 'test'
};
var user1 = request.agent();
user1.post('http://localhost:3000/user/login')
.send(postData)
.end(function (err, res) {
expect(err).to.not.exist;
expect(res.status).to.equal(200);
var result = res.body;
expect(result.data.message).to.equal('Login successful');
user1.get('http://localhost:3000/user')
.end(function (err, res) {
expect(err).to.not.exist;
expect(res.status).to.equal(200);
var result = res.body;
expect(result.data.email).to.equal('john#test.com');
done();
});
});

Why am I getting a 401 response in my tests?

I am trying to test a route with authentication in my Node / Express / Mongoose back-end.
Here's the test file
var should = require('should');
var _ = require('lodash');
var async = require('async');
var app = require('../../../../app');
var request = require('supertest');
var mongoose = require('mongoose');
var User = mongoose.model('User');
var Firm = mongoose.model('Firm');
var firm, user, userPassword, createdFirm, loggedInUser;
describe('GET /api/firms', function(){
beforeEach(function (done) {
firm = new Firm({
company: 'My test company',
corporateMail: 'test.com'
});
userPassword = 'password';
user = new User({
fistname: 'Fake User',
lastname: 'Fake User',
email: 'test#test.com',
job: 'Partner',
firmName:firm.company,
password:userPassword,
isActivated:true,
_firmId:firm._id
});
function createFirm(cb){
request(app)
.post('/api/firms')
.send(firm)
.expect(201)
.end(function(err, res){
if ( err ) throw err;
createdFirm = res.body;
cb();
});
}
function createUser(cb){
request(app)
.post('/api/common/users')
.send(user)
.expect(200)
.end(function(err, res){
createdUser = res.body;
if ( err ) throw err;
cb();
});
};
async.series([function(cb){
createFirm(cb);
}, function(cb){
createUser(cb);
}], done);
});
afterEach(function (done) {
firm.remove();
user.remove();
done();
});
it('should respond with 401 error', function(done) {
request(app)
.get('/api/firms')
.expect(401)
.end(function(err, res) {
if (err) return done(err);
done();
});
});
it('should login', function(done) {
request(app)
.post('/auth/local')
.send({email:user.email, password:user.password})
.expect(200)
.end(function(err, res) {
if (err) return done(err);
done();
});
});
it('should respond with 200 after login', function(done) {
request(app)
.get('/api/firms')
.expect(200)
.end(function(err, res) {
if (err) return done(err);
done();
});
});
});
In the workflow the firm object is created first and then returns its Id so I can create the user with the firmId as a reference.
I would like to test the /api/firms route after the user is authenticated but in spite of my various attempts (using superagent, logging in the before section) I always get a 401 response in the last should section instead of an expected 200.
Actually the important thing to keep in mind is, as KJ3 said, how the authentication is set up. In my case I forgot to mention that I was using jwt. The way it works is the following, you supply a username and a password and the server returns a token created with jwt.
So it makes sense to send the token back for each request in the tests.
The way to achieve this is first by storing the token after authentication in the before section
function createUser(cb){
request(app)
.post('/api/users')
.send(user)
.expect(200)
.end(function(err, res){
if ( err ) throw err;
authToken = res.body.token;
cb();
});
};
Then by adding .set in the request with the token in the correct format ('Bearer ' + token , which is defined in the authentication service):
it('should respond with 200', function(done) {
var authToken = 'Bearer ' + createdUser.token;
request(app)
.get('/api/firms')
.set('Authorization', authToken)
.expect(200)
.end(function(err, res) {
if (err) return done(err);
done();
});
});
In the case the test sends a 200 back, which is expected and sends a 401 if the .set(...) is commented out.
Good news is that this is achieved with supertest, so no need to add anything, less good news is that you need to add the .set(...) to each test request.
If you were to go through the last 2 tests in a browser, depending on how you have it setup, yes it would work thanks to cookies and sessions, but here the /api/firms test is independent of the auth/local test. So a 401 is the correct response.
It really depends on how your auth is setup, but you need to authenticate on the /api/firms test too. Either by sending the credentials again (every single one of my mocha tests authenticates each time) or implement sessions into the tests, see this SO post for some direction.

supertest agent doesn't seem to persist sessions in node.js express.js app

From all the documents and examples I've read, it should be possible to persist a session in supertest using an agent:
var app = require('../../../server'),
should = require('should'),
request = require('supertest'),
mongoose = require('mongoose'),
User = mongoose.model('User'),
_ = require('lodash');
var user = {
name: 'Sterling Archer',
email: 'duchess#isis.com',
password: 'guest'
};
describe('user.me', function() {
var url = '/user';
var agent = request.agent(app);
var new_user = new User(user);
new_user.save();
it('should return a user object', function(done) {
agent
.post('/signin')
.send(_.omit(user, 'name'))
.expect(200).end(function(err, res) {
console.log(res.headers['set-cookie']);
});
agent
.get(url)
.expect(200)
.end(function(err, res) {
should.not.exist(err);
console.log(res.headers['set-cookie']);
res.body.should.have.property('user');
res.body.user.should.have.properties('name', 'email');
done();
});
});
});
The session should persist since each request above is using the same agent. However that doesn't seem to be the case - the output from the set-cookie logs follows:
[ 'connect.sid=s%3AsFl1DQ4oOxC8MNAm79mnnr9q.gMkp8iEWtG8XlZZ2rkmheBwxKAyLyhixqDUOkYftwzA; Path=/; HttpOnly' ]
[ 'connect.sid=s%3AEzfbPyRGMff7yBXc9OAX3vGT.Ze2YoxZzuB6F6OwOk7mvrk96yPP2G4MGV%2Bt1rVjTyS8; Path=/; HttpOnly' ]
passport.js is being used for authentication and sessions. I would expect connect.sid above to be constant for both requests, but it looks like a new session is being created on each call so the agent isn't logged in on the second call and no user object is returned.
When I test my app manually in a browser connect.sid remains constant after login and the functionality I'm testing does work.
I must be doing something wrong with agent, and I'm hoping someone can spot it. Otherwise, suggestions on how I could debug the issue would be much appreciated.
You're sending the second request without waiting for the first one to be responded; if you don't give the agent time to receive the Set-Cookie header in the response and use its value as a the Cookie header in the same request, a new session will be created. Try it this way:
it('should return a user object', function(done) {
agent
.post('/signin')
.send(_.omit(user, 'name'))
.expect(200).end(function(err, res) {
console.log(res.headers['set-cookie']);
agent
.get(url)
.expect(200)
.end(function(err, res) {
should.not.exist(err);
console.log(res.headers['set-cookie']); // Should print nothing.
res.body.should.have.property('user');
res.body.user.should.have.properties('name', 'email');
done();
});
});
});
Esteban's suggestion pointed out that I was overlooking the asynchronous nature of the code. Going back to this example I realized I missed the significance of logging in in a separate test; doing so solved my problem.
Though I'm now creating dependent tests, which I'm not crazy about.
var app = require('../../../server'),
should = require('should'),
request = require('supertest'),
mongoose = require('mongoose'),
User = mongoose.model('User'),
_ = require('lodash');
var user = {
name: 'Sterling Archer',
email: 'duchess#isis.com',
password: 'guest'
};
var agent = request.agent(app);
describe('User Controller', function() {
before(function(done) {
var new_user = new User(user);
new_user.save();
done();
});
describe('user.signin', function() {
var url = '/signin';
it('should signin and return a user object', function(done) {
agent
.post(url)
.send(_.omit(user, 'name'))
.expect(200)
.end(function(err, res) {
should.not.exist(err);
res.body.should.have.property('user');
res.body.user.should.have.properties('name', 'email');
done();
});
});
});
describe('user.me', function() {
var url = '/user';
it('should return a user object', function(done) {
agent
.get(url)
.expect(200)
.end(function(err, res) {
should.not.exist(err);
res.body.should.have.property('user');
res.body.user.should.have.properties('name', 'email');
done();
});
});
});
after(function(done) {
User.remove().exec();
done();
});
});

Resources