Benito Serna

Ruby, Rails, TDD...

I am currently working on a kind of “presentation object” that I called `IdealPayments::GlobalStats`

.

This object will be used in two places, to display a graph and to show the data in a page where we can see all the “ideal payments”.

In my imagination and implementation, this object will depenend on an active record relation and chain the right methods to present the result.

Although I think this is not a big deal, I would like to know what do you think is best way to write unit/micro tests for this object and why?

I have two options…

- Test through the database as if the source (the active record relation) was invisible.
- Test by allowing that “the right messages” will be send to the “source” and expect the right result is returned.

Note: I already have test all the methods on the active record class through the database.

```
module IdealPayments
RSpec.describe GlobalStats do
describe "#generated_amount" do
it "is the sum of the ideal payments amount" do
create :ideal_payment, amount: 1_000
create :ideal_payment, amount: 2_000
expect(subject.generated_amount).to eq 3_000
end
it "is just for projects promoted by us" do
dev_project = create :dev_project, promoter: "other"
project = create :project, dev_project: dev_project
ideal_payments_for_project = create :ideal_payments_for_project, project: project
create :ideal_payment, ideal_payments_for_project: ideal_payments_for_project, amount: 1_000
create :ideal_payment, amount: 2_000
expect(subject.generated_amount).to eq 2_000
end
end
describe "#paid_amount" do
it "is the sum of the ideal payments paid amount" do
create :ideal_payment, paid_amount: 1_000
create :ideal_payment, paid_amount: 1_000
expect(subject.paid_amount).to eq 2_000
end
end
describe "#pending_amount" do
it "is the difference between the generated amount and the paid amount" do
create :ideal_payment, amount: 1_000, paid_amount: 500
create :ideal_payment, amount: 1_000, paid_amount: 200
expect(subject.pending_amount).to eq 2_000 - 700
end
end
describe "#pending_amount_without_delay" do
it "is the pending amount that is not yet delayed" do
create :ideal_payment, paid_amount: 0, amount: 1500, date: Date.current + 10.days
create :ideal_payment, paid_amount: 0, amount: 1000, date: Date.current - 10.days
create :ideal_payment, paid_amount: 1500, amount: 2000, date: Date.current
expect(subject.pending_amount_without_delay).to eq 2000
end
end
describe "#delayed_amount" do
it "is the pending amount that is delayed" do
create :ideal_payment, paid_amount: 0, amount: 1500, date: Date.current
create :ideal_payment, paid_amount: 0, amount: 1000, date: Date.current - 10.days
create :ideal_payment, paid_amount: 1500, amount: 2000, date: Date.current - 5.days
expect(subject.delayed_amount).to eq 1500
end
end
describe "#delayed_amount_less_than_30_days" do
it "is the pending amount that is delayed less than 30 days" do
create :ideal_payment, paid_amount: 0, amount: 8000, date: Date.current + 10.days
create :ideal_payment, paid_amount: 0, amount: 1000, date: Date.current - 30.days
create :ideal_payment, paid_amount: 1500, amount: 2000, date: Date.current - 5.days
expect(subject.delayed_amount_less_than_30_days).to eq 1500
end
end
describe "#delayed_amount_between_30_and_90_days" do
it "is the pending amount that is delayed more than 30 days but less than 90 days" do
create :ideal_payment, paid_amount: 0, amount: 1500, date: Date.current - 90.days
create :ideal_payment, paid_amount: 0, amount: 2500, date: Date.current - 30.days
create :ideal_payment, paid_amount: 0, amount: 1000, date: Date.current - 50.days
create :ideal_payment, paid_amount: 1500, amount: 2000, date: Date.current - 5.days
expect(subject.delayed_amount_between_30_and_90_days).to eq 2500
end
end
describe "#delayed_amount_more_90_days" do
it "is the pending amount that is delayed more than than 90 days" do
create :ideal_payment, paid_amount: 0, amount: 1500, date: Date.current - 100.days
create :ideal_payment, paid_amount: 0, amount: 1000, date: Date.current - 90.days
create :ideal_payment, paid_amount: 1500, amount: 2000, date: Date.current - 95.days
expect(subject.delayed_amount_more_90_days).to eq 2000
end
end
end
end
```

