DEV Community

Patrick Baselier for Kabisa Software Artisans

Posted on • Originally published at kabisa.nl on

You're not stubbing, stupid!

Recently, in a Ruby on Rails project, I was writing a Cucumber scenario that was deleting a resource by having the user clicking a 'Destroy' button. Before the action was executed, the user had to confirm a message shown in a confirmation dialog. You may have seen this dozens of times when scaffolding a Rails application.

Oh and upon deleting, I also had to do a request to an external API (to be more precise: the use case was that of a user unsubscribing, so I had to send a DELETE request to a Mollie API).

So, "nothing new here", I thought. I knew about WebMock, since I wanted to stub the external API request and my test suite was set up to test JavaScript, so I knew I could use the accept_confirm method here that Capybara offers.

Stubbing the request was defined in a support file:

# features/support/webmock.rb

require 'webmock/cucumber'

WebMock.disable_net_connect!(allow_localhost: true)

Before do |_scenario|
  stub_request(
    :delete, 
    %r{https://api.mollie.com/v2/customers/\w+/subscriptions/\w+}
  )
    .to_return(body: {}.to_json)
end

Enter fullscreen mode Exit fullscreen mode

My step implementation looked like this:

# features/step_definitions/general_steps.rb

When('I delete the resource') do
  accept_confirm { click_on 'Destroy' }
end
Enter fullscreen mode Exit fullscreen mode

The test failed! It was telling me that I should stub the DELETE request.

  Real HTTP connections are disabled. Unregistered request: DELETE https://api.mollie.com/v2/customers/...

  You can stub this request with the following snippet:

  stub_request(:delete, "https://api.mollie.com/v2/customers/...").
    ...
    to_return(status: 200, body: "", headers: {})
Enter fullscreen mode Exit fullscreen mode

Wasn't I doing this?

After many (!!!) hours of trying rewriting the stub, rubber ducking with colleagues, writing alternative scenarios, I decided to get rid of the confirmation dialog that was shown to the user. This way I didn't need the accept_confirm and... tadaaa: it worked! My test was passing.

My theory was that accept_confirm executes in a different thread or something in which the stub is not defined (I know a theory can be proven wrong, but this one worked for me).

One way to work around this is, instead of using accept_confirm, is 'overriding' the JavaScript's confirm function:

# features/support/my_world.rb
module MyWorld
  def click_on_and_confirm(locator)
    link_or_button = find(:link_or_button, locator)
    page.evaluate_script('window.confirm = () => true')
    expect(link_or_button['data-confirm']).to be_a(String)
    link_or_button.click
  end
end

World(MyWorld)

# features/step_definitions/general_steps.rb

When('I delete the resource') do
  click_on_and_confirm 'Destroy'
end
Enter fullscreen mode Exit fullscreen mode

This way the DELETE request is being stubbed, you can still show a nice confirmation dialog and your test will pass.

Be aware that during the test, the confirmation dialog will not be shown anymore (due to the 'stubbed' confirm function in JavaScript. Therefor, I added an assertion to check indirectly if the confirmation dialog will be shown (by using the data-confirm attribute added and used by Rails) when clicking the 'Destroy' link or button.

Top comments (0)