factory girl nested attributes - ruby-on-rails

In rails console,I run this command
bill = Bill.last
bill.bill_extras
The result is like that.
[#<BillExtra id: 141, bill_id: 621, content: "extra fee", prize: #<BigDecimal:7fa2e37d4f50,'0.248E3',9(18)>, created_at: "2012-08-02 04:48:12", updated_at: "2012-08-02 04:50:41">]
In spec file,
require 'spec_helper'
require 'pry'
describe Bill do
before(:all) do
#project=Factory(:project_started)
#customer_bill=Factory(:customer_invoice, :project => #project)
#creavite_bill=Factory(:creative_invoice, :project => #project)
#bill_extra=Factory.build(:bill_extra, :bill => #customer_bill)
end
binding.pry
end
When I'm debugging,run these commands in pry,
#customer_bill.bill_extras
it will be an empty array.

I think the syntax isn't quite right there.
FactoryGirl.create(:project_started)
Is the way to create items.

Related

RSpec controller GET #index test is returning an empty ActiveRecord array instead of my model

Please excuse my rustiness, first time touching Rails and this project in quite some time.
Ruby Version: 2.5.0
Rails Version: 5.1.7
RSpec Version: 3.9.3
FactoryBot Version: 6.2.0
This is my scripts_controller_spec.rb file with model creation and the test in question:
require 'rails_helper'
describe ScriptsController, type: :controller do
userID_1 = User.create!(
email: 'ueferfrfrf#u1.com',
password: 'useruser',
password_confirmation: 'useruser'
)
script1 = Script.create!(
name: 'YWoodcutter',
skill: 'Woodcutting',
bot_for: 'TRiBot',
game_for: 'Oldschool Runescape 07',
user_id: userID_1.id
)
script1.save
describe "GET #index" do
it "assigns #scripts" do
get :index
p script1
expect(assigns(:scripts)).to eq([script1])
end
end
When running the tests, the print line above outputs this, as expected:
#<Script id: 1, name: "YWoodcutter", skill: "Woodcutting", bot_for: "TRiBot", game_for: "Oldschool Runescape 07", user_id: 1, created_at:
"2021-10-19 08:29:43", updated_at: "2021-10-19 08:29:43">
However, I get this test failure:
Failures:
ScriptsController GET #index assigns #scripts
Failure/Error: expect(assigns(:scripts)).to eq([script1])
expected: [#<Script id: 1, name: "YWoodcutter", skill: "Woodcutting", bot_for: "TRiBot", game_for: "Oldschool Runescape 07",
user_id: 1, created_at: "2021-10-19 08:29:43", updated_at: "2021-10-19
08:29:43">]
 
got: #<ActiveRecord::Relation []>
(compared using ==)
My scripts_controller.rb index function looks like so:
class ScriptsController < ApplicationController
def index
#scripts = Script.order(:created_at)
end
Let me know if you need any more info, and thanks for your help!
I think the Script object is not getting created before calling the index action. Because of this, you are getting the empty ActiveRecord::Relation. In this situation let! should fix your problem
require 'rails_helper'
describe ScriptsController, type: :controller do
let!(:user_1) do
User.create!(
email: 'ueferfrfrf#u1.com',
password: 'useruser',
password_confirmation: 'useruser'
)
end
let!(:script1) do
Script.create!(
name: 'YWoodcutter',
skill: 'Woodcutting',
bot_for: 'TRiBot',
game_for: 'Oldschool Runescape 07',
user_id: user_1.id
)
end
describe "GET #index" do
before { get :index }
it "assigns #scripts" do
expect(assigns(:scripts)).to eq([script1])
end
end
end
Based on the current code, it seems you were not calling all script.
Using
### Controller
#scripts = Script.all.order(:created_at)
### Test
## Should use factories to create the records
let(:user) do
create(:user, email: 'ueferfrfrf#u1.com',
password: 'useruser',
password_confirmation: 'useruser')
end
let(:script) do
create(:script, name: 'YWoodcutter',
skill: 'Woodcutting',
bot_for: 'TRiBot',
game_for: 'Oldschool Runescape 07',
user: user)
end
should fix it.

Rails Rspec allow multiple method call in one line

desc 'Remove credential state users who no longer request for confirm otp within 10 minutes'
task failed_user_cleaner: :environment do
puts "Daily UserRecord Cleaning CronJob started - #{Time.now}"
#user = User.with_state("credentials").with_last_otp_at(Time.now - 10.minutes)
Users::Delete.new(#user).destroy_all
puts "Daily UserRecord Cleaning CronJob ended - #{Time.now}"
end
Above is crop job rake file code.
then I've tried in many times and found in many times.
But I couldn't find the way to write unit test case for above job.
Help me to write test case correctly.
here is my spec code
require 'rails_helper'
describe 'users rake tasks' do
before do
Rake.application.rake_require 'tasks/users'
Rake::Task.define_task(:environment)
end
context 'when remove credential state users who no longer request for confirm otp within 10 minutes' do
let(:user) { create(:user, last_otp_at: Time.now - 11.minutes, state: "credentials") }
let (:run_users_rake_task) do
Rake.application.invoke_task 'users:failed_user_cleaner'
end
it 'calls right service method' do
#users = Users::Delete.new([user])
expect(#users).to receive(:destroy_all)
run_users_rake_task
end
end
end
here is the error log
Failures:
1) users rake tasks when remove credential state users who no longer request for confirm otp within 10 minutes calls right service method
Failure/Error: expect(#users).to receive(:destroy_all)
(#<Users::Delete:0x0000556dfcca3a40 #user=[#<User id: 181, uuid: nil, phone: "+66969597538", otp_secret: nil, last_otp_at: "2021-09-30 09:32:24.961548000 +0700", created_at: "2021-09-30 09:43:24.973818000 +0700", updated_at: "2021-09-30 09:43:24.973818000 +0700", email: nil, avatar: "https://dummyimage.com/300x300/f04720/153572.png?t...", refresh_token: "eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MzI5Njk4MDQsImV4c...", first_name_en: "Jenise", first_name_th: "Damion", last_name_en: "McCullough", last_name_th: "Beatty", nationality: "TH", thai_national_id: nil, thai_laser_code: nil, company_id: 200, role: nil, state: "credentials", date_of_birth: "2020-10-30 00:00:00.000000000 +0700", deleted_at: nil, password_digest: "$2a$04$jfR9X9ci06602tlAyLOoRewTK1lZ12vJ2cZ9Dc2ov4F...", username: "zreejme238", shopname: nil, access_token: nil, locked_at: nil, login_attempts: 0, locale: "th", scorm_completed: false>]>).destroy_all(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
# ./spec/tasks/users_spec.rb:19:in `block (3 levels) in <top (required)>'
You are creating two instances of Users::Delete when running this test, one within the test and one within the task. Since the instance within the test is not used, it is incorrect to expect it to receive a message.
Rspec has an expectation, expect_any_instance_of, that will fix this however consider reading the full page since it can create fragile or flaky tests. If you wanted to use this method, your test would look something like:
it 'calls right service method' do
expect_any_instance_of(Users::Delete).to receive(:destroy_all)
run_users_rake_task
end
Personally I'd instead check that the expected users were deleted with something like:
it 'removes the user' do
expect { run_users_rake_task }.to change { User.exists?(id: #user.id) }.to(false)
end
Unless you want to use any_instance_of (which is a code smell) you need to stub the Users::Delete method so that it returns a double and put the expectation on the double:
require 'rails_helper'
describe 'users rake tasks' do
before do
Rake.application.rake_require 'tasks/users'
Rake::Task.define_task(:environment)
end
context 'when remove credential state users who no longer request for confirm otp within 10 minutes' do
let(:user) { create(:user, last_otp_at: Time.now - 11.minutes, state: "credentials") }
let(:run_users_rake_task) do
Rake.application.invoke_task 'users:failed_user_cleaner'
end
let(:double) do
instance_double('Users::Delete')
end
before do
allow(Users::Delete).to receive(:new).and_return(double)
end
it 'calls right service method' do
expect(double).to receive(:destroy_all)
run_users_rake_task
end
end
end
However this really just tells us that the API of the service object is clunky and that you should write a class method which both instanciates and performs:
module Users
class Delete
# ...
def self.destroy_all(users)
new(users).destroy_all
end
end
end
desc 'Remove credential state users who no longer request for confirm otp within 10 minutes'
#...
Users::Delete.destroy_all(#user)
# ...
end
require 'rails_helper'
describe 'users rake tasks' do
# ...
context 'when remove credential state users who no longer request for confirm otp within 10 minutes' do
# ...
it 'calls right service method' do
expect(Users::Delete).to receive(:destroy_all)
run_users_rake_task
end
end
end

Rspec: test if method was called on an object inside a job?

I made a job to call a method if ended_at was less than today. For some reason I keep getting failures even if I throw a binding pry right at the moment it is about to break and manually call the method and it works fine. However, I still get a failure if I just let the spec test run on its own.
My job:
class RemoveOldEventsFromAlgoliaJob < ApplicationJob
queue_as :default
def perform
old_events = Event.where("ended_at < ?", Date.today)
old_events.each do |e|
e.remove_from_algolia
end
end
end
My method:
def remove_from_algolia
index = Algolia::Index.new(##ALGOLIA_INDEX_NAME)
index.delete_object(self.id)
end
My spec test:
require 'rails_helper'
RSpec.describe RemoveOldEventsFromAlgoliaJob, type: :job do
it "will remove old events from the index" do
ActiveJob::Base.queue_adapter = :test
event = FactoryGirl.create(:event, title: "EXPIRED EVENT", ended_at: 1.day.ago)
RemoveOldEventsFromAlgoliaJob.perform_now
expect(event).to receive(:remove_from_algolia)
end
end
RSpec output:
Failures:
1) d will remove old events from the index
Failure/Error: expect(event).to receive(:remove_from_algolia)
(#<Event id: 401, uuid: "6e9a6f08-c34d-45af-9f3c-870b28643809", organization_id: nil, event_type_id: nil, name: "cool-event", title: "Cool Event", description: "Rad thing that's gonna happen", platform_type: "ee", platform_id: "12345678", platform_url: "http://event.com/12345678", featured: false, capacity: 100, rsvp_count: nil, attendee_count: nil, status: "upcoming", started_at: "2017-10-21 03:00:39", ended_at: "2017-03-23 07:00:00", deleted_at: "2017-10-21 03:00:39", created_at: "2017-03-24 00:24:54", updated_at: "2017-03-24 00:24:54", location_line_1: nil, location_line_2: nil, location_city: nil, location_state: nil, location_zip: nil, location_country: nil>).remove_from_algolia(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
# ./spec/jobs/remove_old_events_from_algolia_job_spec.rb:10:in `block (2 levels) in <top (required)>'
Finished in 0.8828 seconds (files took 7.12 seconds to load)
1 example, 1 failure
Failed examples:
rspec ./spec/jobs/remove_old_events_from_algolia_job_spec.rb:4 # RemoveOldEventsFromAlgoliaJob will remove old events from the index
Coverage report generated for RSpec to /workproject/coverage. 384 / 496 LOC (77.42%) covered.
I can put a binding.pry inside my job during the spec test and call that method successfully:
4: def perform
5:
6: old_events = Event.where("ended_at < ?", Date.today)
7: old_events.each do |e|
=> 8: binding.pry
9: e.remove_from_algolia
10: end
11: end
[1] pry(#<RemoveOldEventsFromAlgoliaJob>)> e.remove_from_algolia
=> {"deletedAt"=>"2017-03-24T00:37:53.743Z", "taskID"=>206043962, "objectID"=>"409"}
[2] pry(#<RemoveOldEventsFromAlgoliaJob>)>
Try using a stub for event instead of a real Event model:
require 'rails_helper'
RSpec.describe RemoveOldEventsFromAlgoliaJob, type: :job do
it "will remove old events from the index" do
ActiveJob::Base.queue_adapter = :test
event = double('event')
allow(Event).to receive(:where).and_return([event])
expect(event).to receive(:remove_from_algolia)
RemoveOldEventsFromAlgoliaJob.perform_now
end
end

Mongomapper querying on 'belongs_to' model

I have two models:
class Post
include MongoMapper::Document
many :comments
key :content, String
end
and
class Comment
include MongoMapper::Document
belongs_to :post
key :post_id, ObjectId
key :content, String
end
in a rails console session I can find all Posts:
Post.all # -> [#<Post _id: BSON::ObjectId('519b0b613…
and all comments associated with a Post:
post = Post.first # -> #<Post _id: BSON::ObjectId('519b0b613e477b…
post.comments # -> [#<Comment _id: BSON::ObjectId('519d14f93e…
however, the following query strangely returns an empty array
Comment.all # -> []
Why? How can I get a list of all comments independently of the posts?
With models exactly as per your post, it works for me as shown in the the following test,
running rails (3.2.13), mongo_mapper (0.12.0), mongo (1.6.4). Next time please post the full minimal script, you probably just have a simple error.
test/unit/post_test.rb
require 'test_helper'
class PostTest < ActiveSupport::TestCase
def setup
Post.delete_all
Comment.delete_all
end
test "post and comment" do
post = Post.create(:content => 'Twas brillig')
comment = Comment.create(:post_id => post.id, :content => 'and the slythy toves')
post.comments << comment
assert_equal 1, Post.count
assert_equal 1, Comment.count
puts
puts "all posts: #{Post.all.inspect}"
puts "first post comments: #{Post.first.comments.inspect}"
puts "all comments: #{Comment.all.inspect}"
end
end
$ rake test
Run options:
# Running tests:
[1/1] PostTest#test_post_and_comment
all posts: [#<Post _id: BSON::ObjectId('51ddb56a7f11ba9bbf000001'), content: "Twas brillig">]
first post comments: [#<Comment _id: BSON::ObjectId('51ddb56a7f11ba9bbf000002'), content: "and the slythy toves", post_id: BSON::ObjectId('51ddb56a7f11ba9bbf000001')>]
all comments: [#<Comment _id: BSON::ObjectId('51ddb56a7f11ba9bbf000002'), content: "and the slythy toves", post_id: BSON::ObjectId('51ddb56a7f11ba9bbf000001')>]
Finished tests in 0.036030s, 27.7546 tests/s, 0.0000 assertions/s.
1 tests, 0 assertions, 0 failures, 0 errors, 0 skips
ruby -v: ruby 2.0.0p195 (2013-05-14 revision 40734) [x86_64-darwin12.3.0]

RSpec/Mongoid inheritance of defaults completely different result in test/development

This is one of those ones that makes you think you're going insane...
I have a class Section, and a DraftSection that inherits from it:
(Trimmed for brevity)
class Section
include Mongoid::Document
belongs_to :site
field :name, type: String
end
And
class DraftSection < Section
field :name, type: String, default: "New Section"
end
All simple stuff... console proves (again, trimmed for brevity):
004 > site = Site.first
=> #<Site _id: initech, name: "INITECH">
005 > site.sections.build
=> #<Section _id: 1, site_id: "initech", name: nil>
006 > site.draft_sections.build
=> #<DraftSection _id: 2, site_id: "initech", name: "New Section">
As you can see - the draft section name correctly defaults to "New Section" as it is overridden in the subclass.
Now when I run this spec:
describe "#new" do
it "should return a draft section" do
get 'new', site_id: site.id, format: :json
assigns(:section).should == "Something..."
end
end
Which tests this controller method:
def new
#section = #site.draft_sections.build
respond_with #section
end
Which fails (as expected), but with this:
Failure/Error: assigns(:section).should == "Something..."
expected: "Something..."
got: #<DraftSection _id: 1, site_id: "site-name-4", name: nil> (using ==)
What gives???
Update:
I figured it might be an issue with the different environment settings, so I looked at the mongoid.yml config file and saw this in the options:
# Preload all models in development, needed when models use
# inheritance. (default: false)
preload_models: true
I added it to the test environment settings too, but still no joy :(
Update 2 - the plot thickens...
Thought I'd try loading up the console in the test environment and trying the same as before:
001 > site = Site.first
=> #<Site _id: initech, name: "INITECH">
002 > site.draft_sections.build
=> #<DraftSection _id: 1, site_id: "initech", name: "New Section">
WTF?
Ok, no one's listening, but I'll post the solution here for future reference anyway...
For some reason, some time ago I had a debug session that meant I had left this code in my Spork.each_run block
# Reload all model files when run each spec
# otherwise there might be out-of-date testing
# require 'rspec/rails'
Dir["#{Rails.root}/app/controllers//*.rb"].each do |controller|
load controller
end
Dir["#{Rails.root}/app/models//*.rb"].each do |model|
load model
end
Dir["#{Rails.root}/lib//*.rb"].each do |klass|
load klass
end
This was causing the models to get reloaded on each run of a spec. Not surprisingly, this screwed up the way the classes were set up in memory whilst the specs were running.
Definitely explains why it was such a hard one to debug...
So for future googlers with similar inheritance problems in Rspec only - make sure that there's nothing reloading models anywhere in the test stack.

Resources