Reputation: 381
I'm writing a program that allows users to match orders in transactions that end up transferring things between them.
I have a class, TransferAsset,that takes a transaction and creates an asset for the transaction's purchaser:
class TransferAsset
attr_accessor :transaction, :seed_asset
def initialize(opts = {})
@transaction = opts[:transaction]
end
def create_seed_asset
@seed_asset = UserInventoryFactory.new({transaction: transaction}).inventory
end
end
Here's the UserInventory class:
class UserInventoryFactory
attr_accessor :transaction, :inventory
def initialize(opts = {})
@transaction = opts[:transaction]
create_user_inventory
set_values
end
def set_values
set_seed_transaction
set_owner
set_amount
set_purchase_date
set_initial_amount
end
def create_user_inventory
@inventory = UserInventory.new
end
def set_seed_transaction
inventory.seed_transaction_id = transaction.id
end
def set_owner
inventory.user_id = transaction.buyer_id
end
def set_amount
inventory.amount = transaction.amount
end
def set_purchase_date
inventory.purchase_date = transaction.created_at.beginning_of_day
end
def set_initial_amount
inventory.initial_amount = transaction.amount
end
end
I have an rspec test for the TransferAsset class:
describe '#create_seed_asset' do
it 'creates an asset for the purchaser' do
factory = UserInventoryFactory.new({transaction: transaction})
expect(transfer.create_seed_asset).to eql factory.inventory
end
end
The test fails with:
1) TransferAsset#create_seed_asset creates an asset for the purchaser
Failure/Error: expect(transfer.create_seed_asset).to eql factory.inventory
expected: #<UserInventory id: nil, amount: 5000.0, initial_amount: 5000.0, purchase_date: "2014-06-22 05:00:00", user_id: 2, seed_transaction_id: 1, created_at: nil, updated_at: nil>
got: #<UserInventory id: nil, amount: 5000.0, initial_amount: 5000.0, purchase_date: "2014-06-22 05:00:00", user_id: 2, seed_transaction_id: 1, created_at: nil, updated_at: nil>
(compared using eql?)
I've tried all of the rspec equality matchers, and it always fails, which makes me think that I'm writing bad code, either in my test, or in my actual classes.
What am I doing wrong or what can I do better?
Upvotes: 0
Views: 4408
Reputation: 4544
Since you just want to test for object equivalence, you could convert your objects to JSON and compare the results.
It avoids having to redefine equality methods. So you could change your test to look like this:
describe '#create_seed_asset' do
it 'creates an asset for the purchaser' do
factory = UserInventoryFactory.new({transaction: transaction})
expect(transfer.create_seed_asset.to_json).to eql factory.inventory.to_json
end
end
Upvotes: 1
Reputation: 37409
You are trying to compare two different instances of UserInventory
class, each created in create_user_inventory
method:
def create_user_inventory @inventory = UserInventory.new end
Comparing the two gives you false, just like the following:
Object.new.eql? Object.new
# => false
Having two objects with the same class (and even the same attribute values) does not make them equal, and you can't expect the default compare methods to know that they are.
Objects like strings, arrays and maps have their own implementation of comparators with custom semantics, and if you want your classes to have their own semantics - you should also implement their custom comparators, and define those semantics.
See also here:
Ruby has three main equality test methods,
==
,eql?
andequal?
. These methods normally live in the Object class and since all other Ruby classes inherit from Object, they automatically gain access to these three methods. Inside the Object class all there methods do exactly the same thing, they test if two objects are exactly the same object. That is to say, both objects must have the same object id.
Upvotes: 1
Reputation: 170
You need to overwrite equality methods for your class. Right now it is defaulting to comparing object ids and since they aren't the same object in memory it will always be false.
Try overriding == to compare the instance variables of the two objects
The default behavior for == for new classes, ignoring inheritance, is to compare the object ids
Upvotes: 2