```
module IdealPayments
RSpec.describe GlobalStats do
describe "#source" do
it "is by default the ideal payments for projects promoted by us" do
expected = double("Ideal payments for project promoted by us")
allow(IdealPayment).to receive(:for_projects_promoted_by_us).and_return(expected)
expect(subject.source).to eq expected
end
end
describe "#generated_amount" do
it "is the total amount" do
allow(subject.source).to receive(:total_amount).and_return(3_000)
expect(subject.generated_amount).to eq 3_000
end
end
describe "#paid_amount" do
it "is the total paid amount" do
allow(subject.source).to receive(:total_paid_amount).and_return(2_000)
expect(subject.paid_amount).to eq 2_000
end
end
describe "#pending_amount" do
it "is the total paid amount" do
allow(subject.source).to receive(:total_pending_amount).and_return(1_000)
expect(subject.pending_amount).to eq 1_000
end
end
describe "#pending_amount_without_delay" do
it "is the total pending amount of the not delayed payments" do
allow(subject.source).to receive_message_chain(:not_delayed, :total_pending_amount).and_return(1_000)
expect(subject.pending_amount_without_delay).to eq 1_000
end
end
describe "#delayed_amount" do
it "is the total pending amount of the delayed payments" do
allow(subject.source).to receive_message_chain(:delayed, :total_pending_amount).and_return(1_000)
expect(subject.delayed_amount).to eq 1_000
end
end
describe "#delayed_amount_less_than_30_days" do
it "is the total pending amount that is delayed less than 30 days" do
delayed = double("Delayed")
allow(subject.source).to receive(:delayed_less_than).with(30.days).and_return(delayed)
allow(delayed).to receive(:total_pending_amount).and_return(1500)
expect(subject.delayed_amount_less_than_30_days).to eq 1500
end
end
describe "#delayed_amount_between_30_and_90_days" do
it "is the total pending amount that is delayed more than 30 days but less than 90 days" do
delayed = double("Delayed")
allow(subject.source).to receive(:delayed_between).with(30.days, 90.days).and_return(delayed)
allow(delayed).to receive(:total_pending_amount).and_return(2500)
expect(subject.delayed_amount_between_30_and_90_days).to eq 2500
end
end
describe "#delayed_amount_more_90_days" do
it "is the pending amount that is delayed more than than 90 days" do
delayed = double("Delayed")
allow(subject.source).to receive(:delayed_more_than).with(90.days).and_return(delayed)
allow(delayed).to receive(:total_pending_amount).and_return(3500)
expect(subject.delayed_amount_more_90_days).to eq 3500
end
end
end
end
```

```
module IdealPayments
class GlobalStats
def initialize(ideal_payments = IdealPayment.for_projects_promoted_by_us)
@ideal_payments = ideal_payments
end
def source
ideal_payments
end
def generated_amount
ideal_payments.total_amount
end
def paid_amount
ideal_payments.total_paid_amount
end
def pending_amount
ideal_payments.total_pending_amount
end
def pending_amount_without_delay
ideal_payments.not_delayed.total_pending_amount
end
def delayed_amount
ideal_payments.delayed.total_pending_amount
end
def delayed_amount_less_than_30_days
ideal_payments.delayed_less_than(30.days).total_pending_amount
end
def delayed_amount_between_30_and_90_days
ideal_payments.delayed_between(30.days, 90.days).total_pending_amount
end
def delayed_amount_more_90_days
ideal_payments.delayed_more_than(90.days).total_pending_amount
end
private
attr_reader :ideal_payments
end
end
```

- Tools to help you detect n+1 queries
- "Please help me with my n+1 problem..."
- 5 ways to fix the latest-comment n+1 problem
- Guide to name scopes based on column types