rspec/shoulda fails comparing two seemingly identical collections - ruby-on-rails

(mentioning shoulda because I know it is in Gemfile - but don't know how it affects rspec tests, have no experience with it, don't know if it's involved here or not)
I have two [controller] tests:
first:
it { should assign_to( :messages ).with(
current_user.messages.ordered.decorate ) }
and second:
it { should assign_to( :messages ).with(
current_user.messages.order("send_at DESC").decorate ) }
note: the message.rb defines:
scope :ordered, order( 'messages.send_at DESC' )
,so they are testing exactly the same thing - except that ordered method used to sort by a wrong field, and the first test would always pass, expected collection being the same as the actual; hence the 2nd test, which explicitly demands the correct sorting order.
Now, the madness: 1st test passes, 2nd test fails. Rspec prints:
Expected action to assign #<DecoratedEnumerableProxy of
MessageDecorator for [#<Message id: 1, ... >, <Message id: 2, ...>]>,
but got #<DecoratedEnumerableProxy of MessageDecorator for [#<Message
id: 1, ... >, <Message id: 2, ...>]>
You guessed it - the "expected.." and "but got.." parts printed here are absolutely identical. I've triple-checked them with diff. If both tests were failing, I would not be so perplexed, but 1st one passes, and 2nd one fails. I suspected that 1st test must be producing some kind of a side effect, but no, reordering or commenting out one of the tests doesn't change anything.
Any ideas?

Related

Testing an update operation in RSpec, evaluates to true, but test fails?

I have this test:
it "saves the notification id in the referral for future reference" do
expect { subject.perform(*args) }
.to change(referral, :notification_id).from(nil).to(customer_notification_delivery.id)
end
And the code that it runs on top is:
if notification.present?
referral.update(friend_customer_notification_delivery_id: notification.id)
end
I added a few debug messages, to check on them after firing the test, to ensure that this condition was being met, and the code was being run, and I got true for both
p notification.present?
p referral.update(friend_customer_notification_delivery_id: customer_notification_delivery.id)
Anything I am missing? Why the update returns true, but the value is not getting updated on the test?
The output I get:
expected #notification_id to have changed from nil to 5, but did not change
referral in your test and referral in your object-under-test are two different objects, I'm willing to bet. Changes to one do not affect the other. referral in the test does not magically pull up updates from the related database record made by some other code.
I normally do it like this
it "saves the notification id in the referral for future reference" do
expect { subject.perform(*args) }
.to change{ referral.reload.notification_id }.from(nil).to(customer_notification_delivery.id)
end

Heisenbug with Julia's parse?

On today's Advent of Code I needed to parse strings into integers. The function I wrote for that was
function fd(s::String, fromto::UnitRange)::Bool
try
parse(UInt, s) in fromto
catch ArgumentError
false
end
end
That function was called several times within an isvalid which was called for all inputs to count the number of valid things.
The result was always 0 and the respective tests kept failing. Then I extracted one of the failing test things and debugged into isvalid, it passed!
I rearraged a few things and tested more, the same thing kept happening:
Running the code regularly, fd never returned true.
When stepping through, I got true where expected.
After replacing the parse to
function fd(s::String, fromto::UnitRange)::Bool
parsed = tryparse(UInt, s)
if isnothing(parsed)
false
else
parsed in fromto
end
end
it immediately worked always and the exercise was solved.
Shouldn't these two versions of the function always return the same result? What happened here?
Update1
This was the example that I used:
println("Sample is ", isvalid(Dict(
"hcl" => "#623a2f", # check with regex
"ecl" => "grn", # checked with set
"pid" => "087499704", # check with regex
"hgt" => "74in", # check with regex
"iyr" => "2012", # this was parsed
"eyr" => "2030", # this was parsed
"byr" => "1980", # this was parsed
)))
Update 2
This post only has a subset of the code. If you want to try yourself, you can get the full file at GitHub. I also recorded a video showing the differing behavior with and without debugging.
The thing is demonstrated quite clearly with #assert fd("2010", 2000:2020) == fdt("2010", 2000:2020) failing in one scenario and not in the other.

RSpec do not see updated attributes in factory_bot object

I have report object called SiegeReport, that makes some calculations and returns integer object. When there is no warrior in the building, then siege_ability equals 0. The code itself is not important here, because it works fine in console and in application. Factories made by factory_bot work ok in all the other examples. I just have problem with testing the method:
require 'rails_helper'
RSpec.describe Reports::SiegeReport do
subject(:siege_report) { Reports::SiegeReport.new(building: building).call }
let(:building) { create(:building, granary: 100) }
let(:clan) { create(:clan) }
context 'with 1 infantry' do
let(:warrior) { create(:warrior, clan_id: clan.id, building_id: building.id) }
it 'returns 9' do
expect(siege_report).to eq(9)
end
end
end
RSpec returns:
Reports::SiegeReport siege ability with 1 infantry returns 9
Failure/Error: expect(siege_report).to eq(9)
expected: 9
got: 0
(compared using ==)
I checked it with pry and warrior object is valid, even building.warriors returns warrior, but in attributes number of warriors is still 0. The very same example works when i type it manually in console. How can I make RSpec update building attributes before testing?
Ok, I found solution but is really not elegant:
it 'returns 9' do
warrior.save
expect(siege_report).to eq(9)
end
It works but it seems to me that it is not the proper way of testing. Still, I can't find a better solution.

Class methods and expect argument

I have simple test case: (board has_many links, link belongs_to board)
context "with feeds" do
let (:board) {FactoryGirl.create(:board_tree)}
it "returns links from all feeds" do
expect(board.all_links.count).to eq Link.all.count
end
end
It fails (expected 8 got 2) - which is ok, I expected the same.
Now I switch this expect with:
expect(Link.all.count).to eq board.all_links.count
This time I thought it will fail with expected 2 got 8, but instead of it I get: expected 2, got 0.
Any idea why? For me both expectations should be equal.
If you try changing:
let (:board) {FactoryGirl.create(:board_tree)}
into
let! (:board) {FactoryGirl.create(:board_tree)}
you will see that the results won't change when you invert the conditions. The problem is that links are created by rspec the first time that the board variable is referenced, so in the second example, there are no links yet when rspec is evaluating the expect(Link.all.count) part.

Trouble on counting ActiveRecord instances in an array

I am using Ruby on Rails 3 and I would like to solve a issue counting ActiveRecord instances in an array.
I have this code
data = Account.where({:name => "Test_name", :city => "Test_city"}).limit(10)
The data debug is
#<Account:0x000001029d2da0>#<Account:0x000001029d2c60>#<Account:0x000001029d2bc0>#<Account:0x000001029d2b20>
The data inspecting is
"[#<Account name: \"Test_name\", city: \"Test_city\">, #<Account … >, #<Account id… >, …]"
Doubt: The ##<...> should be something like #<Account...>,#<Account...>,<...> (note commas)?
If in my code I use the following
data_count = data.count
The data_count is
nil
Why is it nil? How should I count accounts?
If I use result = data.class the debug of result is nil, but if I use result = data.classthe debug is "{\"inheritable_attributes\":{}}".
If I use Account.find_by_name("Test_name") instead of Account.where(...) I get same results as above.
To get to the bottom of things, start the rails console with:
$ rails c
Given that Account is an ActiveRecord model, you should be able to do the following in the rails console:
> Account.all.count
=> 100
> Account.where(:status=>'active')
=> [ #<Account id: 1, name: "a1", ...>, #<Account id: 2, name: "a2", ...>, #<Account id: 3, name: "a3", ...>, ...]
I'm doing a lot of hand waving here with ... since I don't know your schema. Replace the where condition with whatever works for your situation. The returned value should look like an array with a list of all the rows in the database that match the condition. BTW, an array is a list of element, and inspect (as well as the default display in the console) show element separated by commas. I haven't used debug so I can't comment on what it should do.
You can verify that the returned value is an AREL, and should be able to do some other operations to verify things work as expected.
> Account.where(:status=>'active').class
=> ActiveRecord::Relation
> Account.where(:status=>'active').size
=> 99
> Account.where(:status=>'active').count
=> 99
> Account.where(:status=>'active').limit(10).count
=> 10
If these work as expected in the console, there may be something in the view that is obscuring the correct behavior. In that case you'll need to post the details of your view code. If the strange behavior still occurs in the console, I would suggest posting the minimal parts of the actual model code that still exhibit the problem, along with the migration so we can see the schema.
I think you are having some problem in where condition.
Can you show the attributes value used in where clause.
For me its working fine:
data = Account.where('id != 0').limit(10)
data_count = data.count
Use the following:
data = Account.where("id = 2 and email = 'test_email#test.com'")

Resources