10

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

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.

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.

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

  • 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

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.

Duncan Bayne
  • 3,870
  • 4
  • 39
  • 64
  • Have you verified that the Around hook is triggered with a `puts` or ironically `binding.pry`? – AdamT Oct 23 '13 at 04:40
  • @AdamT: I've verified that the Around hook is being triggered, and I've also tested `Pry::rescue` inside an individual failing step, and that worked fine. I'm just failing to set things up so that *all* exceptions trigger pry. – Duncan Bayne Oct 23 '13 at 04:52
  • Have you looked into `@allow-rescue` for tagged tests? https://github.com/cucumber/cucumber/wiki/Tags – AdamT Oct 23 '13 at 04:56
  • @AdamT: thanks for the suggestion - I just tried it, and it failed :( I'll update the question text accordingly. – Duncan Bayne Oct 23 '13 at 05:07
  • I'm assuming you're starting the server with `rescue rails server`? Or is it `rescue cucumber`? – AdamT Oct 23 '13 at 05:28
  • `rescue cucumber` - this isn't actually a Rails project. I'm trying to catch expectation-related failures (timeouts and such) in the steps, rather than failures in the server itself. – Duncan Bayne Oct 23 '13 at 20:10

3 Answers3

7

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.

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.

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
akostadinov
  • 17,364
  • 6
  • 77
  • 85
  • Are you still using the solution above? No hooks have been added? – Erik Jan 03 '15 at 20:37
  • 1
    @Erik, yes, still works with 1.3.x series. With the upcoming 2.x I just use the After hook which doesn't allow me to continue scenario after a failed step but haven't had time to fiddle with 2.x and make it work as 1.3 – akostadinov Jan 04 '15 at 19:07
1

In the version of Cucumber 2.4.0 the #accept method is resided in Cucumber::Formatter::LegacyApi::Ast::StepInvocationso redefine it and proceed the required action inside it:

Cucumber::Formatter::LegacyApi::Ast::StepInvocation.class_eval do
  alias_method :orig_accept, :accept

  def accept formatter
    orig_accept(formatter)
    if status == :failed
      formatter.runtime.support_code.ruby.current_world.instance_eval do
        # do something as if you are inside the cuke test step
        # like: expect(something).to be_something_else
      end
    end
  end
end
Малъ Скрылевъ
  • 16,187
  • 5
  • 56
  • 69
-1

Have you tried calling:

binding.pry

Just call it within your failing test and have a look around.

AdamT
  • 6,405
  • 10
  • 49
  • 75
  • The problem is that there is no single failing test; the tests fail intermittently, in different places. Hence my desire to trigger pry whenever an exception is raised. – Duncan Bayne Oct 23 '13 at 04:53
  • I'm actually going through that now too. Something to note is the driver. Not sure this helps you debug but for us we realized that the firefox driver is inconsistently failing while poltergeist is consistent. Seems to raise more questions though... – AdamT Oct 23 '13 at 05:01
  • Did you ever get anywhere with this? I failed utterly. – Duncan Bayne Jan 08 '14 at 12:01