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.
Related
I was looking at the RailsGuides and I saw this example:
require 'test_helper'
class UserFlowsTest < ActionDispatch::IntegrationTest
test "login and browse site" do
# login via https
https!
get "/login"
assert_response :success
post_via_redirect "/login", username: users(:david).username, password: users(:david).password
assert_equal '/welcome', path
assert_equal 'Welcome david!', flash[:notice]
https!(false)
get "/articles/all"
assert_response :success
assert assigns(:articles)
end
end
I am confused about the variable "path" in the line assert_equal '/welcome', path. What is this exactly? Is this the url that would show up in your browser after we perform the post_via_redirect action right before it?
I looked into the helpers available in the integration tests, but the document didn't say anything about this "path" variable.
Additionally, I try it at my rails app and it works fine, but when I tried to call it at one of my controller tests, it gave me and undefined local variables or method error. So does it mean that the "path" variable is only available in the integration tests? What if I want to use it in the other types of tests?
path is the url of your current page.
path is request.url without the domain name.
In this test you've been redirected from '/login' to '/welcome'.
I am trying to test my Grape API, but I am receiving a 400 error in my tests, but when I run the action the test is supposed to test, I get a 201 HTTP response as expected. Not sure what is going on here. Below is the specific RSpec test, but you can view the whole project with the factories and the actual Grape API on GitHub at hackcentral/hackcentral. The test below is testing the POST create action on Alpha::Applications. (app/api/alpha/applications.rb)
describe 'POST #create' do
before :each do
#oauth_application = FactoryGirl.build(:oauth_application)
#token = Doorkeeper::AccessToken.create!(:application_id => #oauth_application.id, :resource_owner_id => user.id)
end
context "with valid attributes" do
it "creates a new application" do
expect{
post "http://api.vcap.me:3000/v1/applications?access_token=#{#token.token}", application: FactoryGirl.attributes_for(:application), :format => :json
} .to change(Application, :count).by(1)
end
it "creates a new application, making sure response is #201" do
post "http://api.vcap.me:3000/v1/applications", application: FactoryGirl.attributes_for(:application), :format => :json, :access_token => #token.token
response.status.should eq(201)
end
end
end
I don't understand why are you testing http://api.vcap.me an not localhost?
You usually test the app on the local enviroment. And this is not the right why to test if the server is working either.
Here is an example of how your test should look like.
https://github.com/dblock/grape-on-rails/blob/master/spec/api/ping_spec.rb from an example project
I'm using devise for authentication in a small web application, and I'm having a few problems writing some integration tests.
The tests are going to be simple, such as
login works with valid credentials
login rejected with invalid credentials
...
using the techniques that were described in the rails tutorial, but rather than against a home grown authentication system, I'm attempting to retrofit it against devise.
I can use the sign_in function without any problems, and I'm doing that in one or two of my controller tests, e.g.
require 'test_helper'
class mySimpleControllerTest < ActionController::TestCase
include Devise::TestHelpers
def setup
#user = User.create!(
:firstname => "ANOther",
:surname => 'Person',
:username=> 'aperson',
:email => 'aperson#example.com',
:password => 'pass123',
:password_confirmation => 'pass123'
)
sign_in #user
end
test "should get home" do
get :home
assert_response :success
assert_select "title", "TEST PAGE"
end
end
that works wonderfully well. The problem I have is my integration tests for testing the login functionality. I don't want both setup and teardown functions in there, since some tests will have to check against logged out behaviour, some against logged in, and others against reset password etc.
the following test is always is responding with invalid username and password, even though the passwords are correct. Eventually, I want this test to pass when the username or password is incorrect, but right now it responds this way regardless of whether it is or it isn't.
require 'test_helper'
class UsersLoginTest < ActionDispatch::IntegrationTest
test "should be redirected if root_path is called when logged out" do
get root_path
assert_response :redirect
end
test "login with invalid information" do
get new_user_session_path
assert_template 'devise/sessions/new'
post user_session_path, 'user[email]' => 'aperson#example.com', 'user[password]' => 'pass123'
...
I'm assuming the reason for this is because my test database doesn't contain a real user, in fact, the users table is currently empty. Which makes sense, since no user, it should respond with invalid username or password. However, if that is the case, how can I guarantee that the test database is populated with this default user when calling rake test?
After attempting several ways of getting this to work, I eventually moved all tests over to RSpec and Capybara, which will allow me to post data to forms.
I did test whether or not there was an issue with persistent users and this seemed to not be the case. The problem seemed to be that
post user_session_path, 'user[email]' => ...
didn't seem to actually post anything.
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]
How do you test redirect_to :back in rspec?
I get
ActionController::RedirectBackError:
No HTTP_REFERER was set in the request to this action, so redirect_to :back could not be called successfully. If this is a test, make sure to specify request.env["HTTP_REFERER"].
How do I go about setting the HTTP_REFERER in my test?
Using RSpec, you can set the referer in a before block. When I tried to set the referer directly in the test, it didn't seem to work no matter where I put it, but the before block does the trick.
describe BackController < ApplicationController do
before(:each) do
request.env["HTTP_REFERER"] = "where_i_came_from"
end
describe "GET /goback" do
it "redirects back to the referring page" do
get 'goback'
response.should redirect_to "where_i_came_from"
end
end
end
From the rails guide when requesting the request with the new request style:
describe BackController < ApplicationController do
describe "GET /goback" do
it "redirects back to the referring page" do
get :show,
params: { id: 12 },
headers: { "HTTP_REFERER" => "http://example.com/home" }
expect(response).to redirect_to("http://example.com/home")
end
end
end
If someone stumbles upon this and they're using request specs, you'll need to explicitly set the headers on the request you're making. The format of the test request depends on which version of RSpec you're using and if you can use keyword arguments instead of positional arguments.
let(:headers){ { "HTTP_REFERER" => "/widgets" } }
it "redirects back to widgets" do
post "/widgets", params: {}, headers: headers # keyword (better)
post "/widgets", {}, headers # positional
expect(response).to redirect_to(widgets_path)
end
https://relishapp.com/rspec/rspec-rails/docs/request-specs/request-spec
IMHO the accepted answer is a bit hacky. A better alternative would be to set the HTTP_REFERER to an actual url in your application and then expect to be redirected back:
describe BackController, type: :controller do
before(:each) do
request.env['HTTP_REFERER'] = root_url
end
it 'redirects back' do
get :whatever
response.should redirect_to :back
end
end
Redirecting to a random string constant feels as if it works by accident
You take advantage of rspec's built in functionality to express exactly what you wanted
You don't introduce and repeat magic string values
For newer versions of rspec, you can use expectations instead:
expect(response).to redirect_to :back
In regard to testing :back links in integration tests, I first visit a deadend page, that I think is not likely to be ever used as a link, and then the page I am testing. So my code looks like this
before(:each) do
visit deadend_path
visit testpage_path
end
it "testpage Page should have a Back button going :back" do
response.should have_selector("a",:href => deadend_path,
:content => "Back")
end
However this does have the flaw that if the link is really to the deadend_path, then the test will incorrectly pass.
request.env['HTTP_REFERER'] = '/your_referring_url'