2012-01-12 19 views
64

Ho appena aggiunto shouldjs e mocha alla mia app express per i test, ma mi chiedo come testare la mia applicazione. Mi piacerebbe farlo in questo modo:Come faccio a testare la mia app Express con moka?

Naturalmente, l'ultima prova in questo test-suite dice solo med che la funzione res.render (chiamata entro show_create_user_screen) non è definito, probabilmente siccome il server non è in esecuzione e la configurazione non è stata eseguita. Quindi mi chiedo come gli altri hanno impostato i loro test?

+0

Proprio come aggiungere che l'esempio precedente è stato pubblicato perché era breve e conciso. Di solito testerei che le funzioni oi valori appropriati sugli oggetti req/res siano stati impostati/richiamati dopo che una delle mie funzioni del router è stata chiamata. Per il quale la risposta sotto è adeguata. Non si dovrebbe testare la funzionalità del router, questo è il lavoro del framework web. –

risposta

32

OK, prima anche se test del codice di routing è qualcosa che si può o non può decidere di fare, in generale, cercare di separare la logica di business interessante in puro codice javascript (classi o funzioni) che sono disaccoppiati da espresso o qualunque struttura tu stia usando e usi i test di Vanilla Mocha per provarlo. Una volta raggiunto questo risultato, se vuoi veramente testare i percorsi che configuri in mocha, devi passare i parametri simmetrici req, res nelle tue funzioni middleware per simulare l'interfaccia tra express/connect e il tuo middleware.

Per un semplice caso, è possibile creare un oggetto fittizio res con una funzione render simile a questa.

describe 'routes', -> 
    describe '#show_create_user_screen', -> 
    it 'should be a function', -> 
     routes.show_create_user_screen.should.be.a.function 
    it 'should return something cool', -> 
     mockReq = null 
     mockRes = 
     render: (viewName) -> 
      viewName.should.exist 
      viewName.should.match /createuser/ 

     routes.show_create_user_screen(mockReq, mockRes).should.be.an.object 

Anche solo FYI funzioni middleware non hanno bisogno di restituire un valore particolare, è quello che fanno con i parametri req, res, next che si dovrebbe concentrare su in fase di test.

Ecco alcuni JavaScript come richiesto nei commenti.

describe('routes', function() { 
    describe('#show_create_user_screen', function() { 
     it('should be a function', function() { 
     routes.show_create_user_screen.should.be.a["function"]; 
     }); 
     it('should return something cool', function() { 
     var mockReq = null; 
     var mockRes = { 
      render: function(viewName) { 
      viewName.should.exist; 
      viewName.should.match(/createuser/); 
      } 
     }; 
     routes.show_create_user_screen(mockReq, mockRes); 
     }); 
    }); 
    }); 
+1

Una cosa che il derisione non ti dà è la protezione contro le modifiche API del modulo che stai utilizzando. per esempio.se esprimi aggiornamenti e cambia il nome del rendering, non sei protetto. Idealmente lo stai anche testando, ma a volte i test unitari di integrazione + possono testare un sacco di codice contemporaneamente, il che è una cosa buona o cattiva a seconda di come la si guarda. modifica: Anche se mi piace molto questo metodo di simulazione, è davvero leggero. – timoxley

+61

Potrebbe sempre aggiungere anche js compilato, alcune persone non hanno familiarità con la lettura del coffeescript. –

+0

Non sta testando un dettaglio di implementazione? In realtà vuoi testare cosa contiene l'oggetto 'response' quando viene restituito - se in futuro non utilizzi il metodo 'render' per fare ciò, per esempio durante un refactoring il tuo test fallirà e non ti mostrerà che il tuo codice refactored funziona, come dovrai riscrivere il test? Solo un pensiero! Altrimenti è un modo intelligente di deridere l'oggetto risposta. – SimonB

6

mocha viene fornito con before, beforeEach, after e afterEach per il test bdd. In questo caso, è necessario utilizzare prima la chiamata descrittiva.

describe 'routes' -> 
    before (done) -> 
    app.listen(3000) 
    app.on('connection', done) 
5

ho trovato che sia più semplice per impostare una classe TestServer per essere usato come un aiuto, così come un client http aiutante, e solo fare le richieste reali a un server http reale. Ci possono essere casi in cui si vuole prendere in giro e stub questa roba invece.

// Test file 
var http = require('the/below/code'); 

