さて、これが私のメソッドをユニットテストするために思いついたものです。これには改善の余地がたくさんあることを最初に認めます!
まず、私のserver.coffee
ファイルには次のコードがあります。
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()
まず、このコードはMeteor.settings ["test"]が定義されている場合にのみ実行されます。これは、ローカルでテストを実行するときに実行できますが、本番環境では決して当てはまりません。次に、「tests」ディレクトリでjavascriptまたはcoffeescriptファイルを検索し(私の実装ではサブディレクトリは検索されませんが、追加するのは簡単です)、それらをmocha
インスタンスに追加します。ここでは、優れたmocha javascriptテストライブラリを、 chaiアサーションライブラリと組み合わせて使用しています。
このコードはすべてMeteor.startup
呼び出し内にラップされているため、サーバーの起動時に単体テストが実行されます。コードを変更するたびにMeteorがテストを自動的に再実行するため、これは特に便利です。データベースを分離し、XHRを実行しないという決定のため、私のテストは数ミリ秒で実行されるため、これはそれほど煩わしいことではありません。
テスト自体については、私はする必要があります
chai = require("chai")
should = chai.should()
アサーションライブラリをプルします。ただし、解決すべきトリッキーな問題がまだいくつかあります。まず、Meteorメソッドの呼び出しは、ファイバーにラップされていない場合は失敗します。現在、この問題に対する優れた解決策はありませんが、itShould
mochaのit
関数を置き換えて、テスト本体をファイバー内にラップする関数を作成しました。
# 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())
次は、テスト目的で、私のコレクションをモックコレクションに置き換えるという問題です。コレクションをグローバル変数に入れるという標準的なMeteorの手法に従う場合、これを行うのは非常に困難です。ただし、グローバルオブジェクトでコレクションのプロパティを作成する場合は、それを行うことができます。を介してコレクションを作成するだけmyApp.Collection = new Meteor.Collection("name")
です。before
次に、テストで、コレクションをモックアウトする関数を作成できます。
realCollection = null
before ->
realCollection = myApp.Collection
myApp.Collection = new Meteor.Collection(null)
after ->
myApp.Collection = realCollection
このように、コレクションはテスト実行中にモックアウトされますが、その後復元されるため、アプリを正常に操作できます。他のいくつかのことは、同様の手法でモックすることができます。たとえば、グローバルMeteor.userId()
関数はクライアントが開始したリクエストに対してのみ機能します。私は実際にMeteorに対してバグを報告して、この問題のより良い解決策を提供できるかどうかを確認しましたが、今のところ、テスト用に関数を自分のバージョンに置き換えています。
realUserIdFn = null
before ->
realUserIdFn = Meteor.userId
Meteor.userId = -> "123456"
after ->
Meteor.userId = realUserIdFn
このアプローチはMeteorの一部で機能しますが、すべてではありません。たとえば、this.setUserId
その動作をモックアウトする良い方法がないと思うので、呼び出すメソッドをテストする方法をまだ見つけていません。ただし、全体として、このアプローチはうまくいきます...コードを変更したときにテストを自動的に再実行できるのが大好きです。テストを個別に実行することは、一般的には良い考えです。サーバー上のテストがブロックできることも非常に便利であり、コールバックチェーンなしでテストを簡単に作成できます。テストは次のようになります。
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