Тестирование сервисов Restify с помощью Mocha-Cakes и Coffescript, использование done() в асинхронном режиме вызывает проблемы с тайм-аутом

Я сталкиваюсь с некоторыми проблемами синхронизации с некоторыми mocha- торты тестовые скрипты, работающие с restify Сервисы. Я использую клиент Restify JSON для выполнения вызовов, которые используют обратные вызовы, а не обещания. Я передал готовую функцию своим Given и When, чтобы я мог выполнить необходимую блокировку этих асинхронных вызовов, что предотвращает несогласованные запуски набора тестов (без выполненных, это жеребьевка, какие и сколько Then и And пройдут ).

Я умеренно разбираюсь в кофескрипте и только новичок, когда дело доходит до мокко / мокко-тортов, поэтому я наверняка делаю что-то не так в своем коде. Вот пример пары тестовых случаев, которые терпят неудачу:

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:\Development\Clients\Pensco\AngularJS\Pensco\newaccountwizard.api>mocha test/AccountAPITests.coffee -r should -R spec --compilers coffee:coffee-script/register

Функция: 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 прохождение (6 с) 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)

Теперь я знаю, что мои вызовы REST через client.get/client.post происходят почти мгновенно. Когда я удаляю сделанные и запускаю без них, за исключением первого запуска после перезапуска моего сервера службы восстановления, обычно только первый или второй Then/And терпит неудачу, остальные преуспевают. Существует проблема синхронизации, может быть, несколько миллисекунд, но определенно не 2000 мс. Мне любопытно, почему мои Givens и When внезапно начинают истекать, когда я добавляю вызовы done().

Я почти уверен, что неправильно понимаю, как мокко-торты преобразуют функцию кофескрипта -> Сценарий-> Дано/Когда->Тогда/И/... в описание/вызовы. Я подозреваю, что каким-то образом объем, в котором применяется done, больше, чем может показаться, учитывая характер структуры сценария мокко-тортов ... Я просто не уверен, что именно это за объем.


person jrista    schedule 25.04.2014    source источник


Ответы (1)


Я тоже не знакома с мокко-тортиками. Я использую мокко / (лит) кофе для проверки восстановления. Я нашел удобным оборачивать свои вызовы в промисы, так как последний мокко поддерживает промисы. Тогда мне не нужно заморачиваться с "готово". Также обратите внимание, что вам может потребоваться вызвать res.end() или res.resume() (см. это объяснение)

Только для "ПОЛУЧИТЬ":

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)

Разбейте адрес на компоненты и добавьте значения по умолчанию. («порт» является глобальным модулем.)

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))

Утилита для разбора тела запроса и разрешения.

_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])
person shaunc    schedule 30.04.2014