2013-03-03 50 views
8

我为我的应用程序编写了一些Meteor方法,其中包含各种业务逻辑块。现在我想为这些方法编写单元测试。通过单元测试,我的具体内容是快速测试,其并不:我该如何单元测试我的流星方法?

  • 执行XHR时或
  • 写入数据库

我怎么能去这样做?我目前的想法是,当我在测试配置中启动Meteor服务器时,我应该使用虚拟集合(通过new Meteor.Collection(null))替换我的集合,并让我的单元测试在服务器端运行,并在每个我的每个节点上调用Meteor.call方法从那里依次。我不完全确定如何开始测试,可能我想在自己的应用程序中创建一个自定义的/tests网址,并将它们关闭。这种方法看起来是否合理?是否有任何图书馆/软件包会使我的方法更容易编写单元测试?

回答

1

好的,这是我想出的单元测试我的方法。我会第一个承认在这方面有很大的提升空间!

首先,我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 
0

我一直在使用jasmine来测试我正在处理的一个更大的流星应用程序。它可以做比单元测试更多的工作,并且工作得很好。关于这个的博客文章在我的待办事项上。现在我可以给你这CoffeeScript:

if were_testing() 
    describe 'something', -> 
    it 'should be greater than 0', -> 
     expect(theThing).toBeGreaterThan 0 

were_testing = -> document.location.pathname.replace(/^\/([^\/]*).*$/, '$1') == 'tests' 

jasmine_test = -> 
    jasmineEnv = jasmine.getEnv() 
    jasmineEnv.updateInterval = 1000 

    htmlReporter = new jasmine.HtmlReporter() 
    jasmineEnv.addReporter htmlReporter 

    jasmineEnv.execute() 

此代码在浏览器中运行。如果您想编写脚本,可以在casperjs实例中运行它。由于它经历了客户端上的Meteor初始化,它将执行XHR和数据库查询,但是您可以轻松编写不会做任何额外查询的测试。或者编写访问时触发的函数的子集/ unittests

我们的东西尚未投入生产,但deployscript只是删除上面的茉莉花代码和所有*.spec.coffee文件。

我想将它插入Jenkins以获得适当的持续集成设置,但没有时间设置Jenkins以上的基础知识。我也玩过使用casperjs进行无头浏览,效果很好。

您也可以采取不同的方法,将您的测试代码放入tests/(未由Meteor执行),然后使用require。我很快尝试过,发现它很乏味。

+0

我可能不会理解这一点,但它看起来像这段代码会在浏览器中运行,对吧?在这种情况下,我认为单独测试方法变得非常困难。除非有特别的事情发生,否则你的方法调用将会进行XHR和数据库写入等等,不是吗? – 2013-03-04 20:53:51