使用Mocha-Cakes和Coffeescript测试Restful服务,在异步中使用done()会导致超时问题

时间:2014-04-25 17:11:16

标签: testing coffeescript mocha restify mocha-cakes

我遇到了针对服务运行的一些测试脚本的时间问题。我使用Restify JSON客户端发出调用,这些调用使用回调而不是promises。我已将完成函数传递给我的GivenWhen,以便我可以对这些异步调用执行必要的阻止,从而防止测试套件运行不一致(没有dones,它是一个tossup,以及ThenAnd将通过多少。

我熟练使用coffescript,而且对于mocha / mocha-cake来说只是一个新手,所以我肯定在我的代码中做错了。以下是一些失败的测试用例示例:

require 'mocha-cakes'
should = require 'should'
restify = require 'restify'


Feature "Account API",
  "In order to have control over structured Account documents",
  "as a consumer of investment account information,",
  "I need a RESTful service API.", ->

    Scenario "GET /account/:userid", ->

      client = restify.createJSONClient
        url: "http://localhost:8080",
        version: "*"

      _e1 = null
      _r1 = null

      _e2 = null
      _r2 = null
      _d2 = null

      # GET non-existent account
      Given "I have not yet created the Account", ->
      When "I request the Account", (done) ->
        client.get "/account/99", (err, req, res, obj) ->
          _e1 = err
          _r1 = res
          done()
          err

      Then "it should respond with an error", ->
        _e1.should.be.ok
      And "the status code should be 404", ->
        _r1.should.have.status 404


      # GET existent account
      Given "I have created the Account", (done) ->
        client.post "/account", { userId: 1, accountType: 0, accountCategories: [], beneficiaries: [], accountOwner: { firstName: "Test", lastName: "User" } }, (err) ->
          done()
          err

      When "I request the Account", (done) ->
        client.get "/account/1", (err, req, res, obj) ->
          _e2 = err
          _r2 = res
          _d2 = obj
          done()
          err

      Then "it should responond with a document", ->
        _d2.should.be.ok
      And "it should have the userId 1", ->
        _d2.userId.should.eql 1
      And "it should have an accountOwner property", ->
        _d2.accountOwner.should.be.ok
      And "the status code should be 200", ->
        _r2.should.have.status 200

当我运行它时,我的输出始终如下:

  

C:\发展\客户\ Pensco \ AngularJS \ Pensco \ newaccountwizard.api>摩卡   test / AccountAPITests.coffee -r should -R spec --compilers   咖啡:咖啡脚本/寄存器

     

功能:帐户API

    In order to have control over structured Account documents
    as a consumer of investment account information,
    I need a RESTful service API.


Scenario: GET /account/:userid
  ◦
    - ◊ Given: I have not yet created the Account (pending)
  ◦
    1)  When: I request the Account
  ◦
    √  Then: it should respond with an error
  ◦
    √   And: the status code should be 404
  ◦
    2) Given: I have created the Account
  ◦
    3)  When: I request the Account
  ◦
    √  Then: it should responond with a document
  ◦
    √   And: it should have the userId 1
  ◦
    √   And: it should have an accountOwner property
  ◦
    √   And: the status code should be 200
     

6传球(6s)1等待3失败

     

1)功能:帐户API

    In order to have control over structured Account documents
    as a consumer of investment account information,
    I need a RESTful service API.

Scenario: GET /account/:userid ◦  When: I request the Account:
 Error: timeout of 2000ms exceeded
at [object Object].<anonymous> (C:\Users\Jon\AppData\Roaming\npm\node_modules\mocha\lib\runnable.js:139:19)
at Timer.listOnTimeout [as ontimeout] (timers.js:110:15)
     

2)功能:帐户API

    In order to have control over structured Account documents
    as a consumer of investment account information,
    I need a RESTful service API.

Scenario: GET /account/:userid ◦ Given: I have created the Account:
 Error: timeout of 2000ms exceeded
at [object Object].<anonymous> (C:\Users\Jon\AppData\Roaming\npm\node_modules\mocha\lib\runnable.js:139:19)
at Timer.listOnTimeout [as ontimeout] (timers.js:110:15)
     

3)功能:帐户API

    In order to have control over structured Account documents
    as a consumer of investment account information,
    I need a RESTful service API.

Scenario: GET /account/:userid ◦  When: I request the Account:
 Error: timeout of 2000ms exceeded
at [object Object].<anonymous> (C:\Users\Jon\AppData\Roaming\npm\node_modules\mocha\lib\runnable.js:139:19)
at Timer.listOnTimeout [as ontimeout] (timers.js:110:15)

现在,我知道通过client.get / client.post进行的REST调用几乎是瞬间完成的。当我删除dones并在没有它们的情况下运行时,除了重新启动我的restify服务服务器后的第一次运行时,通常只有第一个或第二个Then / And失败,其余的都成功。有一个时间问题,可能是几毫秒,但绝对不是2000毫秒。我很好奇为什么当我投掷done()电话时,我的Givens和Whens突然开始超时。

我很确定我误解了mocha-cake如何改变coffescript Feature-&gt; Scenario-&gt; Given / When-&gt; Then / And / ... into describe / it calls。我怀疑在某种程度上适用的范围比看起来具有mocha-cake脚本结构的性质要大得多......我只是不确定该范围究竟是什么。

1 个答案:

答案 0 :(得分:0)

我也不熟悉摩卡蛋糕。我正在使用摩卡/(点燃)咖啡来测试。我发现在promises中包含我的调用很方便,因为最新的mocha是有意识的。然后,我不必为&#34;完成&#34;而烦恼。另请注意,您可能需要调用res.end()或res.resume()(请参阅this explanation

仅为&#34; GET&#34;:

Promise = require('bluebird')  # or whatever, I presume
port = 8776                    # ditto

getHttpJson = (addr)->
  addr = normalizeUrl(addr)
  new Promise ( resolve, reject )->
    req = http.get(addr, _completeResponse(resolve) )
      .on( 'error', reject )
    req.end()

一般情况:

requestHttpJson = (method, addr, data)->
  if data?
    data = JSON.stringify(data)
  urlBits = getUrlBits(addr)
  new Promise (resolve, reject)->
    req = http.request(
      method: method
      headers: {
        "Content-Type": "application/json" }
      hostname: urlBits.hostname
      port: urlBits.port
      path: urlBits.pathname
    , _completeResponse(resolve) )
    req.on( 'error', reject )
    if data?
      req.write( data )
    req.end()

postHttpJson = (addr, data)->
  requestHttpJson('POST', addr, data)
putHttpJson = (addr, data)->
  requestHttpJson('PUT', addr, data)
deleteHttpJson = (addr, data)->
  requestHttpJson('DELETE', addr, data)

将地址分解为组件并添加默认值。 (&#34; port&#34;是一个全球模块。)

getUrlBits = (addr)->
  bits = url.parse(addr)
  bits.port = bits.port || port
  bits.hostname = bits.hostname || 'localhost'
  bits.protocol = bits.protocol || 'http'
  return bits

normalizeUrl = (addr)->
  url.format(getUrlBits(addr))

解析请求主体的实用程序&amp;解决。

_completeResponse = (resolve)->
  (res)->
    body = []
    res.on 'data', (data)->
      body.push data
    res.on 'end', ->
      body = body.join ''
      content = if body == '' then null else JSON.parse(body)
      resolve([res,content])