Va bene, ecco cosa ho inventato per testare i miei metodi. Sarò il primo ad ammettere che c'è molto spazio per migliorare in questo!
In primo luogo, nel mio file server.coffee
Ho il seguente codice:
Meteor.startup ->
return unless Meteor.settings["test"]
require = __meteor_bootstrap__.require
require("coffee-script")
fs = require("fs")
path = require("path")
Mocha = require("mocha")
mocha = new Mocha()
files = fs.readdirSync("tests")
basePath = fs.realpathSync("tests")
for file in files
continue unless file.match(/\.coffee$/) or file.match(/\.js$/)
continue if file[0] == "."
filePath = path.join(basePath, file)
continue unless fs.statSync(filePath).isFile()
mocha.addFile(filePath)
mocha.run()
Prima di tutto questo codice solo se corre Meteor.settings [ "test"] è stato definito, che posso fare quando corro i miei test localmente, ma che non dovrebbero mai essere veri nella produzione. Quindi cerca nella directory "tests" per i file javascript o coffeescript (le sottodirectory non vengono cercate nella mia implementazione, ma sarebbe facile aggiungerle) e le aggiunge a un'istanza mocha
. Sto utilizzando l'eccellente libreria di test javascript mocha, combinata con la libreria di asserzione chai.
Tutto questo codice è racchiuso all'interno di una chiamata Meteor.startup
in modo che i test dell'unità vengano eseguiti all'avvio del server. Questo è particolarmente bello perché Meteor esegue automaticamente i miei test ogni volta che cambio uno qualsiasi del mio codice.A causa della decisione di isolare il database e non eseguire XHR, i miei test vengono eseguiti in pochi millisecondi, quindi non è molto fastidioso.
Per le prove stesse, ho bisogno di fare
chai = require("chai")
should = chai.should()
a tirare nella biblioteca asserzione. Ci sono ancora un paio di problemi difficili da risolvere, però. Prima di tutto, le chiamate al metodo Meteor falliranno se non sono avvolte in una fibra. Io attualmente non hanno una buona soluzione a questo problema, ma ho creato la funzione itShould
per sostituire la funzione di moka it
e avvolgere il corpo di prova all'interno di una fibra:
# A version of mocha's "it" function which wraps the test body in a Fiber.
itShould = (desc, fn) ->
it(("should " + desc), (done) -> (Fiber ->
fn()
done()).run())
Il prossimo è il problema di, per i test scopi, sostituendo le mie raccolte con raccolte fittizie. Questo è molto difficile da fare se si segue la pratica standard di Meteor di inserire le proprie raccolte in variabili globali. Tuttavia, se si effettuano le raccolte proprietà su un oggetto globale, è possibile farlo. Crea semplicemente le tue collezioni tramite myApp.Collection = new Meteor.Collection("name")
. Poi, nei test, si può avere un before
funzione di finto la collezione:
realCollection = null
before ->
realCollection = myApp.Collection
myApp.Collection = new Meteor.Collection(null)
after ->
myApp.Collection = realCollection
In questo modo, la vostra collezione viene deriso per tutta la durata della prova, ma poi è restaurato in modo da poter interagire con il app normalmente. Alcune altre cose sono possibili per deridere tramite una tecnica simile. Ad esempio, la funzione globale Meteor.userId()
funziona solo per le richieste avviate dal client. In realtà ho depositato a bug contro Meteor per vedere se sono in grado di fornire una soluzione migliore a questo problema, ma per ora sto solo sostituendo la funzione con la mia versione per il test:
realUserIdFn = null
before ->
realUserIdFn = Meteor.userId
Meteor.userId = -> "123456"
after ->
Meteor.userId = realUserIdFn
Questo approccio funziona per un po ' parti di Meteor, ma non tutte. Ad esempio, non ho trovato un modo per testare i metodi che invocano ancora this.setUserId
, perché non penso che ci sia un buon modo per deridere quel comportamento. Nel complesso, però, questo approccio sta funzionando per me ... Adoro essere in grado di rieseguire i miei test automaticamente quando cambio il codice, e l'esecuzione di test in isolamento è solo generalmente una buona idea. È anche molto conveniente che i test sul server possano bloccarli, rendendoli più semplici da scrivere senza catene di callback. Ecco come si presenta un test:
describe "the newWidget method", ->
itShould "make a new widget in the Widgets collection", ->
widgetId = Meteor.call("newWidget", {awesome: true})
widget = myApp.Widgets.findOne(widgetId)
widget.awesome.should.be.true
Forse non lo capisco, ma sembra che questo codice venga eseguito nel browser, giusto? In tal caso, penso che sia piuttosto difficile testare i metodi da soli. A meno che non stia succedendo qualcosa di speciale, le tue chiamate al metodo eseguiranno XHR e scritture e materiale del database, no? –