I am testing my model with mocha. I have the following files:
test/utils.js
'use strict';
// Modified from https://github.com/elliotf/mocha-mongoose
var config = require('../config/db.js');
var mongoose = require('mongoose');
// ensure the NODE_ENV is set to 'test'
// this is helpful when you would like to change behavior when testing
process.env.NODE_ENV = 'test';
beforeEach(function (done) {
function clearDB() {
for (var i in mongoose.connection.collections) {
mongoose.connection.collections[i].remove(function() {});
}
return done();
}
if (mongoose.connection.readyState === 0) {
mongoose.connect(config.url, function (err) {
if (err) {
throw err;
}
return clearDB();
});
} else {
return clearDB();
}
});
afterEach(function (done) {
mongoose.disconnect();
return done();
});
test/testUserModel.js
'use strict';
// import the moongoose helper utilities
var utils = require('../test/utils');
var should = require('should');
// import our User mongoose model
var User = require('../app/models/user.js');
var testUser1 = new Object({
profilePic: "testPic",
email: "testEmail",
first_name: "fname",
last_name: "lname",
description: "description",
personality: "personality",
phone_number: "phoneNum",
password: "password",
courses: {
course_name: "courseTest"
},
role: "testRole" //student/admin/teacher
});
var testUser2 = new Object({
profilePic: "testPic2",
email: "testEmail2",
first_name: "fname2",
last_name: "lname2",
description: "description2",
personality: "personality2",
phone_number: "phoneNum2",
password: "password2",
courses: {
course_name: "courseTest2"
},
role: "testRole2" //student/admin/teacher
});
describe('Users: models', function () {
describe('#register()', function () {
it('should register a new User', function (done) {
User.register(testUser1, function (err, createdUser) {
should.not.exist(err);
createdUser.email.should.equal("testEmail");
createdUser.first_name.should.equal("fname");
createdUser.phone_number.should.equal("phoneNum");
createdUser.password.should.equal("password");
createdUser.role.should.equal("testRole");
done();
});
});
});
describe('#getAllUsers', function () {
it('should return all users in DB', function (done) {
console.log("about to add stuff");
User.register(testUser1, function (err, createdUser) {
console.log(createdUser);
});
User.register(testUser2, function (err, createdUser) {
console.log(createdUser);
});
User.getAllUsers(function (err, createdUsers) {
should.not.exist(err);
createdUsers.should.be.instanceof(Array).and.have.lengthOf(2);
done();
});
});
});
});
I have tested the register function of User and it works ok in test #register(),
However in #getAllUsers() I do a register on two user objects, testUser1 and testUSer2. But my User.getAllUsers function is returning nothing from the DB. In deployment the two functions work fine, you can add lots of users and they display in a list in GUI. It seems after the User.register function call the DB is getting cleared so when it gets to User.getAllUsers theres nothing there. Any ideas?
With your code:
describe('#getAllUsers', function () {
it('should return all users in DB', function (done) {
console.log("about to add stuff");
User.register(testUser1, function (err, createdUser) {
console.log(createdUser); // register was successfull
});
User.register(testUser2, function (err, createdUser) {
console.log(createdUser); //register was successfull
});
// Here, it should set in callback, when register function was successfull
User.getAllUsers(function (err, createdUsers) {
should.not.exist(err);
createdUsers.should.be.instanceof(Array).and.have.lengthOf(2);
done();
});
});
you can see an example with register was successfull as below:
it('should return all users in DB', function (done) {
console.log("about to add stuff");
User.register(testUser1, function (err, createdUser) {
console.log(createdUser);
// here, it should set in callback, when register is successfull
User.getAllUsers(function (err, createdUsers2) {
should.not.exist(err);
createdUsers2.should.be.instanceof(Array).and.have.lengthOf(1);
done();
});
});
});
Related
I'm confused by this one. First of all this is my code:
router.post('/update', (req, res, next) => {
// Todo legit credit card holding
Account.findOneAndUpdate(
{ _id: req.user._id },
{
$set: {
// username: req.body.username,
creditCardNo: req.body.cardNo,
isPremium: true,
},
},
{ upsert: true },
(err, doc) => {
if (err) {
console.log(err);
}
}
);
var newUser;
Account.findById(req.user._id,(err, doc)=>{
if(err){
console.log(err);
}
else{
newUser = doc;
}
});
console.log(newUser);
res.render('user-pannel/pannel', {
title: 'User pannel',
user: newUser,
});
});
What it does is: It gets the POST call and updates a record in the db. Now I want to basically reload the the page (res.render part) and send the new user object.
I need to send the new one, because the one in req.user is now outdated (was updated before and I'm just printing the old version).
I tried getting around the problem by doing this newUser = doc;, but for some reason the newUservariable is undefined when logged outside of the findById method. Why? If I console log the doc inside of the findById method, it returns the changed object.
I turned it into an async function and awaited its resolution with the desired value.
router.post('/update', async (req, res, next) => {
// Todo legit credit card holding
Account.findOneAndUpdate(
{ _id: req.user._id },
{
$set: {
// username: req.body.username,
creditCardNo: req.body.cardNo,
isPremium: true,
},
},
{ upsert: true },
(err, doc) => {
if (err) {
console.log(err);
}
}
);
const newUser = await new Promise((resolve, reject) => {
Account.findById(req.user._id,(err, doc) => {
if(err) reject(err);
else resolve(doc);
});
});
console.log(newUser);
res.render('user-pannel/pannel', {
title: 'User pannel',
user: newUser,
});
});
I have a simple app with User and Post models,
var mongoose = require("mongoose");
mongoose.connect("mongodb://localhost/assoc", {useMongoClient:true});
mongoose.Promise = global.Promise;
//Post
var postSchema = new mongoose.Schema({
title: String,
content: String
});
var Post = mongoose.model("Post", postSchema);
//User
var userSchema = new mongoose.Schema({
email: String,
name: String,
posts: [postSchema]
});
var User = mongoose.model("User", userSchema);
I Create a user before (name: "gino") and push a post into:
// var newUser = new User({
// email: "a.b#c.it",
// name: "gino"
// });
//
// newUser.posts.push({
// title: "gino's post",
// content: "this is content"
// });
//
// newUser.save(function (err, user) {
// if (err) {
// console.log(err);
// } else {
// console.log(user);
// }
// });
Also create another post to check if Post model works:
// var newPost = new Post({
// title: "honky",
// content: "tonky"
// });
//
// newPost.save(function (err, post) {
// if (err) {
// console.log(err);
// } else {
// console.log(post);
// }
// });
When I try to find "gino" and push a new item into the posts array I have an error trying to save user (user.save) with this snippet:
User.findOne({name: "gino"}, function (err, user) {
if (err) {
console.log(err);
} else {
console.log(user);
user.posts.push({
title: "post",
content: "content"
});
user.save(function (err, user) {
if (err) {
console.log(err);
} else {
console.log(user);
}
});
}
});
When I run the app i got this:
{ MongoError: Unknown modifier: $pushAll
at Function.MongoError.create (appFolder\node_modules\mongodb-core\lib\error.js:31:11)
at toError (appFolder\node_modules\mongodb\lib\utils.js:139:22)
at appFolder\node_modules\mongodb\lib\collection.js:1059:67
at appFolder\node_modules\mongodb-core\lib\connection\pool.js:469:18
at _combinedTickCallback (internal/process/next_tick.js:131:7)
at process._tickCallback (internal/process/next_tick.js:180:9)
name: 'MongoError',
message: 'Unknown modifier: $pushAll',
driver: true,
index: 0,
code: 9,
errmsg: 'Unknown modifier: $pushAll' }
Someone can help me?
Try using findOneAndUpdate instead.
User.findOneAndUpdate(
{ name: "gino" },
{ $push: { posts: { title: 'post', content: 'content' } } },
{ new: true },
function (err, user) {
if(err) console.log("Something wrong when updating data");
console.log(user);
});
Hope it helps!
If you are using 3.5 MongoDB version or higher, can be an issue with $pushAll, which is deprecated.
I founded an option to work around setting usePushEach to true:
new Schema({ arr: [String] }, { usePushEach: true });
Founded in:
https://github.com/Automattic/mongoose/issues/5574#issuecomment-332290518
Can be useful to use the with .push.
I am new to javascript backend and I am currently learning to build a RESTful API using node.js, express.js, sequelize.js with MySQL as my database. I have successfully built a basic Tasks API as a test. I am looking for feedback on if I did this correctly as far as javascript best practices go within one controller. Any feedback will be appreciated.
Current Logic: User can own multiple tasks
Everything works fine. I am authenticating the users using JWT strategy in Passport.js. I am authenticating them at the router level and then I am double-checking their records in db through userid before they are allowed to make any updates or deletes to their own records. Anyways, here is the controller:
'use strict';
var jwt = require('jsonwebtoken');
var config = require('../config'),
db = require('../services/database'),
Task = require('../models/task');
var TaskController = {};
// GET ALL Tasks
TaskController.get = function (req, res) {
if (!req.user.id) {
res.json({ message: 'You are not authorized.' });
} else {
db.sync().then(function () {
return Task.findAll({ where: { userid: req.user.id } }).then(function (result) {
res.status(202).json(result);
});
});
}
}
// POST ONE Task
TaskController.post = function (req, res) {
if (!req.body.task) {
res.json({ message: 'Please provide a task to post.' });
} else {
db.sync().then(function () {
var newTask = {
userid: req.user.id,
task: req.body.task
};
return Task.create(newTask).then(function () {
res.status(201).json({ message: 'Task Created!' });
});
});
}
}
// PUT ONE Task
TaskController.put = function (req, res) {
if (!req.body.task) {
res.json({ message: 'Please provide a task to update.' });
} else {
db.sync().then(function () {
// Find task by task id and user id
Task.find({ where: { id: req.params.id, userid: req.user.id } })
.then(function (task) {
// Check if record exists in db
if (task) {
task.update({
task: req.body.task
}).then(function () {
res.status(201).json({ message: 'Task updated.' });
});
} else {
res.status(404).json({ message: 'Task not found.' });
}
});
});
}
}
// DELETE ONE Task
TaskController.delete = function (req, res) {
if (!req.params.id) {
res.json({ message: 'Please provide a task to delete.' });
} else {
db.sync().then(function () {
Task.find({ where: { id: req.params.id, userid: req.user.id } })
.then(function (task) {
if (task) {
task.destroy({ where: { id: req.params.id } })
.then(function () {
res.status(202).json({ message: 'Task deleted.' });
});
} else {
res.status(404).json({ message: 'Task not found.' });
}
});
});
}
}
module.exports = TaskController;
The TaskController.js looks good but I would suggest moving all the ORM logic (Sequelize) to a file called TaskService.js
Example -
In TaskService.js -
...
exports.delete = function() {
db.sync().then(function () {
Task.find({ where: { id: req.params.id, userid: req.user.id } })
.then(function (task) {
if (task) {
task.destroy({ where: { id: req.params.id } })
.then(function () {
res.status(202).json({ message: 'Task deleted.' });
});
} else {
res.status(404).json({ message: 'Task not found.' });
}
});
});
}
then in TaskController.js -
...
const TaskService = require('./TaskService);
...
TaskController.delete = function(req, res) {
if (!req.params.id) {
res.json({ message: 'Please provide a task to delete.' });
} else {
TaskService.delete();
}
}
One thing I'd like to call out as far as Javascript best practices would be nested promises, which is a bit of an anti-pattern. You lose the power of promise chains when you nest them, in effect creating nested callbacks. Things will start getting weird once you start trying to use .catch() blocks for error handling. A quick refactor with catch blocks might look like this, even though this is still messy because of the conditional based on the whether or not the task exists in the DB:
// PUT ONE Task
TaskController.put = function (req, res) {
if (!req.body.task) {
res.json({ message: 'Please provide a task to update.' });
} else {
db.sync()
.then(function () {
// Find task by task id and user id
// NOTE: we return the promise here so that we can chain it
// to the main promise chain started by `db.sync()`
return Task.find({ where: { id: req.params.id, userid: req.user.id } });
})
.then(function (task) {
// Check if record exists in db
if (task) {
task.update({ task: req.body.task })
.then(function () {
res.status(201).json({ message: 'Task updated.' });
})
.catch(function (updateError) {
// do something with your update error
// catches an error thrown by `task.update()`
});
} else {
res.status(404).json({ message: 'Task not found.' });
}
})
.catch(function (promiseChainError) {
// do something with your promiseChainError
// this catch block catches an error thrown by
// `db.sync()` and `Task.find()`
});
}
}
Alternatively, if you're more comfortable with synchronous style code and have the option of updating your node version to v7+, this is what your functions might look like using async/await:
// PUT ONE Task
TaskController.put = async function (req, res) {
if (!req.body.task) {
res.json({ message: 'Please provide a task to update.' });
} else {
try {
await db.sync();
const task = await Task.find({ where: { id: req.params.id, userid: req.user.id } });
// Check if record exists in db
if (task) {
await task.update({ task: req.body.task });
res.status(201).json({ message: 'Task updated.' });
} else {
res.status(404).json({ message: 'Task not found.' });
}
} catch (error) {
// do some something with your error
// catches all errors thrown by anything in the try block
}
}
}
There's plenty of resources out there about promise chains and handling async methods. Check this one out in particular: http://solutionoptimist.com/2013/12/27/javascript-promise-chains-2/
I'm using a node machines package (machinepack-wepay) to communicate with Wepay and I'd like to be able to chain it properly.
Take the following example where we will be registering a user, creating an account and sending the email confirm. Along the way we will be storing some of the result info in mongo.
var WePay = require('machinepack-wepay');
// ... extraneous code removed for brevity
var member = req.session.member;
if( !_.has( member, 'wepay' ) ) {
WePay.userRegister({
clientId: config.wepay_client_id,
clientSecret: config.wepay_client_secret,
email: member.email,
scope: 'manage_accounts,collect_payments,view_user,send_money',
firstName: member.firstName,
lastName: member.lastName,
originalIp: req.headers['x-forwarded-for'],
originalDevice: req.headers['user-agent'],
tosAcceptanceTime: Math.floor(new Date() / 1000),
callbackUri: config.site_url + '/wepay/user?member=' + member.id,
useProduction: isProd
}).exec({
error: function (err) {
yourErrorHandler(err);
},
success: function (result) {
Member.update({id: member.id}, {wepay: result}, function (err, updated) {
if (err) {
yourErrorHandler(err);
}
else {
member = updated[0];
WePay.accountCreate({
accessToken: member.wepay.access_token,
name: 'Account Name',
description: 'My new account'
}).exec({
error: function (err) {
yourErrorHandler(err);
},
success: function (result) {
Member.update({id: member.id}, {wepay_account: result}, function (err, updated) {
if (err) {
sails.log.error("error updating page:", err);
}
req.session.member = updated[0];
// PATTERN CONTINUES HERE
});
}
});
}
});
}
});
}
else{
WePay.userDetails({
accessToken: member.wepay.access_token,
useProduction: false,
}).exec({
error: function (err){
yourErrorHandler(err);
},
success: function (result){
_.extend( member.wepay, result );
Member.update({id: req.session.current_page.id}, member, function (err, updated) {
if (err) {
sails.log.error("error updating page:", err);
}
req.session.member = updated[0];
// PATTERN CONTINUES HERE
});
},
});
}
Is there simple way of mocking the hapi reply object/function for easy unit testing?
The examples I see for hapi all use server.inject and the "lab" framwork for testing. I'm curious to see how I could keep using mocha and would like to test controller directly rather than injecting into the server.
Should i use sinon to mock the reply object?
test/post.js
before(function () {
PostController = proxyquire('../controllers/post', { 'mongoose': mongooseMock });
});
it('should be able to create a post', function(done){
var request.payload = {foo:bar};
var reply = sinon.spy(); //is this how I should mock this?
PostController.create.handler(request, reply);
reply.should ...// how do I test for statuscode 201, Boom errors, and response msgs
});
controllers/post.js
var Boom = require('Boom')
Post = require('../models/Post')
module.exports = {
create: {
auth: 'token',
handler: function (request, reply) {
var p = new Post({foo:request.payload.foo});
p.save(function (err, results) {
if (!err && results)
reply(results).created();
else {
reply(Boom.badImplementation(err));
}
});
}
}
Finally, should I just switch over to lab instead?
You can use server.inject() with Mocha too. I would just stub Post.save():
Sinon.stub(Post, 'save', function (callback) {
callback(null, { foo: 'bar' });
});
With some more code:
it('creates a post', function (done) {
Sinon.stub(Post, 'save', function (callback) {
callback(null, { foo: 'bar' });
});
server.inject({ method: 'POST', url: '/posts', payload: { foo: 'bar' } }, function (res) {
Post.save.restore();
expect(res.statusCode).to.equal(201);
done();
});
});
If you want to test for the error, you just need to modify the stub:
it('returns an error when save fails', function (done) {
Sinon.stub(Post, 'save', function (callback) {
callback(new Error('test'), null);
});
server.inject({ method: 'POST', url: '/posts', payload: { foo: 'bar' } }, function (res) {
Post.save.restore();
expect(res.statusCode).to.equal(500);
done();
});
});