Getting nilClass error while testing with rspec - ruby-on-rails

I'm testing my project with rspec and now I'm up to the controllers part. I'm testing this method:
def accept
#app = App.find(params[:id])
#invite = Invite.find_by(app: #app.name, receiver: current_dev.id)
#dev = Developer.find(#invite.sender)
#app.developers << #dev
respond_to do |format|
if #invite.destroy
format.html { redirect_to #app, notice: 'A new developer joined your team!' }
format.json { render :show, status: :destroyed, location: #app }
else
format.html { render :back }
format.json { render json: #invite.errors, status: :unprocessable_entity }
end
end
end
and this is the test part:
it "should accept an invite (1)" do
invite = Invite.create(:app => "test", :sender => "2", :receiver => "1")
get :accept, :id => 1
assert_response :success
end
but when I run the rspec command I get this error:
InvitesController should accept an invite (1)
Failure/Error: #dev = Developer.find(#invite.sender)
NoMethodError:
undefined method `sender' for nil:NilClass
So I am assuming that the invite object is nil but I can't figure out why this happens. I test the function via browser and everything works fine. This is also causing same errors in different controller methods, every time just because my invite object is nil. Why is this happening?

SOLVED:
Main reason things weren't working: mismatch between created invite parameters and current parameters (app, current_developer)
Debug: setting breakpoints/printing values of what was needed in the controller and what was needed in the model.
Fixing: created objects that were missing in order to match parameters; correct solution was
it "should accept an invite (1)" do
invite = Invite.create(:app => #app.name, :sender => "2", :receiver => #developer.id)
get :accept, :id => #app.id
assert_response :redirect
end

Related

Rails: No route matches [POST] nested resources

In the routes.rb I have this nested resource
# OBSERVATIVE SESSIONS
resources :observative_sessions do
# OBSERVATIONS
resources :observations
end
In observations_controller.rb
def new
#observative_session = ObservativeSession.find(params[:observative_session_id])
#observation = Observation.new
#observation.observative_session_id = #observative_session.id
end
def create
#observative_session = ObservativeSession.find(params[:observative_session_id])
#observation = #observative_session.observations.build(observation_params)
#observation.user_id = current_user.id
respond_to do |format|
if #observation.save
format.html { redirect_to [#observative_session, #observation], notice: 'Observation was successfully created.' }
format.json { render :show, status: :created, location: #observation }
else
format.html { render :new }
format.json { render json: #observation.errors, status: :unprocessable_entity }
end
end
end
And in observations_controller_test.rb I set up both observation and observative session. The test of new works just fine.
class ObservationsControllerTest < ActionDispatch::IntegrationTest
include Devise::Test::IntegrationHelpers
setup do
#observative_session = observative_sessions(:one)
#observation = observations(:two)
sign_in users(:admin_user)
end
test "should get new" do
get new_observative_session_observation_path(#observative_session)
assert_response :success
end
test "should create observation" do
assert_difference('Observation.count') do
post observative_session_observation_path(#observative_session, #observation), params: { observation: { start_time: #observation.start_time, description: #observation.description, rating: #observation.rating, notes: #observation.notes, celestial_body_name: #observation.celestial_body_name, telescope_name: #observation.telescope_name, binocular_name: #observation.binocular_name, eyepiece_name: #observation.eyepiece_name, filter_name: #observation.filter_name, user_id: #observation.user_id, observative_session_id: #observation.observative_session_id }}
end
But this is the error I get in the creation test
test_should_create_observation
ActionController::RoutingError: No route matches [POST] "/observative_sessions/980190962/observations/298486374"
I can't understand what I'm doing wrong.
Thanks for your help.
When you say POST observation_session_observation_path(#observation_session, #observation) you are telling it to post to a url the has both :observation_session_id and an :id in the params, where the id is that of #obseravtion. However, POST paths for create actions don’t take that last id param (ostensibly you are creating a new record with that action).
Try dropping #observation from your path helper (and make sure you are using the correct create path: observation_session_observations_path(#observation_session).
You can do rake routes to see your routes in your terminal, or localhost:3000/rails/info/routes to see it in the browser.
I also see in your new action you are assigning the observation_session_id manually. I recommend you either do what you do later and call #obervation_session.observations.build, or Observation.new(observation_session: #observation_session). You should avoid setting ids like that.

How to send params with FactoryGirl (as opposed to manually sending the params as a hash)?

I have the following rspec test that works:
it "redirects to the created api_key" do
post :create, :api_key => {:api_identifier => "asdfadsf", :verification_code =>
"12345"}
response.should redirect_to(ApiKey.last) #(or any other test function)
end
But I use Factory girl so I don't have to manually create api_keys.
How can I replicate the above functionality, but use factory girl?
Using:
it "redirects to the created api_key" do
test = FactoryGirl.build(:api_key)
post :create, :api_key => test
response.should redirect_to(ApiKey.last) #(or any other test function)
end
or:
it "redirects to the created api_key" do
post :create, FactoryGirl.build(:api_key)
response.should redirect_to(ApiKey.last) #(or any other test function)
end
Gives me null values for the :api_key value when I arrive at my controller.
For reference, here is my create action that this test is testing:
def create
#api_key = ApiKey.new(params[:api_key])
#api_key.user = current_user
pp #api_key
respond_to do |format|
if #api_key.save
format.html { redirect_to #api_key, notice: 'Api key was successfully created.' }
format.json { render json: #api_key, status: :created, location: #api_key }
else
format.html { render action: "new" }
format.json { render json: #api_key.errors, status: :unprocessable_entity }
end
end
end
try:
post :create, :api_key => FactoryGirl.attributes_for(:api_key)
Using build doesn't actually create a record. It just pretends it did. Using attributes_for will give you the attributes of an object. This is mostly used in the context you describe. Note that this too will not create an object.
What I would do is this if the response is successful/redirect:
response.should be_redirect
Or even better use expect.

post functional test failing

I am trying to create a functional test that tests the create method in one of my controllers. For the life of me, I cannot figure out why this is failing. I am getting one failure, and zero errors:
1) Failure:
test_should_create_order(OrdersControllerTest) [/Users/user/rails_work/depot/test/functional/orders_controller_test.rb:38]:
"Order.count" didn't change by 1.
<3> expected but was
<2>.
So, Im pretty sure this means that my functionals test was unable to make an Order. Here is my test:
setup do
#order = orders(:one)
end
test "should create order" do
assert_difference('Order.count') do
post :create, order: #order.attributes.slice(Order.accessible_attributes)
end
assert_redirected_to store_url
end
my orders fixture:
one:
name: Dave Thomas
address: MyText
email: dave#example.org
pay_type: Check
and my Order#create controller:
def create
#order = Order.new(params[:order])
#order.add_line_items_from_cart(current_cart)
respond_to do |format|
if #order.save
Cart.destroy(session[:cart_id])
session[:cart_id] = nil
format.html { redirect_to store_url, notice: 'Thank you for your order' }
format.json { render json: #order, status: :created, location: #order }
else
#cart = current_cart
format.html { render action: "new" }
format.json { render json: #order.errors, status: :unprocessable_entity }
end
end
end
Now, if I change the setup method in my functional test to:
#order = Order.create(orders(:one))
Instead of:
#order = orders(:one)
The failure disappears, but I get about 8 of these errors:
NoMethodError: undefined method `stringify_keys' for #<Order:0x007f8c62dbb960>
If anyone can help me fix this functional test, I would more than appreciate it. Any and all input is welcome.
Bottom line: assign each order attribute individually.
I'm not familiar with the 'mass assignment' vulnerability (new to Rails), but here (Pragmatic Forums) is a case of someone having difficulty with that specific test because of it.
Try spelling out each attribute of the order individually. Instead of
post :create, order: #order.attributes.slice(Order.accessible_attributes)
use
post :create, order: {
address: #order.address,
email: #order.email,
name: #order.name,
pay_type: #order.pay_type
}
The test as a whole will be this:
test "should create order" do
assert_difference('Order.count') do
post :create, order: { address: #order.address, email: #order.email, name: #order.name, pay_type: #order.pay_type }
end
assert_redirected_to store_path
end

rails 3 undefined method `update_attributes' for nil:NilClass

Im new to Ruby and rails. Im doing a form which updates :owner value in Hardware database. in using custom views name transfer.html.erb. my form code are display bellow.
<%= form_for #hardware, :url => do_transfer_path do |f| %>
this is my custom controller in hardwares_tranfers_controller.rb
def transfer
#hardware = Hardware.find(params[:id])
if current_user.user_lvl == 0
#users = User.order(:user_lvl)
else
#users = User.where(:refer => current_user.id)
respond_to do |format|
format.html # index.html.erb
format.json { render json: #hardwares }
end
end
end
def do_transfer
# transfer_user = User.where(:id => #hardware.owner).email
# TransferLog.create([:user_id => current_user.id, :hardware_id => #hardware.id])
if #hardware.update_attributes(params[:hardware])
format.html { redirect_to users_url, notice: "Hardware was successfully transfer"}
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #hardware.errors, status: :unprocessable_entity }
end
end
and the error I get is
NoMethodError in HardwaresTransfersController#do_transfer
undefined method update_attributes' for nil:NilClass
Aplication Traces
app/controllers/hardwares_transfers_controller.rb:23:indo_transfer'
Parameter
{"utf8"=>"✓",
"_method"=>"put",
"authenticity_token"=>"vkwKyuS4eviRN1ogrpOcGh1Y5OBNX0wiBzpV8taNkq0=",
"hardware"=>{"owner"=>"3"},
"commit"=>"Transfer Hardware"}
my routes.rb
match "hardwares_transfers/" => "hardwares_transfers#do_transfer" , :as => :do_transfer
get "hardwares_transfers/:id/transfer" => "hardwares_transfers#transfer", :as => :transfer_hardwares
rake routes>
do_transfer /hardwares_transfers(.:format) hardwares_transfers#do_transfer
transfer_hardwares GET /hardwares_transfers/:id/transfer(.:format) hardwares_transfers#transfer
hardwares GET /hardwares(.:format) hardwares#index
POST /hardwares(.:format) hardwares#create
new_hardware GET /hardwares/new(.:format) hardwares#new
edit_hardware GET /hardwares/:id/edit(.:format) hardwares#edit
hardware GET /hardwares/:id(.:format) hardwares#show
PUT /hardwares/:id(.:format) hardwares#update
DELETE /hardwares/:id(.:format) hardwares#destroy
You need
def do_transfer
#hardware = Hardware.find(params[:id])
...
end
I'll also take this opportunity to point out that you could probably frame this in a more REST-ful manner
find hardware item:
def do_transfer
#hardware = Hardware.find(params[:id])
...
end
and change route.rb:
match "hardwares_transfers/:id" => "hardwares_transfers#do_transfer" , :as => :do_transfer
The error is NoMethodError in HardwaresTransfersController#do_transfer
undefined method update_attributes' for nil:NilClass.
As the error says, that Rails is trying to call update_attributes on nil:NilClass, which implies that your #hardware object is nil.
So, you should initialize it by
#hardware = Hardware.find(params[:id])
You should always look at the error and give it a thought as to why this error is there, it'll help you in learning better.

RSpec Newbie: Devise/Cancan causing otherwise working controller spec to fail

I'm trying to get an RSpec controller spec to pass. It's almost identical to the scaffold-generated spec, except a user is signed into devise first. If I disable 'load_and_authorize_resource' from the controller (which checks permissions), everything works fine. But if I put the line back in, it fails with:
1) PostsController logged in administrator POST create with valid params assigns a newly created post as #post
Failure/Error: post :create, :post => {'title' => 'test title'}
<Post(id: integer, title: string, cached_slug: string, content: text, user_id: integer, created_at: datetime, updated_at: datetime) (class)> received :new with unexpected arguments
expected: ({"title"=>"test title"})
got: (no args)
# ./spec/controllers/posts_controller_spec.rb:52:in `block (5 levels) in <top (required)>'
I had assumed the spec wasn't logging in the user correctly, but a puts current_user.role.name confirms the user is logged in correctly, and has the necessary role. Performing the actual process in a browser confirms it works as desired.
Anyone have any suggestions? I'm quite stumped. Controller below:
def create
#post = Post.new(params[:post])
#post.user = current_user
respond_to do |format|
if #post.save
flash[:notice] = "Post successfully created"
format.html { redirect_to(#post)}
format.xml { render :xml => #post, :status => :created, :location => #post }
else
format.html { render :action => "new" }
format.xml { render :xml => #post.errors, :status => :unprocessable_entity }
end
end
end
...And the spec
describe "with valid params" do
it "assigns a newly created post as #post" do
Post.stub(:new).with({'title' => 'test title'}) { mock_post(:save => true) }
post :create, :post => {'title' => 'test title'}
assigns(:post).should be(mock_post)
end
...And supporting stuff in the spec:
before(:each) do
#user = Factory(:admin)
sign_in #user
end
def mock_post(stubs={})
#mock_post ||= mock_model(Post, stubs).as_null_object
end
Many thanks...
Try upgrading CanCan to version 1.5. I had the issue earlier but I think it went away when I upgraded.

Resources