74

私はPassport.jsを認証(ローカル戦略)とMochaとSupertestでのテストに使用しています。

Supertestを使用してセッションを作成し、認証されたリクエストを行うにはどうすればよいですか?

4

8 に答える 8

64

zeMircoが指摘しているように、基盤となるsuperagentモジュールはセッションをサポートし、Cookieを自動的に維持します。ただし、文書化されていない機能を介して、superagent.agent()からの機能を使用することは可能です。supertest

require('supertest').agent('url')代わりに使用するrequire('supertest')('url')

var request = require('supertest');
var server = request.agent('http://localhost:3000');

describe('GET /api/getDir', function(){
    it('login', loginUser());
    it('uri that requires user to be logged in', function(done){
    server
        .get('/api/getDir')                       
        .expect(200)
        .end(function(err, res){
            if (err) return done(err);
            console.log(res.body);
            done()
        });
    });
});


function loginUser() {
    return function(done) {
        server
            .post('/login')
            .send({ username: 'admin', password: 'admin' })
            .expect(302)
            .expect('Location', '/')
            .end(onResponse);

        function onResponse(err, res) {
           if (err) return done(err);
           return done();
        }
    };
};
于 2013-08-27T13:11:04.867 に答える
55

そのためにはスーパーエージェントを使用する必要があります。これは下位レベルのモジュールであり、によって使用されsupertestます。「エージェントの永続化」セクションをご覧ください。

var request = require('superagent');
var user1 = request.agent();
user1
  .post('http://localhost:4000/signin')
  .send({ user: 'hunter@hunterloftis.com', password: 'password' })
  .end(function(err, res) {
    // user1 will manage its own cookies
    // res.redirects contains an Array of redirects
  });

これで、を使用user1して認証済みリクエストを作成できます。

于 2012-12-22T10:04:14.233 に答える
30

これを試して、

  var request=require('supertest');
  var cookie;
  request(app)
  .post('/login')
  .send({ email: "user@gluck.com", password:'password' })
  .end(function(err,res){
    res.should.have.status(200);
    cookie = res.headers['set-cookie'];
    done();        
  });

  //
  // and use the cookie on the next request
  request(app)
  .get('/v1/your/path')
  .set('cookie', cookie)
  .end(function(err,res){  
    res.should.have.status(200);
    done();        
  });
于 2013-06-14T15:39:21.997 に答える
11

Andyの回答の補足として、Supertestにサーバーを起動させるには、次のようにします。

var request = require('supertest');

/**
 * `../server` should point to your main server bootstrap file,
 * which has your express app exported. For example:
 * 
 * var app = express();
 * module.exports = app;
 */
var server = require('../server');

// Using request.agent() is the key
var agent = request.agent(server);

describe('Sessions', function() {

  it('Should create a session', function(done) {
    agent.post('/api/session')
    .send({ username: 'user', password: 'pass' })
    .end(function(err, res) {
      expect(req.status).to.equal(201);
      done();
    });
  });

  it('Should return the current session', function(done) {
    agent.get('/api/session').end(function(err, res) {
      expect(req.status).to.equal(200);
      done();
    });
  });
});
于 2014-03-03T23:30:54.447 に答える
8

申し訳ありませんが、提案された解決策のどちらも私には機能しません。

インスタンスを使用supertest.agent()できないため、app事前にサーバーを実行して指定する必要がhttp://127.0.0.1:portあり、さらにスーパーテストの期待値(アサーション)を使用できない、ライブラリを使用できないsupertest-as-promisedなど...

ケースはcookies私にはまったく機能しません。

だから、私の解決策は:

Passport.jsを使用している場合は、「ベアラートークン」メカニズムを利用しており、仕様で次の例を使用できます。

var request = require('supertest');
var should = require('should');

var app = require('../server/app.js'); // your server.js file

