在 Supertest 的响应后访问 "req" 对象

14
有没有办法在使用supertest进行测试时直接访问req对象?我想要测试我的passport策略,所以我想检查req.userreq.session等内容。我知道我可以测试页面重定向或flash,因为这些是我的策略所做的事情,但看起来也有必要查看req对象上是否有一个用户。如果我这样做,我还可以检查任何时候有多少用户。
我将使用“local-signup”策略注册用户,该策略定义如下:
'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);
                    });

                });

            });

        }

    ));

};

我使用这种策略的一种方式是这样的:

我的“本地”策略定义如下:

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

            });
        }

    ));
};

我会像这样同时使用两种策略,例如:

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

我尝试了

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

我在end()函数中记录的res对象太长了,在这里无法显示完整内容。它有一个定义在上面的req对象,但似乎只包括请求打开之前定义的对象和函数。换句话说,它没有req.userreq.session或其他我需要的对象,因为它们是在请求完成并启动新请求后定义的。我注意到它也有状态码,但只有在请求完成后才会定义,所以我可能错过了什么。

是否有任何方法可以在测试结束的请求之后访问req对象?还是我完全走错路了?


你正在尝试测试哪种护照策略?是oAuth策略(如Facebook或Twitter登录)还是一些自定义的策略?你能发布这些策略的代码和/或配置吗? - lukaszfiszer
好的。我没有意识到在处理req.user或其他内容时不同的策略是很重要的。 - trysis
2个回答

17

使用supertest无法实现您想做的事情。

我不确定这是否有所帮助,但我将添加一些上下文来澄清答案:

supertest是在superagent(客户端)之上的封装,具有一些基本的钩子进入express以启动HTTP监听器。在内部,它与启动express应用程序、等待其侦听端口、对该端口进行HTTP请求并解析结果没有任何差异。事实上,这正是它所做的。

因此,supertest只能访问您的客户端可以访问的任何内容(浏览器或某个API客户端)。换句话说,如果它不在HTTP响应正文中,您将无法访问它。req.userreq.sesssion是服务器端状态变量,(很可能)不在响应中(除非您正在做一些奇怪的事情)。

如果您想以您描述的方式进行测试,则必须使用一些其他的测试策略,而不是supertest


你有什么建议吗?我听说过 nock,但是找不到其他的资料。 - trysis
所以我在想supertest可以访问请求之前的请求变量,但不能访问请求之后的请求变量是真的,但原因不同于我之前想的那个原因,即supertest在客户端而非服务器端。 - trysis
嗨,希望这个回答有所帮助 :)!对于你的问题,我没有什么万能的解决方案。我以前没有听说过 nock,但快速查看 GitHub 告诉我那可能不是答案。我可以分享我的策略,但我不确定这里是否容易解释清楚。理想情况下,从测试中删除 HTTP 是你真正需要的。在我的情况下,我将 express 抽象出来,以便我可以独立于传输(HTTP)轻松测试我的控制器。这真的是关键:在 express 参与之前测试函数。不确定这是否有所帮助,但我很乐意在聊天中提供进一步的建议 :)。 - mattr
很有帮助!我简直不敢相信他们没有任何库来测试“express”的服务器端元素。这个答案,可能是重复的,但它说这是实现上的原因,但有理由测试那一面。例如req.user - trysis
1
也许明天我会在聊天中寻求建议。现在虽然这里才刚过11点,但我要去睡觉了。 - trysis
显示剩余2条评论

1
我在考虑如何实现这个功能时发现了这个问题,对我来说,检查请求创建的用户的状态而不是验证req对象的内容效果很好。您可以访问完整的数据库,在那里我假设您的用户以某种方式结束。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接