How would I test sending a route with mocha/chai/superagent in express? - node.js

it('sends login page if we\'re logged out at /', function (done) {
superagent.get('http://localhost:4000/').end(function(err, res){
if(err) {return done(err)}
expect(res).to.have.property('status', 200);
//how would i do something like this?
expect(res.viewRendered).to.be.equal('index.ejs')
done();
});
});
I'm new to testing and I hate it.. a lot. I'm trying to learn the basics and it's the most frustrating learning curve I've ever experienced. I've been looking up documentation for hours and still haven't been able to figure out how to check which route has been rendered

I would go about this another way: instead of relying on the output of the request, and match that up against a template (which can be quite difficult unless you add some sort of identifier to each template, which doesn't feel entirely right), you can take advantage of some internals of Express, and specifically on how it renders templates.
The Express documentation states the following (here):
Express-compliant template engines such as Pug export a function named __express(filePath, options, callback), which is called by the res.render() function to render the template code.
You are not using Pug, but EJS, but the same principle applies: the ejs module exports a function called __express which will be called with full path of the template that should be rendered. And that also happens to be what you want to test for!
So the question now becomes: "how can you test that ejs.__express() gets called with the proper template name?". Answer: you can spy on it.
My favorite module for this is Sinon, so the example below will use that. Sinon is great to spy on existing functions, or to make them do entirely different things if you want.
As an example, I will use the following, very simple, Express app:
// app.js
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.render('index.ejs', { foo : 'bar' });
});
module.exports = app;
We want to test if, when / is requested, the template index.ejs gets rendered.
Instead of using superagent, I'll be using supertest, which is meant for testing HTTP apps.
Here's the annotated Mocha test file:
// Import the Express app (from the file above), which we'll be testing.
const app = require('./app');
// Import some other requirements.
const expect = require('chai').expect;
const supertest = require('supertest');
const sinon = require('sinon');
// Import the EJS library, because we need it to spy on.
const ejs = require('ejs');
// Here's the actual test:
it('sends login page if we\'re logged out at /', function (done) {
// We want to spy on calls made to `ejs.__express`. We use Sinon to
// wrap that function with some magic, so we can check later on if
// it got called, and if so, if it got called with the correct
// template name.
var spy = sinon.spy(ejs, '__express');
// Use supertest to retrieve / and make sure that it returns a 200 status
// (so we don't have to check for that ourselves)
supertest(app)
.get('/')
.expect(200)
.end((err, res) => {
// Pass any errors to Mocha.
if (err) return done(err);
// Magic! See text below.
expect(spy.calledWithMatch(/\/index\.ejs$/)).to.be.true;
// Let Sinon restore the original `ejs.__express()` to its original state.
spy.restore();
// Tell Mocha that our test case is done.
done();
});
});
So what's this magic:
spy.calledWithMatch(/\/index\.ejs$/)
It means: "return true if the function that was being spied on (ejs.__express()) got called with a first argument that matches the regular expression \/index\.ejs$". Which is what you want to test for.
The reason I'm using a regular expression here is because I'm lazy. Because the first argument (filePath in the quote above) will contain the full path to the template file, it can be quite long. You can directly test for it if you want:
spy.calledWith(__dirname + '/views/index.ejs')
But that would break if the location of the template directory got changed. So, like I said, I'm lazy, and I'll use a regular expression match instead.
With tools like supertest, sinon and chai, testing can actually become fun (honest!). I have to agree that the learning curve is rather steep, but perhaps that an annotated example like this can help you get a better idea on what's possible and how to go about it.

Related

Next.js with MySQL/Mongo backend

I have an existing Node.js/Express app which connects to 2 separate databases, it has a MySQL DB for all the relational and a MongoDB store for the non-relational vertical data.
It uses Sequelize and Mongoose and works absolutely swimmingly.
I've been looking at Next.js today and I'm pretty impressed, one of my pet peeves with React is actually how much bootstrapping there is and how much code it takes to achieve something simple. Next.js seems to solve some of those issues for me, so I'm willing to embrace it.
First issue - Is it possible to connect Next.js to existing DB's and read their objects directly in the view?
e.g. ./server.js:
const mongoDb = mongoose.connect(configDB.url); // MongoDB connection
const models = require('./models'); // Sequelize connection
app.prepare().then(() => {
server.use((req, res, next) => {
req.mongodb = mongoDb
req.mysqldb = models
// Logging req.mysqldb/req.mongodb at this point gives the correct result.
next()
});
server.get('*', (req, res) => {
return handle(req, res)
})
})
./pages/index.js:
Index.getInitialProps = async function(req) {
console.log(req.mongodb);
console.log(req.mysqldb)
// Example of what I want: req.mysqldb.users.findAll()....... to populate collection for this view
}
When the console statements are executed in the index.js page, they are logged as undefined.
Ideally I want to use the objects/ORM layer directly in the next.js templates, I do not want to have to call my own API internally, it seems like a huge waste of resources!
Any help, greatly appreciated.
Just for future reference. getInitialProps gets passed in an object with one of the keys being req. So you're meant to do something like the following instead
// add the curly braces around req
Index.getInitialProps = async function({ req }) {
// code
}
This is known as Function Parameter Destructuring and was introduced in ES6. What this accomplishes is similar to the following code
Index.getInitialProps = async function(_ref) {
var req = _ref.req;
}
Meaning, it takes the value of req of the object that gets passed and uses that value.
Well apparently by the time the request gets to the template it has changed a bit! Namely, it is nested within another request object.
req.req.mongodb and req.req.mysqldb both work fine :).

