Mocking mongoskin in node.js application - javascript

As I try to follow the TDD way of development, I still struggle to find out how one can mock certain stuff in JavaScript. I am used to mocking in Java with Mockito and Spring (e.g. inject a mongo mock instead of a real mongo instance), but how do I approach this in JavaScript?
Let me make a simple example node.js with node-restify:
var mongoskin = require('mongoskin');
var restify = require('restify');
// ###############################
// ## Global Configuration
// ###############################
var mongoURL = process.env.MONGOHQ_URL || "mongodb://localhost/test";
var serverPort = process.env.PORT || 5000;
// ###############################
// ## Basic Setup
// ###############################
var server = restify.createServer({
name: 'test'
});
server.use(connect.logger());
server.use(restify.acceptParser(server.acceptable));
server.use(restify.bodyParser());
var db = mongoskin.db(mongoURL);
// ###############################
// ## API
// ###############################
server.get('/api/v1/projects', function (req, res, next) {
db.collection('projects').find().toArray(function (error, projects) {
if (error) {
return next(new restify.InternalError());
}
res.json(200, projects);
return next();
});
});
server.get('/api/v1/projects/:projectId', function (req, res, next) {
if (req.params.projectId === null) {
return next(new restify.InvalidArgumentError('ProjectId must not be null or empty.'))
}
db.collection('projects').findById(req.params.projectId, function (error, project) {
if (error) {
return next(new restify.InternalError());
}
res.json(200, project);
return next();
});
});
// ###############################
// ## Main Server Initialization
// ###############################
server.listen(serverPort, function () {
console.log('%s listening at %s', server.name, server.url);
});
I would like to have now a test javascript file, where I can test those two 'get' methods. Furthermore I would like to mock the mongoskin instance ('db') so that I can use for example JSMockito to spy and pretend some behaviour.
What is now the best approach to this? Can someone post a small example file? And how do I manage to inject the mocked db instance?
Thanks for your help!
Thierry

Plenty of precedence out there for easily mocking a rest api in general:
https://github.com/flatiron/nock
The problem with mocking a database is that its usually got an extremely complex and hairy api. There are two easy (and thus less correct in strict unit testing sense) ways to do this.
One is to have 'models' which wrap your entity access instead of going direct to the database driver. Then you can easily mock your model apis. This is fine, but a little annoying if you're just doing some basic database operations and you don't have a need for a big model abstraction.
The second is to just spin up a database with some test data and hook up to it during the test. This is a bit of a 'functional test' but also in my experience a lot more practical.

Related

Nodejs controller is being messy

