Passing parameters to Express middleware not working - javascript

I'm trying to create an input validation middleware using Express. My goal is to be able to pass 2 parameters to the middleware that validates client input. The problem is, after following multiple resources (including Express docs), my middleware seems to not be working.
// input validator middleware
export const validateInput = (schema: joi.ObjectSchema) => {
console.log('first console log');
return (req: Request, res: Response, next: NextFunction) => {
console.log('second console log');
const { error } = schema.validate(req.body);
if (error) {
const errors = error.details.map((err) => err.message);
next(new InvalidInput(errors));
}
next();
};
};
// middleware call
const commentSchema = joi
.object({
content: joi.string().alphanum().min(3).required(),
})
.options({ abortEarly: false });
export const validateCommentInput = () => {
validateInput(commentSchema);
};
After calling the middleware, I get to the "first console log", but never to the second, and my API just hangs there until I force stop. My solution otherwise would be to just pass req and next as parameters to a function validateInput(req, next, commentSchema);, but I'm not sure that's the proper way to do it. I also tried the async version with the same results.
Any help is greatly appreciated.

Your validateCommentInput function isn't returning the inner function.
The lack of curly braces in a lambda implies a return statement. However, using curly braces means you have to specify return.
So change this:
export const validateCommentInput = () => {
validateInput(commentSchema);
};
to this:
export const validateCommentInput = () => validateInput(commentSchema);

Related

Mock implementation of another module not working in jest

I am unit testing (using jest) a function named "getTripDetails" (inside file trip.js) that calls another file "getTrace.js" from different module (which exports a function as shown below).
I want to mock the call of function "getTrace" while testing "getTripDetails" function.
file: trips.js
const gpsTrace = require("./gpsTrace");
getTripDetails = async(req, res)=>{
let gpsTraceRes = await gpsTrace(req.body, req.adToken)
//more code...
return {status:200};
}
file: getTrace.js
module.exports = async(payload, token) =>{
try {
//code
} catch (e) {
error(e)
throw new Error(e)
}
}
This is what i tried after reading the docs.
file: test.js
let ctrl = require("./trips");
describe("API -- testing", function () {
it("Trip details", async function () {
jest.mock('./gpsTrace');
const gpsTrace = require('./gpsTrace');
gpsTrace.mockImplementation(() => {});
gpsTrace();
await ctrl.getTripDetails({},{});
expect(response.status).to.eql(200);
});
});
It did not get mocked, instead it was calling the original implementation.
Any suggesstions?
You were pretty close! Here are the updated files with comments describing the changes:
gpsTrace.js
Added a console.log message. We won't see this in the test if the mock works successfully.
module.exports = async (payload, token) => {
try {
//code
console.log("You won't see me in the Jest test because of the mock implementation")
} catch (e) {
error(e)
throw new Error(e)
}
}
trips.js
You needed to export your code to be used in other modules. Seeing as you're calling ctrl.getTripDetails() in the test, it makes sense to export your getTripDetails() on an object at the bottom of the file.
const gpsTrace = require("./gpsTrace");
const getTripDetails = async (req, res) =>{
let gpsTraceRes = await gpsTrace(req.body, req.adToken)
//more code...
return { status:200 };
}
module.exports = {
getTripDetails,
}
gpsTrace.test.js
Make sure to import your modules at the top of the file. Remember that ctrl.getTripDetails({}, {}) calls gpsTrace internally, so no need to call it twice in your test. You also needed to save the response returned from getTripDetails into a variable to be able to compare it: const response = await ctrl.getTripDetails({}, {});.
// make sure your require statements go at the top of the module
const gpsTrace = require('./gpsTrace');
let ctrl = require("./trips");
jest.mock('./gpsTrace');
gpsTrace.mockImplementation(() => {});
describe("API -- testing", function () {
it("Trip details", async function () {
// ctrl.getTripDeals() calls your gpsTrace function internally, so no need to call it twice
// gpsTrace(); <-- can be removed
// you needed to save the returned response into a variable to be able to test it.
const response = await ctrl.getTripDetails({}, {});
expect(response.status).toEqual(200);
});
});
Result
After running the test it now successfully passes. Notice that we DO NOT see the console.log message in the gpsTrace function, which indicates our mockedImplementation of the function is working in the test script. 👍

How to unit test an Express Controller with Jest

