如何为调用joins方法的模型做一个规格说明 我有此代码,但无法正常工作,方法加入错误
def applicants_income_average
users.joins(:financial_data).sum(:net_income_verified) /
users.joins(:financial_data).where("financial_data.
net_income_verified IS NOT NULL").size
end
context "#applicants_income_average" do
before do
user_a.build_financial_data(net_income_verified: 12_000)
user_b.build_financial_data(net_income_verified: 13_000)
allow(whitelabel).to receive(:users).and_return([user_a, user_b])
allow(user_a).to receive(:join).and_return(user_a.financial_data)
allow(user_b).to receive(:join).and_return(user_b.financial_data)
end
it "Should return 12_500" do
expect(whitelabel.applicants_income_average).to eql(12_500)
end
end
答案 0 :(得分:0)
诚然,由于ActiveRecord的方法链接,通过数据库测试ActiveRecord可能比模拟更容易。但是,如果您可以忍受链接的allow
语句,则在模拟ActiveRecord时,测试的速度将大大提高。另外,您将不会测试已经由Rails核心团队彻底测试过的东西。
这就是我要解决上述问题的方式。我注释了对代码的一些假设和修改。希望对您有帮助!
class Whitelabel
# I modified this for readability, but in the process discovered
# that the same query could probably be used for both the numerator
# and the denominator.
def applicants_income_average
sum_of_verified_net_income / count_of_users_with_verified_net_income
end
private
def sum_of_verified_net_income
users_with_verified_net_income.sum(:net_income_verified)
end
def count_of_users_with_verified_net_income
users_with_verified_net_income.size
end
# Using a single query and memoizing the results will improve performance
# This will also make it easier to test since we'll only call it once
def users_with_verified_net_income
@users_with_verified_net_income ||= do
users
.joins(:financial_data)
.where('financial_data.net_income_verified IS NOT NULL')
end
end
# I'm assuming this method must exist, given that the
# implementation of applicants_income_average referenced it
def users
User.where('some criteria')
end
end
describe Whitelabel do
subject(:whitelabel) { described_class.new }
describe '#applicants_income_average' do
subject(:applicants_income_average) { whitelabel.applicants_income_average }
let(:users) { instance_double('users') }
let(:users_with_financial_data) { instance_double('users_with_financial_data') }
let(:users_with_verified_net_income) do
instance_double('users_with_verified_net_income', sum: sum, size: size )
end
let(:sum) { 25000 }
let(:size) { 2 }
let(:average) { sum / size }
before do
allow(User).to receive(:where).and_return(users)
allow(users).to receive(:joins).and_return(users_with_financial_data)
allow(users_with_financial_data)
.to receive(:where)
.and_return(users_with_verified_net_income)
end
it 'selects the correct users' do
applicants_income_average
expect(User).to have_received(:where).with('some criteria')
end
it 'joins with financial data model' do
applicants_income_average
expect(users).to have_received(:joins).with(:financial_data)
end
it 'selects the users with verified net income' do
applicants_income_average
expect(users_with_financial_data)
.to have_received(:where)
.with('financial_data.net_income_verified IS NOT NULL')
end
it 'calculates average' do
expect(applicants_income_average).to eq(average)
end
end
end