Ben
Ben

Reputation: 5172

rspec / testing AR objects without reloading

Using factory bot, creating variables like so :

let!(:deal) { create(:deal) }

The following sample :

expect {
  ...stuff that effectively associate an attachment to the deal...
}.to change { deal.attachments.length }.from(0).to(1)

does not pass.

The same test, while reloading the deal :

...to change { deal.reload.attachments.length } ...

does pass.

In frequent other occasions, I end up using this reload method. It feel like this is not the right way to go at all, and that i'm missing the point

What would be the correct way to test changes on records without having to reload them ?

Upvotes: 0

Views: 517

Answers (1)

Daniel Westendorf
Daniel Westendorf

Reputation: 3465

Let's talk about what's happening:

deal.attachments is queried twice, once before your expect block is invoked, and then once after, to compare the difference.

Because rails knows that costs to the database are expensive (performance-wise), it caches the values of has_many relationships to save on performance. So, the first time deal.attachments is called is the only time a database query is actually made. Add a reload forces a second database query, giving you the result you expect.

So, this boils down to "How do I make sure the database is queried each time?"

The method you're using is what I'd consider acceptable but there are some other options. The one I'd suggest is using count instead of length.

This will perform the count using SQL.

This guarantees that a SQL query will be made before and after your expect block is invoked.

expect {
  ...stuff that effectively associate an attachment to the deal...
}.to change { deal.attachments.count }.by(1)

Upvotes: 3

Related Questions