I want to test letsrate generated controller.
But I don;t know how do this, because I can not understand how it works.
rater_controller.rb
class RaterController < ApplicationController
def create
if user_signed_in?
obj = params[:klass].classify.constantize.find(params[:id])
obj.rate params[:score].to_i, current_user, params[:dimension]
render :json => true
else
render :json => false
end
end
end
UPDATE
Letsrate is a gem for rails
rater_controller_spec.rb
require 'rails_helper'
describe RaterController do
describe 'POST create' do
let(:valid_attributes)do {
klass: 'Hotel',
dimension: 'rating',
score: '5'
}
end
it 'user signed in' do
user = create(:user)
hotel = create(:hotel)
post :create, { rate: valid_attributes, rater_id: user.id, rateble_id: hotel.id }
sign_in user
end
end
end
The source code you posted makes it pretty obvious how it works. You need to call the create action in RaterController with these params: klass, id, score, dimension. Let's say the klass param is "Restaurant", which is also the name of an ActiveRecord model class. The controller will query the database for a restaurant with the specified ID. Then it will call the rate method on that object with the specified parameters, which presumably inserts a row into the database representing the user's new rating. To test it, you could simply call the controller action and then check to make sure the row got added to the database.
Related
I am trying to test a service but something wrong is happening when I pass the parameters to the service class, the values are being passed in the wrong way by rspec.
My service is:
class CheckInvitesService
def initialize(user, course)
#user = user
#course = course
end
def call
if UserCourseRegistration.exists?(user_id: #user, course_id: #course)
false
else
UserCourseRegistration.create(user_id: #user,
course_id: #course,
school_id: find_school)
end
end
private
def find_school
school = Course.find(#course).school.id
end
end
My test is:
require 'rails_helper'
RSpec.describe CheckInvitesService do
describe "call" do
context 'invite already exists' do
it 'return' do
#current_user_admin = create(:admin)
#school = create(:school, user: #current_user_admin)
#course = create(:course, user: #current_user_admin, school: #school)
# puts #course
# puts #course.id
#verify = CheckInvitesService.new(#course.id, #current_user_admin.id).call
expect(#verify).to be_falsey
end
end
end
end
I printed #course.id and it returns: 122
But when I call the service class, the parameter inside it has another value, for example the #course.id, i passed takes the value: 627
I get the following error:
Failures:
1) CheckInvitesService call invite already exists return
Failure/Error: school = Course.find(#course).school.id
ActiveRecord::RecordNotFound:
Couldn't find Course with 'id'=627
What is entering the class is another id of course the id 627 and not the 122 that should have been passed via parameter.
It appears your arguments are out of order. CheckInvitesService has:
initialize(user, course)
But when you create the CheckInvitesService object, you're passing course as the first argument.
CheckInvitesService.new(#course.id, #current_user_admin.id).call
Should be
CheckInvitesService.new(#current_user_admin.id, #course.id).call
You can use find_or_create_by in your service, it should work, too.
def call
UserCourseRegistration.find_or_create_by(user: #user, course: #course, school: find_school)
end
In my controller I have the following method:
def set_provider
#provider = Provider.find_by(id: params[:id])
return #provider unless #provider.active
#provider = IntegrationProvider.new(#provider.slug).proxy_to.find_by(id: params[:id])
end
Which I am trying cover with the following test:
context 'active' do
before do
#provider = FactoryGirl.create(:provider, :active)
get :show, id: #provider.id
end
it '200' do
expect(response.status).to be(200)
end
it 'assigns #provider' do
expect(assigns(:provider)).to eq(#provider)
end
end
The IntegrationProvider class is basically a child model of the Provider model. Since these can be dynamic, and I am using FactoryGirl I am thinking a stub would work best here. In the code base the slugs correspond to folders so I do not want to test that other class in this controller class. I will save that for my lib class tests.
Question
How would you stub that IntegrationsProvider class so it returns the Provider model?
Something like this should work:
allow(IntegrationProvider).to receive_message_chain(:new, :proxy_to, :find_by).and_return(yourproviderobjecthere)
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.
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)
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.