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'
confirmfunction in JavaScript. Therefor, I added an assertion to check indirectly if the confirmation dialog will be shown (by using thedata-confirmattribute added and used by Rails) when clicking the 'Destroy' link or button.
Top comments (0)