Search code examples
ruby-on-railsrspeccapybaracapybara-webkit

How to access the same DB data that Capybara uses


I have a very simple password reset form, which is just a text field to enter an email and a submit button.

There are some client-side validations using JS, so I use the Capyabara JS driver when writing spec tests for it.

This test just tests that a password reset token is added to the user's auth_info table.

describe "password reset form", js: true do
  let(:email) { "[email protected]" }

  # Create existing user with an email so we can reset it's password
  let!(:user) { create(:user, email: email) }

  before(:each) do
    fill_in email_field, with: email
    click_button reset_button
  end

  it "generates a new token" do
    # `token` is definitely getting set properly when I pause it here
    # with binding.pry and inspect the object using `user.reload`
    # But when running the test it always shows up as `nil`
    expect(user.reload.auth_info.token).to match(/[A-Fa-f0-9]{32}/)
  end
end

As the comment notes, I know for a fact the token is getting properly set when I inspect it directly using binding.pry. But RSpec and Capybara are seeing it as nil, even after refreshing the model using reload.

Is Capybara maintaining a different cache or something?

Thanks!

EDIT: Also tried different combinations of applying the reload to the User model as well as the AuthInfo model, in case I needed to refresh the latter too


Solution

  • You're using a JS capable browser which means click_button is asynchronous. The result of this is you're executing click_button and then immediately checking for the token before the action triggered by the button has occurred. You can verify this by putting sleep 5 before the expect and the test should pass. The correct way to make the test wait before the check is to use capybaras matchers to look for info on the page that changes once the click_button has completed, something like either of the following

    expect(page).to have_text('text that appears after click_button has succeeded')
    expect(page).to have_selector('div.abcde') #element that appears after click_button has succeeded
    

    Those will make the test wait until the action has completed and then you can check for the token