Express.js Mocha testing having issues connecting to test db - node.js

I am trying to get my test suite to run within my express.js app, but this is my first express.js app as well as my first time working with mocha and I am unsure how to set everything up.
Here is my test
should = require 'should'
assert = require 'assert'
expect = require 'expect'
request = require 'superagent'
mongoose = require 'mongoose'
config = require '../../config/config'
describe "POST", ->
app = require('../../app')
beforeEach (done)->
mongoose.connect config.db, (err) ->
if err
console.log "Error! #{err}"
done()
describe '/users/:id/activities', ->
it 'should return created document', (done) ->
request(app).post('http://localhost:3000/users/1/activities').send(
description: "hello world"
tags: "foo, bar"
).set('Content-Type','application/json')
.end (e, res) ->
console.log(res.body.description)
expect(res.body.description).should.equal('hello world')
done()
A couple of issues I am having are... 1) In the test suite it can't connect to my test db (in my config file); 2) I am getting this error message when trying to post
1) POST /users/:id/activities should return created document:
TypeError: Object #<Request> has no method 'post'
Can someone point me in the right direction on how to get everything running properly?
The error I am getting back from trying to connect to the MongoDB is Error! Error: Trying to open unclosed connection.
I am running the mocha suite by running this command
NODE_ENV=test mocha test/controllers/activities.test.coffee --compilers coffee:coffee-script/register -R spec
Edit
activities.coffee (routes)
express = require 'express'
router = express.Router()
mongoose = require 'mongoose'
Activity = mongoose.model 'Activity'
module.exports = (app) ->
app.use '/', router
router.post '/users/:id/activities', (req, res, next) ->
activity = Activity(
user_id: req.params.id
description: req.body.description,
tags: req.body.tags
)
activity.save (err)->
if not err
res.json(activity)
else
res.json({ error: err })
router.get '/users/:id/activities/:activity_id', (req, res, next) ->
Activity.findById req.params.activity_id, (err, activity) ->
if not err
res.json(activity)
else
res.json({ error: err })
app.js
require('coffee-script/register');
var express = require('express'),
config = require('./config/config'),
fs = require('fs'),
mongoose = require('mongoose');
mongoose.connect(config.db);
var db = mongoose.connection;
db.on('error', function () {
throw new Error('unable to connect to database at ' + config.db);
});
var modelsPath = __dirname + '/app/models';
fs.readdirSync(modelsPath).forEach(function (file) {
if (/\.coffee$/.test(file)) {
require(modelsPath + '/' + file);
}
});
var app = express();
require('./config/express')(app, config);
app.listen(config.port);
exports.app = app;

First, once you have your superagent pointing to you app variable that holds you routes, middlewares and so on, there is no need to specify the url in the methods provided by superagent. You just need to provide the route, just like so:
request(app)
.post('/users/123/activities')
.end(function (err, response) {
});
Second, use the before statement provided by mocha instead of beforeEach, the second one will try to connect to mongo on every unit test you have.
For the first error
request has no method 'post'
Make sure you have installed superagent and you can find it on your node_modules folder.
Hope that helps!

Related

Cannot post request to nodejs route on ECS but can locally (404 error)

I’ve been having an issue with deploying my nodejs App on AWS ECS Fargate. Running the app locally on my device with nodemon or building the app and running the build file is successful and I can ping my routes using postman. The issue happens when I deploy this same exact code on AWS; using postman, to do a POST request, I get a 404 error. Please note, I'm running a Node:14 container.
For reference, my nodejs code is structured in a way where there’s a main route.js file containing all routes, then there are specific route files, for example listingRoute.js, contains all the sub-routes then there are controllers (.js files) containing all the logic where I export the function and tie it with the route in the listingRoute.js example.
Here's what my main Route.js file looks like:
const express = require('express');
const error = require('../Middleware/error');
const listingRoute = require('../Routes/listingRoute');
module.exports = function (app) {
//Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: false , limit : '20mb' }));
app.use('/listing', listingRoute);
//The final middleware to be called in case of an unhandled error.
app.use(error);
process.on('uncaughtException', function(err) {
// Handle the error safely
console.log(err)
})
};
My listingRoute file
const express = require("express");
const route = express.Router();
const listingController = require("../Controllers/listingController");
require('dotenv').config();
route.post("/create", listingController.createListing)
route.post("/update", listingController.updateListing)
route.post("/read", listingController.getListing)
route.post("/delete", listingController.deleteListing)
...
...
...
...
...
route.post("/getMostPopular" , listingController.getMostPopular)
route.post("/getByCategory" , listingController.getByCategory)
route.post("/getAllTOS" , TOSController.getTOSByListing)
route.post("/getTOS" , TOSController.getTOSByID)
route.post("/updateTOS" , TOSController.updateTOS)
route.post("/deleteTOS" , TOSController.deleteTOS)
route.post("/createTOS" , TOSController.createTOS)
route.post("/getListingsByIDs" , listingController.getListingsByIDs)
route.post("/cacheImagesNewCDN" , listingController.cacheImagesNewCDN)
module.exports = route;
My listingController file
const listingModel = require('../Models/listingModel');
const moment = require('moment')
const axios = require('axios');
var fs = require('fs');
const createCsvWriter = require('csv-writer').createObjectCsvWriter;
var fs = require('fs');
//tested
const createListing =async (req, res) => {
try {
//some logic here
}
catch (err) {
console.log(err)
return res.status(500).json({ error: err.message });
}
}
const updateListing = async (req, res) => {
try {
//some logic here
}
catch (err) {
return res.status(500).json({ error: err.message });
}
}
module.exports = {
getListing,
updateListing,
deleteListing,
createListing,
listingwithViews,
advertisedListings,
filterListings,
pressedOnBookNow,
cacheImages,
recommendListings,
getCacheMetaData,
addIndoorAmenity,
missingFromFilter,
adjustCreativeStudios,
listingsToCSV,
getAllListing,
getDiscountedListings,
addRevenueToListings,
getMostPopular,
getByCategory,
getListingsByIDs,
cacheImagesNewCDN,
getOwnersPhones
}
All the routes starting from getMostPopular till the end of the list give an error 404 not found although I have done the same procedure to all of them. Any ideas why this is happening? If you feel this isn't enough information to help diagnose, let me know and i'd be happy to provide more details. You're help would be beyond appreciated, thanks!