I'm new to javascript, node.js (or backend at all). I am trying to create a controller for the login page requests and I am confused about getting data from the MYSQL table and User Authentication and working with JWT package !
In my Controller, I first check if the user input is available in the user table (with a simple stored procedure), then I compare the database password and the user input, after this I want to create a token and with limited time. (I have watched some tutorial videos about JWT and there is no problem with it), my main problem is to figure out how to write a proper controller with this functions?
I have 2 other questions:
1.Is it the right and secure way to get data from MySQL table inside the route? Or should I create a JS class for my controller? (I'm a bit confused and doubtful here)
2.Assuming that comparePassword() returns true, how can I continue coding outside of the db.query callback function scope? Because I have to execute comparePasssword() inside db.query callback
loginController.js :
const { validationResult } = require('express-validator');
const bcrypt = require('bcrypt');
const db = require('../../sqlConnection')
let comparePassword = (dbPass, inputPass) => {
bcrypt.compare(inputPass, dbPass, function(err, result) {
console.log(result)
});
}
// for get request
exports.getController = (req, res) => {
res.send('login')
}
// for post request
exports.postController = (req, res) => {
let errors = validationResult(req)
if(!errors.isEmpty()) {
res.status(422).json({ errors: errors.array() })
}
// find data from MYSQL table
let sql = `CALL findUser(?)`
db.query(sql, [req.body.username], (err, res) => {
if(err) console.log(err)
//console.log(Object.values(JSON.parse(JSON.stringify(res[0]))))
var data = JSON.stringify(res[0])
data = JSON.parse(data).find(x => x)
data ? comparePassword(data.password, req.body.password) : res.status(400).send('cannot find
user')
})
res.send('post login')
}
login.js :
const express = require('express')
const router = express.Router()
const { check } = require('express-validator');
const loginCont = require('../api/controllers/loginController')
router.route('/')
.get(
loginCont.getController
)
.post(
[
check('username').isLength({min: 3}).notEmpty(),
check('password').isLength({min: 4}).notEmpty()
],
loginCont.postController
)
module.exports = router
In my point of view, looks like there is no easy answer for your question so I will try to give you some directions so you can figure out which are the gaps in your code.
First question: MySQL and business logic on controller
In a design pattern like MVC or ADR (please take a look in the links for the flow details) The Controllers(MVC) Or Actions(ADR) are the entry point for the call, and a good practice is to use these entry points to basically:
Instantiate a service/class/domain-class that supports the request;
Call the necessary method/function to resolve what you want;
Send out the response;
This sample project can help you on how to structure your project following a design pattern: https://riptutorial.com/node-js/example/30554/a-simple-nodejs-application-with-mvc-and-api
Second question: db and continue the process
For authentication, I strongly suggest you to take a look on the OAuth or OAuth2 authentication flow. The OAuth(2) has a process where you generate a token and with that token you can always check in your Controllers, making the service a lot easier.
Also consider that you may need to create some external resources/services to solve if the token is right and valid, but it would facilitate your job.
This sample project should give you an example about how to scope your functions in files: https://github.com/cbroberg/node-mvc-api
Summary
You may have to think in splitting your functions into scoped domains so you can work with them in separate instead of having all the logic inside the controllers, then you will get closer to classes/services like: authenticantion, user, product, etc, that could be used and reused amount your controllers.
I hope that this answer could guide you closer to your achievements.

Is there an event emitted for socket.io ack/responses?

I'm using socket.io and express both with feathersjs. For metrics gathering I'm trying to capture round-trips for requests made both through Express as well as through socket.io.
The express side is easy with express middleware.
I can catch the socket.io inbound request via socket.use:
const app = express(feathers());
... (set up feathers services etc.)
app.configure(socketio(function(io) {
io.use(function(socket, next) {
socket.use((packet, next) => {
... (extract the verb and pathing info from the packet)
next();
});
next();
});
});
However, I can't find any equivalent of socket.use on the outbound/acknowledgement side. There's some events inside engine.io under the covers but I can't access them.
I'm really trying to find an equivalent set of events emitted for each request/response (the latter being the equivalent to finish in express).
I can't use connect/disconnect events here; I want to capture each request made over the socket and the responses sent for them, regardless of the feathers service and module.
Feathers.js hooks could be used for this, but it would require passing a bunch of context from the socket.io middleware into feathers, which I was hoping to not do.
Anyone know a way to do this?
In case anyone comes here looking for a way to do this, I'm not sure why I didn't think of it sooner.
The inbound packet (available in socket.use) will include a function as its last parameter if it should be acknowledged.
Wrapping the last function to inject my own logic worked.
pseudo-code
socket.use((packet, next) => {
const id = uuidv4();
console.log(`start of request: ${id}`);
const cb = packet[packet.length - 1];
if (typeof cb === 'function') {
packet[packet.length - 1] = function() {
const [err, data] = arguments;
console.log(`end of request: ${id}`);
if (err) {
console.error(err);
}
cb(err, data);
};
};
next();
});

Get mongodb collection with node

I'm very new to express/mongo stack, so the question is pretty simple, couldn't find anything on stackoverflow which would resolve my problem, so here it is:
I have a index.js file which looks more or less like this:
var mongoose = require('mongoose');
// Connections
var developmentDb = 'mongodb://localhost/admin';
var usedDb;
// If we're in development...
if (process.env.NODE_ENV === 'development') {
// set our database to the development one
usedDb = developmentDb;
// connect to it via mongoose
mongoose.connect(usedDb);
}
// get an instance of our connection to our database
var db = mongoose.connection;
db.once('open', function callback () {
console.log('Databsae Connection Successfully Opened at ' + usedDb);
});
module.exports = db;
Then I'm requireing it in my express route like this:
var express = require('express');
var router = express.Router();
var db = require('../../database');
/* GET users listing. */
router.get('/', function(req, res) {
var users = db.collection('users');
var test = users.find();
res.send(test);
});
module.exports = router;
I'm sending request and the result I'm getting is 'undefined' so nothing is returned from the back end.
Db connection is 100% correct and works.
I'm not entirely sure, do I have to have a schema definition on express side, or is it possible to query any data, without knowing the schema?
What you are missing here is a bit of the "mongoose magic" that is actually happening "behind the scenes " as it were. It's also a base concept of most operations in node.js that the operations ( particularly where IO is concerned ) are largely asynchronous in nature, so you are generally working with callbacks that fire when the operation is complete.
Take this part of your listing:
// get an instance of our connection to our database
var db = mongoose.connection;
db.once('open', function callback () {
console.log('Databsae Connection Successfully Opened at ' + usedDb);
});
module.exports = db;
So while you may have coded that in sequence, the actual order of events is not as you might think. While you can call the db object from mongoose.connection ( and actually this is a connection object and not theDb` object as implemented by the underlying driver ) there is no guarantee that the database is actually connected at this time. In fact, in all likelihood it is not.
The sort point here is that your database connection actually happens after you export the variable from the module, and not before. It does not wait for the preceding line to complete, nor can you make it do so.
Mongoose itself rather has a concept of "models" to represent the collections in your database. So the general approach is to define these model objects and use them for accessing the data:
var Model = mongoose.model( 'Model', modelSchema, 'optionalCollectionName' );
Model.find({}, function(err,data) {
// do stuff in the callback
});
Part of the reason for this ( aside from the schema definitions that are attached ) is that there is actually something else going on here related to the connection. These objects in fact have internal logic that only processes the actions on the tied "collection" object only when the connection to the database is available. So there is an "internal callback" function that is happening here where an internal connection object is actually being used.
Here is some simple code that "overrides" the usage of the internal methods to just try and get the underlying collection object from the driver. It will fail:
var mongoose = require('mongoose'),
Schema = mongoose.Schema
mongoose.connect('mongodb://localhost/test');
var modelSchema = new Schema({},{ strict: false });
var Model = mongoose.model( 'Model', modelSchema, 'optionalCollectionName' );
Model.collection.find({}, function(err,data) {
// do stuff in the callback
});
Since this asked for the collection object as implemented in the underlying driver to be returned and the native .find() method from that object to be used, the problem occurs that the database is not actually connected yet. So in order to make this work, you would need to wrap the call inside an event handler that fired only when the database was truly connected. Or otherwise make sure that a connection had been made before this was called:
mongoose.connection.on('open',function(err,conn) {
// Now we know we are connected.
Model.collection.find({}, function(err,data) {
// do stuff in the callback
});
});
So the model objects are actually doing this for you, and are providing their own implementations of the standard methods such as "find", "update" etc.
If you don't want to do this kind of wrapping, and the definition of models seems like too much work, and even using the { strict: false } modifier here, which relaxes the constraints on the schema to effectively allow any data, then you are probably better off using the base driver rather than mongoose.
But of course you want something smarter than just wrapping all the code inside a callback to the connection. Here is one approach to define an object that you can use to "fetch" the database connection for you, and make sure that nothing is executed until that connection is made:
var async = require('async'),
mongodb = require('mongodb'),
MongoClient = mongodb.MongoClient;
var Model = (function() {
var _db,
conlock;
return {
getDb: function(callback) {
var err = null;
if ( _db == null && conlock == null ) {
conlock = 1;
MongoClient.connect('mongodb://localhost/test',function(err,db) {
_db = db;
conlock == null;
if (!err) {
console.log("Connected")
}
callback(err,_db);
});
} else if ( conlock != null ) {
var count = 0;
async.whilst(
function() { return ( _db == null ) && (count < 5) },
function(callback) {
count++
setTimeout(callback,500);
},
function(err) {
if ( count == 5 )
err = new Error("connect wait exceeded");
callback(err,_db);
}
);
} else {
callback(err,_db);
}
}
};
})();
async.parallel(
[
function(callback) {
console.log("call model");
Model.getDb(function(err,db) {
if (err) throw err;
if (db != undefined)
console.log("db is defined");
callback();
});
},
function(callback) {
console.log("call model again");
Model.getDb(function(err,db) {
if (err) throw err;
if (db != undefined)
console.log("db is defined here as well");
callback();
});
}
],
function(err) {
Model.getDb(function(err,db) {
db.close();
});
}
);
That basically wraps up an object which we called "Model" here, with a single method to .getDb(). That method simply accepts a callback, which is there to be your actual piece of code that you want to access the database with, which in turn exposes the Db object from the connection.
The Db object is stored internally in that object, so it is basically a singleton which only ever connects to the database once. But as your logic is passed in a a callback function, then it will either just pass in the existing stored object, or wait until the connection is made before passing in the code.
Output from the sample usage should be:
call model
call model again
Connected
db is defined
db is defined here as well
And that shows the order of events to how they actually happen.
So there are different ways to handle this. Mongoose models "abstract" a lot of that for you. You can of course either take a base approach with the base driver as given in the example, or take that further and implement your own connection system including overridden methods that does much of the same thing that mongoose is doing underneath. There are also other wrapper libraries that already do this without the schema concepts that is generally inherent to mongoose as well.
Basically though, every higher level library above the base driver is doing much the same as described, where there are wrappers around the methods to make sure the connection is there without needing to embed all of your code in an event listener that is checking for that.

NodeJs, pattern for sync developement

I am new with NodeJs and Express frameworks. I have understood that Node works with only one thread on the server side. So, I have noticed this causes me some problems in order to develop correctly my application.
In my routes folder, I have a file index.js.
This file manage the navigation asked by the user from app.js.
So I decided to create a route function "test".
In this function, I had just that code
exports.test = function(req, res){
res.render('test', {});
};
So simple, so easy. That's rend the template test.jade in my views folder. Greats !
But I wanna complexify the process. In this test route function, I want load some content from my MYSQL database.
For that, I have created a folder Models in the folders node_modules
Inside, I have only 2 file, the first mysqlConnection.js which exports the variable DB in order to make queries.
var mysql = require('mysql');
var DB = mysql.createConnection(
{
host : 'localhost',
user : 'root',
password : '',
database : 'test',
}
);
DB.connect();
module.exports = DB;
In the second file, articles_class.js, I just have
var DB = require('models/mysqlConnection');
var Article = function() {
this.getArticles = function()
{
DB.query('SELECT * FROM articles;', function(err, rows, fields)
{
if (err)
throw err;
else
{
console.log(rows);
return (rows);
}
});
}
}
module.exports = Article;
Go back in my route test function :
I just want to load from table "test" all the articles. Very basic. But not easy.
Why ?
Because before the query is finished, NodeJs respond to the client with the template render, but, unfornlty, without the rows loaded. Asynchronous problem ... Mysql doesn't block the Nodejs javascript Instruction.
The code of the function :
exports.test = function(req, res){
var Article = require('models/articles_class');
a = new Article();
articles = a.getArticles();
console.log(articles); // undefined
res.render('test', {});
};
I Found others subjects in stackoverflow which speak about this problem. Make sync queries, work with callbacks ect ..
But for, here, if I try to manage this problem with callbacks, That's cannot work ... Because I need to send to the client the template with articles but I can't block the process with a sync method.
I am very lost ... I don't understand how I have to build my application. I am not able to create a good proceed in order to manage the sql queries. There is a pattern or a specific method ?
Or perhaps I have to make only ajax requests from the client. I load the template "test". And in a javascript file in the public folder, I ask to the server to load me the articles content and wait success callback function ? it's not very clean ...
Thx for your answers. The others answers I have found didn't help me to understand how manage that with NodeJs.
Pass a callback to getArticles:
exports.test = function(req, res){
var Article = require('models/articles_class');
a = new Article();
a.getArticles( function( articles ) {
console.log(articles); // undefined
res.render('test', { articles: articles });
});
};
Changes to your get articles function:
var DB = require('models/mysqlConnection');
var Article = function() {
this.getArticles = function( callback )
{
DB.query('SELECT * FROM articles;', function(err, rows, fields)
{
if (err)
throw err;
else
{
console.log(rows);
callback && callback(rows);
}
});
}
}
module.exports = Article;
Express will only return the template through the open http connection once res.render() is called. So it's just a matter of passing it as a callback through your call stack, so it should only be called after you have your database rows.
As we are working with callbacks, they don't block your application.

Easy way to handle post data in meteor.js?

I need to handle some POST data in my meteor.js app, is there an easy way to do this?
Very basic, if it was a PHP app I would just want the $_POST variable.
Meteor router
https://github.com/tmeasday/meteor-router#server-side-routing
Meteor.Router.add('/items/:id', 'POST', function(id) {
// update Item Function
return [200, 'ok'];
});
If you are simply looking to intercept the GET and POST data, then send Meteor on it's merry way, you could do something like this on the server.
if (Meteor.isServer) {
var connect = Npm.require('connect');
var app = __meteor_bootstrap__.app;
var post, get;
app
// parse the POST data
.use(connect.bodyParser())
// parse the GET data
.use(connect.query())
// intercept data and send continue
.use(function(req, res, next) {
post = req.body;
get = req.query;
return next();
});
Meteor.startup(function() {
// do something with post and get variables
});
}
EDIT 11/01/13
I ended up creating a smart package for this (for myself). There is no documentation but you are welcome to use it. https://github.com/johnnyfreeman/request-data
To retrieve the foo request variable:
RequestData.get('foo') // --> 'bar'
RequestData.post('foo') // --> 'bar'
Both methods will throw a Meteor.Error if the key isn't found so make sure you use wrap with a try/catch if the variable is optional.
You can use Meteor's Iron Router, docs here, since Router (as mentioned above) is outdated and might be no longer functional.
Router.route('/items/:id', {where: 'server'})
.get(function () {
this.response.end('get request\n');
})
.post(function () {
this.response.end('post request\n');
});
I'm using this package to serialize body data: simple:json-routes. Here is the link.
And this code snippet to access it:
WebApp.connectHandlers.use('/api/request', (req, res, next) => {
console.log(req.body);
});

Categories

Resources