I am using supertest, mocha and expect for testing my app. I encountered an issue where the document returned is null and there is no error.
router.get('/user', function (req, res) {
User.findOne({
_id: '56c59bb07a42e02d11a969ae'
}, function (err, user) {
if(err) return res.status(404).json({message: 'not found: ' + err.message});
res.status(200).json(user);
});
});
When I test this on Postman I always get 200 which is what I expected but when I run the test I get 404 :(
My simple test code below where I always get the 404.
it('get user', function (done) {
request(app)
.get('/user')
.expect(200)
.end(function (err, res) {
if (err) throw err;
done();
});
});
Both Postman and the test are referring to the same mongoose database so I'm sure that it should be able to fetch the user. How mongoose and the app are setup in my server below.
mongoose.connect('mongodb://localhost/scratch', options);
app.listen(port, function () {
console.log('Scratch started on port ' + port);
});
Is there something I need to do to make it work?
I modified the test a bit where the User is created on 'before'.
before(function (done) {
connection.on('error', console.error);
connection.once('open', function () {
done();
});
mongoose.connect(config.db[process.env.NODE_ENV]);
var userInfo = {
"username": "naz2#gmail.com",
"password" : "123456",
"nickname": "naz"
}
var newUser = User(userInfo);
newUser.save(function (err, doc) {
if(err) {
console.log('err: ' + err.message);
} else{
console.log('saved');
}
})
console.log(mongoose.connection.readyState);
done();
});
Then ran the same test and it worked!
My guess is that during the test the app is querying against documents in memory( I verified that by checking the db and the new user was not added) and not to an existing document like I was expecting when testing with Postman. Which means I need to seed the test db first before I can use it for the test.
I am new to Nodejs and I'm curious to what caused the documents to be created in memory and how mongoose/express know that it is ran by a test/supertest and behave accordingly.
Related
var express = require('express');
var pg = require('pg');
var app = express();
var connectionString = "postgres://...";
app.get('/', function (req, res, next) {
pg.connect(connectionString, function (err, client, done) {
if (err) {
console.log("not able to get connection " + err);
res.status(400).send(err);
}
client.query('SELECT * FROM employee WHERE empid=$1', [1], function (err, result) {
done(); // closing the connection;
if (err) {
console.log(err);
res.status(400).send(err);
}
res.status(200).send(result.rows);
});
});
});
app.listen(3000, function () {
console.log('Server is running.. on Port 3000');
});
This is my nodejs file and the connectionString is database information that connected heroku and postgreSQL.
But when I run this one, I only get
client.query('SELECT * FROM employee WHERE empid=$1', [1], function (err, result) {
^
TypeError: Cannot read property 'query' of null
How can I solve it?
In your error handler here:
if (err) {
console.log("not able to get connection " + err);
res.status(400).send(err);
}
client.query(...)
You need to add a return so that after you send the error status, the code does not continue to try to execute client.query() because client does not have a valid value in it if there was an error. So, change to this:
if (err) {
console.log("not able to get connection " + err);
res.status(400).send(err);
return;
}
client.query(...)
Though it has less of a consequence, the same is true here:
if (err) {
console.log(err);
res.status(400).send(err);
}
res.status(200).send(result.rows);
Where you need to add a return:
if (err) {
console.log(err);
res.status(400).send(err);
return;
}
res.status(200).send(result.rows);
The overall issue in these two cases is that while res.status(...).send(...) sends the response back to the client, it does not stop your code from continuing to execute after that so you still need proper flow control with if/else or an appropriate return to control the flow of execution of the code so after an error it doesn't immediately go execute other parts of the code that you don't want it to.
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!
I was wondering , what is the best practice to keep the code DRY when developing node.js api with database calls.
I seems like I'm having alot of repeated code.
For example, look at this:
app.get('/api/users_count', function (req,res) {
pool.connect(function(err, client, done) {
if(err) {
return console.error('error fetching client from pool', err);
}
client.query('SELECT count(*) FROM users;', function(err, result) {
done();
if(err) {
return console.error('error running query', err);
}
res.json({"users count": result.rows[0].count});
});
});
});
and this:
app.get('/api/users/:id', function (req,res) {
pool.connect(function(err, client, done) {
if(err) {
return console.error('error fetching client from pool', err);
}
client.query('SELECT name FROM users WHERE id=$1;',req.param.id, function(err, result) {
done();
if(err) {
return console.error('error running query', err);
}
res.json({"user name": result.rows[0].name});
});
});
});
How can I avoid the repeating of error handing , connect call, and just focus on route and query.
Thanks!
Take a look at sample below, should help
//Create fn that connects, pulls data, and passes it to callback
function customPool(query, values, callback) {
pool.connect(function(err, client, done) {
if(err)
return callback(err);
client.query(query, values, function(q_err, result) {
done();
if(q_err)
return callback(q_err);
callback(null, result.rows);
});
}
}
//Reuse it
app.get('/api/users_count', function (req,res) {
var query = 'SELECT count(*) FROM users;';
customPool(query, undefined, function(err, rows) {
if(err)
return console.error('error fetching client from pool', err);
res.json({"users count": rows[0].count});
});
});
app.get('/api/users/:id', function (req,res) {
var query = 'SELECT name FROM users WHERE id=$1;';
customPool(query, req.params.id, function(err, rows) { //<-- notice, req.params.id not req.param.id
if(err)
return console.error('error fetching client from pool', err);
res.json({"users name": rows[0].name});
});
});
For starters, use pg-promise for database communications, to avoid connecting manually. Then your code will be way simpler, as shown below.
code 1:
app.get('/api/users_count', function (req, res) {
db.one('SELECT count(*) FROM users')
.then(data=> {
res.json({"users count": +data.count});
})
.catch(error=> {
// should provide a response here also ;)
console.error(error);
});
});
code 2:
app.get('/api/users/:id', function (req, res) {
db.one('SELECT name FROM users WHERE id=$1', +req.param.id)
.then(user=> {
res.json({"user name": user.name});
})
.catch(error=> {
// should provide a response here also ;)
console.error(error);
});
});
Then you can simplify it further, by implementing a generic request->response logic, depending on your application's requirements.
I am a newbie in testing in Javascript and trying to test my NodeJS backend using mocha and chai.
How all of my routes are filled with a middleware which does not allow people to go forward if they aren't logged in.
Something like this
app.post('/user/analytics/driverdata', checkauth, function(req, res) {
analytics.driverData(req, res);
});
where checkauth is
var checkauth = function(req, res, next) {
console.log("In checkauth");
if (req.isAuthenticated()) {
next();
} else {
console.log("Doesn't authenticate");
res.status(401);
res.set('Content-Type', 'application/json');
res.end(JSON.stringify({
'success': false
}));
}
};
The isAuthenticated parameter is attached to request by PassportJS, when it deserializes a request.
What I want to do is write a test for
app.post('/user/analytics/driverdata', checkauth, function(req, res) {
analytics.driverData(req, res);
});
this API. in which I am failing as I am not logged in hence unable to reach there.
So I wrote a beforeEach to login the user beforeEach it. It goes like this.
var expect = require('chai').expect;
var request = require('superagent');
beforeEach(function(done){
//login into the system
request
.post("http:localhost:5223/user/authenticate/login")
.send({username : "saras.arya#gmail.com", password : "saras"})
.end(function assert(err, res){
if(err){
console.log(err);
done();
}
else{
done();
}
});
});
I don't know what am I doing wrong and the internet has failed me. Any help to point out where am I going wrong will be appreciated.
After seeing a lot of stuff and fidgeting around I think I finally cracked it. It would be harsh not to mention the answer here which introduced me to the concept of an agent. Which helped me crack this.
Inside your describe block, or probably before the block you can have the following it.
var superagent = require('superagent');
var agent = superagent.agent();
it('should create a user session successfully', function(done) {
agent
.post('http://localhost:5223/user/authenticate/login')
.send({
username: 'whatever#example.com',
password: 'ssh-its-a-secret'
})
.end(function(err, res) {
console.log(res.statusCode);
if (expect(res.statusCode).to.equal(200))
return done();
else {
return done(new Error("The login is not happening"));
}
});
});
the agent variable holds the cookies for you, which are then used by PassportJS to authenticate you.
here is how you do it. So the agent variable is inside a describe. In the same describe inside another it.
it("should test analytics controller", function(done) {
agent.post('http://localhost:5040/user/analytics/driverData')
.send({
startDate: "",
endDate: "",
driverId: ""
})
.end(function(err, res) {
if(!err)
done();
});
});
This function passes like a charm. This was one complete documentation that was missing.
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.