Exporting a Mongoose connection that becomes available in an async callback?

I'm new to coding in Node.js and I've created a DB connection through mongoose, then exporting the database object into my main app.js file:
// db.js
var mongoose = require("mongoose");
var uri = /*The URL*/;
var db = mongoose.connect(uri, {
useMongoClient: true
});
exports.db = db;
Here I'm trying to use that DB connection to perform CRUD operations:
// app.js
var db = require('./db');
app.post('/', function (req, res) {
db.collection(/* … */); // throws error
});
The error is:
TypeError: db.collection is not a function
I think this is because that the call of connect in the db.js is asynchronous and the require in app.js is synchronous - so it'll be undefined when I execute db.collection?? How can Mongoose be used to avoid this error?
Goal: I want to be able to call db.collection.insertOne() within my app.js
I found a somewhat related topic but it doesn't use Mongoose, so I'm confused as to how to resolve this issue: How to export an object that only becomes available in an async callback?
Answering my own question:
It'll need a mongoose model in the app.js. So instead of doing db.collection in the main app.js, I ended up routing the CRUD operations to functions defined in the controller file.
//app.js
var app = express();
var router = express.Router();
app.use(router);
require("./Routes")(router);
router.get("/xxx", xxx.get);
Here are the routes:
//routes.js - takes care of routing URLs to the functions
var XXX = require('../xxx');
module.exports = function(router){
router.get('/XXX', XXX.get)
};
Here is the actual function:
//xxx.js - functions defined here
exports.get = function (req, res) {
XXX.getAll(req.body, function(err, result){
if (!err) {
return res.send(result); // Posting all the XXX
} else {
return res.send(err); // 500 error
}
});
};

Node Express Unit Test JSON Response

Just created a new open source middleware project for express. I want to be able to unit test the json response from the routes it generates... Is there anyway I can do this without actually firing up a grunt serve and checking the url?
So goal would be for someway to run the route but instead of sending json to the browser I can store it in a variable / etc...
What the middleware does is generates routes based on annotations in javascript files.
https://github.com/kmgilbert100/annotation-route-loader
I would like to make my unit test include testing the JSON responses you can see in the above url under tests/routes/**/*
Note app.use(loader) would load all the routes
Below is the current mocha test
// npm modules
const chai = require('chai');
const _ = require('lodash');
const express = require('express');
// local modules
var routeLoader = require('../src/index');
// testing module methods
const assert = chai.assert;
describe('annotation-route-loader', () => {
// store collection of routes
var routePaths = [];
before("Create collection to check from", () => {
var loader = routeLoader({
baseUrl: '/',
path: './routes',
pattern: '**/*.js',
params: {
sports: [
'footbal',
'baseball',
'motocross',
'hockey'
]
}
});
loader['stack'].forEach( stack => {
routePaths.push({
path: stack.route.path,
methods: stack.route.methods
})
})
});
it('Should make sure the default path is valid', (done) => {
// Try And Find Path
var defaultPath = _.find(routePaths, {path: '/'});
assert.isObject(defaultPath);
assert.isTrue(defaultPath.methods.get);
// Make Callback
done()
});
it('Should make sure the sports path is valid', (done) => {
// Try And Find Path
var defaultPath = _.find(routePaths, {path: '/sports'});
assert.isObject(defaultPath);
assert.isTrue(defaultPath.methods.get);
// Make Callback
done()
});
it('Should make sure the sports list path is valid', (done) => {
// Try And Find Path
var defaultPath = _.find(routePaths, {path: '/sports/list'});
assert.isObject(defaultPath);
assert.isTrue(defaultPath.methods.get);
// Make Callback
done()
});
})
Thanks for the comments!
https://github.com/visionmedia/supertest
Ended up using supertest to get the job done!
See below snippet...
var request = require('supertest');
var express = require('express');
var app = express();
app.get('/user', function(req, res) {
res.status(200).json({ name: 'tobi' });
});
request(app)
.get('/user')
.expect('Content-Type', /json/)
.expect('Content-Length', '15')
.expect(200)
.end(function(err, res) {
if (err) throw err;
});

