По общему признанию, может быть проще протестировать 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