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
My step implementation looked like this:
# features/step_definitions/general_steps.rb
When('I delete the resource') do
accept_confirm { click_on 'Destroy' }
end
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: {})
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
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 thedata-confirm
attribute added and used by Rails) when clicking the 'Destroy' link or button.
Top comments (0)