Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

If == matcher fails with empty Diff, error notice should be more helpful. #123

Closed
tvdeyen opened this issue Mar 16, 2012 · 33 comments
Closed

Comments

@tvdeyen
Copy link

tvdeyen commented Mar 16, 2012

Comparing two objects via == that are not equal, but have no differences make rspec fail. The error notice shown is:

Diff:

It would be helpful if an explanation of what's the problem shows up. Like the comparing with eq notice!

@dchelimsky
Copy link
Contributor

Using == we get this:

     Failure/Error: pessimist.should == pessimist
       expected: #<Pessimist:0x007f8d9c1e3508>
            got: #<Pessimist:0x007f8d9c1e3508> (using ==)
       Diff:

Using eq we get this:


     Failure/Error: pessimist.should eq(pessimist)

       expected: #<Pessimist:0x007fab988ef3a0>
            got: #<Pessimist:0x007fab988ef3a0>

       (compared using ==)

       Diff:

These have the same information, just formatted differently. What is it that you would like to see?

@tvdeyen
Copy link
Author

tvdeyen commented Mar 16, 2012

Why it fails. ;)

Please read this again with a 100000 miles perspective:

Expected A
Got A
Diff

What should this mean? Whats the failure?

If one expects that A equals A and it fails with: "Hey you expected A but got A with a difference of none" its not very helpful, isn't it? ;)

I think a little bit more explaination would be helpful.

Using == we get this:

    Failure/Error: pessimist.should == pessimist
      expected: #<Pessimist:0x007f8d9c1e3508>
           got: #<Pessimist:0x007f8d9c1e3508> (using ==)
      Diff:

Using eq we get this:


    Failure/Error: pessimist.should eq(pessimist)

      expected: #<Pessimist:0x007fab988ef3a0>
           got: #<Pessimist:0x007fab988ef3a0>

      (compared using ==)

      Diff:

These have the same information, just formatted differently. What is it that you would like to see?


Reply to this email directly or view it on GitHub:
#123 (comment)

@dchelimsky
Copy link
Contributor

I understand the goal and the motivation. I was asking you for a recommendation on wording. How about "A == A returns true, but the objects do not report any difference."

@tvdeyen
Copy link
Author

tvdeyen commented Mar 16, 2012

Call me stupid, but I think I really don't get it.

The test fails, because "A == A returns true, but the objects do not report any difference."?

What am I missing?

@dchelimsky
Copy link
Contributor

That should have been "A == A returns false ..."

@tvdeyen
Copy link
Author

tvdeyen commented Mar 17, 2012

Phew, and I thought I am loosing my mind. :)

What about: "A == A haven't any difference, but returns false. Are you comparing new instances of ActiveRecord Models? Try to save them first."

Or is this too specific?

@dchelimsky
Copy link
Contributor

Right track but yes, too specific. How about something like:

"A.==(B) returned false even though the diff between A and B is empty. Check the implementation of A.==."

@tvdeyen
Copy link
Author

tvdeyen commented Mar 17, 2012

It's ok for me.

But maybe some more people could share their thoughts on this.

@justinko
Copy link
Contributor

But maybe some more people could share their thoughts on this.

That just slows things down. We can always iterate on @dchelimsky's suggestion after it's released.

P.S. I think @dchelimsky suggestion is plenty good enough.

@myronmarston
Copy link
Member

I like @dchelimsky's suggestion, too.

@justinko
Copy link
Contributor

@tvdeyen would you like to implement this?

@tvdeyen
Copy link
Author

tvdeyen commented Mar 20, 2012

Ok, why not. But you have to guide me the right way.

@agis
Copy link

agis commented Sep 18, 2012

I'm having the same failure message in a spec and I would like to know what it exactly means.

@alindeman
Copy link
Contributor

@Agis-, it's likely due to an object that overrides == to provide a more refined definition of equality. What objects are you comparing?

@agis
Copy link

agis commented Sep 18, 2012

