Как проверить вывод журнала Hubot в тестовом скрипте

Я пишу тест для своего Hubot (который действует как бот Slack). Запускаемый определенными сообщениями Slack, бот отправляет HTTP-запрос POST отдельному приложению Rails. Как я могу проверить в своем тестовом сценарии, что HTTP-запрос был отправлен? Я предполагаю, что я должен проверить содержимое robot.logger (пожалуйста, дайте мне знать, если есть лучший способ) - но если да, то как я могу получить доступ к журналу в тесте?

Скрипт Hubot (по сути, он информирует приложение Rails о пользователе, который покидает офис, чтобы сделать перерыв):

module.exports = (robot) ->
  robot.respond /off to lunch/i, (res) ->
    res.reply('Later alligator')
    robot.logger.info "This user is going on lunch break: #{res.message.user.id}"

    data = JSON.stringify({
      slack_user_id: res.message.user.id
    })
    robot.http(process.env.RAILS_APP_URL + '/break')
      .header('Content-Type', 'application/json')
      .post(data) (err, resp, body) ->
        if err
          robot.logger.info "Encountered an error. #{err}"
          res.reply('Sorry, there was an error recording your break time')
        else
          robot.logger.info 'Successfully sent HTTP POST request to Rails app'

Вывод журнала при выполнении этого скрипта:

INFO This user is going on lunch break: [SLACK_USER_ID]
INFO Successfully sent HTTP POST request to Rails app

Как я упоминал выше, я хотел бы проверить в своем тестовом сценарии, что HTTP-запрос был отправлен, утверждая, что журнал будет включать сообщение 'Successfully sent HTTP POST request to Rails app'. Однако я не знаю, как получить доступ к журналу Hubot в моем тесте. Я думал, что это как-то связано с process.stdout, потому что бот регистрируется в стандартном выводе, но я не мог заставить его работать.

Тестовый сценарий:

Helper = require('hubot-test-helper')
helper = new Helper('../scripts/break-start.coffee')
request = require('request')
expect = require('chai').expect
nock = require('nock')

describe 'bot responds to user message and sends ', ->
  beforeEach ->
    # Set up the room before running the test.
    @room = helper.createRoom()

    # Set up a request interceptor.
    nock(process.env.RAILS_APP_URL)
      .post('/break', { slack_user_id: 'bob' })
      .reply(200)

  afterEach ->
    # Tear down the room after the test to free up the listener.
    @room.destroy()

  context 'user sends lunch message', ->
    beforeEach ->
      @room.user.say('bob', '@hubot Off to lunch')

    it 'responds to users who are off to lunch', ->
      expect(@room.messages).to.eql [
        ['bob', '@hubot Off to lunch']
        ['hubot', '@bob Later alligator']
      # I want to do something like this:
      # expect(robot.log).include('Successfully sent HTTP POST request to Rails app')

Конечно, я вижу в журнале консоли, когда запускаю тест, что HTTP-запрос отправляется, но я также хотел бы подтвердить это, чтобы тест не прошел, если запрос не отправлен.

Вывод журнала при выполнении теста:

INFO This user is going on lunch break: bob
✓ responds to users who are off to lunch
INFO Successfully sent HTTP POST request to Rails app

Спасибо за любую помощь заранее.


person reesaspieces    schedule 08.05.2019    source источник


Ответы (1)


Я бы не советовал писать тесты в зависимости от логов. Журнал является побочным эффектом программы. Если вы измените вывод журнала, тесты не пройдут, даже если функциональность останется правильной.

Вместо этого вы должны использовать библиотеку для имитации и проверки выполнения http-запроса. На самом деле выполнение запроса было бы побочным эффектом, и опять же, его не следует делать в ваших тестах (что, если вы вызовете чрезмерную нагрузку на внешнюю службу из-за запущенных тестов?

Вы уже используете библиотеку nock для перехвата запроса. Его также можно использовать для проверки того, был ли сделан запрос (см. документы об ожиданиях из нока). репо).

Вот пример использования requestScope.done() from nock в вашем тесте.

it 'sends the break request to the rails server', ->
  # capture the request to the rails app
  railsRequest = nock(process.env.RAILS_APP_URL)
    .post('/break', { slack_user_id: 'bob' })
    .reply(200)
  # make the request and wait for it to be completed
  await @room.user.say('bob', '@hubot Off to lunch')
  # check that the request was made
  railsRequest.done()

Я использую await, чтобы убедиться, что вызов функции, которая должна сделать запрос, завершен до его тестирования. Если вы не знакомы с await, вместо этого вы можете переместить проверку (railsRequest.done()) в обработчик обещаний .then() при вызове @room.user.say(...).


Обещанная версия:

Что касается вашего комментария, вот обещанная версия. Вам нужно передать .then функцию. Если вы передадите его .then request.done(), то ожидание request.done() будет выполнено немедленно, а его результат будет передан как обратный вызов обещания. Либо заверните его в другую функцию (см. ниже), либо удалите скобки (.then request.done). Но будьте осторожны со вторым вариантом. Если ваше обещание возвращает значение, оно будет передано обратному вызову. Поскольку это из библиотеки, это может привести к неожиданному поведению, поэтому я бы предложил первый вариант:

it 'sends the break request to the rails server', ->
  # capture the request to the rails app
  railsRequest = nock(process.env.RAILS_APP_URL)
    .post('/break', { slack_user_id: 'bob' })
    .reply(200)
  # make the request and wait for it to be completed
  @room.user.say('bob', '@hubot Off to lunch').then ->
    # check that the request was made
    railsRequest.done()
person caffeinated.tech    schedule 08.05.2019
comment
Благодарю за ваш ответ. Я думал, что использование await требует функции с ключевым словом async. Поскольку async не было, предложенный вами код вернул ReferenceError: await is not defined. Есть ли способ использовать async/await здесь? - person reesaspieces; 09.05.2019
comment
Я также пытался использовать обещание .then, и это сработало: @room.user.say('bob', '@hubot Off to lunch').then(@railsRequest.isDone()). Но если я поменяю местами isDone() на done(), я получу AssertionError, говорящее Mocks not yet satisfied. Не уверен, почему, даже если я прочитал документы, поскольку разница, похоже, заключается во всех указанных вызовах и сингл, но для начала здесь только один макет. Извините за многочисленные вопросы и еще раз спасибо! - person reesaspieces; 09.05.2019