I have a simple express route that GETs a user from mongo
routes.js
router.get('/users/:username', userController.read);
user.controller.js
function read(req, res) {
User.findOne({
username: req.params.username
}, function(err, user) {
if (err) res.status(404).json(err);
res.status(200).json(user);
});
}
I ran some tests through Postman and supertest
here's the supertest call
it('should retrieve user data', function(done) {
request(BASE_URL)
.get('/users/' + testUserData.username)
.expect(200)
.expect({
username: testUserData.username,
email: testUserData.email
}, done)
})
The user's data comes back fine but the status code is 201 instead of 200.
Related
Here is the route I want to test:
app.post('/api/user', (req, res) => {
dbService.replaceUserOnDuplicate(req.body, function returnResponse(insertedId) {
if (insertedId === 0 || insertedId === req.body.id) {
return res.sendStatus(200);
}
// TODO_MINH: Send an appropriate error to handle by the front-end
return res.send({});
});
});
I can use chai-http to do something like this (psuedo-code):
it('test', function (done) {
chai.request(server)
.post('/api/user')
.send({ user: SomeUserObject })
.end(function (err, res) {
res.should.have.status(200);
done();
});
});
However, the api/users route makes a Database call. How would I use sinon to stub this method (replaceUserOnDuplicate) so that it returns a dummy response (like 0, or anything)?
Is it possible? I'm looking at the Chai-HTTP syntax and I see no room to insert any Sinon stubbed methods.
For reference, here is the dbService (mySQL node.js):
replaceUserOnDuplicate: function(user, callback) {
this.tryConnect().getConnection(function(err, con) {
var sql = queries.ReplaceUserOnDuplicate;
// Insert parameters
con.query(sql, [user.id, user.googleID, user.gender, user.firstName, user.lastName, user.email, user.isProfileSetUp, user.location, user.phoneNumber,
// On Duplicate Key Update parameters
user.googleID, user.gender, user.firstName, user.lastName, user.email, user.isProfileSetUp, user.location, user.phoneNumber],
function (err, result) {
con.release();
if (err) throw err;
return callback(result.insertId);
});
});
},
Thanks for your help!
A potential solution: if I use middleware to set the property of req.db to our dbService object, then I can dependency inject the dbService's calls within chai-http...By sending them parameters with the .send(). I believe .send() can be chained.
Is this valid?
Example (Middleware):
var exposeDb = function(req, resp, next){
req.dbService= dbService;
next();
};
app.use('/api/user', exposeDb, next);
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.
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.
I write test for my sails application, but have some problem in controller. When user signup I check for duplication, if yes - render form with error, if no - render form with success message. So can anyone know how to test it right? Or maybe you can suggest better code structure, thanks a lot.
Controller :
User.findOne({ email: req.body.email }, function(err, user) {
if(err) return res.json(err)
if(!user) {
User.create({email:req.body.email, password:req.body.password}).exec(function createCB(err, user){
if(err) return res.json(err);
res.view('signup', { message: 'Done, user created'})
})
} else
res.view('signup', { message: 'User already exist'})
})
Test :
it('should show error if duplicated user - TODO', function (done) {
request(sails.hooks.http.app)
.post('/signup')
.send({ email: 'test#test.te', password: 'test' })
.expect(200)
.end(done)
})
So question is, how can I test res.view?
I usually use simple request at my test if I want to test my view. Like this:
it('able to test view', function(done) {
require('request-promise')('http://localhost:1337/')
.then(function(res) {
res.should.contains('Done, user created');
done();
})
.catch(done);
});
It will check the whole body, is there any match string that we provide. Remember to do npm install request-promise --save-dev first (or any relevant library that can make request).
Or you can use supertest library as mentioned by #Yann like this
it('should be able to test view', function(done) {
require('supertest')
.agent(sails.hooks.http.app)
.post('/signup')
.send({ email: 'test#test.te', password: 'test' })
.expect(200)
.end(function(err, res) {
should.not.exist(err);
res.should.contains('Done, user created');
return done();
});
});
You can use a library like supertest to do an HTTP request:
it('should be able to test view', function(done) {
require('supertest')
.agent(sails.hooks.http.app)
.post('/signup')
.send({ email: 'test#test.te', password: 'test' })
.expect(200)
.end(function(err, res) {
should.not.exist(err);
res.should.contains('Done, user created');
return done();
});
});
Question:
How do would I write a post request test in mocha that tests if the response matches?
The response will just be a url string as it is a redirect for a 3rd party service.
Working Example Payload:
curl -H "Content-Type: application/json" -X POST -d '{"participant":{"nuid":"98ASDF988SDF89SDF89989SDF9898"}}' http://localhost:9000/api/members
member.controller.js // post method
// Creates a new member in the DB.
exports.create = function(req, res) {
Member.findByIdAndUpdate(req.body.participant.nuid,
{ "$setOnInsert": { "_id": req.body.participant.nuid } },
{ "upsert": true },
function(err,doc) {
if (err) throw err;
res.send({
'redirectUrl': req.protocol + '://' + req.get('host') + '/registration/' + req.body.participant.nuid
})
}
);
};
Expected res.send
{"redirectUrl":"http://localhost:9000/registration/98ASDF988SDF89SDF89989SDF9898"}
Working Example GET request Test
var should = require('should');
var app = require('../../app');
var request = require('supertest');
describe('GET /api/members', function() {
it('should respond with JSON array', function(done) {
request(app)
.get('/api/members')
.expect(200)
.expect('Content-Type', /json/)
.end(function(err, res) {
if (err) return done(err);
res.body.should.be.instanceof(Array);
done();
});
});
it('should respond with redirect on post', function(done) {
// need help here
});
});
Try with this:
it('should respond with redirect on post', function(done) {
request(app)
.post('/api/members')
.send({"participant":{"nuid":"98ASDF988SDF89SDF89989SDF9898"}})
.expect(200)
.expect('Content-Type', /json/)
.end(function(err, res) {
if (err) done(err);
res.body.should.have.property('participant');
res.body.participant.should.have.property('nuid', '98ASDF988SDF89SDF89989SDF9898');
});
done();
});
You can also set the type to "form" and content type to json as I show below:
it("returns a token when user and password are valid", (done) => {
Users.createUserNotAdmin().then((user: any) => {
supertestAPI
.post("/login")
.set("Connection", "keep alive")
.set("Content-Type", "application/json")
.type("form")
.send({"email": user.email, password: "123456"})
.end((error: any, resp: any) => {
chai.expect(JSON.parse(resp.text)["token"].length).above(400, "The token length should be bigger than 400 characters.");
done();
})
});
});
You also have to set the body-parser when you create the server as I show below:
server.use(bodyParser.urlencoded({ extended: false }));
server.use(bodyParser.json());