These are the actual specs: https://gist.github.com/3744048

@alindeman
Copy link
Contributor

Ah, that's ActiveRecord. If two ActiveRecord objects are being compared and both of their #ids are nil, it checks checks whether they are exactly the same instance (which they are not in this case).

tldr: it doesn't look like album saved correctly; otherwise it would have a database-assigned #id.

@myronmarston
Copy link
Member

FWIW, the message that was added here gets printed in some cases where it makes no sense. See #143.

@agis
Copy link

agis commented Sep 18, 2012

I see, thanks @alindeman.

@mjacobus
Copy link

mjacobus commented Jun 6, 2013

Same problem here. Inside rspec [hash] == [hash] returns false. In terminal it returns true. Looks like the implementation is working, but the spec test says otherwise.

Terminal:

race = Race.first
teams = race.teams.map(&:attributes)
RaceExportation.new(race).to_hash[:teams] == teams # true

Inside rspec

require 'spec_helper'

describe RaceExportation do
  let(:race) { category.race }
  let(:team) { create(:team, race: race) }

  describe "#to_hash" do
    subject { RaceExportation.new(race).to_hash }

    it "sets :teams key" do
      subject[:teams].should == [team.attributes]
    end
  end
end
  1. RaceExportation#to_hash sets :teams key
    Failure/Error: subject[:teams].should == [team.attributes]
       expected: [{"id"=>40, "race_id"=>107, "name"=>"The Name 6", "slug"=>"the-name-6", "code"=>"avg", "created_at"=>Thu, 06 Jun 2013 11:44:01 BRT -03:00, "updated_at"=>Thu, 06 Jun 2013 11:44:01 BRT -03:00}]
            got: [{"id"=>40, "race_id"=>107, "name"=>"The Name 6", "slug"=>"the-name-6", "code"=>"avg", "created_at"=>Thu, 06 Jun 2013 11:44:01 BRT -03:00, "updated_at"=>Thu, 06 Jun 2013 11:44:01 BRT -03:00}] (using ==)
       Diff:[{"id"=>40, "race_id"=>107, "name"=>"The Name 6", "slug"=>"the-name-6", "code"=>"avg", "created_at"=>Thu, 06 Jun 2013 11:44:01 BRT -03:00, "updated_at"=>Thu, 06 Jun 2013 11:44:01 BRT -03:00}].==([{"id"=>40, "race_id"=>107, "name"=>"The Name 6", "slug"=>"the-name-6", "code"=>"avg", "created_at"=>Thu, 06 Jun 2013 11:44:01 BRT -03:00, "updated_at"=>Thu, 06 Jun 2013 11:44:01 BRT -03:00}]) returned false even though the diff between [{"id"=>40, "race_id"=>107, "name"=>"The Name 6", "slug"=>"the-name-6", "code"=>"avg", "created_at"=>Thu, 06 Jun 2013 11:44:01 BRT -03:00, "updated_at"=>Thu, 06 Jun 2013 11:44:01 BRT -03:00}] and [{"id"=>40, "race_id"=>107, "name"=>"The Name 6", "slug"=>"the-name-6", "code"=>"avg", "created_at"=>Thu, 06 Jun 2013 11:44:01 BRT -03:00, "updated_at"=>Thu, 06 Jun 2013 11:44:01 BRT -03:00}] is empty. Check the implementation of [{"id"=>40, "race_id"=>107, "name"=>"The Name 6", "slug"=>"the-name-6", "code"=>"avg", "created_at"=>Thu, 06 Jun 2013 11:44:01 BRT -03:00, "updated_at"=>Thu, 06 Jun 2013 11:44:01 BRT -03:00}].==.
  1. RaceExportation#to_hash sets :teams key
    Failure/Error: subject[:teams].should eq([team.attributes])
       expected: [{"id"=>42, "race_id"=>109, "name"=>"The Name 6", "slug"=>"the-name-6", "code"=>"avg", "created_at"=>Thu, 06 Jun 2013 11:45:46 BRT -03:00, "updated_at"=>Thu, 06 Jun 2013 11:45:46 BRT -03:00}]
            got: [{"id"=>42, "race_id"=>109, "name"=>"The Name 6", "slug"=>"the-name-6", "code"=>"avg", "created_at"=>Thu, 06 Jun 2013 11:45:46 BRT -03:00, "updated_at"=>Thu, 06 Jun 2013 11:45:46 BRT -03:00}]

       (compared using ==)

       Diff:[{"id"=>42, "race_id"=>109, "name"=>"The Name 6", "slug"=>"the-name-6", "code"=>"avg", "created_at"=>Thu, 06 Jun 2013 11:45:46 BRT -03:00, "updated_at"=>Thu, 06 Jun 2013 11:45:46 BRT -03:00}].==([{"id"=>42, "race_id"=>109, "name"=>"The Name 6", "slug"=>"the-name-6", "code"=>"avg", "created_at"=>Thu, 06 Jun 2013 11:45:46 BRT -03:00, "updated_at"=>Thu, 06 Jun 2013 11:45:46 BRT -03:00}]) returned false even though the diff between [{"id"=>42, "race_id"=>109, "name"=>"The Name 6", "slug"=>"the-name-6", "code"=>"avg", "created_at"=>Thu, 06 Jun 2013 11:45:46 BRT -03:00, "updated_at"=>Thu, 06 Jun 2013 11:45:46 BRT -03:00}] and [{"id"=>42, "race_id"=>109, "name"=>"The Name 6", "slug"=>"the-name-6", "code"=>"avg", "created_at"=>Thu, 06 Jun 2013 11:45:46 BRT -03:00, "updated_at"=>Thu, 06 Jun 2013 11:45:46 BRT -03:00}] is empty. Check the implementation of [{"id"=>42, "race_id"=>109, "name"=>"The Name 6", "slug"=>"the-name-6", "code"=>"avg", "created_at"=>Thu, 06 Jun 2013 11:45:46 BRT -03:00, "updated_at"=>Thu, 06 Jun 2013 11:45:46 BRT -03:00}].==.
  1. RaceExportation#to_hash sets :teams key
    Failure/Error: subject[:teams].should =~ [team.attributes]
       expected collection contained:  [{"id"=>44, "race_id"=>111, "name"=>"The Name 6", "slug"=>"the-name-6", "code"=>"avg", "created_at"=>Thu, 06 Jun 2013 11:48:04 BRT -03:00, "updated_at"=>Thu, 06 Jun 2013 11:48:04 BRT -03:00}]
       actual collection contained:    [{"id"=>44, "race_id"=>111, "name"=>"The Name 6", "slug"=>"the-name-6", "code"=>"avg", "created_at"=>Thu, 06 Jun 2013 11:48:04 BRT -03:00, "updated_at"=>Thu, 06 Jun 2013 11:48:04 BRT -03:00}]
       the missing elements were:      [{"id"=>44, "race_id"=>111, "name"=>"The Name 6", "slug"=>"the-name-6", "code"=>"avg", "created_at"=>Thu, 06 Jun 2013 11:48:04 BRT -03:00, "updated_at"=>Thu, 06 Jun 2013 11:48:04 BRT -03:00}]
       the extra elements were:        [{"id"=>44, "race_id"=>111, "name"=>"The Name 6", "slug"=>"the-name-6", "code"=>"avg", "created_at"=>Thu, 06 Jun 2013 11:48:04 BRT -03:00, "updated_at"=>Thu, 06 Jun 2013 11:48:04 BRT -03:00}]

