开发者

Why does the "each" iterator method break rspec?

Background

I'm attempting to test my models.

app/models/user.rb

class User < ActiveRecord::Base

  has_many :payor_transactions, class_name: 'Transaction', inverse_of: :payor, foreign_key: :payor_id
  has_many :payee_transactions, class_name: 'Transaction', inverse_of: :payee, foreign_key: :payee_id

  def transactions
    transactions = Transaction.where(["payor_id=? O开发者_StackOverflow中文版R payee_id=?", self.id, self.id])
    transactions
  end

end

app/models/transaction.rb

class Transaction < ActiveRecord::Base

  attr_accessor :user

  belongs_to :payor, class_name: 'User'
  belongs_to :payee, class_name: 'User'

end

In the Transactions class, @user is an ephemeral object instance representing the user accessing the model.

spec/models/user_spec.rb

require 'spec_helper'

describe User do

  let(:user) { Factory(:user) }
  let(:user2) { Factory(:user) }
  let(:user3) { Factory(:user) }

  let(:transaction_user_user2) { Factory(:transaction, payor: user, payee: user2) }
  let(:transaction_user2_user) { Factory(:transaction, payor: user2, payee: user) }
  let(:transaction_user2_user3) { Factory(:transaction, payor: user2, payee: user3) }

  describe ".transactions" do
    it "should include payor and payee transactions but not 3rd party transactions" do
      user.transactions.should == [transaction_user_user2, transaction_user2_user]
      user2.transactions.should == [transaction_user_user2, transaction_user2_user, transaction_user2_user3]
      user3.transactions.should == [transaction_user2_user3]
    end
  end

end

Using rspec 2.6.4, factory_girl 2.1.2, rails 3.1.0, ruby 1.9.2p290. As shown, the spec passes.

Problem

When I modify the transactions method in app/models/user.rb to iterate over the results such that it reads:

class User < ActiveRecord::Base

  has_many :payor_transactions, class_name: 'Transaction', inverse_of: :payor, foreign_key: :payor_id
  has_many :payee_transactions, class_name: 'Transaction', inverse_of: :payee, foreign_key: :payee_id

  def transactions
    transactions = Transaction.where(["payor_id=? OR payee_id=?", self.id, self.id])
    transactions.each {|transaction| transaction.user = self}
    transactions
  end

end

the method transactions now returns [] in rspec, however it works perfectly in the app views.

Since Transaction.user is ephemeral (representing the user accessing the transaction) it must be set (if it exists) every time a Transaction is initialized or built from db records.

I'm at a loss for where to begin to debug this.

All suggestions appreciated!


I think your problem lies in the fact that let is lazy. Basically what is happening is that the transactions are not even created yet when the transactions method is called in the test. Use let! for a non-lazy version. See let and let! for more details.


Couldn't you just return payor_transactions + payee_transactions instead of manually selecting them?


Following the suggestion from @obrok, the solution I settled on to retain the advantage of lazy-loading let in other tests was to touch each transaction before testing User#transactions as so:

describe ".transactions" do
  it "should include payor and payee transactions but not 3rd party transactions" do

    [transaction_user_user2, transaction_user2_user, transaction_user2_user3].each do |transaction|
      [transaction.payor_id, transaction.payee_id].each {|id| id.should_not be_nil }
    end

    user.transactions.should == [transaction_user_user2, transaction_user2_user]
    user2.transactions.should == [transaction_user_user2, transaction_user2_user, transaction_user2_user3]
    user3.transactions.should == [transaction_user2_user3]
  end
end
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