describe('Some auth-required API', function () {
  var token;

  before(function (done) {
    request(app)
      .post('/auth/local')
      .send({
        email: 'test@example.com',
        password: 'the secret'
      })
      .end(function (err, res) {
        if (err) {
          return done(err);
        }

        res.body.should.to.have.property('token');
        token = res.body.token;

        done();
      });
  });

  it('should respond with status code 200 and so on...', function (done) {
    request(app)
      .get('/api/v2/blah-blah')
      .set('authorization', 'Bearer ' + token) // 1) using the authorization header
      .expect(200)
      .expect('Content-Type', /json/)
      .end(function (err, res) {
        if (err) {
          return done(err);
        }

        // some `res.body` assertions...

        done();
      });
  });

  it('should respond with status code 200 and so on...', function (done) {
    request(app)
      .get('/api/v2/blah-blah')
      .query({access_token: token}) // 2) using the query string
      .expect(200)
      .expect('Content-Type', /json/)
      .end(function (err, res) {
        if (err) {
          return done(err);
        }

        // some `res.body` assertions...

        done();
      });
  });
});

ユーザーを認証するためのヘルパー関数が必要になる場合があります。

test/auth-helper.js

'use strict';

var request = require('supertest');
var app = require('app.js');

/**
 * Authenticate a test user.
 *
 * @param {User} user
 * @param {function(err:Error, token:String)} callback
 */
exports.authenticate = function (user, callback) {
  request(app)
    .post('/auth/local')
    .send({
      email: user.email,
      password: user.password
    })
    .end(function (err, res) {
      if (err) {
        return callback(err);
      }

      callback(null, res.body.token);
    });
};

生産的な一日を!

于 2016-06-03T08:38:19.473 に答える
3

CookieSessionミドルウェアを使用していると仮定します。

grubが述べたように、あなたの目標はあなたのリクエストに渡すためのクッキー値を取得することです。ただし、何らかの理由で(少なくとも私のテストでは)、supertestは同じテストで2つのリクエストを実行しません。したがって、適切なCookie値を取得する方法をリバースエンジニアリングする必要があります。まず、Cookieを作成するためのモジュールを要求する必要があります。

var Cookie          = require("express/node_modules/connect/lib/middleware/session/cookie")
  , cookieSignature = require("express/node_modules/cookie-signature")

はい、それは醜いです。それらをテストファイルの先頭に置きます。

次に、Cookieの値を作成する必要があります。beforeEach認証されたユーザーを必要とするテストのために、これをに入れます。

var cookie = new Cookie()
  , session = {
      passport: {
        user: Test.user.id
      }
    }

var val = "j:" + JSON.stringify(session)
val = 's:' + cookieSignature.sign(val, App.config.cookieSecret)
Test.cookie = cookie.serialize("session",val)

Test.user.idbeforeEach以前は、「ログイン」しようとしているユーザーを定義するチェーンの部分で定義されていました。の構造sessionは、Passportが(少なくとも現在)現在のユーザー情報をセッションに挿入する方法です。

Cookieベースのセッションを使用している場合にPassportがフォールバックするConnectCookieSessionミドルウェアを使用した行var val"j:"削除された行。"s:"最後に、Cookieをシリアル化します。それが"session"私のCookieセッションミドルウェアを構成した方法だからです。また、App.config.cookieSecret他の場所で定義されており、Express /ConnectCookieSessionミドルウェアに渡す秘密である必要があります。Test.cookie後でアクセスできるように、それを隠しておきます。

さて、実際のテストでは、そのCookieを使用する必要があります。たとえば、次のテストがあります。

