I'm new to RSpec, I wonder why I didn't pass this test
before(:each) { get :index }
it "assigns all favorites as #favorites" do
favorite = FactoryGirl.create(:favorite)
expect(assigns(:favorites)).to eq([favorite])
end
It says
1) FavoritesController GET index assigns all favorites as #favorites
Failure/Error: expect(assigns(:favorites)).to eq([favorite])
expected: [#<Favorite id: 1, patient_id: 6, doctor_id: 5>]
got: #<ActiveRecord::Relation []>
(compared using ==)
Diff:
## -1,2 +1,2
-[#<Favorite:0x000000058a5ca0 id: 1, patient_id: 6, doctor_id: 5>]
+[]
It seems assigns(:favorites) got empty. I have tried another approach as well
def valid_attributes
doctor = FactoryGirl.create(:doctor)
patient = FactoryGirl.create(:patient)
FactoryGirl.attributes_for(:favorite, doctor_id: doctor.id, patient_id: patient.id)
end
it "assigns all favorites as #favorites" do
favorite = Favorite.create! valid_attributes
expect(assigns(:favorites)).to eq([favorite])
end
And it got the same error. Any inputs would be helpful for me and I'd like to ask if there is any way to simplify it.
Update
app/controllers/favorite_controller.rb
class FavoritesController < ApplicationController
before_action :set_favorite, only: [:destroy]
before_action :authenticate_user!
def index
#favorites = Favorite.where(:patient_id => current_user.id).order(id: :asc)
end
end
spec/controllers/favorite_controller_spec.rb
require 'spec_helper'
describe FavoritesController, type: :controller do
login_patient
describe "GET index" do
let!(:favorite) { FactoryGirl.create(:favorite) }
before { get :index }
it { expect(response).to render_template(:index) }
it { expect(response).to be_success }
it { expect(response).to have_http_status(200) }
it "blocks unauthenticated access", :skip_before do
expect(response).to redirect_to(new_user_session_path)
end
it "assigns all favorites as #favorites" do
expect(assigns(:favorites).to_a).to eq([favorite])
end
end
end
spec/support/controller_helpers.rb
module ControllerHelpers
def login_patient
before :each do |example|
unless example.metadata[:skip_before]
#request.env["devise.mapping"] = Devise.mappings[:user]
#patient = FactoryGirl.create(:patient)
sign_in :user, #patient
end
end
end
end
You are creating the records after you send the request so by the time the request finishes, the record you just created isn't included in the list of favorites. Change your test code to the following
let!(:favorite) { FactoryGirl.create(:favorite) }
before { get :index }
it "assigns all favorites as #favorites" do
expect(assigns(:favorites)).to eq([favorite])
end
This will probably still fail because assigns(:favorites) is an ActiveRecord::Relation object so you have to call to_a
expect(assigns(:favorites).to_a).to eq([favorite])
UPDATE:
Since the favorite is being filtered by patient, you have to make sure that the created favorite in the test belongs to the patient. You can do that by changing the favorite to
let!(:favorite) { FactoryGirl.create(:favorite, patient: #patient)
Try to create the records before the request:
let!(:favorite) { FactoryGirl.create(:favorite) }
before(:each) { get :index }
it "assigns all favorites as #favorites" do
expect(assigns(:favorites).to_a).to eq([favorite])
end
Related
I want to test show action in my Shipment controller. To do so I've prepared fairly simple specs:
RSpec.describe ShipmentsController, type: :controller do
describe 'GET #show' do
let(:params) { { id: shipment.id, product_id: product.id } }
let!(:product) { create(:product) }
let!(:shipment) { create(:shipment, product: product) }
context 'when params are valid' do
before { get :show, params: params }
it 'return valid json' do
expect(JSON.parse(response.body)).to eq(expected_json)
end
end
end
end
ShimpentsController.rb
class ShipmentsController < ApplicationController
before_action :set_product
attr_reader :shipment
def show
#shipment = Shipment.find(params[:id])
#items = shipment&.grouped_shipment_items
end
private
def set_product
#product = Product.find(params[:product_id])
end
end
When I use postman everything went well - it returns expected json but in the RSpec test I'm getting:
response.body
=> ""
I think you need to add render_views in your controller spec file.
RSpec.describe ShipmentsController, type: :controller do
render_views
describe 'GET #show' do
let(:params) { { id: shipment.id, product_id: product.id } }
let!(:product) { create(:product) }
let!(:shipment) { create(:shipment, product: product) }
context 'when params are valid' do
before { get :show, params: params }
it 'return valid json' do
expect(JSON.parse(response.body)).to eq(expected_json)
end
end
end
end
Reference: https://rubyinrails.com/2019/04/11/rails-test-jbuilder-json-response-with-rspec/
I think you are not making a request for JSON response with rspec. You can check by putting a breakpoint in your controller action, then checking
request.format.json?
In order to ask for JSON response from an rspec test, you should add as: :json to the end of the request. The request should look like this:
get :show, params: params, as: :json
I'm biginer.
I studied Rspec.
I made an implementation that allowed me to do good on my posts.
But on the browser I do the expected move, but the test does not pass.
The destroy action goes through the test, but the create action does not pass the test.
My error is
Failure/Error: expect { post :create, format: :js, params: { post_id: post1.id, id: like.id } }.to change(Like, :count).by(1)
expected #count to have changed by 1, but was changed by 0
My code is
require 'rails_helper'
RSpec.describe LikesController, type: :controller do
let!(:user) { create(:user) }
let!(:post1) { create(:post, user: user) }
let!(:like) { create(:like, user_id: user.id, post_id: post1.id) }
describe "#create" do
before do
sign_in user
end
it "response Ajex" do
post :create, format: :js, params: { post_id: post1.id, id: like.id }
expect(response.content_type).to eq 'text/javascript'
end
it "success like function" do
expect { post :create, format: :js, params: { post_id: post1.id, id: like.id } }.to change(Like, :count).by(1)
end
end
describe "#destroy" do
before do
sign_in user
end
it "response Ajex" do
delete :destroy, format: :js, params: { post_id: post1.id, user_id: user.id, id: like.id }
expect(response.content_type).to eq 'text/javascript'
end
it "delete like function" do
expect { delete :destroy, format: :js, params: { post_id: post1.id, user_id: user.id, id: like.id } }.to change(Like, :count).by(-1)
end
end
end
likes_controller.rb
class LikesController < ApplicationController
def create
#like =
current_user.likes.find_or_create_by(post_id:params[:post_id])
#likes = Like.where(post_id: params[:post_id])
#post = Post.find(params[:post_id])
end
def destroy
like = current_user.likes.find_by(post_id: params[:post_id])
like.destroy
#likes = Like.where(post_id: params[:post_id])
#post = Post.find(params[:post_id])
end
end
I cannot solove this problem.
Please teach me a hint.
You've got an error in your code somewhere, most likely, which is why the Like count fails to increment. First, I'd try and figure out why it isn't incrementing. Since you asked for a hint, here's one way you can split out the "success like function" block:
context "valid" do
before do
post :create, format: :js, params: { post_id: post1.id, id: like.id }
end
it "success" do
# You can inject a binding.pry here if needed
expect(response.status).to eq(200)
end
it "response" do
# You can inject a `binding.pry` here if needed
# You can also inspect the `response.body` with puts if needed
expect(JSON.parse(response.body)).to include(
# You would modify this to match the shape of your response
post: a_hash_including(
like: like.id
)
)
end
end
You'll want to install pry-rails and pry-byebug gems (for inspecting).
The reason behind splitting them up is it makes it easier to determine the issue (you can have a valid response code but not the expected result, for example). This comes with some caveats (it will make for slower tests) but in this example it will make it easier to determine why your post is failing.
The snippet above should help you debug the error; once you fix it you can revert back to your previous method of checking.
I'm trying to fix some tests that I have written in my comments controller. As of now, with my current tests I get this error:
Failure/Error: #outlet = Outlet.find(params[:comment][:outlet_id])
ActiveRecord::RecordNotFound:
Couldn't find Outlet with 'id'=
Here is an example of some of the tests
describe '#create' do
context 'with valid attributes' do
before :each do
#outlet = FactoryGirl.create(:outlet)
#user = FactoryGirl.create(:user)
#comment_params = FactoryGirl.attributes_for(:comment)
end
let(:create) { post :create, params: { outlet_id: #outlet.id, user_id: #user.id, comment: #comment_params } }
it "creates new comment" do
expect { create }.to change { Comment.count }.by(1)
end
it "increases the post comment count by 1" do
expect { create }.to change { #outlet.comments.count }.by(1)
end
it "increases user comment count by 1" do
expect { create }.to change { #user.comments.count }.by(1)
end
end
end
I'm pretty sure this is happening because of my create statement in my tests
let(:create) { post :create, params: { outlet_id: #outlet.id, user_id: #user.id, comment: #comment_params } }
Here is my comments controller create action
def create
#outlet = Outlet.find(params[:comment][:outlet_id])
#comment = #outlet.comments.build(comment_params)
#comment.user_id = current_user.id
if #comment.save
redirect_to(#outlet)
end
end
I'm pretty sure it is not working, because the outlet_id that it is looking for is a nested parameter inside of the comments parameter. How would I fix my rspec test to have it look for a nested parameter?
Just pass your params as arguments to the post call, nesting as necessary, e.g.:
post :create, user_id: #user.id, comment: { outlet_id: #outlet.id }
I would like to do a specific search using ransack but my test always returns all instances.
My test:
RSpec.describe UsersController, type: :controller do
describe "GET #index" do
context 'ransack search by email' do
let!(:user1) { create(:user, email: 'user1#example.com') }
let!(:user2) { create(:user, email: 'user2#example.com') }
context 'finds specific user' do
before { get :index, q: '2' }
it "should find just one user" do
expect(assigns(:users).first).to eq [user2]
end
it { should respond_with(:success) }
it { should render_template(:index) }
end
My controller:
class UsersController < ApplicationController
def index
#q ||= User.ransack(params[:q])
#users = #q.result(distinct: true)
end
end
What am I doing wrong?
The param q: should be like
q: {email_cont: '2'}
I have a Transaction model, in which I have the following scope :
scope :ownership, -> { where property: true }
I made some tests of the controller (thanks to M. Hartl). There they are :
require 'spec_helper'
describe TransactionsController do
let(:user) { FactoryGirl.create(:user) }
let(:product) { FactoryGirl.create(:givable_product) }
before { be_signed_in_as user }
describe "Ownerships" do
describe "creating an ownership with Ajax" do
it "should increment the Ownership count" do
expect do
xhr :post, :create, transaction: { property: true, user_id: user.id, product_id: product.id }
end.to change(Transaction.ownership, :count).by(1)
end
it "should respond with success" do
xhr :post, :create, transaction: { property: true, user_id: user.id, product_id: product.id }
expect(response).to be_success
end
end
describe "destroying an ownership with Ajax" do
let(:ownership) { user.transactions.ownership.create(product_id: product.id, user_id: user.id) }
it "should decrement the Ownership count" do
expect do
xhr :delete, :destroy, id: ownership.id
end.to change(Transaction.ownership, :count).by(-1)
end
it "should respond with success" do
xhr :delete, :destroy, id: ownership.id
expect(response).to be_success
end
end
end
end
And there is the destroy method of my Transaction controller :
def destroy
#transaction = Transaction.find(params[:id])
#property = #transaction.property
#product = #transaction.product
#transaction.destroy
respond_to do |format|
format.html { redirect_to #product }
format.js
end
end
But when I run the tests, one of them fails, and I don't understand how or why :
1) TransactionsController Ownerships destroying an ownership with Ajax should decrement the Ownership count
Failure/Error: expect do
count should have been changed by -1, but was changed by 0
# ./spec/controllers/transactions_controller_spec.rb:31:in `block (4 levels) in <top (required)>'
Can you help me about it ?
You can use 'let!'.
About 'let' and 'let!': https://www.relishapp.com/rspec/rspec-core/v/2-6/docs/helper-methods/let-and-let
From the RSpec documentation there's a difference between let and let! (see here);
Use let to define a memoized helper method. The value will be cached
across multiple calls in the same example but not across examples.
Note that let is lazy-evaluated: it is not evaluated until the first
time the method it defines is invoked. You can use let! to force the
method's invocation before each example.
In your destroy method use let!(:ownership) so that the ownership object is not cached after it is destroyed.