好的,这是我想出的单元测试我的方法。我会第一个承认在这方面有很大的提升空间!
首先,我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 [“测试”]已经定义,我可以当我运行做我的测试在本地,但在生产中永远不会是真的。然后它在“测试”目录中搜索javascript或coffeescript文件(在我的实现中不搜索子目录,但可以很容易地添加它),并将它们添加到mocha
实例中。我在这里使用优秀的mocha javascript测试库,并结合chai断言库。
所有这些代码被包装在一个Meteor.startup
调用中,以便我的单元测试在服务器启动时运行。这是非常好的,因为每当我更改我的任何代码时,Meteor会自动重新运行我的测试。由于决定隔离数据库而不执行XHR,我的测试运行几毫秒,所以这不是很烦人。
对于测试本身,我需要做的
chai = require("chai")
should = chai.should()
拉进来的断言库。不过,仍然有一些棘手的问题需要解决。首先,如果Meteor方法调用不包含在光纤中,它将会失败。我目前还没有很好的解决了这个问题,但我创建了itShould
功能,以取代摩卡的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()
函数仅适用于客户端启动的请求。其实我已经提起针对a bug流星,看看他们是否能提供更好的解决了这个问题,但现在我只是用我自己的版本替换功能来进行测试:
realUserIdFn = null
before ->
realUserIdFn = Meteor.userId
Meteor.userId = -> "123456"
after ->
Meteor.userId = realUserIdFn
这种方法适用于一些流星的部分,但不是全部。例如,我还没有找到一种方法来测试调用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
我可能不会理解这一点,但它看起来像这段代码会在浏览器中运行,对吧?在这种情况下,我认为单独测试方法变得非常困难。除非有特别的事情发生,否则你的方法调用将会进行XHR和数据库写入等等,不是吗? – 2013-03-04 20:53:51