Node.js - Testing REST API routes with supertest - node.js

Req.body is not accessible in the routes while making a post request. It would be highly appreciative of someone if he/she help me getting through it. Here is screenshot of my microservice.test.js file. Am I missing something?
import request from "supertest";
import mongoose from "mongoose";
import config from "../config/env";
import routes from "../server/routes";
import { parseResponse, doRequest } from "../server/utils/helperFunctions";
const app = express();
app.use("/", routes);
jest.setTimeout(30000);

The code provided doesn't provide much insight, as I would expect all of the handling of the request to be in your route handler. Is the issue that you are unable to access the body when running tests with supertest? Or that it isn't working at all. More information would be very helpful.
If it is a supertest issue, I would recommend checking out the docs for good examples. Here is one I pulled directly from the NPM site where they POST some data with a request body and then verify the response body:
describe('POST /user', function() {
it('user.name should be an case-insensitive match for "john"', function(done) {
request(app)
.post('/user')
.send('name=john') // x-www-form-urlencoded upload
.set('Accept', 'application/json')
.expect(function(res) {
res.body.id = 'some fixed id';
res.body.name = res.body.name.toLowerCase();
})
.expect(200, {
id: 'some fixed id',
name: 'john'
}, done);
});
});
Also, if you are trying to test your server you should probably import your server code instead of creating a new express instance. For example, in your server code you'll have something like this:
server.js
const express = require('express');
const app = express();
app.use('/', ...) // middleware/route config
...
module.exports = app;
Your server would then use this server like this:
index.js
const app = require('./server')
const port = 4000
app.listen({ port }, () => {
const location = `http://localhost:${port}`
logger.info(`🚀 Server ready at ${location}`)
})
module.exports = app
Now that you have structured your code this way, in your test you can import your server as well (so you are testing your actual server, not a new server that you made up):
server.test.js
const app = require('../../../server');
const request = require('supertest')(app);
describe('Server', () => {
it('does a thing', async () => {
const { body } = await request
.post('http://path/to/test')
.send({ data: 'some data' })
.set('Content-Type', 'application/json')
.set('Accept', 'application/json')
.expect(200);
expect(body.thing).toBeTrue();
});
});

expressjs version lower than 4 include body parsing middleware
import bodyParser from 'body-parser';
app.use(bodyParser());
example test
it('.post should work with data', function (done) {
var app = express();
app.use(bodyParser());
app.post('/', function(req, res){
res.send(req.body.name);
});
request(app)
.post('/')
.send({ name: 'tobi' })
.expect('tobi', done);
})

Related

problem while POSTing to the server in Express

