Response object in Rspec 1.3.4 with Capybara - ruby-on-rails

I'm trying to use the following test to see if a login form correctly redirects:
it "should allow you to login" do
visit "/"
fill_in 'Email', :with => 'admin#example.com'
fill_in 'Pass', :with => 'password'
click_button 'Login'
response.should be_redirect
end
However, rspec fails with the message that response doesn't exist. I'm running a rails 2.3.4 app, so I have rspec 1.3.4 installed. How do I test for the response?

I assume this is an integration spec (what RSpec calls a 'request spec' in version 2)? If so, the response object is not available. For that, you'll need a controller spec, e.g.:
describe MyController do
it "should redirect" do
post :some_path, {:email => 'admin#example.com', :pass => 'password'}
response.should be_redirect
end
end
Or in your integration spec, test the expected path after a successful redirect. This works in RSpec 2, not sure about RSpec 1:
current_path.should == "/some_path"

I think you need to ask Capybara instead of the rails test helpers.
session.status_code.should == "301" # or 302
should do it.
Look at the examples here:
http://rubydoc.info/github/jnicklas/capybara/master/Capybara/Session#status_code-instance_method

Related

Capybara issue: #request must be an ActionDispatch::Request

I'm having problems making Capybara work with Rails. Just testing that suposedly interesting test thing. OK, in the attached code there are a couple of equivalent tests. The first one is made with shoulda-context + Test::Unit that comes with Rails. The second test is made with capybara and shoulda-context too.
require 'integration_test_helper'
class UsersTest < ActionDispatch::IntegrationTest
fixtures :all
context "signup" do
context "failure" do
setup do
#attr = { :name => "", :email => "", :password => "", :password_confirmation => "" }
end
should "not make a new user" do
assert_no_difference 'User.count' do
post_via_redirect "users", :user =>#attr # enviem les dades d'un nou usuari via create (POST /users)
assert_template 'users/new' # ens retorna a users/new, que significa que no s'ha creat l'usuari
assert_select "div#error_explanation" # comprovem que conte missatges d'error
end
end
should "not make a new user (capybara)" do
assert_no_difference 'User.count' do
visit '/signup'
fill_in 'Name', :with => #attr[:name]
fill_in 'Email', :with => #attr[:email]
fill_in 'Password', :with => #attr[:password]
fill_in 'Confirmation', :with => #attr[:password_confirmation]
click_button 'Sign Up!'
assert_template 'users/new' # ens retorna a users/new, que significa que no s'ha creat l'usuari
assert_select "div#error_explanation" # comprovem que conte missatges d'error
end
end
end
end
While the first one works OK, the capybara one throws this error message:
================================================================================
Error:
test: signup failure should not make a new user (capybara). (UsersTest):
ArgumentError: #request must be an ActionDispatch::Request
test/integration/users_test.rb:30:in `block (4 levels) in <class:UsersTest>'
test/integration/users_test.rb:23:in `block (3 levels) in <class:UsersTest>'
================================================================================
the required *integration_test_helper.rb* file is an accumulator of all suposed solutions I've found googling around and that don't work for me.
require 'test_helper'
require 'capybara/rails'
require 'database_cleaner'
# Transactional fixtures do not work with Selenium tests, because Capybara
# uses a separate server thread, which the transactions would be hidden
# from. We hence use DatabaseCleaner to truncate our test database.
DatabaseCleaner.strategy = :truncation
class ActionDispatch::IntegrationTest
# Make the Capybara DSL available in all integration tests
include Capybara::DSL
# Stop ActiveRecord from wrapping tests in transactions
self.use_transactional_fixtures = false
teardown do
DatabaseCleaner.clean # Truncate the database
Capybara.reset_sessions! # Forget the (simulated) browser state
Capybara.use_default_driver # Revert Capybara.current_driver to Capybara.default_driver
end
end
Has anybody a solution? Should I try another Integration frmawork such as webrat?
My setup is:
marcel#pua:~/Desenvolupament/Rails3Examples/ror_tutorial$ rake about
About your application's environment
Ruby version 1.9.2 (x86_64-linux)
RubyGems version 1.8.15
Rack version 1.3
Rails version 3.1.3
JavaScript Runtime therubyracer (V8)
Active Record version 3.1.3
Action Pack version 3.1.3
Active Resource version 3.1.3
Action Mailer version 3.1.3
Active Support version 3.1.3
Middleware ActionDispatch::Static, Rack::Lock, #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x00000002b9bac0>, Rack::Runtime, Rack::MethodOverride, Rails::Rack::Logger, ActionDispatch::ShowExceptions, ActionDispatch::RemoteIp, Rack::Sendfile, ActionDispatch::Reloader, ActionDispatch::Callbacks, ActiveRecord::ConnectionAdapters::ConnectionManagement, ActiveRecord::QueryCache, ActionDispatch::Cookies, ActionDispatch::Session::CookieStore, ActionDispatch::Flash, ActionDispatch::ParamsParser, ActionDispatch::Head, Rack::ConditionalGet, Rack::ETag, ActionDispatch::BestStandardsSupport
Application root /mnt/dropbox/Dropbox/DESENVOLUPAMENT/Rails3Examples/ror_tutorial
Environment development
Database adapter sqlite3
Database schema version 20120127011330
Also
shoulda-context (1.0.0)
capybara (1.1.2)
Thanks
You're mixing up your test types and attempting to assert a template in the wrong type of test. You should only be asserting templates within your Functional tests, where you're just directly testing a controller and not actually simulating a user interaction.
Capybara is meant specifically for Integration testing, which is essentially running tests from the viewpoint of an end-user interacting with a browser. In these tests, you should not be asserting templates because an end-user can't see that deep into your application. What you should instead be testing is that an action lands you on the correct path.
current_path.should == new_user_path
page.should have_selector('div#error_explanation')
See "The DSL" section in Capybara's README on git: https://github.com/jnicklas/capybara
Official explanation for your issue: https://github.com/jnicklas/capybara/issues/240
For my completeness, because i'll know that I'll come back to this link over and over:
Those using test unit and capybara this is a good primer too: from techiferous.
Notice the use of assert page.has_content?("something")
This is good to use as well as assert_equal some_path, current_path for testing routes.
Is it the most complete, don't know, but you don't need much more.
Thanks for your tips #Ryan. I was trying to figure out how to translate some RSpec Integration test examples from http://ruby.railstutorial.org/chapters/sign-up#sec:rspec_integration_tests into Test::Unit + Capybara. The original RSpec Integration test was
it "should not make a new user" do
lambda do
visit signup_path
fill_in "Name", :with => ""
fill_in "Email", :with => ""
fill_in "Password", :with => ""
fill_in "Confirmation", :with => ""
click_button
response.should render_template('users/new')
response.should have_selector("div#error_explanation")
end.should_not change(User, :count)
end
end
So, after your answer, I supose that the original example should not contain response.should render_template('users/new')