How do I integration test a node express controller?

We started a node application where we are going to use Express as the router. In one of our controllers, we have this defined:
var express = require('express');
var router = express.Router();
// api/products
router.get('/', function (req, res) {
res.json({ products: [] });
});
// api/products/:id
router.get('/:id', function (req, res) {
res.json({ id: req.params.id });
});
module.exports = router;
I've been trying to follow examples on how to test this and came up with:
var hippie = require('hippie');
var server = require('../controllers/products');
describe('Products Controller Tests', function () {
describe('/products endpoint', function () {
it('returns an array of products', function (done) {
hippie(server)
.json()
.get('/products')
.expectStatus(200)
.end(function (err, res, body) {
if (err) throw err;
done();
});
});
});
});
But I'm always getting the following error:
Test Name: Products Controller Tests /products endpoint returns an array of products
Test Outcome: Failed
Result StandardOutput:
not ok 1 Products Controller Tests /products endpoint returns an array of products
TypeError: Cannot read property 'apply' of undefined
at Immediate. (C:\Api\node_modules\express\lib\router\index.js:618:14)
tests 1
pass 0
fail 1
What is the best way to unit test these controllers? Is there a different library I should be using?
What about extracting the behaviour of your routes and unit testing that?
Something like this
var express = require('express'),
router = express.Router(),
routes = require('./routes');
router.get('/:id', routes.getId);
So that in your unit test you can have
describe('getId', function() {
var route = require('../routes').getId;
var req = // build your request object with params and so on
var res = // stub this, for example with sinon
it('whatever', function() {
route(req, res);
expect(res.send.calledOnce).to.be.true;
// and so on
});
});
Once you're covered this with unit tests (faster to run, you can hook them up on pre-commit hook), you can integration test the route, without the need to mock the underlying HTTP library, with proper calls using request for example.

Cant test DELETE method using mocha and supertest

I'm trying to build a RESTful API for a node app.
I built the routes and everything is running fine. But when I try to test it, it cant get the DELETE method to work, despite of it working normally not under tests.
Here are the codes for the server and test.
Server:
// set up
var express = require('express');
var app = express(); // create our app w/ express
var path = __dirname; //root path
// configuration
app.configure(function() {
app.use(express.static(path));
//app.use(express.logger('dev')); // log every request to the console
app.use(express.json());
app.use(express.urlencoded()); // pull information from html in POST
app.use(express.methodOverride()); // simulate DELETE and PUT
});
function start() {
// routes
require('./app/routes.js')(app);
// listen (start app with node server.js)
app.listen(process.env.PORT || 5000);
console.log("Server listening for incoming conections..");
}
//************************
exports.start = start;
exports.server = app;
Test:
var should = require('should');
var assert = require('assert');
var request = require('supertest');
var mongoose = require('mongoose');
var express = require('express');
var server = require(__dirname + './../index.js');
describe('Routing', function() {
var url = 'http://localhost:5000';
it('should return status 200 after DELETING a bus', function(done) {
request(url)
.delete('/api/buses/' + bus.id)
.end(function(err, res) {
if (err) {
throw err;
}
res.should.have.status(200);
done();
});
});
});
And this is the error message it throws:
Routing
1) should return status 200 after DELETING a bus
✖ 1 of 1 test failed:
1) Routing should return status 200 after DELETING a bus:
TypeError: Object #<Object> has no method 'delete'
at Context.<anonymous> (/home/roger/Documents/Buse/test/test.js:63:16)
at Test.Runnable.run (/home/roger/Documents/Buse/node_modules/mocha/lib/runnable.js:196:15)
at Runner.runTest (/home/roger/Documents/Buse/node_modules/mocha/lib/runner.js:351:10)
at /home/roger/Documents/Buse/node_modules/mocha/lib/runner.js:397:12
at next (/home/roger/Documents/Buse/node_modules/mocha/lib/runner.js:277:14)
at /home/roger/Documents/Buse/node_modules/mocha/lib/runner.js:286:7
at next (/home/roger/Documents/Buse/node_modules/mocha/lib/runner.js:234:23)
at Object._onImmediate (/home/roger/Documents/Buse/node_modules/mocha/lib/runner.js:254:5)
at processImmediate [as _immediateCallback] (timers.js:330:15)
make: *** [test] Error 1
Just to be clear there is no method delete with supertest but the correct method is del.
so a delete request should be tested like this:
var app=require('./app')
var request=require('supertest')
//with mocha for instance.
describe('test delete',function(){
it('should respond 200',function(done){
request(app).del('/path').expect(200).end(done);
})
});
And one needs to pass the app (express app) , not a url or a string.
Take a look at the supertest GitHub-page.
You may pass an http.Server, or a Function to request()
You are passing a string to the function request. Try passing your express-server object as the function parameter.
EDIT: as seen in comments and #mpm:s answer, the issue was the usage of reserved function delete() instead of package-specific function del().

Resources