使用 pry-rescue 调试 Cucumber 步骤中的异常步骤、异常、pry、rescue

2023-09-08 00:30:52 作者:爱是包容

我在我的 Cucumber 功能中添加了一个 Around 钩子,我希望它会在引发异常时导致 pry-rescue 开始 pry:

I've added an Around hook to my Cucumber features that I had hoped would cause pry-rescue to start pry when an exception was thrown:

Around do |scenario, block|
  Pry::rescue do
    block.call
  end
end

Around 钩子肯定会被调用,但是在步骤中抛出的异常不会被挽救.例如.这一步:

The Around hook is definitely being called, however exceptions thrown within steps aren't rescued. E.g. this step:

When(/^I perform the action$/) do
  raise 'hell'
end

... 导致功能失败,但不会让我在控制台中窥探.

... causes the feature to fail, but doesn't drop me into pry at the console.

是否可以对 Cucumber 使用 pry-rescue ?我也将此作为 问题 提出,因为我怀疑它可能是一个错误.

Is it possible to use pry-rescue with Cucumber? I've raised this as an issue as well, as I suspect it might be a bug.

更新:根据 AdamT 在评论中的建议,我已经:

Updated: as per a suggestion from AdamT in the comments, I've:

@allow-rescue 标签添加到调用故意破坏步骤的功能中添加了 puts 日志记录以验证 Around 钩子是否被调用 added the @allow-rescue tag to the feature calling the deliberately broken step added puts logging to verify that the Around hook is being called

引发异常时仍然无法进入pry,但我可以从puts语句中看到它正在进入Around hook.

It's still failing to enter pry when the exception is raised, but I can see from the puts statements that it's entering the Around hook.

推荐答案

我想做同样的事情 - 在步骤失败时进行调试.您的钩子无法工作,因为已经捕获了失败的步骤异常.似乎没有标准的方法可以用黄瓜做你想做的事.但是如果你看一下 lib/cucumber/ast/step_invocation.rbinvoke(runtime, configuration) 方法,你就会明白我在说什么.

I wanted to do the same thing - debug when a step fails. Your hook cannot work because a failing step exception is caught already. There seems to be no standard way of doing what you want with cucumber. But if you look at lib/cucumber/ast/step_invocation.rb the invoke(runtime, configuration) method, you will see what I am talking about.

在方法步骤级别的异常被捕获.最后一个 rescue 块是我们要插入调试代码的地方.所以在最新的 cucumber 1.3.12 的第 74 行,我插入了:

In the method step level exceptions are caught. And the last rescue block is where we want to insert our debugging code. So in latest cucumber 1.3.12, on line 74 I inserted:

        require 'byebug'
        byebug

现在一旦发生瞬时故障,我会收到提示:

And now once the transient failure happens I get a prompt:

[71, 80] in /home/remote/akostadi/.rvm/gems/ruby-2.1.1/gems/cucumber-1.3.10/lib/cucumber
/ast/step_invocation.rb
   71:             failed(configuration, e, false)
   72:             status!(:failed)
   73:           rescue Exception => e
   74:             require 'byebug'
   75:             byebug
=> 76:             failed(configuration, e, false)
   77:             status!(:failed)
   78:           end
   79:         end
   80:       end

您可以在其中插入其他调试代码.

You can insert other debugging code in there though.

我在想,黄瓜项目是否会接受捐款,以便在那里有一个钩子.

I'm thinking if cucumber project will accept a contribution to have a hook there instead.

更新:这是我的最新版本.该版本的优点是您在进入调试器之前会获得失败日志.你也可以(至少用撬)到达黄瓜 World 并在里面启动 pry 来玩弄,就好像这是你的测试代码一样.我在 cuke google 群组中打开了 讨论 来查看如果可以在上游实现类似的东西.如果你想让黄瓜成为标准,请给出你的声音和建议.所以只需将下面的代码放在 support/env.rb 中:

UPDATE: here's my latest version. The positives in that version are that you get failure log before falling into a debugger. Also you can reach (at least with pry) to the cucumber World and launch pry inside to play around as if this is your test code. I've opened a discussion in the cuke google group to see if something similar can be implemented upstream. Give your voice and suggestions if you want to have it standard in cucumber. So just put the below code in support/env.rb:

  Cucumber::Ast::StepInvocation.class_eval do
    ## first make sure we don't lose original accept method
    unless self.instance_methods.include?(:orig_accept)
      alias_method :orig_accept, :accept
    end

    ## wrap original accept method to catch errors in executed step
    def accept(visitor)
      orig_accept(visitor)
      if @exception
        unless @exception.class.name.start_with?("Cucumber::")
          # @exception = nil # to continue with following steps
          # cd visitor.runtime/@support_code
          # cd @programming_languages[0].current_world
          # binding.pry
          require 'pry'
          binding.pry
        end
      end
    end
  end