describe('my_controller', function() { 
    var server; 

    before(function() { 
     var router = require('path/to/some/router'); 
     server = http.server.create(router); 
     server.start(); 
    }); 

    after(function() { 
     server.stop(); 
    }); 

    describe("GET /foo", function() { 
     it('returns something', function(done) { 
      http.client.get('/foo', function(err, res) { 
       // assertions 
       done(); 
      }); 
     }); 
    }); 
}); 


// Test helper file 
var express = require('express'); 
var http  = require('http'); 

// These could be args passed into TestServer, or settings from somewhere. 
var TEST_HOST = 'localhost'; 
var TEST_PORT = 9876; 

function TestServer(args) { 
    var self = this; 
    var express = require('express'); 
    self.router = args.router; 
    self.server = express.createServer(); 
    self.server.use(express.bodyParser()); 
    self.server.use(self.router); 
} 

TestServer.prototype.start = function() { 
    var self = this; 
    if (self.server) { 
     self.server.listen(TEST_PORT, TEST_HOST); 
    } else { 
     throw new Error('Server not found'); 
    } 
}; 

TestServer.prototype.stop = function() { 
    var self = this; 
    self.server.close(); 
}; 

// you would likely want this in another file, and include similar 
// functions for post, put, delete, etc. 
function http_get(host, port, url, cb) { 
    var options = { 
     host: host, 
     port: port, 
     path: url, 
     method: 'GET' 
    }; 
    var ret = false; 
    var req = http.request(options, function(res) { 
     var buffer = ''; 
     res.on('data', function(data) { 
      buffer += data; 
     }); 
     res.on('end',function(){ 
      cb(null,buffer); 
     }); 
    }); 
    req.end(); 
    req.on('error', function(e) { 
     if (!ret) { 
      cb(e, null); 
     } 
    }); 
} 

var client = { 
    get: function(url, cb) { 
     http_get(TEST_HOST, TEST_PORT, url, cb); 
    } 
}; 

var http = { 
    server: { 
     create: function(router) { 
      return new TestServer({router: router}); 
     } 
    }, 

    client: client 
}; 
module.exports = http; 
+0

Mi sono appena reso conto che mi mancava il punto della tua domanda, ma forse questo ti aiuterà comunque. Personalmente non collaudo direttamente le funzioni del router. Ho appena testato via HTTP le richieste che il server faccia sostanzialmente quello che dovrebbe, quindi testare tutte le logiche di business separatamente poiché è tutto in file al di fuori del controller comunque. –

+0

Hai un riferimento a 'percorso/a/alcuni/router' e sarebbe utile vedere il contenuto di quel file. –

64

trovato un'alternativa in connect.js tests suites

Stanno usando supertest per testare un'applicazione di connessione senza legarsi al server di qualsiasi porta e senza l'utilizzo di mock-up.

Ecco un estratto dalla suite di test middleware statica di connessione (utilizzando moka come il test runner e Supertest per asserzioni)

var connect = require('connect'); 

var app = connect(); 
app.use(connect.static(staticDirPath)); 

describe('connect.static()', function(){ 
    it('should serve static files', function(done){ 
    app.request() 
    .get('/todo.txt') 
    .expect('contents', done); 
    }) 
}); 

Questo funziona per le applicazioni espresse così

+0

posso accettare solo una risposta, altrimenti questo sarebbe accettato così =) –

+1

app.request non ha funzionato per me nel più recente espresso/collega, così ho aggiornato questa risposta per abbinare l'utilizzo all'indirizzo https: // github.com/visionmedia/supertest – Aaron

+0

La clausola su "supertest" sembra fuorviante. Sembra che ci sia alcuna menzione di esso nel [collegare code] (https://github.com/search?q=supertest+repo%3A%22senchalabs%2Fconnect%22&type=Code&ref=searchresults). In ogni caso la risposta di Alexandru sembra migliore delle altre. – yanychar

21

Si potrebbe provare Supertest e quindi l'avvio e l'arresto del server sono risolti:

var request = require('supertest') 
    , app  = require('./anExpressServer').app 
    , assert = require("assert"); 

describe('POST /', function(){ 
    it('should fail bad img_uri', function(done){ 
    request(app) 
     .post('/') 
     .send({ 
      'img_uri' : 'foobar' 
     }) 
     .expect(500) 
     .end(function(err, res){ 
      done(); 
     }) 
    }) 
}); 
+0

Supertest funziona per me un almeno una dozzina di progetti. Per la vittoria! – deepelement

+1

Mi chiedo quale sia la differenza tra 'supertest' e' chaihttp'? – toszter

Problemi correlati