I have an issue using my session stored return_to URL in my integration tests.
Because my controller can be accesses from different places I store the referer in the session on the new action and redirect to it in my create action.
cards_controller.rb:
class CardsController < ApplicationController
...
def new
#card = current_user.cards.build
session[:return_to] ||= request.referer
end
def create
#card = current_user.cards.build(card_params)
if #card.save
flash[:success] = 'Card created!'
redirect_to session.delete(:return_to) || root_path
else
render 'new', layout: 'card_new'
end
end
...
end
As I only use the create action in my test I would like to set the session variable in the integration test as I do in my unit test but it doesn't work. I always get redirected to the root path.
cards_interface_test.rb:
class CardsInterfaceTest < ActionDispatch::IntegrationTest
test 'cards interface should redirect after successful save' do
log_in_as(#user)
get cards_path
assert_select 'a[aria-label=?]', 'new'
name = "heroblade"
session[:return_to] = cards_url
assert_difference 'Card.count', 1 do
post cards_path, card: { name: name, icon: 'white-book', color: 'indigo', contents: 'subtitle | Rogue feature'}
end
assert_redirected_to cards_url
follow_redirect!
assert_match name, response.body
assert_select 'td', text: name
end
end
The test fails on the assert_redirected_to line.
I tried to call get new_card_path first but made no difference and now I'm a little bit lost. I don't know if this should basically work but I made a tiny mistake or if I try to do something completely against best practices and should refactor all my interface tests to use something like Selenium or similar tools.
I tried as well to provide the session variable as part of the request like the rails guide describes for functional tests with no effects:
post cards_path, {card: { name: name, icon: 'white-book', color: 'indigo', contents: 'subtitle | Rogue feature' }}, {'return_to' => cards_url}
I don't know if manually setting session is possible in integration tests (guess rather not) but you should be able to set the referer because it's just a normal HTTP header. Headers can be passed as the 3rd parameter to the request method helper (get etc.) in integration tests.
So, I think you should first call the new action with the referer header set (so that it gets into the session) and then the create action should work including the redirect.
class CardsInterfaceTest < ActionDispatch::IntegrationTest
test 'cards interface should redirect after successful save' do
log_in_as(#user)
# visit the 'new' action as if we came from the index page
get new_card_path, nil, referer: cards_url
assert_difference 'Card.count', 1 do
post cards_path, card: { name: name, icon: 'white-book', color: 'indigo', contents: 'subtitle | Rogue feature'}
end
assert_redirected_to cards_url
# ...
end
end
First we try get the new action with the referer set as if we came from the index page (so that the referer can get into the session). The rest of the test stays the same.
Related
A User can submit a form to create a Scheduling from more than one place and should subsequently be returned to the original place of form submission.
My Schedulings Controller, therefore relies on request.referrer. It works as intended in development and looks something like this:
class SchedulingsController < ApplicationController
#stuff not relevant to the question removed
def create
#scheduling = current_user.schedulings.build(scheduling_params)
if #scheduling.save
redirect_to request.referrer
flash[:success] = "scheduled!"
else
# do something not relevant to the question
end
end
end
I wish to run a Rails integration test to test this. request.referrer however appears to always be nil in the Test environment, so with help from an answer here I have worked around this by including a headers hash in the Post request like this:
class SchedulingsCreateTest < ActionDispatch::IntegrationTest
test "valid input for new scheduling" do
assert_difference 'Scheduling.count', 1 do
post schedulings_path, params: { scheduling: { start_time: Time.now },
headers: { "HTTP_REFERER" => "http://example.com/workouts" }
end
follow_redirect!
assert_template 'workouts/index'
assert_not flash.empty?
end
This test fails at assert_not flash.empty?
What is happening, why is the flash assessed as empty?
I note that if, in the controller, I change redirect_to request.referrer to redirect_to workouts_path (or workouts_url), the test passes.
Thanks for your interest and any help.
Daniel
I have broadly solved my own question by changing the value in the headers hash from:
"http://example.com/workouts" to "http://www.example.com/workouts"
the former is shown in an answer here and is sourced directly from the Rails Guide, however working in Rails 5, I found the “www” is necessary.
I got to the bottom of this with an extra line in my test:
assert_redirected_to workouts_url before the follow_redirect!, which failed and identified the url discrepancy.
Daniel
I'm getting something really weird when I execute my tests with rails test.
I'm trying to understand and create my first controller tests and I get something unexpected.
I get this error in the console:
F
Failure:
FeedbacksControllerTest#test_should_create_resource [/Users/Daniel/GitHub/haeapua-rails/test/controllers/feedbacks_controller_test.rb:15]:
Expected response to be a <2XX: success>, but was a <302: Found> redirect to <http://www.example.com/>
Response body: <html><body>You are being redirected.</body></html>
here is my test
test "should create resource" do
assert_difference 'Feedback.count', 1 do
post feedbacks_url, params: { feedback: { email: "me#myemail.com", summary: "my feedback", rating: 5 } }
end
assert_response :success
assert_redirected_to root_url
end
here is my controller
class FeedbacksController < ApplicationController
# GET /feedbacks/new
def new
#feedback = Feedback.new
# #feedback.user = current_user if user_signed_in?
end
# POST /feedbacks
def create
#feedback = Feedback.new(feedback_params)
# #feedback.user = current_user if user_signed_in?
if #feedback.save
# flash[:info] = "We got it, thanks. Someone will contact you ASAP once we read it"
redirect_to root_url
else
render :new
end
end
private
def feedback_params
params.require(:feedback).permit(:email, :summary, :rating)
end
end
When I put a "puts root_url" in my feedback controller just before the redirect_to I get the value "http://www.example.com/". I search for root_url in my whole code and the only part I have it is in the Controller and in my routes root to: 'static_pages#concept'
I'm using devise, do you think it could be because of that? I'm a bit lost and don't know where to start looking!
It's a weird quirk but rails has two types of url_helpers for routes. something_url and something_path. The first is an absolute path, http://www.awesome.com/something) the second is the relative path (/someurl).
Unless you have a domain name set explicitly on your test environment, use the _path helpers in your tests. If you want to use the _url helpers then you need to configure a base url in your application's test environment (config/environments/test.rb)
Maybe some default config is setting this value. https://github.com/teamcapybara/capybara/blob/master/lib/capybara.rb#L452
Capybara.configure do |config|
# ...
config.default_host = "http://www.example.com"
# ...
end
You have to override this setting in your project config.
If it's not capybara it could be rails issue ? From the input of this thread https://github.com/rspec/rspec-rails/issues/1275 I would try something like this
# config/environment/test.rb
config.action_controller.default_url_options = {
host: '0.0.0.0:3000' # or whatever your host is
}
In the thread I found:
# putting this into initializer of test framework
[ApplicationController, ActionController::Base].each do |klass|
klass.class_eval do
def default_url_options(options = {})
{ :host => "example.com" }.merge(options) # or localhost:8888 or 0.0.0.0:3000 or whatever your server is
end
end
end
I'm really stuck here. I have a Language model, that gets updated in this method:
def update
#language = Language.find(params[:id])
if #language.update_attributes(language_params)
flash[:success] = 'Language information updated.'
redirect_to #language
else
#skill_errors = #language.errors
render 'edit'
end
end
The intended behaviour for a successful update reproduces when I run it on my local server, object gets updated, flash appears and redirect to #language happens.
In the test, however, I only get the 200: Success response, and the object doesn't get updated. Here's the test code:
test 'should allow update when logged in as admin user' do
sign_in(#admin)
patch language_path(#ruby_language), params: { language: { name: 'Test'} }
assert_not flash.empty?
assert_redirected_to #ruby_language
#ruby_language.reload
assert_equal 'Test', #ruby_language.name
end
#admin and #ruby_language are defined in fixtures. All the asserts in this test fail, including the last one, with reload. My guess is that there might be some routing glitch caused by my usage of Devise and/or Kaminari gems? On the other hand, my Language routes are very simple:
resources :languages, concerns: :paginatable (the concern is here for human-readable URL formatting). Please, keep in mind that everything works as intended, only tests fail for some reason... Thanks in advance!
It turned out I was simply missing a required parameter in my update hash. Used Arun Kumar's tip of adding puts #response.body after patch.
I was looking into some rspec testing lately and I was wondering how to properly test controllers. My controller is fairly simple so it shouldn't be something too hard:
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
# GET /users
def index
#q = User.search(params[:q])
#users = #q.result(distinct: true)
#q.build_condition if #q.conditions.empty?
#q.build_sort if #q.sorts.empty?
end
# GET /users/1
def show
end
# GET /users/new
def new
#user = User.new
end
# GET /users/1/edit
def edit
end
def archive
#q = User.search(params[:q])
#users = #q.result(distinct: true)
#q.build_condition if #q.conditions.empty?
#q.build_sort if #q.sorts.empty?
end
# POST /users
def create
#user = User.new(user_params)
if #user.save
redirect_to users_path, notice: 'Student was successfully added.'
else
render action: 'new'
end
end
# PATCH/PUT /users/1
def update
if #user.update(user_params)
redirect_to #user, notice: 'Student information was successfully updated.'
else
render action: 'edit'
end
end
# DELETE /users/1
def destroy
#user.destroy
redirect_to users_url, notice: 'Student information was successfully deleted.'
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def user_params
params.require(:user).permit(:firstName, :lastName, :email, :dateOfBirth, :notes, :sex, :archive, :category => [])
end
end
So far I have written 2-3 tests but I am not sure if they even do anything:
describe 'GET #index' do
it "displays all users" do
get :index
response.should be_redirect
end
end
describe 'GET #new' do
it "creates a new user" do
get :new
response.should be_redirect
end
end
I tried doing the same for edit and show but they didn't work and I am not sure why (because as I said, I don't know what I am doing).
Could anyone give me a few test examples for these methods or could redirect me to an rspec guide for rails4?
Are you expecting the controller #index action to redirect? Because that wouldn't be typical.
I would
describe 'GET #index' do
get 'index'
it {expect(response).to be_success)}
end
This line...
it "displays all users" do
in a controller spec makes me wonder if your confusing controller and request specs. I did this when I first got running with testing. "Displaying all users" sounds like a request spec to me. Testing if a page redirects or response status codes is more akin to controller specs.
I found http://betterspecs.org/ to be a really helpful resource in understanding testing better.
RE: WHAT to test
This worked for me but results may vary.
Controller Specs - Don't test controllers
Controllers should be skinny so you're just testing whether Rails is working. e.g. an index action may contain #users = User.all or similar and very little else. What is there to test there? Nothing. If you have lots of code in your controller actions then it probably shouldn't be there. Move it out to the models. Remember: Fat models, skinny controllers. This is an example of how testing creates better code. I have very few controller specs and I think nearly all of them are double checking authorisation to pages. I only use them where there's code in the controller. Here's an example:
context "Non admin signed in" do
before(:each) do
sign_in user
controller.stub!(:current_user).and_return(user)
end
it {subject.current_user.should_not be_nil}
it "deny non admin access to index" do
sign_in user
get 'index'
expect(response).to render_template("pages/access_denied")
end
end
Request Specs Test what you would test in a browser (20% of tests)
Imagine that you weren't doing RSpec testing. If you're like me then this is not too hard to imagine. How would you test the thing you want to build? Chances are that the first thing you'd do is load up a browser and see if something is on the page that you were expecting. That IS a request spec. It's that simple. Request specs are the automated ways of loading up a browser, clicking on a few buttons and checking what happened. Whatever it is your checking in the browser... check that same thing using Capybara. If it has Javascript on the page then you'll need Webkit or Selenium on top of Capybara to push the buttons as you would. With selenium you actually see the browser window pop up on the desktop as if a mysterious gremlin had taken control of your keyboard. Don't test anything in a request spec that you wouldn't be testing manually in a browser. That means don't check the state of other models in the database. Request specs are what the user can see. If you can't see it, don't test it.
Model specs - Test what you would test in the console (80% of tests)
Before I became a good TDD/BDD boy I found I spent a lot of time loading up irb or console and making models and doing X to see if Y would happen. Automate that thing. That's a model spec. When your request spec fails (which it should at first if it's doing anything useful) then drop down into the model spec. A failing request spec might be:
it {expect(page.find('#login_box')).to have_content 'Logged in as Kevin Monk'}
from
no method full_name for instance of User
And if you weren't a TDD good boy you might load up the console and find what was happening with the full_name method.
> rails console
$> kevin = User.find(1)
$> kevin.full_name
And then visually check that you get the full name baack but this should be done as a model spec.
I hope that helps. A problem I've had with a lot of books on testing is that the authors tend to be such experts and therefore don't appreciate that us mortals really need to understand the basic premise of what it is your supposed to be testing.
you have a typo in your spec code , you have to change respone, for response
I think that´s the problem
you can find more information in about test controllers in
https://www.relishapp.com/rspec/rspec-rails/docs/controller-specs
regards
I have a controller create action that creates a new blog post, and runs an additional method if the post saves successfully.
I have a separate factory girl file with the params for the post I want to make. FactoryGirl.create calls the ruby create method, not the create action in my controller.
How can I call the create action from the controller in my RSpec? And how would I send it the params in my factory girl factories.rb file?
posts_controller.rb
def create
#post = Post.new(params[:post])
if #post.save
#post.my_special_method
redirect_to root_path
else
redirect_to new_path
end
end
spec/requests/post_pages_spec.rb
it "should successfully run my special method" do
#post = FactoryGirl.create(:post)
#post.user.different_models.count.should == 1
end
post.rb
def my_special_method
user = self.user
special_post = Post.where("group_id IN (?) AND user_id IN (?)", 1, user.id)
if special_post.count == 10
DifferentModel.create(user_id: user.id, foo_id: foobar.id)
end
end
end
Request specs are integration tests, using something like Capybara to visit pages as a user might and perform actions. You wouldn't test a create action from a request spec at all. You'd visit the new item path, fill in the form, hit the Submit button, and then confirm that an object was created. Take a look at the Railscast on request specs for a great example.
If you want to test the create action, use a controller spec. Incorporating FactoryGirl, that would look like this:
it "creates a post" do
post_attributes = FactoryGirl.attributes_for(:post)
post :create, post: post_attributes
response.should redirect_to(root_path)
Post.last.some_attribute.should == post_attributes[:some_attribute]
# more lines like above, or just remove `:id` from
# `Post.last.attributes` and compare the hashes.
end
it "displays new on create failure" do
post :create, post: { some_attribute: "some value that doesn't save" }
response.should redirect_to(new_post_path)
flash[:error].should include("some error message")
end
These are the only tests you really need related to creation. In your specific example, I'd add a third test (again, controller test) to ensure that the appropriate DifferentModel record is created.