I've got a problem testing the following controller code:
def publish
if #article.publish
flash[:notice] = "Article '#{#article.title}' was published."
else
# This is not tested
flash[:error] = "Error publishing article."
end
redirect_to :action => :index
end
Where the function publish looks like that:
def publish
self.toggle!(:is_published)
end
Function toggle! is atomic and in theory will fail only when there is a problem with database (in practice I can find number of scenarios where the error should be detected because someone breaks the publish method implementation). How can I test in Cucumber that the correct message is shown in case of error?
Here, check these out:
http://blog.flame.org/2009/11/19/how-i-test-ruby-on-rails-with-rspec-and-cucumber
it "tells me to bugger off (not admin)" do
login_user
users = make_users
get :index
flash[:error].should match "You must be an administrator to access this page."
response.should redirect_to(root_path)
end
Hope this helps :)
Related
In my rails app I am trying to do a basic test which tests that after a folder creation, the user is redirected to the folder show page. I have implemented this code and it works when I do it all through the browser however the test fails and gives me this error:
Failure/Error: response.should redirect_to folder_path(folder)
ArgumentError:
comparison of Array with Array failed
I opened this test block in pry and this was what it also said:
ArgumentError: comparison of Array with Array failed
from /Users/XXXX/.rvm/gems/ruby-2.1.5/gems/actionpack-4.2.0/lib/action_dispatch/journey/formatter.rb:43:in `sort'
Anyone know why this error is occuring?
Here is the test block for reference:
context "with valid inputs" do
let(:alice) { Fabricate(:user) }
let(:folder) { Fabricate.attributes_for(:folder) }
before do
login_user(alice)
post :create, folder: folder
end
it "redirects to the folder show page" do
response.should redirect_to folder_path(folder)
end
and the corresponding controller code:
def create
new_folder(folder_params)
if #folder.save
flash[:success] = "Folder Created"
redirect_to folder_path(#folder)
else
flash[:danger] = "An Error occured."
render :new
end
end
As said in the comments, Fabricate.attributes_for is not creating an instance of a model but a hash of all the attributes of your model (without the id attributes). So when you pass folder to folder_path, rails is looking for the id of a Hash.
Here is an way to test your redirection :
it "redirects to the folder show page" do
response.should redirect_to folder_path(Folder.last)
end
Be sure also that the last folder is the one you want :
it "creates a folder" do
Folder.last.attributes.except(:id).each do |key, value|
folder[key].should eq(value)
end
end
So I am currently writing a test for a controller in an existing controller that just didn't have one before. What I want to test is a redirect that happens when someone is not allowed to edit something vs someone that is allowed to edit it.
the controller action being edit
def edit
if !#scorecard.reviewed? || admin?
#company = #scorecard.company
#custom_css_include = "confirmation_page"
else
redirect_to :back
end
end
So if a scorecard has been reviewed then only an admin can edit that score.
The routes for that controller..
# scorecards
resources :scorecards do
member do
get 'report'
end
resources :inaccuracy_reports, :only => [:new, :create]
end
and finally the test
require 'spec_helper'
describe ScorecardsController do
describe "GET edit" do
before(:each) do
#agency = Factory(:agency)
#va = Factory(:va_user, :agency => #agency)
#admin = Factory(:admin)
#company = Factory(:company)
#scorecard = Factory(:scorecard, :level => 1, :company => #company, :agency => #agency, :reviewed => true)
request.env["HTTP_REFERER"] = "/scorecard"
end
context "as a admin" do
before(:each) do
controller.stub(:current_user).and_return #admin
end
it "allows you to edit a reviewed scorecard" do
get 'edit', :id => #scorecard.id
response.status.should be(200)
end
end
context "as a va_user" do
before(:each) do
controller.stub(:current_user).and_return #va
end
it "does not allow you to edit a reviewed scorecard" do
get 'edit', :id => #scorecard.id
response.should redirect_to :back
end
end
end
end
so a va when trying to edit a reviewed score will be redirected back, where an admin won't.
but when running this through rspec I get
ScorecardsController
GET edit
as a admin
allows you to edit a reviewed scorecard
as a va_user
does not allow you to edit a reviewed scorecard (FAILED - 1)
Failures:
1) ScorecardsController GET edit as a va_user does not allow you to edit a reviewed scorecard
Failure/Error: response.should redirect_to :back
Expected response to be a redirect to </scorecard> but was a redirect to <http://test.host/>
# ./spec/controllers/scorecards_controller_spec.rb:33:in `block (4 levels) in <top (required)>'
Finished in 0.48517 seconds
2 examples, 1 failure
so I don't know if its working or not since I set the request.env["HTTP_REFERER"] = "/scorecard" as the place that should be the :back as it where. or am I missing the idea all together looking at httpstatus there are the 300 responses that I could use but I wouldn't know where to start?
any help would be awesome
EDIT
I could test it by doing it like this
...
response.status.should be(302)
but I got the idea from this question and it sounds like this could be powerful as it specifies the url redirected to.
Anyone have a working test like this?
To make the test more readable you can do this:
(rspec ~> 3.0)
expect(response).to redirect_to(action_path)
This line has problem
response.should redirect_to :back
The logic is not correct. You should expect #edit to redirect to :back path you set before, which is /scorecard. But you set :back here. In the context of Rspec, :back should be empty at each example.
To revise, just set it as
response.should redirect_to '/scorecard'
For testing if redirects happened, with no matching route
(just to test redirection, i used this when route is too long :D ).
You can simply do like:
expect(response.status).to eq(302) #redirected
In my case, it was not returning a response. If you end up in this situation, you can do:
expect(page.current_path).to eql('expected/path')
I want to write controller test related password update test. I find authenticated person in the controller's first line with where condition. How can I write test related this line. I couldn't yiled any idea.
ChangePasswordsController
def update
person = Person.where(_id: session[:user_id]).first
identity = Identity.where(_id: person.user_id).first
unless params[:new_password] != params[:new_password_confirmation]
identity.password = params[:new_password].to_s
identity.password_confirmation = params[:new_password].to_s
identity.save
redirect_to root_url, :notice => "Password has been changed." + person.user_id
else
redirect_to :back, :alert => "Password & password confirmation are not match"
end
end
ChangePasswordsController Test
describe ChangePasswordsController do
setup do
request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:identity]
#auth=request.env["omniauth.auth"]
end
it "should have edit action" do
get :edit
assert_response :success
end
it "should find person" do
...
end
it "should find identity" do
...
end
end
I don't think you should write a test whether the where finds a person or not. In this context you'd better just check whether the update method calls the find method on Person with the given parameter. Use something like this:
Person.should_receive(:find).with(id: <the userid you've setup for the test>) {[Person.new]}
It should be able to also check first but I can't remember, something to do with chainstubbing.
This following Controller test is failing, and I can't figure out why:
describe "GET 'index'" do
before(:each) do
#outings = FactoryGirl.create_list(:outing, 30)
#user = FactoryGirl.create(:user)
end
it "should be successful" do
get :index
response.should be_success
end
end
Rspec offers up the (rather unhelpful) error:
Failure/Error: response.should be_success
expected success? to return true, got false
Here's the code for the actual Controller, too:
def index
if #user
#outings = Outing.where(:user_id => #user.id)
#outing_invites = OutingGuest.where(:user_id => #user.id)
else
flash[:warning] = "You must log in to view your Outings!"
redirect_to root_path
end
end
Anyone have an idea what's causing my test to fail? I assume it may have something to do with the conditional in the Outing Controller, but I have no idea what a passing test would look like...
You're confusing instance variables between two separate classes - the controller is its own class and the specification is its own class. They don't share state. You could try this simple example to get a better understanding...
def index
// obvious bad code, but used to prove a point
#user = User.first
if #user
#outings = Outing.where(:user_id => #user.id)
#outing_invites = OutingGuest.where(:user_id => #user.id)
else
flash[:warning] = "You must log in to view your Outings!"
redirect_to root_path
end
end
I'll guess that FactoryGirl.create_list(:outing, 30) doesn't create an outing associating the first user with the outing since you're creating the user after you create the outing so your Outing.where will fail as well.
Its important to understand that when you are including the database in your test stack the database needs to contain the data in the way the test expects. So if your controller is querying for outings belonging to a specific user your spec needs to setup the environment such that the user the controller will retrieve (in this case, the terrible line with User.first from my example) will also have the outings associated with it that the specification is expecting.
In Michael Hartl's (wonderful) Rails Tutorial I'm getting an unexpected Rspec test failure of the form:
"Expected response to be a redirect to
<http://test.host/signin> but was a
redirect to
<http://test.host/signin?notice=Please+sign+in+to+access+this+page.>."
(Find this in Section 10.3.) So, from the very error, you can se that the server is redirecting to the appropriate page except that there is an additional notice to "Please sign in". The code of the test looks like this:
describe "GET 'index'" do
describe "for non-signed-in users" do
it "should deny access" do
get :index
response.should redirect_to(signin_path)
flash[:notice].should =~ /sign in/i
end
end
...
Am I doing something wrong here? How best should I fix this?
Update:
If I change the code to
response.should redirect_to('http://test.host/signin?notice=Please+sign+in+to+access+this+page.')
then I get and actual Ruby error
Failure/Error: flash[:notice].should
=~ /sign in/i
expected: /sign in/i,
got: nil (using =~)
Resolution
The problem was an omitted comma in my deny_access function:
redirect_to signin_path :notice => "Please sign in to access this page."
should have been
redirect_to signin_path, :notice => "Please sign in to access this page."
This fixed it. Now, the side affect of this fix was interesting wasn't it? Does this imply that signin_path is itself a function that can take a hash or arguments to be appended to the end of the path? Weird, wild, and wonderful.
What do your authentication methods look like? I haven't seen the flash notice added to the query string like that before :/ It leads me to believe your methods might be incorrect. It should be something like:
def deny_access
redirect_to signin_path, :notice => "Please sign in to access this page."
end
OR
def deny_access
flash[:notice] = "Please sign in to access this page."
redirect_to signin_path
end