Rspec new expectation syntax - rspec2

I've the following rspec unit test:
require 'spec_helper'
describe Article do
describe ".recents" do
it "includes articles created less than one week ago" do
article = Article.create(created_at: Date.today - 1.week + 1.second)
expect(Article.recents).to eql([article])
end
it "excludes articles published at midnight one week ago" do
article = Article.create!(:created_at => Date.today - 1.week)
expect(Article.recents).to be_empty
end
end
end
and the Articlemodel:
class Article < ActiveRecord::Base
attr_accessible :description, :name, :price, :created_at
scope :recents, where('created_at <= ?', 1.week.ago)
end
when I run my tests I get:
1) Article.recents includes articles created less than one week ago
Failure/Error: expect(Article.recents).to eql([article])
expected: [#<Article id: 60, name: nil, description: nil, price: nil, created_at: "2012-11-14 00:00:01", updated_at: "2012-11-21 10:12:33", section_id: nil>]
got: [#<Article id: 60, name: nil, description: nil, price: nil, created_at: "2012-11-14 00:00:01", updated_at: "2012-11-21 10:12:33", section_id: nil>]
(compared using eql?)
Diff:#<ActiveRecord::Relation:0x007ff692bce158>.==([#<Article id: 60, name: nil, description: nil, price: nil, created_at: "2012-11-14 00:00:01", updated_at: "2012-11-21 10:12:33", section_id: nil>]) returned false even though the diff between #<ActiveRecord::Relation:0x007ff692bce158> and [#<Article id: 60, name: nil, description: nil, price: nil, created_at: "2012-11-14 00:00:01", updated_at: "2012-11-21 10:12:33", section_id: nil>] is empty. Check the implementation of #<ActiveRecord::Relation:0x007ff692bce158>.==.
# ./spec/models/article_spec.rb:7:in `block (3 levels) in <top (required)>'
Could someone please help me to figure out what's the error in my test?
It seems good for me.

You are comparing an activerecord relation (Article.recents) to an array ([article]), which is why the expectation is failing. (It looks like they are the same in the spec results because inspect converts the relation into an array before printing it out.)
Change your first expectation to this:
expect(Article.recents.to_a).to eql([article])

Related

Difference between expect and expect_any_instance_of

I have method in my controller that looks like this:
def resend_confirmation
#current_user.send_confirmation_instructions
render json: nil, status: 200
end
I've written following spec for that method:
require 'rails_helper'
describe 'POST /api/v1/users/:id/resend_confirmation' do
let!(:current_user) { create(:user) }
before do
expect(current_user).to receive(:send_confirmation_instructions)
post resend_confirmation_api_v1_user_path(current_user),
headers: http_authorization_header(current_user)
end
describe 'response' do
it 'is empty' do
expect(response.body).to eq 'null'
end
end
it 'returns 200 http status code' do
expect(response.status).to eq 200
end
end
But the problem is that this spec is not passing. This line is failing:
expect(current_user).to receive(:send_confirmation_instructions)
when I change that to
expect_any_instance_of(User).to receive(:send_confirmation_instructions)
everything works pretty well. Could someone explain me why spec with expect syntax is not passing?
EDIT:
This how error looks:
Failures:
1) POST /api/v1/users/:id/resend_confirmation returns 200 http status code
Failure/Error: expect(current_user).to receive(:send_confirmation_instructions)
(#<User id: 4175, email: "test1#user.com", date_of_birth: "1990-01-01", created_at: "2016-07-18 06:56:52", updated_at: "2016-07-18 06:56:52", sex: "male", touch_id_enabled: false, first_name: "Test", last_name: "User", athena_health_patient_id: nil, photo_url: nil, admin: false, one_signal_player_id: "1", phone_number: nil, state: nil, address: nil, city: nil, zip_code: nil, phone_number_confirmed: false>).send_confirmation_instructions(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
# ./spec/requests/api/v1/user/resend_confirmation_spec.rb:7:in `block (2 levels) in <top (required)>'
2) POST /api/v1/users/:id/resend_confirmation response is empty
Failure/Error: expect(current_user).to receive(:send_confirmation_instructions)
(#<User id: 4176, email: "test2#user.com", date_of_birth: "1990-01-01", created_at: "2016-07-18 06:56:53", updated_at: "2016-07-18 06:56:53", sex: "male", touch_id_enabled: false, first_name: "Test", last_name: "User", athena_health_patient_id: nil, photo_url: nil, admin: false, one_signal_player_id: "2", phone_number: nil, state: nil, address: nil, city: nil, zip_code: nil, phone_number_confirmed: false>).send_confirmation_instructions(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
# ./spec/requests/api/v1/user/resend_confirmation_spec.rb:7:in `block (2 levels) in <top (required)>'
expects(...) sets the expectation on a particular instance. When the POST request is executed, your app will try to identify the user referenced in your request information, and will create an instance that represents it.
However, that instance is not the same you prepared in your test. It indeed references the same user object, but it's not the same Ruby object.
Therefore, inside your tests, the current_user that is used is not the one you set the expectations on.
Using expect_any_instance_of instead affects every instance of user created, hence also the one that will be created to satisfy the request.

Ruby validation passes in console, fails in rake task

This is my EmailContact model:
class EmailContact < ActiveRecord::Base
validates :email, :presence => true, :email => true
end
I am using the ruby gem valid_email.
I run the following in my rails console, in the same environment as my rake task I will show later:
>> email_contact = EmailContact.new(:email => 'a253545#gmail.com')
>> email_contact.valid?
true
So, as you can see, in the rails console I am building an EmailContact and it is valid.
Then I run this in my rake task:
list_entity = {:branch=>"Nashua Branch-YMCA of Greater Nashua", :branch_id=>"485", :call_type=>nil, :client_id=>"2264", :client_name=>"YMCA of Greater Nashua", :date_of_birth=>nil, :email=>"a253545#gmail.com", :first_name=>"Sridhar", :last_name=>"Tipirneni", :list_entity_id=>"277795", :mem_id=>"4085008", :mem_unit_id=>"2138728", :member_id=>"0213262-01", :membership_type=>"Dual 2 Adult Family", :membership_type_id=>"5203", :most_recent_join_date=>nil, :old_membership_type=>nil, :phone_number=>"(970)456-1010", :primary_language=>"English", :termination_date=>nil, :termination_reason=>nil, :unit_id=>"0213262", :unit_type=>nil, :visits=>nil, :"#i:type"=>"c:NpsListEntityDto"}
email_contact = EmailContact.new(list_entity.except(:"#i:type"))
puts email_contact.valid?
This returns false. The only validation, at all, is the email. Why does this email validate successfully in my console but fail in my rake task?
FYI, when I remove :email => true from my EmailContact model and only validate the presence of an :email, they both work fine. So the issue is definitely within the :email => true piece of my validation, but I don't understand why it passes in one place and fails in another.
EDIT
In my console, my model looks like this when using the full list_entity:
#<EmailContact id: nil, branch: "Nashua Branch-YMCA of Greater Nashua", branch_id: 485, call_type: nil, client_id: 2264, client_name: "YMCA of Greater Nashua", date_of_birth: nil, email: "a253545#gmail.com", first_name: "Sridhar", last_name: "Tipirneni", list_entity_id: 277795, mem_id: "4085008", mem_unit_id: "2138728", member_id: "0213262-01", membership_type: "Dual 2 Adult Family", membership_type_id: 5203, most_recent_join_date: nil, old_membership_type: nil, phone_number: "(970)456-1010", primary_language: "English", termination_date: nil, termination_reason: nil, unit_id: "0213262", visits: nil, loaded_at: nil, failed_at: nil, unit_type: nil, created_at: nil, updated_at: nil, list_id: nil>
In my rake task, when I run email_contact.inspect, this is returned:
#<EmailContact id: nil, branch: "Nashua Branch-YMCA of Greater Nashua", branch_id: 485, call_type: nil, client_id: 2264, client_name: "YMCA of Greater Nashua", date_of_birth: nil, email: "a253545#gmail.com", first_name: "Sridhar", last_name: "Tipirneni", list_entity_id: 277795, mem_id: "4085008", mem_unit_id: "2138728", member_id: "0213262-01", membership_type: "Dual 2 Adult Family", membership_type_id: 5203, most_recent_join_date: nil, old_membership_type: nil, phone_number: "(970)456-1010", primary_language: "English", termination_date: nil, termination_reason: nil, unit_id: "0213262", visits: nil, loaded_at: nil, failed_at: nil, unit_type: nil, created_at: nil, updated_at: nil, list_id: nil>
As you can see, they are both the exact same - The console model is valid, the rake model is invalid.
EDIT 2
I am using the valid_email gem, mentioned above. Here is the filepath:
/Users/luigi/.rvm/gems/ruby-2.0.0-p247#hub/gems/valid_email-0.0.4/lib/valid_email/email_validator.rb
All of my other gems are stored here as well it seems like.
It may also be worth mentioning that I get this warning before the validation fails:
[deprecated] I18n.enforce_available_locales will default to true in
the future. If you really want to skip validation of your locale you
can set I18n.enforce_available_locales = false to avoid this message.
20 hours later, I found the issue.
Using savon, all of the strings returned in my hash were being converted to a datatype of Nori::StringWithAttributes. The encoding was the same (UTF-8), but the class was different.
Running email_contact.email = email_contact.email.to_s prior to checking if the model is valid solves the issue.

Why does ActiveRecord association return two copies of every associated object

I have...
class Report < ActiveRecord::Base
has_and_belongs_to_many :elements
end
class Element < ActiveRecord::Base
has_and_belongs_to_many :reports
end
I'm seeing some very weird behaviour at the command line:
$Element.all
[#<Element id: 1, name: "fdafda", created_at: "2013-03-12 02:10:56", updated_at: "2013-03-12 02:10:56">,
#<Element id: 2, name: "Foo", created_at: "2013-03-14 10:46:56", updated_at: "2013-03-14 10:46:56">,
#<Element id: 3, name: "Bar", created_at: "2013-03-14 10:47:03", updated_at: "2013-03-14 10:47:03">]
$ Report.first.elements
[#<Element id: 2, name: "Foo", created_at: "2013-03-14 10:46:56", updated_at: "2013-03-14 10:46:56">,
#<Element id: 2, name: "Foo", created_at: "2013-03-14 10:46:56", updated_at: "2013-03-14 10:46:56">,
#<Element id: 3, name: "Bar", created_at: "2013-03-14 10:47:03", updated_at: "2013-03-14 10:47:03">,
#<Element id: 3, name: "Bar", created_at: "2013-03-14 10:47:03", updated_at: "2013-03-14 10:47:03">]
(rdb:2) Report.first.elements.uniq
[#<Element id: 2, name: "Foo", created_at: "2013-03-14 10:46:56", updated_at: "2013-03-14 10:46:56">,
#<Element id: 3, name: "Bar", created_at: "2013-03-14 10:47:03", updated_at: "2013-03-14 10:47:03">]
How is the duplication of elements in Report.first.elements even possible? And how can I stop it?
rails -v
Rails 3.2.11
$ ruby -v
ruby 1.9.2p320 (2012-04-20 revision 35421) [x86_64-darwin12.2.1]
Found the answer. Had a report selector in the form at reports/id/elements/new. If the user selected the current report in the report selector, two links between the element and the report would be created in the join table.

Testing model that acts_as_nested_set with RSpec

I have an Organization model that acts_as_nested_set, using awesome_nested_set:
class Organization < ActiveRecord::Base
acts_as_nested_set
attr_accessible :name, :location_id, :parent_id
has_many :org_prod_relationships, dependent: :destroy
has_many :products, through: :org_prod_relationships
def has_product?(prod)
org_prod_relationships.find_by_product_id(prod.id)
end
def add_product!(prod)
org_prod_relationships.create!(product_id: prod.id)
end
def publish_product!(prod)
self.descendants.each do |d|
d.add_product!(prod)
end
end
end
How can I write a test in RSpec for publish_product!, and/or is this the wrong approach to creating org_product_relationships in a nested set, and therefore hard to test? My non-working attempt is here (clipped from the larger spec file) https://gist.github.com/3911555.
EDIT: Updating to include the error message. Note, lines 79 and 80 are:
it { should have_product(product) }
its(:products) { should include(product) }
in the gist.
Failures:
1) Organization publishes_product
Failure/Error: it { should have_product(product) }
expected #has_product?(#<Product id: 34, name: "floo powder", created_at: "2012-10-21 14:15:08", updated_at: "2012-10-21 14:15:08", photo_file_name: nil, photo_content_type: nil, photo_file_size: nil, photo_updated_at: nil>) to return true, got false
# ./spec/models/organization_spec.rb:79:in `block (3 levels) in <top (required)>'
2) Organization publishes_product products
Failure/Error: its(:products) { should include(product) }
expected [] to include #<Product id: 35, name: "floo powder", created_at: "2012-10-21 14:15:08", updated_at: "2012-10-21 14:15:08", photo_file_name: nil, photo_content_type: nil, photo_file_size: nil, photo_updated_at: nil>
Diff:
## -1,2 +1,2 ##
-[#<Product id: 35, name: "floo powder", created_at: "2012-10-21 14:15:08", updated_at: "2012-10-21 14:15:08", photo_file_name: nil, photo_content_type: nil, photo_file_size: nil, photo_updated_at: nil>]
+[]
# ./spec/models/organization_spec.rb:80:in `block (3 levels) in <top (required)>'
Finished in 29.93 seconds
184 examples, 2 failures
Failed examples:
rspec ./spec/models/organization_spec.rb:79 # Organization publishes_product
rspec ./spec/models/organization_spec.rb:80 # Organization publishes_product products
I think you have to reload an object. Try the following:
#organization.products.reload
After you publish products in the before statements. Basically once the #organization is saved, calling #organization.products has a force_reload=false. Reference: http://guides.rubyonrails.org/association_basics.html#has_many-association-reference
Also since you asked, given that you have set up the relationships, you actually should be able to define your methods using the products relationship:
def has_product?(prod)
products.include? prod
end
def add_product!(prod)
products << prod
end
I think if you define the methods that way, you may not have to reload because the organization.products association is already updated.
Hope that helps.

Monitoring process interference Factory's instance initialization

In my project, the user will have many items, which have an onshelf_at attribute default to DateTime.now at its creation.
# item.rb
class Item < ActiveRecord::Base
before_create :calculate_onshelf_time
default_scope :order => 'items.onshelf_at DESC'
def calculate_onshelf_time
self.onshelf_at = DateTime.now
end
end
In the user model test, I tried to convince myself the retrieved order is indeed items.onshelf_at DESC. So I made the following snippet, but the result turned out to be reverse. (namely [#item1, #item2])
# spec/models/user_spec.rb
before :each do
#user = User.create(#attr)
#item1 = Factory(:item, :owner=>#user, :onshelf_at => 2.days.ago, :created_at => 2.days.ago)
#item2 = Factory(:item, :owner=>#user, :onshelf_at => 1.day.ago, :created_at => 1.day.ago)
end
it "should have the right items in the right order" do
#user.items.should == [#item2, #item1]
end
I checked the console, and found that onshelf_at wasn't listening to the instance initialization of Factory Girl. In its stead, it followed before_create rule, and valued to the time when test was run!
Failure/Error: #user.items.should == [#item2, #item1]
expected: [#<Item id: 2, description: "this is an item", img_link: "http://www.example.com/photos/some_pic.jpg", category_id: 5, onshelf: true, created_at: "2011-11-20 11:19:15", updated_at: "2011-11-21 11:19:15", onshelf_at: "2011-11-21 11:19:15", owner_id: 1>, #<Item id: 1, description: "this is an item", img_link: "http://www.example.com/photos/some_pic.jpg", category_id: 5, onshelf: true, created_at: "2011-11-19 11:19:15", updated_at: "2011-11-21 11:19:15", onshelf_at: "2011-11-21 11:19:15", owner_id: 1>]
got: [#<Item id: 1, description: "this is an item", img_link: "http://www.example.com/photos/some_pic.jpg", category_id: 5, onshelf: true, created_at: "2011-11-19 11:19:15", updated_at: "2011-11-21 11:19:15", onshelf_at: "2011-11-21 11:19:15", owner_id: 1>, #<Item id: 2, description: "this is an item", img_link: "http://www.example.com/photos/some_pic.jpg", category_id: 5, onshelf: true, created_at: "2011-11-20 11:19:15", updated_at: "2011-11-21 11:19:15", onshelf_at: "2011-11-21 11:19:15", owner_id: 1>] (using ==)
How can I fix this?
A quick fix would be to add a condition to your calculate_onshelf_time method:
self.onshelf_at = DateTime.now if self.onshelf_at.nil?
But - you don't even need all this. If you can (that is, if you have control over the schema), replace the onshelf_at attribute with a created_at column, which will automatically be set by Rails at the time of creation. If you're using migrations:
create_table :foo do |t|
# ...
t.timestamps
end
will add created_at and updated_at timestamps to the model.

Resources