14

Is there any way to directly access the req object in supertest, while/after the request is being tested? I want to test my passport strategies, so I want to check req.user, req.session, and perhaps others. I know I can test page redirects or flash, as those are what my strategies do, but it seems useful to see if there is a user on the req object, as well. If I do this, I can also check how many users there are at any one time.

I will sign users up with the "local-signup" strategy, which is defined thusly:

'use strict';

// get passport & mongoose
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var UserModel = require('mongoose').model('User');

module.exports = function() {

    // signup function
    passport.use('local-signup', new LocalStrategy({
            passReqToCallback: true // pass the entire request to the callback
        },

        function(req, username, password, done) {

            process.nextTick(function() {

            // find a user with the same username
                UserModel.findOne({username: username}, function(err, user) {

                    // if there is an error, log it then return it
                    if(err) {
                        console.log("Error finding a user in the database: " + err);
                        return done(err);
                    }

                    // if a user was already found
                    if(user) {
                        return done(null, false, "User already exists");
                    }

                    // if we get this far, create a new user from the request body
                    var newUser = new UserModel(req.body);

                    // save it and sign it in
                    newUser.save(function(err) {
                        if(err) {
                            console.log("Error during signup: " + err);
                            return done(err);
                        }
                        return done(null, newUser);
                    });

                });

            });

        }

    ));

};

One way I use this strategy is like this:

My "local" strategy is defined like this:

'use strict';

var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var UserModel = require('mongoose').model('User');

module.exports = function() {

    // create our local passport strategy & use it
    passport.use(new LocalStrategy({

            // use the default names for the username & password fields
            usernameField: 'username',
            passwordField: 'password'
        },

        // main strategy function
        function(username, password, done) {

            // find user with given username
            UserModel.findOne({
                username: username
            },

            // with this username, do this
            function(err, user) {

                // if there's an error, log it then pass it along
                if(err) {
                    console.log("Error during login: " + err);
                    return done(err);
                }

                // if the username and/or password is incorrect, return an error
                // along with a message
                if(!user || !user.authenticate(password)) {
                    return done(null, false, {
                        message: 'Invalid username and/or password'
                    });
                }

                // if everything is correct, return the user document from the database
                return done(null, user);

            });
        }

    ));
};

I use both strategies like this, for example:

app.route(pageName).post(function(req, res, next) {
    passport.authenticate(strategyName, function(err, user, info) {
        if(err || !user) {
            res.status(401).send(info);
        }
        else {
            req.login(user, function(err) {
                if(err) {
                    res.status(400).send(err);
                }
                else {
                    res.send(null);
                }
            });
        }
    })(req, res, next);
});

I tried

request = require('supertest');
this.authServer = require('../my-server');

request(this.authServer)
    .put('/signup')
    .set('Content-Type', 'application/json')
    .set('Host', 'konneka.org')
    .send(this.fullUser)
    .end(function(req, res, done) {
        console.log(res);
    });

The res object I logged, inside the end() function, which was way too long to show here, has a req object defined on it, but it seems to only have the objects & functions that were defined before the request was opened. In other words, it does not have req.user, req.session, or other objects I want, because they are defined after the request completes and a new request is started. I noticed it has status codes, as well, which are only defined after the request completes, so I must be missing something.

Is there any way to get access to the req object after the request you are testing is ended? Or am I going about this completely the wrong way?

trysis
  • 8,086
  • 17
  • 51
  • 80
  • What kind of passport strategies are you trying to test? Are those oAuth strategies (like Facebook or Twitter Login) or some custom ones? Could you post the code and/or configuration of those strategies? – lukaszfiszer Nov 07 '14 at 16:43
  • OK. I didn't realize different strategies mattered when it comes to `req.user` or whatever. – trysis Nov 07 '14 at 17:23

2 Answers2

17

You cannot do what you want using supertest.

Not sure if this helps but I'll add a little context to clarify the answer:

supertest is a wrapper on top of superagent (client side) with some basic hooks into express to start up the HTTP listener. Under the hood, it really is not any different from starting up your express app, waiting for it to listen on a port, making an HTTP request to that port, and parsing the result. In fact, that is exactly what it does.

So essentially supertest only has access to what ever your client would have access to (a browser or some API client). In other words, if it isnt in the HTTP response body, you wont have access to it. req.user and req.sesssion are server side state variables that are (most likely) not in the response (unless you are doing something strange).

If you want to test in exactly the way you describe, you will have to use some alternative strategy of testing, not supertest.

mattr
  • 5,458
  • 2
  • 27
  • 22
  • Do you have any suggestions? I have heard of `nock`, but can not find much else. – trysis Nov 08 '14 at 02:25
  • So what I was thinking about `supertest` having access to request variables from before the request, but not after, is true, but for a different reason from what I thought, namely, that `supertest` is on the client side, not the server. – trysis Nov 08 '14 at 02:28
  • hey, hope this answer was helpful :)! I don't really have a silver bullet for you. I have not previously heard of nock, but looking at the github quickly tells me that probably isn't the answer. I can share my strategy however i'm not sure it is easily explained here. Ideally removing HTTP from the test is really what you need. In my case, i abstracted out express so that I can easily test my controllers independent of transport (HTTP). That is really the key: testing the functions before express gets involved. Not sure if this helps, but I'd be glad to give further advice in chat :) – mattr Nov 08 '14 at 03:07
  • It was helpful! I can't believe they don't have *some* library to test `express`'s server-side elements. [this answer](https://stackoverflow.com/questions/16603732/are-the-req-and-res-objects-somehow-accessible-globally-in-nodeexpress), which maaaaay be a dupe, says it's because that's implementation, but there are reasons to test that side. `req.user`, for one. – trysis Nov 08 '14 at 03:11
  • great, yeah that thread has the right idea. More unit-test-based, less integration-test-base (HTTP). `supertest` is more on the integration test side of things. Anyways, best of luck! – mattr Nov 08 '14 at 03:15
  • 1
    Maybe tomorrow I will ask for advice in chat. Right now I'm going to bed even though it's barely 11 here. – trysis Nov 08 '14 at 04:05
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/64536/discussion-between-trysis-and-mattyice). – trysis Nov 08 '14 at 14:40
1

I found this question when I thought I wanted to do this, and for me it worked well to check the status of the user created by the request instead of verifying the content of the req object. You do have access to the full database where I assume you users somehow ends up.

Smolkis
  • 11
  • 2