I'm learning Express and I face an issue which I can't understand.
When I route to /addPerson I expect to log the name: 'Mike', age: 30 to the console. Instead I got nothing logged to the console. What's wrong in my code?
here's the server.js code
const Express = require('express'),
app = Express(),
PORT = process.env.PORT || 5000,
parser = require('body-parser'),
data = []
// initialize the main project folder
app.use(Express.static('public'))
// running the server
app.listen(PORT, () => {
console.log(`Server is running at port ${PORT}`);
})
// include body parser to handle POST requests
app.use(parser.urlencoded({extended: false}))
app.use(parser.json())
// setup CORS
const cors = require('cors')
app.use(cors())
// GET request
app.get('/', (req, res) => {
res.send('<h1>Home Page</h1>')
})
app.get('/addPerson', (req, res) => {
res.send('<h1>Hello Hany</h1>')
})
// POST request
app.post('/addPerson', (req, res) => {
data.push(req.body)
console.log(data);
})
and here is the client side app.js code
const postData = async ( url = '', data = {})=>{
console.log(data);
const response = await fetch(url, {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
},
// Body data type must match "Content-Type" header
body: JSON.stringify(data),
});
try {
const newData = await response.json();
console.log(newData);
return newData;
}catch(error) {
console.log("error", error);
}
}
postData('/addPerson', {name: 'Mike', age: 30});
this the files structure
Alright, I've taken a look at your code and this is what I've noticed. Within your server.js file you have this code block:
app.get('/addPerson', (req, res) => {
res.send('<h1>Hello Hany</h1>')
})
That is sending back a static H1 tag when the user creates a get request to localhost:5000/addPerson. Then, directly below that you have your post route but you're never fully accessing that from anywhere (I looked through all your app.js code to double check).
Instead, I have changed your code to return a static html file with a button that allows you to call this function (just as an example so you can see that your routes do in fact work). This isn't the cleanest solution to your problem but I just wanted to make sure you see where the problem lies as I've been in your shoes before when I first started working with express. You can take a look at the CodeSandbox I setup below to replicate your issue and take a look through all the code to get an understanding.
To properly solve your issue using the app.js file you would have to serve the javscript file as your "frontend". Personally I'm a big fan of React so I usually serve my frontend with React, while my backend is express. You can also very easily serve this file using NodeJS in a similar fashion that you are with your "backend". If you were to take the React approach you would be able to modify this code:
app.get("/addPerson", (req, res) => {
res.sendFile(path.resolve(__dirname, "public", "index.html"));
});
To find the frontend section you desire using React (I can recommend react-router if you require multiple routes but I don't want to overwhelm you with too much information yet) and complete the same function. If you have any questions feel free to reach out and let me know! Hopefully this helps!

Axios Vue app only Post Request Network error

Using the Following Stack Express,Vue,SQL,Axios
GET request is working fine in postman as well from Axios
POST request created error in both attached screenshots
To make sure the Backend is working fine I have tried sending the Data directly from
<form action="url" method="POST">
it is Working fine and data is storing in the database
I have Tried few workaround like disabling SSL setting in postman and Played with proxy setting Also having CORS enabled in the backend and tried some Allow content and header things. Nothing worked
Not able to figure out the Problem in the POST Request. Please Help
--Request Error in the browser from Axios ----
Axios Browser Error
-postman Error when doing POST Request---
Postman Error
---Backend Index.js file---
// const express = require("express");
"use strict";
import express from "express";
const app = express();
import cors from "cors";
//listening on this port
app.listen(3000);
app.use(cors()); // to get Data from Other Domains
app.get("/", function (req, res) {
res.send("Application Started");
console.log("Application Started");
});
//Routes
app.use("/product", require("./routes/product"));
---product.js routes files---
import express from "express";
const router = express.Router();
import bodyParser from "body-parser";
//Middleware
router.use(bodyParser.urlencoded({ extended: true })); // To Parse the body data
//Importing Main Controller
import conProduct from "../controllers/ConProduct";
//Defining functions as per Routes
router.post("/add", conProduct.add); //Showing all the products
router.get("/get", conProduct.get); //Showing all the products
//Exporting Router
module.exports = router;
---Controller for Product file ConProducts.js ---
import sqlConfig from "../database/dbConfig";
let sql = sqlConfig.mysql_pool;
exports.add = (req, res) => {
console.log(req.body);
const { name, shortDescription, price, discount } = req.body;
let addQuery =
"INSERT INTO products(name,short_description,price,discount) VALUES('" +
name +
"','" +
shortDescription +
"','" +
price +
"','" +
discount +
"');";
sql.query(addQuery, (err, result) => {
if (err) throw err;
console.log(result);
res.send("product uploaded");
});
};
--Frontend axios Request --
let formData = {
name: this.name,
shortDescription: this.shortDescription,
price: this.price,
discount: this.discount,
};
console.log(formData);
axios
.post("/product/add", formData)
.then((res) => console.log(res))
.catch((error) => console.log(error));
I got the Answer I was missing a middleware app.use(bodyParser.json) in index.js and because of that there was no value going into database and thus, there was a network error.
I realized that, you are sending a PUT request to backend but your API controller receiving POST requests. So, Don't they have to be same type of request ?

Subdomain Integration Testing With Nodejs and Supertest

How to write integration tests for a multi-tenant app with subdomain.
I am working on a multi-tenant application. I am trying to write integration tests for a subdomain but can't seem to get it working.
Here is what I have done:
test file
import supertest from 'supertest';
import http from 'http';
import app from '../../../app';
const baseUrl = 'censio.lvh.me:7000';
describe('Censio tests', () => {
let server;
let request;
beforeAll((done) => {
server = http.createServer(app);
server.listen(7000, done);
request = supertest(server);
});
afterAll((done) => {
server.close(done);
});
describe('Signin', () => {
it('should respond with missing fields ...', async () => {
const response = await request
.post(`${baseUrl}/signin`)
.send({})
.set('Accept', 'application/json');
expect(response.status).toBe(400);
});
});
});
app.js
import '#babel/polyfill';
import bodyParser from 'body-parser';
import cookieParser from 'cookie-parser';
import express from 'express';
import subdomain from 'express-subdomain';
import censioRouter from './tenants/censio';
const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(cookieParser());
// Subdomain definitions
app.use(subdomain('censio', censioRouter));
app.get('*', (req, res) => res.status(200).json({ message: 'Project started' }));
export default app;
When I check response.text I see
<!DOCTYPE html>
<html lang=\"en\">
<head>
<meta charset=\"utf-8\">
<title>Error</title>
</head>
<body>
<pre>Cannot POST /:7000censio.lvh.me/signin</pre>
</body>
</html>
It puts the post in front. When I get rid of the port, I receive this error: connect ECONNREFUSED 127.0.0.1:80
What do I do?
I finally got it to work. I am posting this for whoever may need this solution in the future.
First, I gained full understanding of how to use a custom domain to test my application locally. I had to modify my /etc/hosts file. This post helped me with that.
I modified my tests (as seen in my question above). Instead of passing app to supertest, I passed the base url I wanted i.e my url containing my subdomain(censio.mydomain.com:myport). This is how it looks like now.
import supertest from 'supertest';
import http from 'http';
import app from '../../../app';
describe('Censio tests', () => {
describe('Signin', () => {
let server;
let request;
beforeAll((done) => {
server = http.createServer(app);
server.listen(7000, done);
request = supertest('http://censio.mydomain.com:7000');
});
afterAll((done) => {
server.close(done);
});
it('should respond with missing fields ...', async () => {
const response = await request
.post('/signin')
.send({})
.set('Accept', 'application/json');
expect(response.status).toBe(400);
});
});
});
Notice I changed where I am calling my before and after hooks.
I hope this helps you as it helped me.

Express app won't let me check what's inside req

I know my problem may seem not very specific, but I'm having problem describing what's happening because I don't understand it :(
So I've written small express app with ssr (for react) and jwt authentication. The SSR part works nice but the rest is crap.....
import 'babel-polyfill';
import express from 'express';
import bodyParser from 'body-parser';
import logger from 'morgan';
import authRouter from './auth/authRouter';
const app = express();
app.use(express.static('public'));
app.use(logger('dev'));
app.use(bodyParser.json({ type: '*/*' }));
app.use(bodyParser.urlencoded({ extended: false }));
//a lot of irrelevant ssr code
authRouter(app);
app.listen(3000, function(err) {
if (err) {
console.log(err);
}
console.log('main server on port 3000');
});
This is my main server file. My first problem is I don't see ANY console.logs from my files. There's nothing in my terminal. That's the reason I can't see how does my requests look like in my app. I'm testing it using postman like that:
And that's the authRouter I'm using above in main server file:
import express from 'express';
import { signup, signin } from '../controllers/authentication';
import { jwtLogin, localLogin } from '../services/passport';
import passport from 'passport';
passport.use(jwtLogin);
passport.use(localLogin);
//if user is auth'd do not create session for him
const requireAuth = passport.authenticate('jwt', { session: false });
const requireSignin = passport.authenticate('local', { session: false });
const authRouter = function(app) {
app.get('/auth', requireAuth, function(req, res) {
res.send({ hi: 'there' });
});
app.post('/auth/signin', requireSignin, signin); // irrelevant right now
app.post('/auth/signup', signup);
};
export default authRouter;
And that's signup function I'm using in router:
const signup = (req, res, next) => {
console.log('reqqqqqqq', req);
const { email, password, passwordCheck } = req.body; //code crashes here
//some code I deleted for everyones convenience
};
Every request I make my app crashes because req.body is undefined. I can't log req because I can't see any logs. I also tried sending back stringified version of my request body but every time i get "TypeError: Converting circular structure to JSON".
I'll be happy to add any information you may need
EDIT:
I'm gonna check that later at home but now I'm thinking there is something wrong with ssr part of my app because I don't even see that 'main server on port 3000' log..... At the same time server responds with right html, js files and routing works well so.....anyway I'm gonna look it up later
Try using util.inspect from node:
const util = require('util');
// usage in your code
const signup = (req, res, next) => {
console.log(util.inspect(req, { showHidden: true, depth: null }));
const { email, password, passwordCheck } = req.body;
...
};
I solved it.....unexpectedly the problem was lying in my package.json file.
My start script looked like this:
"scripts": {
"start": "nodemon build/server.bundle.js | nodemon build/api.bundle.js",
...
}
And because of that I had these weird errors. Only api.bundle.js file was running correctly......
Anyway thanks for your help ;)

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;
});

Resources