@mjacobus
Copy link

mjacobus commented Jun 6, 2013

Turns out it is something related to Time. As my time stubbing didnt work I did that workaround (im not very proud of it).

describe RaceExportation do
  def remove_times(object)
    if object.class == Array
      object.map{|e| remove_times(e)}
    else
      object.except('created_at', 'updated_at')
    end
  end

   # let(){}..

   it "sets :teams key" do
      remove_times(subject[:teams]).should == remove_times([team.attributes])
   end
end

@AdrienBD
Copy link

AdrienBD commented Sep 7, 2015

Sorry to reopen this issue but I am facing the empty diff message again...
This time I am not comparing rails Object but their json :

RSpec::Mocks::MockExpectationError: ElasticSearchUtils received :index_stat with unexpected arguments
  expected: (:download, {"action"=>"download", "active"=>true, "created_at"=>Sat, 05 Sep 2015 10:04:59 CEST +02:00, "device_id"=>nil, "mobile_application_id"=>189, "mobile_application_update_id"=>292, "store_id"=>932, "updated_at"=>Mon, 07 Sep 2015 10:04:59 CEST +02:00, "user_id"=>663, "version"=>1, "id"=>"55ed452b4d42507f6e000000"})
       got: (:download, {"action"=>"download", "active"=>true, "created_at"=>Sat, 05 Sep 2015 10:04:59 CEST +02:00, "device_id"=>nil, "mobile_application_id"=>189, "mobile_application_update_id"=>292, "store_id"=>932, "updated_at"=>Mon, 07 Sep 2015 10:04:59 CEST +02:00, "user_id"=>663, "version"=>1, "id"=>"55ed452b4d42507f6e000000"})
