Forum: RSpec [cucumber] webrat+selenium integration problem: record not saved to database?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Balint E. (Guest)
on 2009-03-23 21:57
(Received via mailing list)
Hey,

I am writing a simple cucumber feature for a Rails app to test if the
webrat+selenium integration was successful:

 Scenario: Successful log in
    Given the user "pepito" exists # (1)
    And the user "pepito" is active
    When I go to the login page
    And I fill in "login" with "pepito"
    And I fill in "password" with "secret"
    And I press "submit" # (2)
    Then I should see a confirmation message

Now I think I managed to set up the architecture all right (using sep.
setup files for plain and javascript -selenium- features as described
here:
http://wiki.github.com/aslakhellesoy/cucumber/sett...).
The problem is that the "Then I should see a confirmation message"
fails.

I put a breakpoint at (2) to investigate the problem. I've found that:

1. the user is succesfully created (User.first in a rails console
gives back pepito)
2. I can log in successfully from the console (User.authenticate
("pepito", "secret") gives back the user meaning successful
authentication.
3. The login and password fields are correctly filled with the values
(checking that in the launched FF browser)
4. If I manually submit the form at the breakpoint, it also fails to
log in, so it's not that the "And I press "submit" step does something
other than submitting the form.
5. the users table in the database that selenium uses is empty.
(mysql> select * from users;
Empty set (0.00 sec) ). That surprises me a bit since in theory
selenium does not use transactional fixtures so the record should
appear right away after (1) runs.

Here are my cucumber config file(s):
http://gist.github.com/83635

I run the feature like this:

cucumber -r features/support/env.rb -r features/support/enhanced.rb -r
features/step_definitions features/enhanced/login.feature

And these are the setup steps to create and activate the user:

Given /^the user "(.*)" exists$/ do |login_name|
  User.find_by_login(login_name) || Factory
(:user_with_password, :login => login_name)
end

Given /^the user "(.*)" is active$/ do |login_name|
  User.find_by_login(login_name).activate!
end

I also see in the log that the form is submitted with the correct
parameters.

Please let me know if you see what I am missing or what I should do
differently.

Thank you,
Balint
Ben M. (Guest)
on 2009-03-24 03:13
(Received via mailing list)
Balint E. wrote:
>     And I fill in "password" with "secret"
>
> 5. the users table in the database that selenium uses is empty.
> (mysql> select * from users;
> Empty set (0.00 sec) ). That surprises me a bit since in theory
> selenium does not use transactional fixtures so the record should
> appear right away after (1) runs.
>

Webrat's Selenium adapter will not change your DB settings.  On line 10
on your env.rb file you actually have transactional support turned on.
You need to comment that line out when using Selenium.  For more info
please read about this problem on the wiki:

http://wiki.github.com/aslakhellesoy/cucumber/trou...

HTH,
Ben
Mike G. (Guest)
on 2009-03-24 18:13
(Received via mailing list)
If you actually want to use transactional fixtures with selenium, try
out
http://github.com/gaffo/mainline

Something I coded up that allows selenium to work with transactional
fixtures.
Balint E. (Guest)
on 2009-03-24 23:48
@Ben: Thank you, I fixed that and am using your database_cleaner to
truncate the database.

Turns out the problem (or rather, another problem) was that I set up a
separate environment (selenium) for the selenium tests but still some of
the steps used the test environment and thus the database record was
generated in the test database not in the selenium one where it should
have been.

I struggled to get the record creation happen in the selenium database
but finally I gave up and am now using the test for both selenium and
"plain" cucumber features. So the database_cleaner serves me even
better. It works! My current config is here:
http://gist.github.com/83635. Once again, thank you for your help.

@Mike: I may check that out, thank you!

Balint

Ben M. wrote:
> Balint E. wrote:
>>     And I fill in "password" with "secret"
>>
>> 5. the users table in the database that selenium uses is empty.
>> (mysql> select * from users;
>> Empty set (0.00 sec) ). That surprises me a bit since in theory
>> selenium does not use transactional fixtures so the record should
>> appear right away after (1) runs.
>>
>
> Webrat's Selenium adapter will not change your DB settings.  On line 10
> on your env.rb file you actually have transactional support turned on.
> You need to comment that line out when using Selenium.  For more info
> please read about this problem on the wiki:
>
> http://wiki.github.com/aslakhellesoy/cucumber/trou...
>
> HTH,
> Ben
Ben M. (Guest)
on 2009-03-25 01:46
(Received via mailing list)
Balint E. wrote:
> but finally I gave up and am now using the test for both selenium and
> "plain" cucumber features. So the database_cleaner serves me even
> better. It works! My current config is here:
> http://gist.github.com/83635. Once again, thank you for your help.
>

Couple things I'd like to point out.  In your enhanced.rb you don't need
to do the Before hook yourself.  You can just require
'database_cleaner/cucumber'.  I've updated your gist to use that.
In your plain.rb it seems like you are trying to truncate your database
just once upon startup.  If that is the case then the recommended way is
to use the clean_with method like so:
DatabaseCleaner.clean_with :truncation

Again, I have updated the gist to reflect this.

-Ben
Balint E. (Guest)
on 2009-03-25 23:42
Ben M. wrote:

> Couple things I'd like to point out.  In your enhanced.rb you don't need
> to do the Before hook yourself.  You can just require
> 'database_cleaner/cucumber'.  I've updated your gist to use that.
> In your plain.rb it seems like you are trying to truncate your database
> just once upon startup.  If that is the case then the recommended way is
> to use the clean_with method like so:
> DatabaseCleaner.clean_with :truncation
>
> Again, I have updated the gist to reflect this.
>
> -Ben

Hey Ben, thanks a lot.

The problem I am experiencing now is that information stored in the
session does not seem to be retained between steps (again, only in the
case of selenium sessions, plain sesssion work fine). So the login
function works fine now but when I go to another page afterwards it
throws a big error because the action tries to render something based on
the current user.

Is the session store I am using relevant? I used the default
cookie-based storage and then tried to change to the active-record based
one to no avail. My config is at http://gist.github.com/83635

Thank you,
Balint
Balint E. (Guest)
on 2009-03-26 01:25
> Hey Ben, thanks a lot.
>
> The problem I am experiencing now is that information stored in the
> session does not seem to be retained between steps (again, only in the
> case of selenium sessions, plain sesssion work fine). So the login
> function works fine now but when I go to another page afterwards it
> throws a big error because the action tries to render something based on
> the current user.
>
> Is the session store I am using relevant? I used the default
> cookie-based storage and then tried to change to the active-record based
> one to no avail. My config is at http://gist.github.com/83635
>
> Thank you,
> Balint

Ok, I think I have the problem and it has nothing to do with session
storage. I intend to explain it below so others might benefit from it.

  Scenario: Successfully sharing album with group via the "Share with
group" link
    Given I am logged in as "pepito"
    When I go to my photosets page

I used some existing step definitions I wrote for non-selenium features
to log in the user like so:

  Given /^I log in as "(.*)"$/ do |login_name|
    @user = User.find_by_login(login_name)
    post "/session", :login => @user.login, :password => 'secret' # !!!
  end

  Given /^I am logged in as "(.*)"$/ do |login_name|
    Given %(the user "#{login_name}" exists)
    Given %(the user "#{login_name}" is active)
    Given %(I log in as "#{login_name}")
  end

When the feature gets to "When I go to my photosets page", the selenium
browser is launched but there is no user logged in here, there is no
session, so that step will fail miserably. That's because the line
denoted by !!! does not log in the user in the selenium session but in
the "test session" (I know this is not the proper definition but I
cannot precisely name it).

So if I need a logged in user in a selenium feature I should go the
"hard way" and start from scratch:

  Given the user "pepito" exists
  And the user "pepito" is active
  When I go to the login page
  And I fill in "login" with "pepito"
  And I fill in "password" with "secret"
  And I press "entrar"
  And I go to my photosets page
  ...

It takes some time to get one's head around realizing there is a test
world and a selenium world and keep in mind which action modifies which
world and learn how they can interact with each other (through the
database I guess if they use the same environment).

Please correct me if I am wrong in anything I say above,
Balint
Ben M. (Guest)
on 2009-03-26 01:59
(Received via mailing list)
Balint E. wrote:
>> cookie-based storage and then tried to change to the active-record based
> group" link
>
> the "test session" (I know this is not the proper definition but I
>   And I press "entrar"
>
>
Yep.. that is what I thought was happening.  The "test session" is rails
integration session that webrat uses.
I would still use your login step but just have it hit the form.   So
something like:

  Given /^I log in as "(.*)"$/ do |login_name|
    @user = User.find_by_login(login_name)
    vist "/session"
    fill_in "login", :with => login_name
    fill_in "password", :with => 'secret'
    click_button "entrar"
  end



With that step definition you don't need to pollute all of your features
with the steps to login.  As it turns out you can have the best of both
worlds.  Meaning, if you want webrat to use the faster post method only
for non-selenium runs you can.  Like so:

  Given /^I log in as "(.*)"$/ do |login_name|
    @user = User.find_by_login(login_name)
    webrat.atutomate do
      vist "/session"
      fill_in "login", :with => login_name
      fill_in "password", :with => 'secret'
      click_button "entrar"
    end

    webrat.simulate do
      post "/session", :login => @user.login, :password => 'secret'
    end
  end



In short, the webrat#automate block is only executed with an adapter
that is automating a real browser.  Likewise the webrat#simulate block
is only executed when the adapter is simulating a browser, such as the
rails adapter. Does that make sense?

-Ben
Ben M. (Guest)
on 2009-03-26 02:22
(Received via mailing list)
Balint E. wrote:
>>
> function works fine now but when I go to another page afterwards it
> throws a big error because the action tries to render something based on
> the current user.
>
> Is the session store I am using relevant? I used the default
> cookie-based storage and then tried to change to the active-record based
> one to no avail. My config is at http://gist.github.com/83635
>

Hmm.. I've never had issues with sessions not persisting when using the
selenium adapter.  Nothing in your config looks wrong.
What is your login method?  It is telling Selenium to go to the login
page and submit the proper credentials?

Ben
Balint E. (Guest)
on 2009-03-26 03:00
Ben M. wrote:

>
> With that step definition you don't need to pollute all of your features
> with the steps to login.  As it turns out you can have the best of both
> worlds.  Meaning, if you want webrat to use the faster post method only
> for non-selenium runs you can.  Like so:
>
>   Given /^I log in as "(.*)"$/ do |login_name|
>     @user = User.find_by_login(login_name)
>     webrat.atutomate do
>       vist "/session"
>       fill_in "login", :with => login_name
>       fill_in "password", :with => 'secret'
>       click_button "entrar"
>     end
>
>     webrat.simulate do
>       post "/session", :login => @user.login, :password => 'secret'
>     end
>   end
>

Excellent (and elegant), it works now!

Thank you for your help along the way,
Balint
Mateusz J. (Guest)
on 2009-04-05 23:41
Balint E. wrote:
> Ben M. wrote:
>
>> Couple things I'd like to point out.  In your enhanced.rb you don't need
>> to do the Before hook yourself.  You can just require
>> 'database_cleaner/cucumber'.  I've updated your gist to use that.
>> In your plain.rb it seems like you are trying to truncate your database
>> just once upon startup.  If that is the case then the recommended way is
>> to use the clean_with method like so:
>> DatabaseCleaner.clean_with :truncation
>>
>> Again, I have updated the gist to reflect this.
>>
>> -Ben
>
> Hey Ben, thanks a lot.
>
> The problem I am experiencing now is that information stored in the
> session does not seem to be retained between steps (again, only in the
> case of selenium sessions, plain sesssion work fine). So the login
> function works fine now but when I go to another page afterwards it
> throws a big error because the action tries to render something based on
> the current user.
>
> Is the session store I am using relevant? I used the default
> cookie-based storage and then tried to change to the active-record based
> one to no avail. My config is at http://gist.github.com/83635
>
> Thank you,
> Balint

hi Ben

I am newbie with cucumber and bdd. I was wondering if you can take a
look on my problems.
I have similar problem like Balint. I test my own and clearance features
with selenium and webrat. I don't have to say that with werbrat
everything is perfect. When I run features with selenium I get error
nil.session what is connected with code from clearance features ie:
<pre>
Then /^I should not be signed in$/ do
  assert_nil request.session[:user_id]
end
</pre>
in clearance_step file

It seems like selenium has problem with access to "request" object
That makes my test fail all the time.

Another problem I met is with waiting for response after "press button"
method
All the time I get response with code before button pressed
It's connected with selenium_session.rb code and require me to change a
bit
<pre>
    def click_button(button_text_or_regexp = nil, options = {})
      if button_text_or_regexp.is_a?(Hash) && options == {}
        pattern, options = nil, button_text_or_regexp
      elsif button_text_or_regexp
        pattern = adjust_if_regexp(button_text_or_regexp)
      end
      pattern ||= '*'
      locator = "button=#{pattern}"

      selenium.wait_for_element locator, :timeout_in_seconds => 5
      selenium.click locator
      selenium.wait_for_page_to_load(5) ## add this line to get correct
response
    end
</pre>
I don't know why I have to change it
Thanks
Balint E. (Guest)
on 2009-04-06 01:08
Hi Mateusz,

>
> hi Ben
>
> I am newbie with cucumber and bdd. I was wondering if you can take a
> look on my problems.
> I have similar problem like Balint. I test my own and clearance features
> with selenium and webrat. I don't have to say that with werbrat
> everything is perfect. When I run features with selenium I get error
> nil.session what is connected with code from clearance features ie:
> <pre>
> Then /^I should not be signed in$/ do
>   assert_nil request.session[:user_id]
> end
> </pre>
> in clearance_step file
>
> It seems like selenium has problem with access to "request" object
> That makes my test fail all the time.

I cannot tell you whether step definitions should have access to the
request via the request variable (they probably should). But are you
sure you need to test the session directly like this? I searched for
'request' or 'session' in my step definitions and have not found any.
Usually I check whether

1.  a certain thing is on the page which usually involves checking the
response body with the help of the webrat steps file (e.g I should see
"You are logged in")
2. a new model instance has (not) been created or have an attribute set
to a certain value, etc. In brief, database related things.

>
> Another problem I met is with waiting for response after "press button"
> method
> All the time I get response with code before button pressed
> It's connected with selenium_session.rb code and require me to change a
> bit
(...)
> I don't know why I have to change it
> Thanks

That's a bug in webrat I also came across and fixed:
https://webrat.lighthouseapp.com/projects/10503/ti...

It will surely be integrated in the next webrat release, you can use the
supplied patch.

Hope that helps,
Balint
Ben M. (Guest)
on 2009-04-06 01:32
(Received via mailing list)
Mateusz Juraszek wrote:
>>> to use the clean_with method like so:
>> case of selenium sessions, plain sesssion work fine). So the login
>>
> Then /^I should not be signed in$/ do
>   assert_nil request.session[:user_id]
> end
> </pre>
>

Yuck!  I realize that you didn't write this and that Clearance gave you
this step out of the box:
http://github.com/thoughtbot/clearance/blob/fa424b...

It is, IMO, a very bad way to verify if the user is logged in.  (I
actually used an example very similar to this in my MWRC
presentation[1]. One of the reasons that makes it bad is that it makes
it difficult to switch from using rails integration session to an
automated browser solution like Selenium.

> in clearance_step file
>
> It seems like selenium has problem with access to "request" object
> That makes my test fail all the time.
>

Yep, that object exists in rails integration sessions, which is what
webrat's :rails mode uses.  To avoid this error you will need to specify
behaviour, not implementation.  I'm guessing that this step is called
after you have logged out.  So, the way I would test this is that I
would test it from the point of view of the user.  The user doesn't know
about a session, much less how we are implementing our authentication
system.  :)  When this user logs out they probably see a message
indicating that they have been logged out though.  So, something like
this would be better:

Then /^I should not be signed in$/ do
  response.should contain("You have been logged out.")
end

(Note: I'm not sure if clearance says this exactly, but you get the
idea...)

Using a step like this will allow you to switch between multiple
adapters since you should always have the response object.


>         pattern = adjust_if_regexp(button_text_or_regexp)
> I don't know why I have to change it
>

Hmmm.. Yeah, I don't know about that.  You should probably ask the
webrat mailing list or open up a ticket[2].



-Ben

1.
http://mwrc2009.confreaks.com/14-mar-2009-15-00-bd...
2. http://wiki.github.com/brynary/webrat/get-in-touch
Mateusz J. (Guest)
on 2009-04-06 08:56
That makes more sense than my theory.
Thank you Ben and thank you Balint.

BTW nice presentation
Mateusz J. (Guest)
on 2009-04-06 09:22
>> Another problem I met is with waiting for response after "press button"
>> method
>> All the time I get response with code before button pressed
>> It's connected with selenium_session.rb code and require me to change a
>> bit
> (...)
>> I don't know why I have to change it
>> Thanks
>
> That's a bug in webrat I also came across and fixed:
> 
https://webrat.lighthouseapp.com/projects/10503/ti...
>
> It will surely be integrated in the next webrat release, you can use the
> supplied patch.
>
> Hope that helps,
> Balint


As you can see in my first post I actually used your solution first
(selenium.wait_for_element locator, :timeout_in_seconds => 5 for each
method) but it doesn't work for me, because in my case selenium compares
expectations with page source before action (page source before sending
form and get redirection etc). Then I added
selenium.wait_for_page_to_load(5) to get correct request.
This topic is locked and can not be replied to.