#ruby-on-rails-3 #unit-testing #rspec2 #ruby-1.9.2
#ruby-on-rails-3 #модульное тестирование #rspec2 #ruby-1.9.2
Вопрос:
Предыстория
Я пытаюсь протестировать свои модели.
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=? OR 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
В классе Transactions @user является экземпляром эфемерного объекта, представляющим пользователя, обращающегося к модели.
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
Использование rspec 2.6.4, factory_girl 2.1.2, rails 3.1.0, ruby 1.9.2p290. Как показано, спецификация проходит.
Проблема
Когда я изменяю transactions
метод в 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=? OR payee_id=?", self.id, self.id])
transactions.each {|transaction| transaction.user = self}
transactions
end
end
теперь метод transactions
возвращается []
в rspec, однако он отлично работает в представлениях приложения.
Поскольку Transaction.user является эфемерным (представляющим пользователя, обращающегося к транзакции), он должен устанавливаться (если он существует) каждый раз, когда транзакция инициализируется или создается из записей БД.
Я в недоумении, с чего начать отладку этого.
Все предложения приветствуются!
Комментарии:
1. Я все еще изучаю rspec, поэтому я бы предположил, что это ошибка в моей конфигурации rspec, а не ошибка в самом rspec.
2. мой spec_helper.rb, на всякий случай: gist.github.com/1274147
Ответ №1:
Я думаю, ваша проблема заключается в том, что let
он ленив. По сути, происходит то, что транзакции еще даже не созданы, когда transactions
метод вызывается в тесте. Используйте let!
для не ленивой версии. Смотрите let и let!для получения более подробной информации.
Ответ №2:
Не могли бы вы просто вернуть payor_transactions payee_transactions вместо того, чтобы выбирать их вручную?
Комментарии:
1. Я намерен добавить подкачку, поэтому мне нужно, чтобы объединение происходило на уровне БД, но
transactions = self.payor_transactions self.payee_transactions
работало бы.2. Достаточно справедливо. Подкачка всегда усложняет задачу.
Ответ №3:
Следуя предложению @obrok, решение, на котором я остановился, чтобы сохранить преимущество отложенной загрузки let
в других тестах, заключалось в том, чтобы касаться каждой транзакции перед тестированием User#transactions как таковой:
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