Rspec: как проверить повтор? - PullRequest
0 голосов
/ 04 октября 2018

Я реализовал Оптимистическую блокировку для состояния гонки.Если lock_version не совпадает с обновленным lock_version в базе данных, тогда триггером будет retry.Можете ли вы предложить, как проверить это retry событие

#Product: Model's new field:
    #  lock_version                       :integer(4)      default(0), not null

def recalculate
  method_1
  self.save!
end

private
def method_1
    begin
      ####
      ####
      if self.lock_version == Product.find(self.id).lock_version
         Product.where(:id => self.id).update_all(attributes)
      else
         raise ActiveRecord::StaleObjectError.new(self, "test")
      end
    rescue ActiveRecord::StaleObjectError => e
        if tries < 3
           tries += 1
           sleep(1 + tries)
           self.reload
           retry
        else
           raise Exception.new(timeout.inspect)
        end
    end
end

Rspec Unit Test:

  it 'if car is updated then ActiveRecord::StaleObjectError should be raised' do
    prod_v1 =Product.find(@prod.id)
    prod_v2 = Product.find(@prod.id)
    prod_v1.recalculate
    prod_v1.reload  # will make lock_version of prod_v1 to 1
    prod_v2.recalculate # howvever lock_version of prod_v2 is still 0.
    expect{car_v2.send(:method1)}.to receive(:retry)
  end

1 Ответ

0 голосов
/ 04 октября 2018

Я бы начал с разбивки сложности вашего метода1 на что-то вроде этого.

private
def stale_object?
  self.lock_version == Product.find(self.id).lock_version
end

def attempt_to_rescue_stale_object
  if rescue_stale_object_retries =< 3
    rescue_stale_object_retries += 1
    sleep( 1 + rescue_stale_object_retries)
    retry # and the sleep stuff, not sure how you
  else
    raise Exception.new(timeout.inspect)
  end
end

def rescue_stale_object_retries
  @rescue_stale_object_retries ||= 0
end

def method_1
  begin
    raise ActiveRecord::StaleObjectError.new(self, "test") if stale_object?
    Product.where(:id => self.id).update_all(attributes)
  rescue ActiveRecord::StaleObjectError => e
    attempt_to_rescue_stale_object
    reload # You can omit the self here
   else
      raise Exception.new(timeout.inspect)
    end
  end 
end

Теперь вы можете протестировать его так, как вам нравится, сложность логики спасения заключена в метод.Вы не зависите от тестирования фактического поведения в нем (это теперь неактивный вызов, но он может измениться, скажем, на выполнение фонового задания).

[https://relishapp.com/rspec/rspec-mocks/docs/setting-constraints/receive-counts][1] дает нам что-то вроде этого

expect{car_v2}.to receive(:attempt_to_rescue_stale_object).exactly(3).times
car_v2.method1

Еще одно преимущество разбиения сложности на мелкие части состоит в том, что ваше намерение лучше донести до ваших коллег-разработчиков (другого человека).или вы через 6 месяцев забыли о своей первоначальной цели тестирования)

...