Mocha: How to test Express rendered views

-- Background
I'm trying to test an Express application. This is a hobby project for me, so I have not used Express to serve a JSON api (as it is usually done).
-- The Problem
The issue is, I can't figure out a way to test the view's content to ensure that the page is actually being rendered against the view file in the project itself.
-- What I've Tried
For instance, I can't test a simple index page, because the jade file starts with extends layout. This makes it difficult to test if dynamic content is being rendered.
Does anyone have suggestions for testing whether the view is actually being rendered?
it ('renders the index page', function(done) {
var jade = require('jade');
var should = require('should');
var fs = require('fs');
supertest(app)
.get('/')
.expect(200)
.end(function(err, res) {
var rawJade = fs.readFileSync('./views/index.jade').toString();
res.text.should.equal(rawJade.convertToHtml()); // jade supports a function like this
});
)};
There are, as usual, several ways to attack this problem. Before I continue, I encourage you to ask yourself why you need to do this. Tests like this are extremely fickle. If you make some tiny change, it is going to force you to rewrite tests that are now failing.
That said, I think the easiest way to start adding tests that assert proper rendering is with https://github.com/cheeriojs/cheerio
A basic example would look like the following:
it ('renders the index page', function(done) {
var should = require('should');
var cheerio = require('cheerio');
supertest(app)
.get('/')
.expect(200)
.end(function(err, res) {
err.should.not.be.ok();
res.should.be.ok();
var $ = cheerio.load(res.body);
var header = $('h1:first');
header.should.equal('Hello World!');
done();
});
)};
Now you aren't going to be testing whether the rendered view looks exactly like what you want (I mean you could but that would be tedious). But that also means that if you make some small insignificant change, the whole thing won't come crumbling down. Instead you can focus on testing whether key aspects of your UI are rendering properly (e.g. The title of the page is there, with the correct spelling and class/id properties)

Cascade-like rendering with Express JS

With an express app running on a node server, how would I go about recursively searching for a render file from the full path right back to the beginning of the supplied URL.
For example, if someone was to hit my server with www.somewebsite.com/shop/products/product, the render engine would first check that there is an index.jade file in shop/products/product/. If none is found it would then check shop/products/, and subsequently shop/.
var express = require('express');
var app = express();
app.get('/*', function(req, res){
res.render(req.path + '/index.jade', function(err, html){
// some loopback code which alters the path and recalls the render method
})
});
The problem is that the response object is not passed to the render callback, so I'm unable to recall render on the response. I'm looking to create a loop because the URL paths may be any number of directories deep, so I can't just assume I only need to cascade for a definitive number of times.
Anyone see a way round this?
You should be able to use the response object from the closure. I think (assuming express allows you to call res.render a second time) you could use code like this answer to achieve what you want:
var express = require('express');
var app = express();
app.get('/*', tryRender);
function tryRender(req, res){
res.render(req.path + '/index.jade', function(err, html){
if (err) {
req.path = 'mynewpath';
tryRender(req, res);
}
})
}
Note: You will need to add a base case or this function will recurse infinitely if it doesn't find a view that works :D
In the event that express doesn't allow a subsequent call to res.render, you'll probably need to find out if the file exists on the file system yourself.

Is it OK to add data to the response object in a middleware module in Express.js?