it("should logout a user", function(done) {
  r = request(App.app)
    .del(App.Test.versionedPath("/logout"))
    .set("cookie", Test.cookie)
    // ... other sets and expectations and your .end
}

setwith"cookie"と。の呼び出しに注意してくださいTest.cookie。これにより、リクエストで作成したCookieを使用するようになります。

そして今、あなたはユーザーがログインしているとアプリを偽って考えさせました、そしてあなたは実際のサーバーを動かし続ける必要はありません。

于 2013-08-05T12:36:50.820 に答える
0

これは、再利用可能であるという追加の利点があるきちんとしたアプローチです。

const chai = require("chai")
const chaiHttp = require("chai-http")
const request = require("supertest")

const app = require("../api/app.js")

const should = chai.should()
chai.use(chaiHttp)


describe("a mocha test for an expressjs mongoose setup", () => {
  // A reusable function to wrap your tests requiring auth.
  const signUpThenLogIn = (credentials, testCallBack) => {
    // Signs up...
    chai
      .request(app)
      .post("/auth/wizard/signup")
      .send({
        name: "Wizard",
        ...credentials,
      })
      .set("Content-Type", "application/json")
      .set("Accept", "application/json")
      .end((err, res) => {
        // ...then Logs in...
        chai
          .request(app)
          .post("/auth/wizard/login")
          .send(credentials)
          .set("Content-Type", "application/json")
          .set("Accept", "application/json")
          .end((err, res) => {
            should.not.exist(err)
            res.should.have.status(200)
            res.body.token.should.include("Bearer ")
            // ...then passes the token back into the test 
            // callBack function.
            testCallBack(res.body.token)
          })
      })
  }

  it.only("flipping works", done => {
    // "Wrap" our test in the signUpThenLogIn function.
    signUpLogIn(
      // The credential parameter.
      {
        username: "wizard",
        password: "youSHALLpass",
      },
      // The test wrapped in a callback function which expects 
      /// the token passed back from when signUpLogIn is done.
      token => {
        // Now we can use this token to run a test... 
        /// e.g. create an apprentice.
        chai
          .request(app)
          .post("/apprentice")
          .send({ name: "Apprentice 20, innit" })
           // Using the token to auth! 
          .set("Authorization", token)
          .end((err, res) => {
            should.not.exist(err)
            res.should.have.status(201)
            // Yep. apprentice created using the token.
            res.body.name.should.be.equal("Apprentice 20, innit")
            done()
          })
      }
    )
  })
})

ボーナス素材

さらに再利用可能にするには、関数を「myMochaSuite.js」というファイルに入れます。このファイルは、APIサーバーをテストするときに「describe」を置き換えることができます。ウィザードになって、この「スイート」にすべての前/後のものを入れてください。例えば:

// tests/myMochaSuite.js  
module.exports = (testDescription, testsCallBack) => {
  describe(testDescription, () => {
    const signUpThenLogIn = (credentials, testCallBack) => {
      // The signUpThenLogIn function from above
    }

    before(async () => {
      //before stuff like setting up the app and mongoose server.
    })
    beforeEach(async () => {
      //beforeEach stuff clearing out the db
    })
    after(async () => {
      //after stuff like shutting down the app and mongoose server.
    })
    
    // IMPORTANT: We pass signUpLogIn back through "testsCallBack" function.
    testsCallBack(signUpThenLogIn)
  })
}
// tests/my.api.test.js
// chai, supertest, etc, imports +
const myMochaSuite = require("./myMochaSuite")

// NB: signUpThenLogIn coming back into the tests.
myMochaSuite("my test description", signUpThenLogIn => {
   it("just works baby", done => {
     signUpThenLogIn(
       {username: "wizard", password: "youSHALLpass"},
       token => {
         chai
           .request(app)
           .get("/apprentices/20")
           // Using the incoming token passed when signUpThenLogIn callsback.
           .set("Authorization", token)
           .end((err, res) => {
             res.body.name.equals("Apprentice 20, innit")
             done()
           })
       }
     )
   })
})

これで、すべてのテストにさらに再利用可能なスイート「ラッパー」ができ、整理されたままになります。

于 2020-08-08T11:07:03.297 に答える
0

GraphQlの完全な例:

const adminLogin = async (agent) => {
  const userAdmin = await User.findOne({rol:"admin"}).exec();
  if(!userAdmin) return new Promise.reject('Admin not found')
  return agent.post('/graphql').send({
    query: ` mutation { ${loginQuery(userAdmin.email)} }`
  })//.end((err, {body:{data}}) => {})
}

test("Login Admin", async (done) => {
  const agent = request.agent(app);
  await adminLogin(agent);
  agent
    .post("/graphql")
    .send({query: `{ getGuests { ${GuestInput.join(' ')} } }`})
    .set("Accept", "application/json")
    .expect("Content-Type", /json/)
    .expect(200)
    .end((err, {body:{data}}) => {
      if (err) return done(err);
      expect(data).toBeInstanceOf(Object);
      const {getGuests} = data;
      expect(getGuests).toBeInstanceOf(Array);
      getGuests.map(user => GuestInput.map(checkFields(user)))
      done();
    });
})
于 2020-08-20T09:02:12.800 に答える