i have created an rspec test like :
it "should redirect to '/tavern' with an error if user already has a tavern quest" do
user = mock('User')
user.stub(:has_tavern_quest).and_return(true)
post :new_quest, :quest_type => 3
flash[:error].should_not be_nil
response.should redirect_to tavern_path
end
Then, i wrote the controller part :
# check if user already has a tavern quest
if current_user.has_tavern_quest?
flash[:error] = 'You already have a quest to finish !'
redirect_to tavern_path and return
end
And the model part :
def has_tavern_quest?
TavernQuest.exists?(self.id)
end
I would expect that the test succeeds, now but i get :
1) TavernController POST '/quest/' to get a new quest of quest_type == 3 should redirect to '/tavern' with an error if user already has a tavern quest
Failure/Error: flash[:error].should_not be_nil
expected: not nil
got: nil
# ./spec/controllers/tavern_controller_spec.rb:29
Do i have a mistake somewhere ?
THE MACRO FOR LOGIN USER :
module ControllerMacros
def login_user
before(:each) do
#request.env["devise.mapping"] = :user
#user = Factory.create(:user)
sign_in #user
end
end
end
Untested:
it "should redirect to '/tavern' with an error if user already has a tavern quest" do
controller.stub_chain(:current_user,:has_tavern_quest?).and_return(true)
post :new_quest, :quest_type => 3
flash[:error].should_not be_nil
response.should redirect_to tavern_path
end
Your mock doesn't do anything... perhaps you meant to use it somewhere?
I personally dislike mocking in this case and feel it's obfuscation. If you are using Devise you could use their test helpers to sign in as a user.
Related
Hi I am new to rspec (and unit testing in general) and want to test the following method:
class HelloController < ApplicationController
def hello_world
user = User.find(4)
#subscription = 10.00
render :text => "Done."
end
end
I am trying to use Rspec like so:
Describe HelloController, :type => :controller do
describe "get hello_world" do
it "should render the text 'done'" do
get :hello_world
expect(response.body).to include_text("Done.")
end
end
end
I would like to simply test that the method works properly and renders the test "done". I get the following error when I run the test:
Failure/Error: user = User.find(4)
ActiveRecord::RecordNotFound:
Couldn't find User with 'id'=4
But how do I properly create a user with that id before executing it? I have tried the following based on other tutorials and questions but it doesn't work:
describe "get hello_world" do
let(:user) {User.create(id: 4)}
it "should render the text 'done'" do
get :hello_world
expect(response.body).to include_text("Done.")
end
end
Thank you in advance.
Hey so really no action (e.g. def hello_world) should rely on a specific id. So a simple alternative could be to use user = User.last or to find the user by name user = User.find_by(name: "name"). Then in the test you would create any user if you using User.last in the action.
describe "get hello_world" do
let(:user) {User.create!}
it "should render the text 'done'" do
get :hello_world
expect(response.body).to include_text("Done.")
end
end
or if you are searching by name you can make a user with that name;
describe "get hello_world" do
let(:user) {User.create!(name: "name")}
it "should render the text 'done'" do
get :hello_world
expect(response.body).to include_text("Done.")
end
end
Hope this helps, questions welcome.
Do you really mean to use 'user = User.find(4)'? If you really meant to do that, you should stub the User's find method and return a user object.
it "should render the text 'done'" do
u = User.new #a new user, your test database is empty, so there's no user with id 4
User.stub(find: u) #stub the User's find method to return that new user
get :hello_world
expect(response.body).to include_text("Done.")
end
Another option is to send the user_id via params
it "should render the text 'done'" do
u = User.create(.... your user params)
get :hello_world, user_id: u.id
expect(response.body).to include_text("Done.")
end
and
def hello_world
user = User.find(params[:user_id])
#subscription = 10.00
render :text => "Done."
end
Anyway, I don't think you should be doing that, a hardcoded id is a bad sign. If you need to control users registrations and logins you can use something like Devise, and you may need to create an login a user before the spec.
Hi I am implementing a method to delete a user account in my web application. My controller:
class UsersController < ApplicationController
before_filter :set_current_user
def user_params
params.require(:user).permit(:user_id, :first_name, :last_name, :email, :password, :password_confirmation)
end
def delete_account
#user = User.find_by_id(params[:id])
if #user.present?
#user.destroy
flash[:notice] = "User Account Deleted."
end
redirect_to root_path
end
def destroy
User.delete(:user_id)
redirect_to root_path
end
end
My rspec:
require 'spec_helper'
require 'rails_helper'
require'factory_girl'
describe UsersController do
describe "delete account" do
before :each do
#fake_results = FactoryGirl.create(:user)
end
it "should call the model method that find the user" do
expect(User).to receive(:find).with(params[:id]).and_return (#fake_results)
end
it "should destroy the user account from the database" do
expect{delete :destroy, id: #fake_results}.to change(User, :count).by(-1)
end
it "should redirect_to the home page" do
expect(response).to render_template(:home)
end
end
end
The first error is
Failure/Error: expect(User).to receive(:find).with(params[:id]).and_return (#fake_results)
NameError:undefined local variable or method `params' for #<RSpec::ExampleGroups::UsersController::DeleteAccount:0x00000007032e18>
I know what this error means but I don't know how to correct it. How can I pass the user id from the controller to rspec?
The second error is:
Failure/Error: expect(response).to render_template(:home)
expecting <"home"> but rendering with <[]>
I think there is something wrong with my controller method. It should redirect to the home page but it doesn't.
params is not available in your tests, it's available in your controller.
Looks like you create a test user in your test:
#fake_results = FactoryGirl.create(:user)
Then, you can use the id of this test user (#fake_results.id) instead of trying to use params[:id]:
expect(User).to receive(:find).with(#fake_results.id).and_return (#fake_results)
Although, you may want to change the name from #fake_results to something more meaningful e.g. test_user or so.
However, this should fix both of your problems as your second problem is there because of the first problem. As it's failing to delete the user in the first place, it's not being redirected to the root path and hence the home template is not rendering.
This is my controller
def log_in
admin=Admin.find_by_name(params[:name])
unless admin.nil?
admin=admin.authenticate(params[:password])
if admin==false
# wrong password,login again
# redirect_to sign_in_admins_url
else
session[:admin]=admin.name
redirect_to root_path
end
end
end
And This is the rspec code:
describe "Post request for admins" do
it "should log_in with right name and password" do
post :log_in,name:"ryan",password:"123456"
controller.params[:name].should=="ryan"
controller.params[:password].should=="123456"
session[:admin].should eq "ryan"
end
end
And I the assertion always returned
expected 'ryan',got nil
Why did this happen? I'm still new for rspec... follow the tutorials, think it should work
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 have a Rails 3 project in which I want to store the current company selected in a session variable.
I'm working with the staff controller spec and would like to stub out current_company for now as I'm isolating my spec example for the staff new controller action.
it "should call current_company" do
company = mock_model(Company, :id => "1")
controller.should_receive(:current_company).and_return(company)
get :new
end
Here is my new action for the staff controller
def new
#staff = Staff.new
#staff.company_id = current_company.id
end
I keep getting error
Failure/Error: get :new
NameError:
undefined local variable or method `current_company' for #<StaffsController:0x000000028d6ad8>
I've also tried just stubbing it out instead of using should_receive
controller.stub!(:current_company).and_return(company)
I get the same error.
Your code looks fine to me, it should work. There must be some other problem we are not seeing. I notice the controller name is "StaffsController" -- is that correct? Double-check the names of the controller and the corresponding spec -- they should be the same.
I think it was bombing out on the 'should be successful' example/test, so I've put my stubbing in a before block.
require 'spec_helper'
describe StaffsController do
describe "GET 'new'" do
let(:staff) { mock_model(Staff, :company_id= => nil)}
let(:company) { mock_model(Company, :id => 1)}
before do
Staff.stub!(:new).and_return(staff)
controller.stub!(:current_company).and_return(company)
end
it "should be successful" do
get :new
response.should be_success
end
it "should call current_company" do
controller.should_receive(:current_company).and_return(company)
get :new
end
end
end
This works for:
class StaffsController < ApplicationController
def new
#staff = Staff.new
current_company.id
end
end