5

I'm testing an express server using super-test and I need to test a post call. I assume the post should be successful and return a status of 200 but it is returning 401. I've been told by someone that I need to pass a request body with the post but I'm unsure exactly how to do this.

I've attempted to use .send({name: 'aName'}) but that gives me the same 401 code.

Below is the app.js

require('dotenv').config();
const express = require('express');
const bodyParser = require('body-parser');
const hateoasLinker = require('express-hateoas-links');
const AValidator = require('./AValidator');
const BValidator = require('./BValidator');
const schema_v1 = require("./schema.json");
const {
    logService: logger
} = require("@utils");

let aValidator = AValidator(schema_v1);

let ValidatorApi = BValidator.ValidatorApi('api');
let adminValidator = BValidator.ValidatorAdmin('admin');
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(hateoasLinker);

app.post('/*/activate',admiValidator, (req, res) => {
    console.log("In Activate===============>");
    res.status(200);
    res.json({
        rel: "self",
        method: "POST",
        title: 'Activate Solution',
        href: "/activate"
    });
});

Here is the code for the BValidator

ValidatorAdmin = function(callType){
    return function (req,res,next){
        let authoizationHeader = req.headers['authorization'];
        try {
        Verifier.verifyPayload(authoizationHeader, callType, (verificationError) => {
            if (verificationError) {
                res.setHeader('Content-Type', 'application/json');
                res.status(401);
                res.json({
                    message : "verificationError "+verificationError.message
                });
            } else {
                next();
            }
        });
        } catch (authorizationError) {
            res.setHeader('Content-Type', 'application/json');
            res.status(401);
            res.json({
                message : authorizationError.message
            });

        }
    }
}

Here is the app.test.js

const request = require('supertest');
const bodyParser = require('body-parser');
let AValidator = require('../src/AValidator');
let BValidator = require('../src/BValidator');
BValidator = jest.fn();
AValidator = jest.fn();
app = require('../src/app');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

describe('Test os GET/POST calls in app.js', ()=>{

  test('Tests activate post', (done)=>{

    BValidator.mockReturnValue({
      ValidatorApi: (req,res,next)=>{
        next();
      },
      ValidatorAdmin:(req,res,next)=>{
        next();
      }
    });

    AValidator.mockImplementation((schema)=>{
      return function (req,res,next){
        next();
      }
    });


    request(app)
      .post('/test/activate')
      .set({name:'josh'})
      .then((response)=>{
        expect(response.statusCode).toBe(200);
        done();
      })

  })


});

So ultimately I'd like this post to resolve successfully and return a status code of 200.
Nino Filiu
  • 16,660
  • 11
  • 54
  • 84