I have been getting terribly confused with how to test my controller functions. I realize that I have to mock my dependencies, request, response, and the controller functions. Here's what I have so far:
OrdersController.js
const OrderService = require('../services/orderServices')
module.exports = class OrdersController {
static async apiGetOrders(req, res, next) {
try {
const orders = await OrderService.getOrders()
return res.status(200).json(orders)
} catch (error) {
return res.status(500).json({ error: 'Unable to get orders' }) // 500, Internal Service Error, generic
}
}
static async apiPostOrder(req, res, next) {
// All good, create an orderDocument
try {
const orderDocument = {
_id: null, // undefined at this point Mongo creates this _id for us
orderId: req.body.orderId,
cookies: req.body.cookies,
daySelected: req.body.daySelected,
timeSelected: req.body.timeSelected,
userInfo: req.body.userInfo,
createdAt: new Date(),
}
await OrderService.addOrder(orderDocument)
return res.status(201).send('success') // status OK, something was Created
} catch (error) {
return res.status(500).json({ error }) // 500, Internal Server Error
}
}
OrdersController.spec.js
import OrderService from '../services/orderServices'
import { mockOrder, mockOrders } from '../mocks/fixtures'
import OrdersController from '../controllers/ordersController'
jest.mock('../controllers/ordersController.js')
const mockRequest = () => {
return {}
}
const mockResponse = (mockOrders) => {
const res = {};
res.status = jest.fn().mockReturnValue(200);
res.json = jest.fn().mockReturnValue(mockOrders);
return res;
}
// #3 Test the OrdersControllers
// mock dependencies: req, res, and spyOn the controller functions
describe('Orders Controller', () => {
test('[Positive], should call OrderService.getOrders and receive status 200', async () => {
jest.spyOn(OrdersController, 'apiGetOrders')
const req = mockRequest()
const res = mockResponse(mockOrders)
await OrdersController.apiGetOrders(req, res)
expect(res.status).toHaveBeenCalledWith(200)
expect(res.json()).toEqual(mockOrders)
})
test('[Negative], error yields status 500', async () => {
jest.spyOn(OrdersController, 'apiGetOrders')
const req = mockRequest()
const res = mockResponse({status: 500, error: 'Unable to get orders'})
await OrdersController.apiGetOrders(req, res)
expect(res.status).toHaveBeenCalledWith(500)
expect(res.json()).toEqual(error)
})
})
I'm trying to test the happy path and the negative path on the get request. I followed this expample, https://codewithhugo.com/express-request-response-mocking/, and read all of the jest docs, https://jestjs.io/docs/mock-functions. The error that I receive is:
Questions:
Am I actually writing the tests correctly?
Am I also supposed to mock the OrderService?
Should I use Sinon or is Jest more than sufficient?
I am also new to Jest and am struggling with finding documentation that goes to enough detail to suggest to me what I'm doing wrong. But in your case, I think you might be spying on the wrong thing. The OrdersController is the subject of the test, so I don't believe that you should be mocking that. Rather you should spy on OrdersController's dependency, Orderservice and mock its methods.
Also, its not clear to me why you have next in
static async apiGetOrders(req, res, next)
You don't seem to use it in the body of the method anywhere, so hanging any testing off that value, probably won't work either.

axios.post not returning data from server: "Cannot destructure property 'data' of '(intermediate value)' as it is undefined"

I am trying to get data from server via axios.post().
Decided to use POST and not GET because I want to send an array with ids to look up in the database, which might be too large to fit in GET query params.
I managed to send an array with ids in the body of the POST. This reaches my server. I can successfully find the items in the data base. The items are then returned in the response. The data shows up in Chrome devtools > Network (status 200). I also get the right stuff back when sending a request manually using Postman.
Everything seems to be working fine, but the response does not arrive in my data variable in the axios function.
I spent the day trying out the solutions to all the similar answers here. Nothing worked...
I also tried GET and sending the ids in query params instead, which gives the same error. I suspect I am doing something wrong with async/await because I am getting this "intermediate value" thingy.
Thanks in advance for the help.
CLIENT axios functions
const url = 'http://localhost:5000';
export const getStuff = Ids => {
axios.post(
`${url}/cart/stuff`,
{
Ids: Ids,
},
{
headers: {
'Content-Type': 'application/json',
},
}
);
};
CLIENT actions
import * as api from '../api';
export const getStuff = Ids => async dispatch => {
try {
// Ids is an array like ["5fnjknfdax", "5rknfdalfk"]
const { data } = await api.getStuff(Ids);
// this gives me the error in the title, data never comes through
//dispatch(-dolater-);
} catch (error) {
console.log(error);
}
};
SERVER controllers
export const getStuff = async (req, res) => {
try {
const { Ids } = req.body;
const stuff = await STUFF.find().where('_id').in(Ids);
console.log('SERVER', stuff);
// this works until here. request comes through and
// I can successfully find the stuff I want in the database
res.status(200).json(stuff); // this also works, response is being sent
} catch (error) {
res.status(404).json({ message: error });
}
};
SERVER routes
router.post('/cart/stuff', getStuff);
You have some extra curly braces here (or a missing return, depending on how you look at it). When you use a lambda (arrow function) with curly braces, you have to explicitly return a value or else it will return undefined. Change your code from this:
export const getStuff = Ids => {
axios.post(...);
};
to one of these:
// Option 1
export const getStuff = Ids => {
return axios.post(...);
};
// Option 2
export const getStuff = Ids => axios.post(...);
Either format will return the actual axios promise, instead of the default undefined.
export const fetchPost = () => {
return axios.get(url);
};
This works for me!!

Nock interceptors chaining, second mock ignored

A simple example of a mocking chain of requests with nock.
const request = require('request-promise');
module.exports = () => {
const URL1 = 'https://my.host.com/a/b/c/d';
const URL2 = 'https://my.host.com/a/b/x/y?k=v';
const options = {
method: 'POST',
uri: URL2,
body: {
some: 'payload'
},
json: true
};
return request(URL1)
.then(() => request(options))
.catch(e => console.error(e))
};
and test for it:
require('should');
const nock = require('nock');
const testFn = require('./');
describe('Check endpoint requests', () => {
beforeEach(() => {
nock.disableNetConnect();
});
afterEach(() => {
nock.cleanAll();
nock.enableNetConnect();
});
it('should hit correct endpoints', () => {
const scope = nock(`https://my.host.com/a/b`, {
encodedQueryParams: true,
})
.get('/c/d')
.reply(200)
.post('/x/y', {
some: 'payload'
})
.query({k: 'v'})
.reply(200);
testFn().then(() =>
scope.isDone().should.be.true()
);
});
});
As a result during the tests, the second "POST" request mock is completely ignored. After the hitting first mock URL1 - nock clearing the pending mocks for that scope and marks it as done.
The thing which counts I thing is that the basic URL is the same.
Is it a bug, or I use it incorrectly.
You have a few minor issues in your test.
First, the value passed to nock should just be the origin and shouldn't include part of the path. Instead, in your case, get and post should have the full path.
Second, you want to remove encodedQueryParams: true. That flag means the interceptor is created using already encoded query/search params, however, you're calling it like .query({k: 'v'}), which is not pre-encoded.
The last issue is that you weren't telling Mocha when the test was finished. So it was completing the test before having all of its results. There are two ways to achieve this. Either accept an argument in the it callback, done is the nomenclature. Or make the callback async and await your requests. I've implemented the latter below.
it('should hit correct endpoints', async () => {
const scope = nock('https://my.host.com')
.get('/a/b/c/d')
.reply(200)
.post('/a/b/x/y', {
some: 'payload'
})
.query({k: 'v'})
.reply(200);
await testFn();
scope.isDone().should.be.true();
});

How to test function which returns function with parameters?

I want to test the code below, but i'm unsure how to test this function since it returns a function with parameters. You can see in the image that i'm trying to achieve 100% test coverage and to do that I need a test that gets into the returned function.
const jwt = require('express-jwt')
function validateJwt (tokenConfig) {
if (!tokenConfig || !tokenConfig.secret) {
throw new TypeError('tokenConfig param must be defined and have attribute "secret"')
}
return (req, res, next) => {
jwt(_.extend({}, tokenConfig, {
requestProperty: 'tkn',
getToken: ReqHelpers.getEitherTkn
}))
}
}
Test method which obviously fails with the error AssertionError: expected [Function] to be true
it('should succeed', () => {
let result = middleware.validateJwt({secret: 'foo'})
expect(result).to.be.true
})
For this kind of test, what we can do is to spy on jwt function call and checking its arguments.
Updated:
since express-jwt return function, we need to involve proxyquire to spy the function. Ref: https://github.com/thlorenz/proxyquire
You can do something like this:
const proxyquire = require('proxyquire');
const sinon = require('sinon');
const jwtSpy = sinon.spy();
const middleware = proxyquire('./middleware', { 'express-jwt': jwtSpy }); // 'express-jwt' comes from your require statement for this package
it('should call jwt', () => {
const req = sinon.spy();
const res = sinon.spy();
const next = sinon.spy();
middleware.validateJwt({secret: 'foo'})(req, res, next);
expect(jwtSpy.called).to.be.ok;
expect(jwtSpy.calledWithArg({ secret: 'foo', requestProperty: 'tkn'}).to.be.ok; // for checking the arguments
})
Hope it helps
Right, so two things.
First, in your test you need to execute the returned function rather than testing it directly. Unfortunately I’m on my phone and can’t post code right now.
Second, your function that’s returned doesn’t return anything itself, as it just calls the jwt function. This isn’t necessarily a problem. As long as jwt() is updating some object or variable in your test space, you can test the current state of that obj/variable in your test instead of interrogating the function directly.

Categories