Diff:

I even checked that the created_at and updated_at times where identical to the millisecond.
Could anyone help me on that ?

@JonRowe
Copy link
Member

JonRowe commented Sep 7, 2015

Are the times equal in ruby? Regardless of clock equalness.

@AdrienBD
Copy link

AdrienBD commented Sep 7, 2015

Not sure if I understand what you asked for but this returns true :

p json_1['created_at'] == stat_1.created_at

json_1 being the json created with the as_json method of stat_1. stat_1 is a mongoid entity I create at the beginning of the test.

@JonRowe
Copy link
Member

JonRowe commented Sep 7, 2015

Same for updated_at and gist how you're comparing json_1 with stat_1?

@AdrienBD
Copy link

AdrienBD commented Sep 7, 2015

Here is the gist with the prints in a before ... do block, both returning true : https://gist.github.com/AdrienBD/1eb993716eebad9a918b

@JonRowe
Copy link
Member

JonRowe commented Sep 7, 2015

I'd guess id is a string in one and a Bson::ObjectId in another...

@AdrienBD
Copy link

AdrienBD commented Sep 7, 2015

No because the method is as follows :

def index_download_stats
    indexed_ids = []
    download_stats = Stat.downloads.not_indexed.all
    download_stats.each do |stat|
      json = stat.as_json(except: [:_id, :indexed])
      json['id'] = stat.id.to_s
      result = ElasticSearchUtils.index_stat :download, json
      indexed_ids << stat.id if result['created']
    end
    Stat.in(_id: indexed_ids).update_all indexed: true
  end

As you may see here, I give to the method ElasticSearchUtils.index_stat the json where I replaced the id with its string equivalent. In my test, I just want to make sure the method is called with the correct argument. Apparently it is called with different arguments but the message does not tell me what is different... If I had a string on one side and a Bson on the other, I would see it in the diff wouldn't I ?

@JonRowe
Copy link
Member

JonRowe commented Sep 7, 2015

If the diff is empty but they are not equal it's a symptom of something being printed to_s in the diff thats not in the object, time is the usual culprit but there are others.

@AdrienBD
Copy link

AdrienBD commented Sep 7, 2015

Well if I fail to remove the Bson id from the Stat object in the test, I get an error but with an explicit diff that I can use :