jwolsborn
  • 576
  • 1
  • 6
  • 26
  • You probably need `.post('/test/activate', {name: 'josh'})`.. and remove the `.set` – Isaac Vidrine Mar 29 '19 at 19:47
  • unfortunately that doesn't work either :-( – jwolsborn Mar 29 '19 at 19:48
  • Okay, just looked at supertest docs. If `.send` didn't work, then theres a problem with the `admiValidator`. Im assuming it expects a token or something in the header of the request, and if there isn't returns 401 unauthorized. – Isaac Vidrine Mar 29 '19 at 19:50
  • Well I did just notice that when i use .send I get a 400 error instead of a 401. So I guess that's a change at least – jwolsborn Mar 29 '19 at 19:52
  • Have you tried testing out the endpoint manually in postman? – Isaac Vidrine Mar 29 '19 at 19:53
  • No. I may go that route. the person I'm testing the code for seems pretty sure this is what I need to do but I may try that – jwolsborn Mar 29 '19 at 19:55
  • Run in debug mode, set some breakpoints and step through to see whats going on. If non of your breakpoints are being hit, then you need to debug the `admiValidator` middleware, because thats where it is failing. – Isaac Vidrine Mar 29 '19 at 19:56
  • Well the admiValidator is mocked out by the ValidatorAdmin so it should only hit that next() – jwolsborn Mar 29 '19 at 19:57
  • Also reading `supertest` api, `.set` will set request header fields. `.send` will add fields to body of the request. – Isaac Vidrine Mar 29 '19 at 19:57
  • Yeah idk, your best bet is to manually test and set breakpoints in the endpoint to see whats going on first. – Isaac Vidrine Mar 29 '19 at 20:08
  • BTW, if you're getting a 401, there is a 100% chance its your middleware, because you aren't sending back a 401 in your code. `admiValidator` is being hit regardless of what you've got set in your test class – Isaac Vidrine Mar 29 '19 at 20:10
  • Yea I'm looking at it and it seems the middleware isnt getting mocked. Good eye – jwolsborn Mar 29 '19 at 20:13
  • Yeah again idk what `admiValidator` is doing without seeing the code, but its expecting a header or something with the value 'admin', so in the test you have to pass that with the request. – Isaac Vidrine Mar 29 '19 at 20:19
  • Now should that matter if I mock it like I intended above? I'll post the validator code though – jwolsborn Mar 29 '19 at 20:32
  • @IsaacVidrine so I added that code and yea I noticed that the actual function was getting called instead of the mock. That 401 is coming from Verifier.verifyPayload which causes an error. But since I'm unit testing I shouldn't really be dealing with that and the whole ValidatorAdmin should be mocked out...in theory. – jwolsborn Mar 29 '19 at 20:33
  • Idk what `.mockReturnValue` and `.mockImplementation` is doing, but I know it doesn't matter what you have there because `admiValidator` will always get called on every request to your endpoint. But yeah, definitely add the validator code. – Isaac Vidrine Mar 29 '19 at 20:35
  • well the admiValidator is BValidator.ValidatorAdmin which is why I'm mocking it – jwolsborn Mar 29 '19 at 20:36
  • Okay, your middleware is expecting a header field called `authorization`. If it exists, it then calls `Verifier.verifyPayload`.... so if you can post that code too. – Isaac Vidrine Mar 29 '19 at 20:36
  • It doesn't matter that you're calling `.mockReturnValue` and `.mockImplementation` in your test, because if the request being sent doesn't have the header field `authorization` and a correct value, you're going to always get a 401. You have to set the correct request header in the test – Isaac Vidrine Mar 29 '19 at 20:37
  • Unfortunately I don't have that code available to me. But shouldn't I be able to stub the function that is assigned to admiValidator thus keeping it from ever reaching Verifier.verifyPayload? I guess my point is I don't want that middleware to ever truly be called, just a mock that returns next() – jwolsborn Mar 29 '19 at 20:40
  • Post code for `BValidator.mockReturnValue` and `AValidator.mockImplementation` – Isaac Vidrine Mar 29 '19 at 20:45
  • Sorry the code above for Validator is BValidator that was a typo. The AValidator doesn't come into play here – jwolsborn Mar 29 '19 at 20:46
  • Post code/definition of `BValidator.mockReturnValue`... that is where the truth lies. – Isaac Vidrine Mar 29 '19 at 20:52
  • well the code that is there is the code for it. .mockReturnValue is a function provided by jest that allows you to mock the return value of a function – jwolsborn Mar 29 '19 at 20:53

2 Answers2

12

I am assuming you have set the parser correctly. Example:

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

Once this is set, I believe you're missing the headers. Example:

const payload = {name: 'john', email: 'xyz@sadfjak.com', password: '2342388' };
const res = await request(app)
            .post('/api/register')
            .send(payload)
            .set('Content-Type', 'application/json')
            .set('Accept', 'application/json')
Sheraz Ahmed
  • 564
  • 9
  • 29
-3

Your problem is that you have a wrong understanding of what those mock functions are doing. First of all, you are completely overwriting the original values of AValidator and BValidator with jest.fn().

So doing

let AValidator = require('../src/AValidator');
let BValidator = require('../src/BValidator');

in your test is redundant.

The purpose of mockReturnValue is so that you can call that function and get back the return value you specified.

Taken straight from Jest docs

const myMockFn = jest
  .fn()
  .mockReturnValue('default')
  .mockReturnValueOnce('first call')
  .mockReturnValueOnce('second call');

// 'first call', 'second call', 'default', 'default'
console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn());

You are never using or calling your mock functions, and furthermore your api has no clue they even exist.

The solution is to provide the appropriate headers in the request when you run your tests so they don't fail in the middleware. Also, to do that, you have to know what Verifier.verifyPayload is doing.

with supertest your request should look like

request(app)
.post('/test/activate')
.set({authorization: 'a_valid_value_goes_here'})
.then((response)=>{
   expect(response.statusCode).toBe(200);
   done();
})
Isaac Vidrine
  • 1,568
  • 1
  • 8
  • 20