qodot
qodot

Reputation: 431

rails rspec update_all method works in controller but doesn't in rspec

** controller

puts merchants.first.is_blocked // 1

Merchant.update_all( {:is_blocked => 0}, "mch_id IN (#{@merchants.map(&:mch_id).join(",")})" )

puts merchants.first.is_blocked // 0, so it works

** rspec

expect {
  get :update_merchants, params
}.to change { merchant_1.is_blocked }.from(1).to(0)

// result should have been changed to 0, but is now 1

i don't know the reason why "update_all" method works in controller and doesn't work in rspec

Upvotes: 3

Views: 1829

Answers (2)

Jon
Jon

Reputation: 10918

Firstly, your update_all line is pretty nasty. Assuming @merchants is an AREL query with the conditions required to locate the merchants you want to update, please consider converting it to this:

@merchants.update_all(is_blocked: 0)

If @merchants is simply an array of records, then use this:

Merchant.where(id: @merchants.map(&:id)).update_all(is_blocked: 0)

Secondly, a GET request should NEVER modify the database. Consider changing this to a PUT request.

Finally, if your merchant_1 instance variable was created/fetched before calling your method, your test still has the pre-change value. You either need to re-fetch it, or call reload on it before you'll see the updated values.

expect {
  get :update_merchants, params
}.to change { merchant_1.reload.is_blocked }.from(1).to(0)

Upvotes: 6

Chris Peters
Chris Peters

Reputation: 18090

Your call to update the is_blocked attribute on all merchants should read like this:

Merchant.where(id: @merchants.map(&:id)).update_all(is_blocked: 0)

To answer your question in your comment on @Jon's answer (about the difference between update_all and update_attributes):

  • update_all will run one SQL query directly in the database to update the data.
  • update_attributes runs one SQL query per object instance that you run it on.

That said, update_all is much more efficient when you use it because the database does all of the work, and it's good at mass UPDATE operations like that (unlike looping through a Ruby array and running individual SQL queries for each record).

Then you need to make sure that you're reloading your merchant_1 in your spec to bust Rails's request-level query cache on that object:

expect {
  get :update_merchants, params
}.to change { merchant_1.reload.is_blocked }.from(1).to(0)

Upvotes: 1

Related Questions