How do I mock express.Application with Jest? - node.js

Some class that takes express as an argument (basic DI):
class App {
constructor(express: express.Application) { /* ... */ }
}
My test:
// this doesn't work ("argument not assignable"):
//const expressMock = jest.mock("express");
//let app = new App(expressMock);
// so how do I mock it?
const expressMock = ???
let app = new App(expressMock);
How do I mock express.Application with Jest? The whole thing, not just a request, or route, etc.

Technically, express.Application cannot be mocked, it's an interface. It's an object that it represents that should be mocked.
As jest.mock documentation states, it
Returns the jest object for chaining.
So this is not a correct way to retrieve mocked object:
const expressMock = jest.mock("express");
A correct one would be
jest.mock("express");
...
const expressMock = require("express");
Moreover, App accepts not express factory function but application object, which is a result of express() call.
jest.mock without factory function results in auto-mocked express factory which won't produce proper application object.
Since expressMock is passed to App directly in tests, there's no necessity to mock Express module. A mock that contains bare minimum implementation can be passed instead:
const expressMock = {
use: jest.fn(),
...
} as any as express.Application;
let app = new App(expressMock);

Related

Node.js Route to Controller not applying Controller constructor

I have an express.js application that uses the express.Router() to connect my endpoints to controllers.
My goal is to have an object newed up in the controller constructor so I can use it in all controller functions without having to new it up in each one.
The constructor runs correct, and the object is available within the constructor. But whenever I call any actions of the controller, the object is null.
Here is the router
const express = require('express');
const componentController = require('../controllers/component');
const router = express.Router();
// component routes
router.get('/components', componentController.getComponents);
module.exports = router;
And here is my controller.
const LogService = require('../services/logService');
class ComponentController {
constructor() {
this.logger = new LogService('ComponentController');
this.logger.logDebug('test1','test1');
}
async getComponents(req, res) {
const test = new LogService('ComponentController');
test.logDebug('test2','test2');
this.logger.logDebug('test3','test3')
res.json('');
}
}
module.exports = new ComponentController();
I want the LogService to be available in the controller actions. The first two logs work correctly, test1 and test2. But test3 throws an error saying logger is undefined.
Why is this.logger undefined in later functions? How can I fix this issue?
try to refactor getComponents to an arrow function.
Here is why: https://javascript.plainenglish.io/this-binding-in-es6-arrow-function-70d80e216238
You can also do this:
router.get('/components', componentController.getComponents.bind(componentController));

Please explain the basic server setup code of Express.js

In this code snippet:
Const express = require('express')
Const app = express();
/*Typeof express = function
Typeof app = function*/
app.get()
My question is: if app is a function then how can we use a dot operator with it to call get function, and if we are creating a object of function express then why didn't we use new keyword to create an object.
Secondly, module.exports exports the literals in object format then why we are getting typeof express here a function.
If I am wrong anywhere, please correct me.
In JavaScript functions are objects, so this is valid:
function x() {
console.log("this is x()");
}
x.y = function() {
console.log("this is x.y()");
}
x();
x.y();
Express and other JavaScript tools use this feature extensively.
If you're used to other languages where functions are just functions and not objects themselves this will seem extraordinarily strange.

How to check the content of Koa's ctx.query?

For a Koa-based API server, I want to check what parameters the URL query contains.
My setup looks as simple as this:
const Koa = require('koa')
const app = new Koa()
const Router = require('koa-router')
router = new Router()
router.get('/', ctx => {
console.log(ctx.query)
})
app.use(router.routes())
app.use(router.allowedMethods())
app.listen(3000)
It seems that ctx.query has a structure like an object but doesn't work as one.
Methods like ctx.query.hasOwnProperty() or ctx.query.toString() result in an error saying that it is not a function.
Though, Object.keys(ctx.query) gives an array of the keys—which is confusing to me because it apparently is an object and should have above methods.
What is ctx.query exactly? How can I make the failing methods from above working?
ctx.query is the return from Node.js' querystring.parse() method. From the documentation:
The object returned by the querystring.parse() method does not
prototypically inherit from the JavaScript Object. This means that
typical Object methods such as obj.toString(), obj.hasOwnProperty(),
and others are not defined and will not work.
You can check Koa's request implementation.

Is there a way to track an instance in Node without passing it around everywhere?

I have a singleton logger file. When a request comes into Express, I use middleware to set the request ID.
// Relevant parts of server.js
import express from 'express';
import requestIdMiddleware from './request-id-middleware';
const app = express();
app.use(requestIdMiddleware);
--
// Relevant parts of request-id-middleware.js
const uuid = require('uuid/v4');
const { setRequestId } = require('./logger');
module.exports = function(req, res, next) {
const id = uuid();
req.id = id;
// This sets a static variable on the plain logger object
setRequestId(id);
next();
};
--
// Relevant parts of logger.js
module.exports = {
request_id: null,
setRequestId: id => {
this.request_id = id;
},
log: message => {
// sends a log out using this.request_id, which is shared by all users of the server
}
}
Using the plain object now means everyone is sharing the same value. So despite each request calling setRequestId, it means if the first request takes longer than the second, it may use the second request's ID when referencing logger's value.
It seems I would need to make the logger a class instead of a plain object, instantiate it in my request middleware, and then pass the instance through to ensure unique request IDs across multiple calls from same request. Although unlikely, hoping to find a way around needing to pass a variable down into anything I want to log from.

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