Here's the basic setup. I'm trying to create a simple middleware component that would allow me to easily pass data from my route directly to my javascript in the client side. (Very similiar to the Gon gem in ruby). The way I'm doing it is by having a module that looks like this:
module.exports = function(){
return function(req,res,next){
var app = req.app;
if(typeof(app) == 'undefined'){
var err = new Error("The JShare module requires express");
next(err);
return;
}
res.jshare = {};
app.dynamicHelpers({
includeJShare: function(req,res){
if(typeof(res.jshare) === 'undefined'){
return "";
}
return function(){
return '<script type="text/javascript">window.jshare=' + JSON.stringify(res.jshare) + '</script>';
}
}
});
next();
};
}
Then, in my route I can do this:
exports.index = function(req, res){
res.jshare.person = {firstName : "Alex"};
res.render('index', { title: 'Express' })
};
Finally in the layout.jade:
!{includeJShare()}
What that does is in outputs a line of javascript on the client that creates the exact JSON object that was created server side.
Here's the question; it all works as expected, but being new to Express and Node.js in general, I was just curious if attaching properties onto the response object is OK, or is there something wrong with doing it that I'm simply overlooking? For some reason it doesn't pass my "smell test" but I'm not sure why.....
I know this is an old thread, but there is something else to add to this topic.
Express has a response.locals object which is meant for this purpose - extending the response from middleware to make it available to views.
You could add a property directly to the response object, and as #hasanyasin indicated, is how JavaScript is designed. But Express, more specifically, has a particular way they prefer we do it.
This may be new in express 3.x, not sure. Perhaps it didn't exist when this question was asked.
For details, see
http://expressjs.com/en/api.html#res.locals
There is also an app.locals for objects which don't vary from request to request (or response to response I suppose).
http://expressjs.com/en/api.html#app.locals
See also: req.locals vs. res.locals vs. res.data vs. req.data vs. app.locals in Express middleware
It is perfectly OK. It is how JavaScript is designed. Only thing you should be careful is to not accidentally overriding already existing properties or being overridden by others. To be safer, instead of adding everything directly to req/res objects, you might consider going a level deeper:
res.mydata={}
res.mydata.person= ...
Like that.
Use res.locals for including custom variables in your response object.

How to Make a Call to Koa.js App Instance for Unit Tests

I don't know how I'd term this maybe 'static call to a koa router'? Does that seem like the right wordage here for what I'm really trying to accomplish if you were to talk about it technically?
Anyway, I'm using koa-router and I'm coding unit tests (not integration tests). So I do not want to invoke .listen() on my koa app because of that reason...it would create an http server which now makes my test an integration tests.
Instead in my test I simply want to make a straight call to the app object instance and call a route and be able to return no results and check that I returned no results in the response.
How can you do that? I can't find an example and I've tried all sorts of pseudo code attemps agains the koa app object.
If you want to test the function that koa-router routes to then just perform a unit test on that function and leave the routing out of it.
To me it sounds like you've got a file such as app.js and it contains all your code. What you can do is create a router.js file to put you route bindings and a services.js file where you can put your application logic.
So for example app.js might look like:
var koa = require("koa");
var app = module.exports = koa();
var router = require('./router.js');
app.use(router.unsecured.middleware());
app.listen(3000);
And router.js might look like:
var router = require("koa-router");
var service = require("./services.js");
var unsecured = module.exports.unsecured = new router();
unsecured.post('/account/signin', service.signinUser);
unsecured.post('/account/register', service.registerUser);
And services.js might look like:
module.exports.signinUser = function*(signinDetails) {
// contains your application signin logic
};
module.exports.registerUser = function*(registerDetails) {
// contains your application register logic
};
So in this manner you can individually test services.js. I don't see any value in individually testing router.js since it is so trivial. As #Dan Pantry shows you can test routing as part of an integration test using supertest.
Edit:
So this is a little experimental test I was playing around with to test that the routing is correct. I'm using mocha as the test runner and the code example I posted in my original code.
// standard library
var assert = require("assert");
// in app objects
var router = require('./router.js');
var service = require('./service.js');
describe("routing tests", function() {
it("test register routing, POST", function*(done) {
// arrange
var unsecured = router.unsecured;
var path = '/account/register';
var httpMethod = 'POST';
var expected = service.register.toString();
var actual;
// act
for (var i = 0; i < unsecured.stack.length; i++)
{
var pathMatch = unsecured.stack[i].path === path;
var methodMatch = unsecured.stack[i].methods.indexOf(httpMethod) >= 0;
if (pathMatch && methodMatch)
{
actual = unsecured.stack[i].middleware.toString();
break;
}
}
// assert
try {
assert.equal(expected, actual);
done();
} catch(err) {
done(err);
}
});
});
There is probably a neater way of doing this (and a more modular way for testing multiple paths) but as I said this is just a basic example to verify the routing is calling the correct service. What I'm doing is delving into the koa-router object to verify what path is bound to what service code depending on the HTTP method (e.g. POST, GET, etc).
If you have your routing and your services in modules this test completely avoids dealing with the main koa app. Although technically this test spans multiple units (the routing and the service code) so it would technically be an integration test but it does mean you don't go near app.listen() which is what you didn't want to call in your tests.

Resources