0

I have this integration test for my Rails App:

require 'test_helper'

class StudyCapybaraTest < ActionDispatch::IntegrationTest
  def setup
    @user = users(:archer)
    @vocabs = @user.vocabs
    Capybara.register_driver :selenium_chrome do |app|
      Capybara::Selenium::Driver.new(app, :browser => :chrome)
    end
    Capybara.current_driver = :selenium_chrome
    #Capybara.default_wait_time = 5
    visit login_path
    fill_in "session_email",    with: @user.email
    fill_in "session_password", with: 'password'
    click_button "session_commit"
  end

  test "full study process" do
    assert_title "Home | Word Up"
    visit study_user_path(@user)
    ....
  end
end

Weirdly, when I remove the first line of the first test "full study process"

assert_title "Home | Word Up"

the test fails because the test user doesn't seem to be logged in. The same problem occurs when I move

visit study_user_path(@user)

into the setup function (as it was before).

But that doesn't change anything about the sequence and logic, right? The only thing I can think of, is that the assertion comes to early, and the app doesn't have time to execute the instructions needed to meet the assertions.

Is this a timing issue, and if so, how can I prevent them from happening in the future? Thx!

Flip
  • 6,233
  • 7
  • 46
  • 75

1 Answers1

1

First, your intuition on it being a timing issue is correct. click_button does just that - it clicks the button. It doesn't wait for a form to post, it doesn't wait for any ajax to occur, etc. So without the assert_title your test is clicking the button, and immediately changing the url in the browser. The changing the url in the browser would have the effect of canceling any form submission or behavior that was triggered by the click_button call. You need to wait after the click_button for something that changes on the page as a result of clicking the button, along the lines of

assert_text('You are now logged in')

Secondly, the setup method is run before each test so you really don't want to be registering the driver in there, since it only needs to be done once.

Thomas Walpole
  • 48,548
  • 5
  • 64
  • 78
  • Thx, Tom. Would you show me how I can set my test up so it works without abusing assertions as timeouts / breaks? Actually I didn't want to make this assertion assert_title "Home | Word Up" . I originally wrote it to see what's happening. Optimally I would like to log in and jump to study_user_path right away as this is what I want to test. Also, where would you register the driver? (I need it in every test) – Flip Jul 29 '15 at 06:54
  • 1
    Register your driver in test_helper. – Thomas Walpole Jul 29 '15 at 16:14
  • Why do you consider having an assertion there to be abusing assertions? All its saying is make sure this text appears - which is exactly what your test needs to do after logging in to know that the login has completed. I guess if you wanted you could write a helper function wait_for_login_to_complete or something that just does the assert, which may make your test read a bit cleaner. – Thomas Walpole Jul 29 '15 at 16:15
  • Regarding your wanting to log in and jump to study_user_path right away - Rather than actually logging in, you can potentially short circuit that, in tests where you're not actually testing logging in behavior, depending on what authentication library you're using - example for Devise https://github.com/plataformatec/devise/wiki/How-To:-Test-with-Capybara - – Thomas Walpole Jul 29 '15 at 16:23
  • Thanks again for your help, Tom. I considered it abusive since in this test I just want to test the study process as the login process is tested in its own test. So I just wanted to get to the study page as quick as possible without asserting anything on the way until I get there. Having to have assertions on the way to make it actually work just doesn't make sense to me. Especially since I read that Capybara should be clever enough to wait for changes. Why is that not happening here? – Flip Jul 30 '15 at 09:35
  • Tom, I wrote another question where I try to find out how to write a sign-in-method for my integration tests so I don't spam the comments here. In case you wanna have a look: http://stackoverflow.com/questions/31721337/how-can-i-write-a-shortcut-for-signing-a-user-in-in-my-integration-tests – Flip Jul 30 '15 at 10:32
  • It's smart enough to wait if you tell it what to wait for -- thats all the assertion is doing, telling it what to wait for. You could write find(:css, 'div.notice', text: 'You are now logged in') instead if you want (obviously changed to match what actually appears on your app when a login completes). That would do the same wait function - without actually having 'assert' in the name of the method call – Thomas Walpole Jul 30 '15 at 16:50