How to test file download with Rails, Paperclip and RSpec request spec?

I have a request spec that is trying to test file download functionality in Rails 3.1 for me. The spec (in part) looks like this:
get document_path(Document.first)
logger(response.body)
response.should be_success
It fails with:
Failure/Error: response.should be_success
expected success? to return true, got false
But if I test the download in the browser, it downloads the file correctly.
Here's the action in the controller:
def show
send_file #document.file.path, :filename => #document.file_file_name,
:content_type => #document.file_content_type
end
And my logger gives this information about the response:
<html><body>You are being redirected.</body></html>
How can I get this test to pass?
Update:
As several pointed out, one of my before_filters was doing the redirect. The reason is that I was using Capybara to login in the test, but not using it's methods for navigating around the site. Here's what worked (partially):
click_link 'Libraries'
click_link 'Drawings'
click_link 'GS2 Drawing'
page.response.should be_success #this still fails
But now I can't figure out a way to test the actual response was successful. What am I doing wrong here.
Most likely, redirect_to is being called when you run your test. Here's what I would do to determine the cause.
Add logging to any before filters that could possibly run for this action.
Add logging at several points in the action itself.
This will tell you how far execution gets before the redirect. Which in turn will tell you what block of code (probably a before_filter) is redirecting.
If I had to take a guess off the top of my head, I'd say you have a before_filter that checks if the user is logged in. If that's true, then you'll need to make sure your tests create a logged-in session before you call the login-protected action.
I was getting the same redirect until I realized that my login(user) method was the culprit. Cribbed from this SO link, I changed my login method to:
# file: spec/authentication_helpers.rb
module AuthenticationHelpers
def login(user)
post_via_redirect user_session_path, 'user[email]' => user.email, 'user[password]' => user.password
end
end
In my tests:
# spec/requests/my_model_spec.rb
require 'spec_helper'
require 'authentication_helpers'
describe MyModel do
include AuthenticationHelpers
before(:each) do
#user = User.create!(:email => 'user#email.com', :password => 'password', :password_confirmation => 'password')
login(#user)
end
it 'should run your integration tests' do
# your code here
end
end
[FWIW: I'm using Rails 3.0, Devise, CanCan and Webrat]

rails routes undefined in rspec before(:all) filter?

I'm setting up some integration tests using capybara and rspec.
In a single test, this works:
describe "SIGN IN, POST post" do
it "redirects to /posts after creating a new post" do
visit new_artist_session_path
fill_in 'Email', :with => 'vargas#vargas.com'
fill_in 'Password', :with => 'password'
click_link_or_button 'artist_submit'
visit "/artists/vargas/posts"
page.should have_content("Upload")
click_button 'Upload'
URI.parse(current_url).path.should == "/artists/vargas/posts"
end
end
However, I want to move the "sign_in" portion to a before(:all) filter block so that I can DRY up my tests. However it seems that within the before(:all) block, the same code gives this error:
Failure/Error: visit new_artist_session_path
NameError:
undefined local variable or method `new_artist_session_path' for #<RSpec::Core::ExampleGroup::Nested_1:0x0000010399e388>
It seems that the routes url helpers are not available from within the before block? How do I remedy this?
it appears that the paths helpers are available only between the "it" and "do". Just not in the before(:all) method. So I ended up just creating a method for signing in and included it in each test that needed it.

Rails by Example Tutorial Request Examples and Capybara

In the Rails on Rails Tutorial by Michael Hartl, the Request Examples make assertions on the response. I installed the cabybara and steak gem to create acceptance tests. After installing capybara, the requests examples are configured to use capybara. capybara examples have a different syntax and don't recognize the response.
How do I reset the Request Examples to run as RSpec example?
Test Error:
4) Users signup failure should not make a new user
Failure/Error: click_button
wrong number of arguments (0 for 1)
# ./spec/requests/users_spec.rb:13
# ./spec/requests/users_spec.rb:7
Request Example
describe "failure" do
it "should not make a new user" do
lambda do
visit signup_path
fill_in "Name", :with => ""
fill_in "Email", :with => ""
fill_in "Password", :with => ""
fill_in "Confirmation", :with => ""
click_button
response.should render_template('users/new')
end.should_not change(User, :count)
end
end
This might have something to do with rspec-rails request_example_group.rb.
request_example_group.rb
If the capybara lines are commented out, then the Request Examples are not defaulted to capybara.
capybara do
include Capybara
end
In the Acceptance examples, include capybara.
require File.expand_path(File.dirname(__FILE__) + '/acceptance_helper')
feature "This Is My First Feature", %q{
In order to ...
As a ...
I want to ...
} do
include Capybara
scenario "Scenario name" do
visit signup_path
fill_in "Name", :with => ""
fill_in "Email", :with => ""
fill_in "Password", :with => ""
fill_in "Confirmation", :with => ""
click_button "Sign up"
page.has_content?('welcome to the sample app')
end
end
I ended up forking rspec-rails and commenting out the capybara lines.
In the Gemfile, the gem installed from the fork.
Why bother? We want both white box and black box testing.
As you correctly point out in your answer, rspec-rails will include Capybara if it is installed. I think they're assuming that it never hurts to include Capybara, just in case you need it. Is that a problem for you? In other words, what specifically is the Capybara DSL conflicting with?
(This might be also be worth bringing up on the RSpec mailing list.)
As for the error you posted, it seems that you are simply missing an argument to the click_button method.

Rspec - Rails - How to follow a redirect

Does anyone know how to make rspec follow a redirect (in a controller spec)? (e.g test/unit has follow_redirect!)
I have tried "follow_redirect!" and "follow_redirect" but only get
undefined method `follow_redirect!' for #<Spec::Rails::Example::ControllerExampleGroup::Subclass_1:0xb6df5294>
For example:
When I create an account the page is redirected to accounts page and my new account should be at the top of the list.
it "should create an account" do
post :create, :name => "My New Account"
FOLLOW_REDIRECT!
response.code.should == "200"
accounts = assigns[:accounts]
accounts[0].name.should == "My New Account"
end
But FOLLOW_REDIRECT! needs to be changed to something that actually works.
I think this is the default behavior for rspec-rails controller tests, in the sense that you can set an expectation on the response status and/or path, and test for success.
For example:
it "should create an account" do
post :create
response.code.should == "302"
response.should redirect_to(accounts_path)
end
You can access the redirect location with
response.headers['Location']
you could then request that directly.
If you want to test the redirect you are moving outside of the rspec-rails domain.
You can use Webrat or some other integration-test framework to test this.
The easiest way to solve this without resorting to integration testing is probably to mock out the method that is causing the redirect.
The spec is out of scope, if you want to follow a redirect use request spec, the equivalent of integration test in Test::Unit.
In request specs follow_redirect! works as well as in Test::Unit.
Or if you want to redirect inmediately use _via_redirect as suffix for the verb, example:
post_via_redirect :create, user: #user
Try to use integration/request tests. They are using web-like acces through routing to controllers.
For example:
I have for Rails 2 app in file /spec/integration/fps_spec.rb
require 'spec_helper'
describe "FinPoradci" do
it "POST /fps.html with params" do
fp_params={:accord_id => "FP99998", :under_acc => "OM001", :first_name => "Pavel", :last_name => "Novy"}
fp_test=FinPoradce.new(fp_params)
#after create follow redirection to show
post_via_redirect "/fps", {:fp => fp_params}
response.response_code.should == 200 # => :found , not 302 :created
new_fp=assigns(:fp)
new_fp.should_not be_empty
new_fp.errors.should be_empty
flash[:error].should be_empty
flash[:notice].should_not be_empty
response.should render_template(:show)
end
end
and it works. Until you want to send headers (for basic http authorization).
env={'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials(user,password)}
post_via_redirect "/fps", {:fp => fp_params}, env
is fine for create, but after redirection it returns 401 and needs new authorization.
SO I have to split it in 2 tests: creation and show on result of creation.
For RSpec / Capybara + Rails
response_headers['Location']
But it works only if there is no delay before redirect.
If it is there, then it's harder to follow the logic.

Resources