RSpec::Mocks::MockExpectationError: ElasticSearchUtils received :index_stat with unexpected arguments
  expected: (:download, {"_id"=>BSON::ObjectId('55ed51324d4250818c000000'), "action"=>"download", "active"=>true, "created_at"=>Sat, 05 Sep 2015 10:56:18 CEST +02:00, "device_id"=>nil, "mobile_application_id"=>239, "mobile_application_update_id"=>342, "store_id"=>1282, "updated_at"=>Mon, 07 Sep 2015 10:56:18 CEST +02:00, "user_id"=>913, "version"=>1, "id"=>"55ed51324d4250818c000000"})
       got: (:download, {"action"=>"download", "active"=>true, "created_at"=>Sat, 05 Sep 2015 10:56:18 CEST +02:00, "device_id"=>nil, "mobile_application_id"=>239, "mobile_application_update_id"=>342, "store_id"=>1282, "updated_at"=>Mon, 07 Sep 2015 10:56:18 CEST +02:00, "user_id"=>913, "version"=>1, "id"=>"55ed51324d4250818c000000"})
Diff:
@@ -1,6 +1,5 @@
 [:download,
- {"_id"=>BSON::ObjectId('55ed51324d4250818c000000'),
-  "action"=>"download",
+ {"action"=>"download",
   "active"=>true,
   "created_at"=>Sat, 05 Sep 2015 10:56:18 CEST +02:00,
   "device_id"=>nil,

So I guess the culprit is not in the BSON id this time, no ?

@AdrienBD
Copy link

AdrienBD commented Sep 7, 2015

I was thinking maybe the diff is not empty but is actually some white spaces but I have no idea what could create that and how to check that.

@ryanwanger
Copy link

Getting something similar at the moment...

`Failure/Error: expect(ExpandedCompany.find(@company.id)).to eq(@expanded_company)

   expected: #<Company:0x007fe6a1813be0 @id="company-1", @sf_id="", @name="Company 1", @created_at="", @modified_at="", @url="", @logo_url="", @status="", @brief_description="", @description="", @email="", @location={}, @full_time_employees=0, @angellist_url="", @twitter_url="", @crunchbase_url="", @sessions=[], @session_names=[], @programs=[], @roles=[], @funding=[{"round_amount"=>100}], @press=[], @jobs=[], @tags=[], @people=[]>
        got: #<Company:0x007fe6a1813be0 @id="company-1", @sf_id="", @name="Company 1", @created_at="", @modified_at="", @url="", @logo_url="", @status="", @brief_description="", @description="", @email="", @location={}, @full_time_employees=0, @angellist_url="", @twitter_url="", @crunchbase_url="", @sessions=[], @session_names=[], @programs=[], @roles=[], @funding=[{"round_amount"=>100}], @press=[], @jobs=[], @tags=[], @people=[]>

   (compared using ==)

   Diff:

`

Not sure why these objects are not == ?

Edit: I'm using SimpleDelegator, if that could be the issue.

@JonRowe
Copy link
Member

JonRowe commented Sep 10, 2015

@ryanwanger you have to specifically delegate == to the underlying object if you want active record like == behaviour, two different simple delegator instances won't eq each other but will print out an inspect string form the underlying object e.g.:

2.2.3 :001 > require 'delegate'
 => true
2.2.3 :002 > object = Object.new
 => #<Object:0x007fc8921de8b0>
2.2.3 :003 > klass = Class.new(SimpleDelegator)
 => #<Class:0x007fc89221c700>
2.2.3 :004 > klass.new(object) == klass.new(object)
 => false
2.2.3 :005 > klass.new(object).inspect
 => "#<Object:0x007fc8921de8b0>"

@atz
Copy link

atz commented Jun 10, 2017

It seems to depend on what is delegated:

> file = File.open('/etc/passwd')
 => #<File:/etc/passwd> 
> file == file
 => true 
> SimpleDelegator.new(1) == SimpleDelegator.new(1)
 => true 
> SimpleDelegator.new(file) == SimpleDelegator.new(file)
 => false 

This seems like a flaw in SimpleDelegator, since the internal objects themselves can tell you they are equivalent:

SimpleDelegator.new(file).__getobj__ == SimpleDelegator.new(file).__getobj__
 => true 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests