RSpec match_array fail even when arrays are the same - ruby-on-rails

Note: my app works just fine. I'm just unable to do the right rspec for it.
trash_controller.rb:
class TrashController < ApplicationController
before_action :set_slide, only: [ :show, :destroy, :restore ]
def set_slide
#trashed_slide = Slide.only_deleted.find(params[:id])
end
def show
end
trash_controller_spec.rb:
describe TrashController do
let(:album) { create(:album) }
let(:slide) { build(:slide) }
describe "GET #show" do
before do
slide.save
slide.destroy
get :show, id: slide.id
end
it { expect(assigns(:trashed_slide)).to match_array(Slide.only_deleted.to_a) }
end
error:
1) TrashController GET #show should contain exactly #<Slide id: 1, album_id: 1, description: "Brennon Prosacco", created_at: "2014-04-02 06:06:03", updated_at: "2014-04-02 06:06:03", photo_file_name: "sample_2.jpg", photo_content_type: "image/jpeg", photo_file_size: 204509, photo_updated_at: "2014-04-02 06:06:03", photo_fingerprint: "4dbd1870094527b8c4ddca6afd415eb9", deleted_at: "2014-04-02 06:06:03", photo_processing: false>
Failure/Error: it { expect(assigns(:trashed_slide)).to match_array(Slide.only_deleted.to_a) }
expected an array, actual collection was #<Slide id: 1, album_id: 1, description: "Brennon Prosacco", created_at: "2014-04-02 06:06:03", updated_at: "2014-04-02 06:06:03", photo_file_name: "sample_2.jpg", photo_content_type: "image/jpeg", photo_file_size: 204509, photo_updated_at: "2014-04-02 06:06:03", photo_fingerprint: "4dbd1870094527b8c4ddca6afd415eb9", deleted_at: "2014-04-02 06:06:03", photo_processing: false>
# ./spec/controllers/trash_controller_spec.rb:25:in `block (3 levels) in <top (required)>'
I don't get what it's about as both lines looks the same. Any ideas ?

I would not expect Slide.only_deleted.find(params[:id]) to return an array. find returns just one slide. Therefore I would change the expectation to:
expect(assigns(:trashed_slide)).to eq(Slide.only_deleted.first)

Related

RSpec: invalid model with searchkick

I'm not very sure I'm doing it right, but I have two models: House that has_one Address.
The Address model has:
class Address < ApplicationRecord
searchkick
belongs_to :house
end
I'm trying to test my house_controller with RSpec like this
RSpec.describe HousesController do
context 'GET #index' do
before { get :index }
it { is_expected.to render_template('index') }
it 'assigns #houses' do
h = create(:house)
expect(assigns(:houses).results).to eq([h])
end
...
Nevertheless I always get a result which is not the one I expect.
The code of my controller is the following:
def index
if params[:term].present?
#houses = House.search(params[:term])
else
#houses = House.search('*')
end
end
I'm not sure I understand it, but could it be that since I'm using FactoryBot, it is creating lots of houses, and then when getting in the index method, there's a bunch of houses there and not only and precisely h?
This is my failure:
Failures:
1) HousesController GET #index assigns #houses
Failure/Error: expect(assigns(:houses).results).to eq([h])
expected: [#<House id: 763, rent: 1173, deposit: 739, description: "Rerum cado curso curo alias.", preferred_ge...2018-11-26 21:40:43", available_at: "2018-12-17", user_id: 15945, lease_length: nil, built_in: nil>]
got: [#<House id: 215, rent: 0.839e3, deposit: 0.797e3, description: "Rerum aeneus taceo crepusculum aestu...2018-11-26 21:17:53", available_at: "2018-12-17", user_id: 15776, lease_length: nil, built_in: nil>]
(compared using ==)
Diff:
## -1,2 +1,5 ##
-[#<House id: 763, rent: 1173, deposit: 739, description: "Rerum cado curso curo alias.", preferred_gender: 0, created_at: "2018-11-26 21:40:43", updated_at: "2018-11-26 21:40:43", available_at: "2018-12-17", user_id: 15945, lease_length: nil, built_in: nil>]
+[#<House id: 215, rent: 0.839e3, deposit: 0.797e3, description: "Rerum aeneus taceo crepusculum aestus.", preferred_gender: 0, created_at: "2018-11-25 12:50:11", updated_at: "2018-11-25 12:50:11", available_at: "2018-12-16", user_id: 8065, lease_length: nil, built_in: nil>,
+ #<House id: 235, rent: 0.519e3, deposit: 0.642e3, description: "Cicuta totidem arbustum arcesso fugit tego.", preferred_gender: 0, created_at: "2018-11-25 12:54:28", updated_at: "2018-11-25 12:54:28", available_at: "2018-12-16", user_id: 8085, lease_length: nil, built_in: nil>,
+ #<House id: 648, rent: 0.668e3, deposit: 0.1104e4, description: "Corporis tametsi demens.", preferred_gender: 0, created_at: "2018-11-26 21:17:43", updated_at: "2018-11-26 21:17:43", available_at: "2018-12-17", user_id: 15775, lease_length: nil, built_in: nil>,
+ #<House id: 649, rent: 0.799e3, deposit: 0.611e3, description: "Ut ancilla tredecim.", preferred_gender: 0, created_at: "2018-11-26 21:17:53", updated_at: "2018-11-26 21:17:53", available_at: "2018-12-17", user_id: 15776, lease_length: nil, built_in: nil>]
# ./spec/controllers/houses_controller_spec.rb:12:in `block (3 levels) in <top (required)>'
I'm starting with RSpec now and it's really taking me effort and hours to try to grab the grasp of it, thanks a lot in advance!
Searchkick docs about disabling indexing for tests with RSpec.
You don't want to be updating your objects in Elasticsearch always while running tests. You want to do it only when you'll be explicitly testing your search functionality (or indexing/deleting from index). To do so, you will have to disable searchkick callbacks, define a custom tag for your tests and enable indexing only for these tests. You may have to handle cleaning your index after a test/groups of tests as well.
#vich's point is also important, you currently create your object too late, after the request.
I would change your setup to:
context 'GET #index', :search do
let!(:house) { create(:house) }
before { get :index }
it 'assigns #houses' do
expect(assigns(:houses).results).to eq([house])
end
end
Try creating your house in the before block:
context 'GET #index' do
before do
let!(:house) { create(:house) }
get :index
end
it { is_expected.to render_template('index') }
it 'assigns #houses' do
expect(assigns(:houses).results).to eq([house])
end
end
A couple of things to note:
As opposed to let, let! is immediately invoked (thus creating your record before the index action is hit)
Add a breakpoint (IDE) or use a debugger (byebug, pry, etc.) and put it before the get :index call to see what (if any) houses already exist.

rspec undefined method `id' for nil:NilClass

require 'rails_helper'
feature "comment" do
given(:current_user) do
create(:user)
end
given(:undertaking) do
create(:undertaking)
end
background do
login_as(current_user)
end
scenario "can create comment" do
#below two because undertaking = user_id:2 & asking_id:1
create(:user)
create(:asking)
p undertaking
p Asking.find(1)
p User.find(2)
p User.find(1)
p Undertaking.all
visit undertaking_path(undertaking)
expect(current_path).to eq undertaking_path(1)
within("form#undertake-form-test") do
fill_in "content" , with: "heyheyhey"
end
click_button 'Send'
expect(page).to have_content 'heyheyhey'
end
end
This is spec/features/comment_spec.rb.
and this below is result command rspec.
#<Undertaking id: 1, title: "MyString", content: "MyText", result: false, user_id: 2, asking_id: 1, created_at: "2016-12-13 15:07:08", updated_at: "2016-12-13 15:07:08">
#<Asking id: 1, content: "MyText", fromlang: "MyString", tolang: "MyString", usepoint: 1, finished: false, title: "MyString", deadline: nil, user_id: 1, created_at: "2016-12-13 15:07:08", updated_at: "2016-12-13 15:07:08">
#<User id: 2, email: "shiba.hayato2#docomo.ne.jp", created_at: "2016-12-13 15:07:08", updated_at: "2016-12-13 15:07:08", provider: nil, uid: nil, name: "Shiruba", occupation: "大学生", age: 10, sex: "男性", content: "heyheyheyeheyeheye", skill: "日本語検定3級", picture: "/assets/default_user.jpg", point: 500, country: "Japan", language1: "Japanese", language2: "Korea", language3: "English">
#<User id: 1, email: "shiba.hayato1#docomo.ne.jp", created_at: "2016-12-13 15:07:08", updated_at: "2016-12-13 15:07:08", provider: nil, uid: nil, name: "Shiruba", occupation: "大学生", age: 10, sex: "男性", content: "heyheyheyeheyeheye", skill: "日本語検定3級", picture: "/assets/default_user.jpg", point: 500, country: "Japan", language1: "Japanese", language2: "Korea", language3: "English">
#<ActiveRecord::Relation [#<Undertaking id: 1, title: "MyString", content: "MyText", result: false, user_id: 2, asking_id: 1, created_at: "2016-12-13 15:07:08", updated_at: "2016-12-13 15:07:08">]>
F
Failures:
1) comment can create comment
Failure/Error: <%= #undertaking.id %>
ActionView::Template::Error:
undefined method `id' for nil:NilClass
and this below is undertaking_controller.rb.
class UndertakingController < ApplicationController
def show
#undertaking=Undertaking.find(params[:id])
#comment=Comment.new do |c|
c.user=current_user
end
end
end
and this below is undertaking/show.html.erb.
<%= #undertaking.id %>
Why do I have the error? Why #undertaking is nil in view although Undertaking.first is not nil in spec/features/comment_spec.rb?Please help me.
I think it has to do with the naming used for your controller . The convention is undertakings/show.html.erb for the view instead of undertaking/show.html.erb . I would also use
class UndertakingsController < ApplicationController
instead of
class UndertakingController < ApplicationController
Finally I would check that all my routes also have the correct naming. Hope that helps. Good luck

RSPEC Error: Expected #<ActiveRecord::Associations::CollectionProxy > got: nil

I am trying to test the following controller action(Using Ruby on Rails, RSPEC, and FactoryGirl):
class ContactsController < ApplicationController
before_action :authenticate_user!
def index
#contacts = current_user.contacts
# #contacts = Contact.all
end
Here is my contacts_controller_spec.rb file:
require 'rails_helper'
describe ContactsController do
before do
#user = FactoryGirl.create(:user_with_contacts)
sign_in #user
end
describe "GET INDEX" do
it "assigns #contacts" do
expect(assigns(:contacts)).to eq(#user.contacts)
end
end
Failure/Error: expect(assigns(:contact)).to eq([contact])
expected: [#<Contact id: 295, first_name: "Loy", email: "leon#hegmannhintz.net", phone_number: "6044339393", created_at: "2015-09-12 19:13:42", updated_at: "2015-09-12 19:13:42", last_name: "Wyman", user_id: 343>]
got: #<Contact id: nil, first_name: nil, email: nil, phone_number: nil, created_at: nil, updated_at: nil, last_name: nil, user_id: nil>
And here is my users_spec.rb file:
FactoryGirl.define do
factory :user do
email { Faker::Internet.email }
password { "32423fdsfasf42" }
factory :user_with_contacts do
transient do
contacts_count 2
end
after(:create) do |user, evaluator|
create_list(:contact, evaluator.contacts_count, user: user)
end
end
end
end
Any Help please? I have been stuck on this for a long time.
If i call
puts #user.inspect
I get
#<User id: 340, email: "johnson_kaulke#brekke.com", encrypted_password: "$2a$04$Si5k6Q1eYERvhQITXKBoIOGEzPyK50E3IQ.yjRcqmDj...", reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, sign_in_count: 0, current_sign_in_at: nil, last_sign_in_at: nil, current_sign_in_ip: nil, last_sign_in_ip: nil, created_at: "2015-09-12 19:13:42", updated_at: "2015-09-12 19:13:42">
and
calling
puts #user.contacts.inspect
I get
#<ActiveRecord::Associations::CollectionProxy [#<Contact id: 289, first_name: "Fae", email: "ariane#johnston.net", phone_number: "6044339393", created_at: "2015-09-12 19:13:42", updated_at: "2015-09-12 19:13:42", last_name: "Spinka", user_id: 340>, #<Contact id: 290, first_name: "Marcellus", email: "chloe_deckow#buckridge.net", phone_number: "6044339393", created_at: "2015-09-12 19:13:42", updated_at: "2015-09-12 19:13:42", last_name: "Bashirian", user_id: 340>]>
Its just when i call the assigns(:contacts) that the problem happens!
I think you forgot to invoke the controller action. :)
Try adding get :index
it "assigns #contacts" do
get :index
expect(assigns(:contacts)).to eq(#user.contacts)
end

Why is my after_save callback stopping my ActiveRecord association from saving properly?

When I comment out my after_save call back, my ActiveRecord associations work just fine. In Rails Console, you'd see:
> #report = Report.create :name => "foo"
=> #<Report id: 9, name: "foo", created_at: "2013-03-05 09:51:55", updated_at: "2013-03-05 09:51:55">
> #question = #report.questions.create :description => "bar"
=> #<Question id: 18, standard_id: nil, description: "bar", element_id: nil, condition_id: nil, blueprint_name: nil, blueprint_url: nil, created_at: "2013-03-05 09:52:32", updated_at: "2013-03-05 09:52:32", additive: false, instructions: nil>
> #report.questions
=> [#<Question id: 18, standard_id: nil, description: "bar", element_id: nil, condition_id: nil, blueprint_name: nil, blueprint_url: nil, created_at: "2013-03-05 09:52:32", updated_at: "2013-03-05 09:52:32", additive: false, instructions: nil>]
> #question.reports
=> [#<Report id: 9, name: "foo", created_at: "2013-03-05 09:51:55", updated_at: "2013-03-05 09:51:55">]
However, the associations stop working when I add the following after_save callback to question.rb:
def create_matching_surveys
self.reports.each do |report|
report.reviews.each do |review|
review.competitors.each do |competitor|
competitor.surveys.find_or_create_by_question_id(self.id)
end
end
end
end
Then, in Rails Console, you get:
> #report = Report.create :name => "foo"
=> #<Report id: 13, name: "foo", created_at: "2013-03-05 10:20:51", updated_at: "2013-03-05 10:20:51">
> #question = #report.questions.create :description => "bar"
=> #<Question id: 24, standard_id: nil, description: "bar", element_id: nil, condition_id: nil, blueprint_name: nil, blueprint_url: nil, created_at: "2013-03-05 10:21:02", updated_at: "2013-03-05 10:21:02", additive: false, instructions: nil>
> #report.questions
=> [#<Question id: 24, standard_id: nil, description: "bar", element_id: nil, condition_id: nil, blueprint_name: nil, blueprint_url: nil, created_at: "2013-03-05 10:21:02", updated_at: "2013-03-05 10:21:02", additive: false, instructions: nil>]
> #question.reports
=> []
This happens whether or not the report has reviews that have competitors.
The strange thing is I thought the callback was meant to happen after the question was saved? So by rights the association should save too before any of this happens, right?
How do I fix it?
UPDATE
I think I have to call the callback in the right spot in the object's life cycle, but I can't find that spot. Here's why I think this:
> #report = Report.create :name => "foo"
=> #<Report id: 20, name: "foo", created_at: "2013-03-05 12:29:35", updated_at: "2013-03-05 12:29:35">
> #question = #report.questions.create :description => "bar"
=> #<Question id: 31, standard_id: nil, description: "bar", element_id: nil, condition_id: nil, blueprint_name: nil, blueprint_url: nil, created_at: "2013-03-05 12:30:14", updated_at: "2013-03-05 12:30:14", additive: false, instructions: nil>
> #question.reports
=> []
> #question.update_attributes :description => "foo"
=> true
> #question.reports
=> [#<Report id: 20, name: "foo", created_at: "2013-03-05 12:29:35", updated_at: "2013-03-05 12:29:35">]
BTW, the method is now in question_observer.rb:
class QuestionObserver < ActiveRecord::Observer
def after_save(model)
model.reload
model.reports.reload
model.reports.each do |report|
report.reviews.each do |review|
review.competitors.each do |competitor|
competitor.surveys.find_or_create_by_question_id(model.id)
end
end
end
return true
end
end
The answer was to use a neat new callback hook called after_commit which was introduced with Rails 3.
See http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html#method-i-after_commit.
The only issue is after_commit doesn't work "out of the box" with transactional fixtures, but there are plenty of solutions out there, and I found this one worked well for me: https://supportbee.com/devblog/2012/01/14/testing-after_commitafter_transaction-with-rspec/

Why does Rails give me a 'No Method Error' for an attribute that exists on my model? - Rails 3

This is the error I get:
NoMethodError in Devise::RegistrationsController#create
undefined method `trial_duration' for nil:NilClass
app/models/user.rb:120:in `set_trial_end'
This is the relevant parts of my User.rb
before_create :set_trial_end
def set_trial_end
plan = self.plan
end_of_trial = Date.today + self.plan.trial_duration.days
self.trial_end_date = end_of_trial.to_date
end
What's strange is that if I look in my DB, for a user that is assigned a plan and look at the trial_duration attribute, I get a reply:
ruby-1.9.2-p0 > tu
=> #<User id: 29, email: "test50#abc.com", encrypted_password: "$2a$10$TKKQmBYem.vDq.mwDutv2u92Vick0X0jAIUQT6A9.FM....", password_salt: "$2a$10$TKKQmBYem.vDq.mwDutv2u", reset_password_token: nil, remember_token: nil, remember_created_at: nil, sign_in_count: 1, current_sign_in_at: "2011-06-18 23:03:12", last_sign_in_at: "2011-06-18 23:03:12", current_sign_in_ip: "127.0.0.1", last_sign_in_ip: "127.0.0.1", username: "test50", first_name: "Test", last_name: "Fifty", created_at: "2011-06-18 23:02:48", updated_at: "2011-06-18 23:03:12", invitation_token: nil, invitation_sent_at: nil, plan_id: 2, current_state: nil, confirmation_token: nil, confirmed_at: "2011-06-18 23:03:12", confirmation_sent_at: "2011-06-18 23:02:47", space_used: 0, failed_attempts: 0, unlock_token: nil, locked_at: nil, trial_end_date: "2011-07-02", active_subscription: nil, customer_id: nil>
ruby-1.9.2-p0 > tu.plan
=> #<Plan id: 2, name: "Boutique", storage: 100.0, num_of_projects: 99999, num_of_clients: 10, cached_slug: "boutique", created_at: "2011-01-31 09:46:57", updated_at: "2011-08-11 08:22:48", amount: 19, trial_duration: 14, trial_duration_unit: "days", currency: "USD", billing_cycle: 28, billing_cycle_unit: "days">
ruby-1.9.2-p0 > tu.plan.trial_duration
=> 14
ruby-1.9.2-p0 > tu.plan.trial_duration.days
=> 14 days
So not sure why Rails is giving me that error.
Any ideas?
Edit 1:
In my view, I have this:
<% if params[:promo] %>
<%= text_field_tag "xcode", nil, :placeholder => "Coupon Code" %><br />
<%= hidden_field_tag(:plan_id, "1") %>
<% end %>
And in the POST, from my log, you can see the parameter plan_id set to 1
Started POST "/users" for 127.0.0.1 at 2011-08-24 03:11:07 -0500
Processing by Devise::RegistrationsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"Jp2GHxnuVOVnI/sfr1CB4EQ9URCTJynv/2Ek4AiU8Lg=", "user"=>{"username"=>"test.user", "first_name"=>"Testing", "last_name"=>"User", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "email"=>"testuser#abc.com"}, "xcode"=>"testcouponcode", "plan_id"=>"1", "commit"=>"Register"}
nil
But I am still getting the error Plan can't be blank on the next page.
I have added the after_validation callback instead of after_create, so I no longer the NoMethodError. But the problem isn't solved.
You're using a before_create hook that's assuming that self.plan is set:
before_create :set_trial_end
def set_trial_end
#...
end_of_trial = Date.today + self.plan.trial_duration.days
And your error says:
undefined method `trial_duration' for nil:NilClass
So self.plan is nil during your set_trial_end call.
Perhaps you want an after_create hook so that you'll have some instance data to work with. Even then you'd want to check for self.plan.nil? (just in case) and use a validation to make sure you get one:
class User < ActiveRecord::Base
validates_presence_of :plan_id
#...
Or maybe an after_validation hook would serve you better.

Resources