18

I am testing an express API with supertest. I am trying to pass in body parameters into the test, as can be seen in the code snippets below, but it appears that the body parameters don't get passed in correctly since I get an error message that the body parameters are undefined.

Running the test with command mocha --recursive returns the following error:

Cannot read property 'email' of undefined


Below is the code from file email-suite.js referencing supertest

'use strict';
var express = require("express");
var bodyParser = require('body-parser');
var mongoose = require("mongoose");

var app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
var supertest = require("supertest");
var chai = require("chai");
var should = chai.should();

var api = require("../server.js");

describe("Email Module", function() {
    this.timeout(25000);

    before(function(done){
        mongoose.createConnection(/* connectionstring */);

        mongoose.connection.on('open', function(err) {
            if(err) console.log(err);
            console.log('connected to server');
        });

        done();
    });

    it("Check Email Count", function(done) {
        var body = { email: "email@email.com" };

        supertest(api)
            .post("/emailCount")
            .set('Accept', 'application/json')
            .send(body)  // body is undefined
            .expect(200)
            .expect('Content-Type', /json/)
            .end(function(err, res) {
                if(err) return done(err);
                res.body.count.should.equal(2);
                done();
            });
    });
});

Below is the code from file email-api.js

'use strict';
var express = require("express");
var bodyParser = require('body-parser');
var router = express.Router();
var app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

router.post('/emailCount', function(req, res) {
    var email = req.body.email; // req.body is undefined
}

module.exports = router;

Below is the code from the file server.js

var express = require("express");
var app = express();

app.set("port", process.env.PORT || 3000);

var router = require("./user/email-api");

app.use('/', router);

app.listen(app.get("port"), function() {
    console.log("App started on port " + app.get("port"));
});

module.exports = app;
Rosco Kalis
  • 567
  • 3
  • 13
dpen82
  • 244
  • 1
  • 2
  • 13

2 Answers2

28

Put body-parser always after express object and before every routes in main server file like this

 var app = express();
 app.use(bodyParser.json());
 app.use(bodyParser.urlencoded({extended: true}));

//Router task start from here

Other wise always will get undefined since router call first and body parsed later.

abdulbarik
  • 6,101
  • 5
  • 38
  • 59
  • Glad to help you :) – abdulbarik Sep 13 '16 at 14:22
  • 10
    This did not solve the problem for me. Still getting "undefined" values for any request body object, in each field – Tom Goldenberg Nov 10 '17 at 17:00
  • i just had this exact problem, spent hours debugging, couldnt figure out what the hell was wrong. it was just order of body parser and routes. i cant believe this. thank you sir – Leo Mar 17 '23 at 16:47
0

Thank you abdulbarik for your answer. I want to add some extra information to aid clarity in case people are still getting undefined values for the request body object, and if (as in my case) your routers and tests are setup differently.

Here is the router that we shall test:

// router.js

const express = require("express");

const router = express.Router();

router.post("/", (req, res) => {
  res.json({ success: true, data: req.body });
});

module.exports = router;

The following test code will result in the request body being undefined, and thus the test failing:

// router.test.js

const express = require("express");
const request = require("supertest");
const bodyParser = require("body-parser");

// set up the test app - this will fail
const app = express();
app.use("/routerPath", require("./router")); // this will cause the test to fail, as the router should be setup after the body-paser
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

// run the test
it("successful response", async () => {
  const response = await request(app)
    .post("/routerPath")
    .send({
      firstname: "John",
      lastname: "Smith",
    })
    .set("Accept", "application/json");

  expect(response.status).toEqual(200);
  expect(response.body).toEqual({
    success: true,
    data: {
      firstname: "John",
      lastname: "Smith",
    },
  });
});

The reason why, as explained by abdulbarik, is that the body-parser code should always be before the router code, so that the parser runs before the router. To make the test pass, simply swap these lines around:

// set up the test app - this will work now
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use("/routerPath", require("./router")); // the router setup should happen after the body-parse setup

I hope that is a helpful clarification.

James Porter
  • 158
  • 1
  • 6