2016-05-17 45 views
12

TL; DR - Qual è il modo di testare le risorse in un'API nodo (Express) che utilizza JWT per l'autenticazione solo con il token stesso concesso ad un login username/password?Come testare un'API di nodo che utilizza l'autenticazione JWT (con accesso utente per ottenere token)

Sono un po 'nuovo alla sperimentazione e volevo avere qualche consiglio. L'obiettivo finale è quello di avere un'API completamente testata e quindi iniziare a imparare come ottenere quello collegato a una soluzione di integrazione continua.

tecnologie in uso

  • ho scritto un'API in Nodo utilizzando espresso.
  • Mongo è il database.
  • Mongoose viene utilizzato come ODM.
  • jsonwebtoken il pacchetto viene utilizzato per creare/verificare token.
  • Il passaporto viene utilizzato per aggiungere facilmente l'autenticazione utente come middleware Express sui percorsi.

API Informazioni

L'API ha varie risorse - le specifiche di cui non sono importanti a questa domanda, ma consente solo finta che sia l'applicazione Todo onnipresente per semplicità.

Ogni singola risorsa salvata nel database è associata a un singolo utente.

L'API utilizza JWT per l'autenticazione tra i diversi endpoint di risorse. Il token stesso contiene l'ID utente univoco che viene archiviato sulla risorsa in un database Mongo. Per ottenere il token stesso, l'utente deve prima registrarsi (che restituisce un token) e quindi accedere per ottenere un nuovo token.

Codice di pretesa.

ho intenzione di semplificare il codice qui sotto e non fare uso di qualsiasi file di configurazione dell'ambiente, ecc ...

app.js

var express = require('express'); 
var app = express(); 
var mongoose = require('mongoose'); 
var bodyParser = require('body-parser'); 
var passport = require('passport'); 

mongoose.connect('mongodb://localhost/somedatabasename'); 

app.set('port', process.env.PORT || 3000); 
app.use(bodyParser.urlencoded({ extended: true })); 
app.use(bodyParser.json()); 

app.use(passport.initialize()); 
// ... Passport JWT Strategy goes here - omitted for simplicity ... 

var userRouter = require('./api/users/routes'); 
app.use('/users', userRouter); 
var todoRouter = require('./api/todos/routes'); 
app.use('/todos', todoRouter); 

app.listen(app.get('port'), function() { 
    console.log('App now running on http://localhost:' + app.get('port')); 
}); 

./api/todos/routes.js

var router = require('express').Router(); 
var controller = require('./controller'); 
var passport = require('passport'); 

router.route('/') 
    .all(passport.authenticate('jwt', { session: false})) 
    .get(controller.getAll) 
    .post(controller.create); 

router.route('/:id') 
    .all(passport.authenticate('jwt', { session: false})) 
    .get(controller.getOne) 
    .put(controller.update) 
    .delete(controller.delete); 

module.exports = router; 

./api/users/routes.js

var router = require('express').Router(); 
var controller = require('./controller'); 
var passport = require('passport'); 

router.route('/') 
    // User signup 
    .post(controller.create); 

router.route('/me') 
    // User Login 
    .post(passport.authenticate('local', { session: false}), controller.login) 
    // Get current user's data 
    .get(passport.authenticate('jwt', { session: false}), controller.getOne) 
    // Update current user's data 
    .put(passport.authenticate('jwt', { session: false}), controller.update) 
    // Delete current user 
    .delete(passport.authenticate('jwt', { session: false}), controller.delete); 

module.exports = router; 

./api/users/model.js

var mongoose = require('mongoose'); 
var bcrypt = require('bcrypt'); 

var UserSchema = new mongoose.Schema({ 
    username: { 
    type: String, 
    required: true, 
    unique: true 
    }, 
    password: { 
    type: String, 
    required: true 
    } 
}); 

// ... for simplicity imagine methods here to 
// - hash passwords on a pre save hook using bcrypt 
// - compare passwords using bcrypt when logging in 

module.exports = mongoose.model('User', UserSchema); 

./api/todos/model.js

var mongoose = require('mongoose'); 
var Schema = mongoose.Schema; 

var momentSchema = new Schema({ 
    title: { 
    type: String 
    }, 

    // Bunch of other fields here... 

    _user: { 
    type: Schema.Types.ObjectId, 
    ref: 'User' 
    } 
}); 

module.exports = mongoose.model('Moment', momentSchema); 

ho omesso parte del codice esempio per tenerlo pulito e semplice.

Per esempio, il controllore dell'utente di includerebbe le modelle e le sue funzioni sarebbero:

  • controller.create - iscrizione per i nuovi utenti (rendimenti token)
  • controller.login - dopo la combinazione nome utente/password confermata da Passport Local, quindi restituire un token valido
  • controller.getOne - basato sull'uso r ID recuperato dal token JWT, restituisce i dati dell'utente da Mongo usando Mongoose.
  • controller.update - Aggiornare i dati dell'Utente in Mongo utilizzando Mongoose
  • controller.delete - Cancellare i dati dell'Utente in Mongo utilizzando Mongoose

controller Il del Todo sarebbe fare qualcosa di simile - basta interagire con i dati Mongo tramite Mongoose ma le query includeranno sempre l'ID utente per associare l'elemento Todo specifico (ad esempio) con l'utente autenticato (autenticato tramite JWT).

Testing Conundrum

Come potrei fare per testare qualcosa di simile utilizzando una combinazione di Mocha, Chai e Supertest?

Would I:

  • creare un database di prova all'interno di Mongo e hanno la stringa di connessione essere diverso nelle prove? Ciò significherebbe il salvataggio dei dati effettivi memorizzati nel database per il test.
  • Mock i dati in qualche modo e non utilizzare affatto un database di test? Ma allora come viene gestito il salvataggio/login dell'utente per recuperare il token?

In che modo i test possono funzionare localmente durante lo sviluppo rispetto a quando si esegue una distribuzione utilizzando uno strumento CI (qualcosa che devo ancora raggiungere nei miei studi)?

Tutta l'assistenza sarebbe molto apprezzato e spero che ho dato abbastanza informazioni con i dati/codice fittizio di cui sopra:./

+0

Ciao! L'hai capito? Sto anche cercando di implementare questo tipo di test. –

risposta

1

Durante il test, è normalmente deridere il tuo Mongo DB (qualcosa come mongo-mock In questo modo, non hai bisogno di un vero database in esecuzione per eseguire i test (non stai testando il database, ma il tuo codice)

Durante il test, sostituire lo mongodb con mongo-mock e quindi eseguire il test. Per ottenere il token, avresti bisogno di postare sul tuo URL /me con credenziali fittizie valide, quell'endpoint restituirebbe il token, che useresti quindi alla prossima chiamata per testare l'altro endpoint.

Per quanto riguarda il token, di solito lo controllo all'inizio della richiesta prima di entrare negli altri endpoint.(Non ho usato il passaporto, ma l'idea è):

app.use(validate_jwt_middleware); 
app.use('/users', userRouter); 

questo modo, se il token non è valido, è valida per l'intero sito, non solo la vostra sezione.

Inoltre, non sto utilizzando SuperTest, ma chai-http, quindi non posso aiutarti con le tue specifiche.

Spero che questo aiuti,