rspec create() method not working - ruby-on-rails

I am working on an online learning portal like bento. For a user to unlock the next lesson, they must have to review at least one submission from the other user on the assignment of any previous lessons(complete by the current user). Considering this logic, I have a rspec test where I have tested if the page is being redirected to the other user's submission or not? In order to pass the test, I have to create dummy submission. For that, I am using create() method but the redirection to submission page is failing on the test but it works fine on the website. The code responsible for redirection in submission_controller is
def review_submission
#submission = Submission.where('lesson_id < ?', current_user.submissions.last.assignment.lesson_id.to_i)
.shuffle[1]
if #submission.nil?
redirect_to #assignment
else
redirect_to assignment_submission_path(assignment_id: #assignment.id, id: #submission.id)
end
end
The code of the test is
it "redirects to submission path" do
admin_user = create(:admin_user)
sign_in admin_user
user = create(:user)
sign_in user
assignment = create(:assignment, admin_user_id: admin_user.id)
#I was creating dummy submissions data using rspec create method.
submission = create(:submission, lesson_id: 1)
post :create, params: { submission: attributes_for(:submission, user_id: user.id), assignment_id: assignment.id}
expect(response).to redirect_to assignment_submission_path( assignment_id: assignment.id, id: Submission.last.id)
end
Dummy data is coming from FactoryGirl
factory :submission do
user_id 1
assignment_id 1
lesson_id 2
content "this is the submission of my assignment"
factory :invalid_submission do
content ""
end
end
Any suggestions are highly appreciated. Thank you in advance.

It looks like #submission is nil, so it's redirecting you to the Attachments#show page instead of the Submissions#show page. You're setting it like:
#submission = Submission.where('lesson_id < ?', current_user.submissions.last.assignment.lesson_id.to_i).shuffle[1]
It's trying to find the Submission based on current_user.submissions. Your Factory is just making the submission like:
submission = create(:submission, lesson_id: 1)
That defaults the user_id to be 1, which probably isn't the ID of your signed in user (since you create the admin user above it). Try passing in the user_id to your FactoryGirl's creation of the Submission. May also want to pass in the assignment_id too actually, so the Submission will correctly belong to the Assignment.
submission = create(:submission, lesson_id: 1, user_id: user.id, assignment_id: assignment.id)
Then the Submission should be assigned to your User, and hopefully the Controller will find it and your test will pass.

Related

Feedback on Admin set user as mod in rails

i'm a beginner to Rails and is following Michael Hartl's book. In chapter 9, where he set up an Admin role, and admin can delete users. I want to extend this feature by allowing admin to set users to Mod as well.
The plan is this:
1.An logged in admin go to the users page, where he sees a list of users(users_url)
2.And next to their name, the admin have an option of 'Set Mod', when the admin clicks that, it sets the user's mod attribute to true.
$ rails g migration add_mod_to_users mod:boolean
then in the migrated file, set default to false, and
$ rake db:migrate
In the routes.rb
get 'setmod' => 'users#setmod'
and in users_controller.rb
def setmod
if logged_in? && current_user.admin?
#user = User.find(params[:id])
#user.update_attribute(:mod, true)
flash[:success] = "User ID #{#user.id} is now a mod!"
redirect_to users_url
else
flash[:warning] = "You can't do that!"
redirect_to users_url
end
end
In the list of users view file:
<% if current_user.admin? && !current_user?(user) %>
| <%= link_to "Set Mod", setmod_path(:id => user.id) %>
Is this the right approach to it? I'm a beginner so i'd be glad if i can get some feedback on this. Does my code have any vulnerable spots? What's a better way doing it?
Also, i tried to do some test on this
in test/controllers/users_controller_test.rb
# This test passes
test "should redirect setmod when not logged in" do
get :setmod
assert_redirected_to users_url
end
# This test failed
test "should redirect setmod when logged in as a non-admin" do
log_in_as(#other_user)
assert_no_difference '#user.mod' do
get :setmod, id: #user
end
assert flash.empty?
assert_redirected_to users_url
end
I know the second test's code is wrong, but i can't figure out the right way to do it.
You're not passing a user id into the setmod route.
Change to this:
get 'setmod/:id' => 'users#setmod'
Now, navigating to http://localhost:3000/setmod/1 will attempt to set the user (with id=1) to mod.
However, you can use Rails' built-in resources to handle this just fine.
routes.rb
resources :users
your view
link_to("Promote the user to MOD", user_path(#user, status: 1), method: :put)
Clicking that link will update the single attribute for a user shown on the page. You would simply authenticate the admin in the users_controller.rb update action.

Get any id to any user exists in the database

I am new to rails and have a task that asks me to send an invitation for any user to be admin in my magazine here is my piece of code
def invite
inviteUser = { 'user_id' => current_user.id, 'Magazine_id' => params[:id] }
CollaborationInvitation.create(inviteUser)
#magazine = Magazine.find(params[:id])
redirect_to :back
rescue ActionController::RedirectBackError
redirect_to root_path
end
I need to replace current_user.id with something that refers to any user's id which exists in my database to send him an invitation to be admin with me I tried to add #User=Users.All and then pass it as a variable but it got me an error I tried a lot of things but every time I get an error except for adding current_user.id
ps: I am using devise for authentication
You asked a couple things, and it is kind of confusing what you want to do.
Here is how you get all ids of records in a model.
Rails4: User.ids
Rails3: User.all.map(&:id)
Or (not sure if #pluck is in Rails 3 or not)
User.pluck(:id)
If you want to get a random user (you mentioned "any user") you could do.
User.find(User.pluck(:id).sample)
Though I think what you really want to do is to pass the id or some other attribute of a user as a param to the action and send that user an invitation.
Presumably you either have a post or get route for "users#invite" (the action you wrote in your question). You can add a named parameter there or you can pass a url param or if you are using a post route, you could add the param to the post body.
Then in your contoller you can do something like this (I'll use email as an attribute):
def invite
#user = User.find_by(email: params[:user_email])
#Rails 3 like this
# #user = User.find_by_email(params[:user_email])
# now do stuff with user
end
User.all will return you the collection of users. So,
Find the user object to get an id...
Try this code....
def invite
inviteUser = { 'user_id' => User.find_by_email('user#example.com').id, 'Magazine_id' => params[:id] }
CollaborationInvitation.create(inviteUser)
#magazine = Magazine.find(params[:id])
redirect_to :back
rescue ActionController::RedirectBackError
redirect_to root_path
end
You can try
User.last.id
or
User.find_by_email("xyz#test.com").id
or
User.where(email: "xyz#test.com").first.id
Replace xyz#test.com with desired user email address. To get more details on rails active record query interface, please read rails guides http://guides.rubyonrails.org/active_record_querying.html

How to call the create action from the controller in RSpec

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.

Rspec testing instance variables with user creation

I'm testing to make sure that a created user is assigned to my instance variable #user. I understand what get means, but I'm not sure what to write for the test. I'm returning with an argument error for a bad URI or URL. What's wrong with my test and how do I fix it?
it "checks #user variable assignment for creation" do
p = FactoryGirl.create(:user)
get :users
# I'm confused on what this line above means/does. What does the hash :users refer
#to
assigns[:user].should == [p]
end
The expected URI object or string error refers to get :users and the error is as follows
Failure/Error get :users
ArgumentError:
bad argument: (expected URI object or URI string)
I guess that what you want is
it "checks #user variable assignment for creation" do
p = FactoryGirl.create(:user)
get :show, id: p.id
assigns(:user).should == p
end
The line you were not sure about checks that content of the assigned variable (#user) in the show view of the user p, is equal to the p user you just created more information there
what action are you trying to test? usually, for creation, you need to test that the controller's "create" action creates a user and assigns an #user variable
I would test it this way:
describe 'POST create' do
it 'creates a user' do
params = {:user => {:name => 'xxx', :lastname => 'yyy'}}
User.should_receive(:create).with(params)
post :create
end
it 'assigns the user to an #user instance variable' do
user = mock(:user)
User.stub!(:create => user)
post :create
assigns(:user).should == user
end
end
notice that I stub/mock all user methods, since you are testing a controller you don't have to really create the user, you only test that the controller calls the desired method, the user creation is tested inside the User model spec
also, I made 2 tests (you should test only 1 thing on each it block if possible, first it test that the controller creates a user, then I test that the controller assigns the variable
I'm assuming your controller is something like this:
controller...
def create
#user = User.create(params[:user])
end
which is TOO simple, I guess you have more code and you should test that code too (validations, redirects, flash messages, etc)

Set current_user in test

I have a test that looks like this:
test "should get create" do
current_user = FactoryGirl.build(:user, email: 'not_saved_email#example.com')
assert_difference('Inquiry.count') do
post :create, FactoryGirl.build(:inquiry)
end
assert_not_nil assigns(:inquiry)
assert_response :redirect
end
That's testing this part of the controller:
def create
#inquiry = Inquiry.new(params[:inquiry])
#inquiry.user_id = current_user.id
if #inquiry.save
flash[:success] = "Inquiry Saved"
redirect_to root_path
else
render 'new'
end
end
and the factory:
FactoryGirl.define do
factory :inquiry do
product_id 2
description 'I have a question about....'
end
end
but I keep getting errors in my tests:
1) Error:
test_should_get_create(InquiriesControllerTest):
RuntimeError: Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id
What am I doing wrong? I need to set the current_user, and I believe I am in the test, but obviously, that's not working.
You didn't create current_user. It was initialized only in test block.
There are two differents ways to do it:
First, use devise test helpers. Something like that
let(:curr_user) { FactoryGirl.create(:user, ...attrs...) }
sign_in curr_user
devise doc
Second, you can stub current_user method in your controllers for test env
controller.stub(current_user: FactroryGirl.create(:user, ...attrs...))
And you should use FactoryGirld.create(...) instead of FactoryGirl.build(...), because you factory objects have to be persisted.(be saved in db and has id attribute not nil)
There are several things which come to mind:
FactoryGirl.build(:user, ...) returns unsaved instance of a user. I'd suggest to use Factory.create instead of it, because with unsaved instance there's no id and there's no way for (usually session based) current_user getter to load it from database. If you're using Devise, you should "sign in" user after creating it. This includes saving record in DB and putting reference to it into session. See devise wiki
Also, passing ActiveRecord object to create action like this looks weird to me:
post :create, FactoryGirl.build(:inquiry)
Maybe there's some rails magic in play which recognizes your intent, but I'd suggest doing it explicitly:
post :create, :inquiry => FactoryGirl.build(:inquiry).attributes
or better yet, decouple it from factory (DRY and aesthetic principles in test code differ from application code):
post :create, :inquiry => {product_id: '2', description: 'I have a question about....'}
This references product with id = 2, unless your DB doesn't have FK reference constraints, product instance may need to be present in DB before action fires.

Resources