I've got a few questions about Capybara. And I might as well ask here since the RDOC in the github page for Capybara is great to get it set up and running. But where is the API or list of available methods??
First. Per *_spec.rb file, should scenario only exist once? Or is it fine to have multiple scenario's in one file?
For example, in spec/request/user_spec.rb:
require 'spec_helper'
feature 'User actions' do
background do
data = {
:first_name => 'foo',
:last_name => 'bar',
...
}
user = User.new(data, :as => :user)
user.save
end
scenario 'User can browse home page' do
visit root_path
page.should have_content('Homepage')
end
scenario 'User should not be able to visit the dashboard' do
visit dashboard_root_path
page.should have_content('You are not authorized to access this page.')
end
end
If there is anything wrong with the code structure above, or if there is room for improvement. I am open feedback.
Second. I notice with the code above. If I have config.use_transactional_fixtures = false in spec/spec_helper.rb, it saves the user twice. This means, in my test database / user table, I would have 2 users named 'foo bar'. Is this normal?
Third. I have a form that has an HTML button. When user clicks on this button, jQuery submits the form. How would I test this with Capybara? I don't think click_button "Add" will do the trick.
Fourth. How would I sign in users in Capybara? I am using Devise. Would sign_in User.first do the trick? And would I be able to access current_user in Capybara?
Lastly, if anyone knows any "Getting Started" guides / tutorials on Rspec + Capybara. Please do mention.
I've also switched over to writing request specs ever since i decided that I was no longer liking Cucumber.
ONE) Having multiple scenarios is indeed fine. You get to use all the other great features of rspec, so I would suggest also using contexts as in the code at the bottom.
TWO) This can probably be solved by using the Rspec Set Gem And the Database Cleaner Gem. Also: The Original Rationale for Set
Warning: make sure you set up DatabaseCleaner correctly when you use set. My own setup (which may be a little overkill but is working for me):
config.before(:suite) do
DatabaseCleaner.clean_with :truncation
end
config.before(:all) do
DatabaseCleaner.clean_with :truncation
end
config.after(:all) do
DatabaseCleaner.clean_with :truncation
end
config.after(:suite) do
DatabaseCleaner.clean_with :truncation
end
THREE) yep! click_button "Add" should work! The complete capybara API is useful but took me a while to grok. Of most relevant importance are the actions and rspec matchers.
example:
click_button "Add"
page.should have_content("Successfully Added")
you can narrow the scope with element finders.
FOURTH) Devise provides helpers. there is a sign_in helper. read the dox :). Here's a demo:
feature 'User actions' do
background do
data = {
:first_name => 'foo',
:last_name => 'bar',
...
}
#user = User.new(data, :as => :user)
#user.save
end
context "no user is signed in" do
scenario 'User can browse home page' do
visit root_path
page.should have_content('Homepage')
end
scenario 'User should not be able to visit the dashboard' do
visit dashboard_root_path
page.should have_content('You are not authorized to access this page.')
end
end
context "user is signed in" do
before :each do
sign_in #user
end
[more scenarios]
end
end
ultimately of course, you'd prolly want to split this up into more specific features. Probably have a "Public Navigation" Feature for all the tests that are about guests seeing content, and then a separate feature for a user signing in, etc.
I am not aware of capybara, but a full list of available methods can be found here:
http://rubydoc.info/github/jnicklas/capybara/master#
hope, that helps
Related
Im trying to set up a (basic) test for a new feature I am going to implement. I have a job controller and instead of default showing all jobs I like to hide all the ones which is archived. I tried different ways but it seems like i am missing a piece or two of this puzzle. First i tried with calling 'visit' but get the message it does not exist. Second approach is using 'render' but that also ends up in a error saying render does not exists. (can i even use these methods in a controller spec?)
Is it wrong to put this in a controller test?
2 last test are causing errors
require "rails_helper"
require "spec_helper"
describe JobsController, :type => :controller do
context 'GET index' do
it "should be successful" do
get :index
expect(response).to be_success
end
it "renders the index template" do
get :index
expect(response).to render_template("index")
end
it "should not show any archived jobs as default" do
visit jobs_path
page.should have_no_content("Archived")
end
it 'should show the headers' do
render :template => 'job/index', :layout => 'layouts/job'
rendered.should_not contain('Archived')
end
end
end
Capybara is used for feature specs, and its matchers can be used in view specs.
Controller specs, by default, don't actually render the view because they're the wrong place to be checking for page content - https://www.relishapp.com/rspec/rspec-rails/docs/controller-specs
You should probably move some of your tests to feature tests/view tests
I'm trying to write all my Capybara code to not use any CSS or funny matchers. For acceptance test purposes, I'm using Capybara to navigate only by button and link text that is visible to the user.
So I have a really simple test that asserts that an administrator can edit any user:
it 'allows an administrator to edit any user' do
user = login_admin_user
user1 = FactoryGirl.create(:user)
click_link "Users"
current_path.should eq(users_path)
click_link "Edit" # This is the problem
current_path.should eq(edit_user_path(user1))
fill_in "Last name", with: "Myxzptlk"
click_button "Update User"
page.should have_content("Myxzptlk")
end
Of course the problem line above is not specific enough; there will be 2 lines in the table (user and user1). I'm pretty new to TDD, so how do I use Capybara to select the correct link using only visible text?
I'm not sure why you're avoiding 'CSS or funny matchers'. If you don't want to put them in your test, abstract them away into helper methods.
In my specs I have a helper method like this:
module FeatureHelper
def within_row(text, &block)
within :xpath, "//table//tr[td[contains(.,\"#{text}\")]]" do
yield
end
end
end
And then in my specs I can call it like:
within_row(user1.name) do
click_link 'Edit'
end
The helper module goes inside the spec/support folder, and then gets loaded into my specs by doing:
config.include FeatureHelper, type: :feature
in my spec_helper.rb.
I have built out a controller spec for my user model, and it passes, although I feel like it should fail. The spec is below:
describe UsersController do
include Devise::TestHelpers
let (:user) do
FactoryGirl.create(:user)
end
let (:other_user) do
FactoryGirl.create(:user)
end
before :each do
#request.env["devise.mapping"] = Devise.mappings[:user]
sign_in user
end
describe "#index" do
it "can GET 'index'" do
get :index
response.should be_success
end
it "populates a list of users" do
get :index
expect(assigns(:users)).to eq([user])
end
end
end
The line "expect(assigns(:users)).to eq([user])" passes, but I feel like it should fail. After all, I've created two users (user and other_user) up at the top. I'm clearly misunderstanding what's going on, so can someone explain it?
let is lazy; it won't create the user until the first time it's called (at which point the result is memoized). Since you call user but not other_user, only the first user is actually created, and the spec passes.
Use let! if you want eager evaluation, or make sure you call other_user somewhere.
RSpec Documentation
I'm building a Rails 3 app using Devise, with Capybara for UI testing. The following test is failing:
class AuthenticationTest < ActionController::IntegrationTest
def setup
#user = User.create!(:email => 'test#example.com',
:password => 'testtest',
:password_confirmation => 'testtest')
#user.save!
Capybara.reset_sessions!
end
test "sign_in" do
# this proves the user exists in the database ...
assert_equal 1, User.count
assert_equal 'test#example.com', User.first.email
# ... but we still can't log in ...
visit '/users/sign_in'
assert page.has_content?('Sign in')
fill_in :user_email, :with => 'test#example.com'
fill_in :user_password, :with => 'testtest'
click_button('user_submit')
# ... because this test fails
assert page.has_content?('Signed in successfully.')
end
end
... but I have no idea why. As you can see from the code, the user is being created in the database; I'm using the same approach to create the user as I did in seeds.rb.
If I run the test through the debugger, I can see the user in the database and verify that the page is loading. But still the authentication fails; I can verify this because if I change the assertion to test for the failure case, the test passes:
# verify that the authentication actually failed
assert page.has_content?('Invalid email or password.')
I'm used to Rails 2, & using Selenium for this sort of testing, so I suspect I'm doing something daft. Could someone please point me in the right direction here?
I was having the same issue and found a thread with a solution:
RSpec.configure do |config|
config.use_transactional_fixtures = false
config.before(:suite) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end
For the DatabaseCleaner stuff to work you'll need to include the database_cleaner gem. If you haven't used it before, you may need to rake db:test:prepare before rerunning your tests. I hope this works for you, too!
I've run into a similar problem before. Setting the password directly has some weird effects because it's supposed to be encrypted and stored with a salt--sometimes it works for me and other times it doesn't. I have a hard time remembering which specific cases were problematic. I'd recommend the following, in this order (for simplicity)
Verify that the password field is getting filled in properly and passed as the right param (not necessary if you're using Devise's autogenerated view and haven't touched it)
if your site can run in development mode (i.e. no log in bugs), then just boot it up and log in manually
If not, insert debugger as the first line in your sessions_controller. Then check params and make sure the password is correct and in params[:user][:password].
If you didn't override Devise's sessions_controller, then you can find your Devise path with bundle show devise. Then look for the create action within (devise path)/app/controllers/devise/sessions_controller.rb
Change your test setup to create a user through the web interface, to ensure the password gets set properly, then try running your test again
I had the same issue with a setup fairly similar to yours. In my case, switching to ActiveRecord sessions in the initializer solved the problem.
Additionally, make sure you call #user.skip_confirmation! if you are using the "confirmable" module in devise.
I'm trying to move to rspec for testing, but I can't get controller testing to work with user authentication. Testing a route that doesn't require a user works.
require 'spec_helper'
describe UddsController do
include Devise::TestHelpers
render_views
before (:each) do
#user = User.new(:email => "test#user.com", :username => "test123")
#user.roles << Role.find_or_create_by_name("admin")
#user.save
sign_in #user
end
it "should get index" do
get :index
response.should be_success
end
I just get
2) UddsController should get index
Failure/Error: response.should be_success
expected success? to return true, got false
# ./spec/controllers/udds_controller_spec.rb:21
I'm not using factory_girl. All the example fixes here and on google seem to. I don't see why what I've done wouldn't work. Error is useless in debugging.
I know this question has been asked to death. I've read every blog and forum I can find. I'm at a dead end.
It might be redirecting. be_success returns false for any non-2xx status codes (like 302). Try checking the value of response.status -- if it's redirecting, it probably means your authentication or authorization scheme is producing an unexpected result.
Try adding puts response.body before your assertion, and see what the page you are requesting says. If you can't figure it out from there, perhaps edit your question to include the output?
If you are using devise's "confirmable" module which makes users click a link in an email to confirm their address, then when you use "sign_in" in the tests you also have to fake email confirming. It looks something like this:
sign_in #user
#user.confim!