Подтвердить что ты не робот

Как проверить, что определенная функция использует транзакцию в Rails и rspec 2

У меня есть функция модели, которую я хочу убедиться, что использует транзакцию. Например:

class Model 
  def method
    Model.transaction do
      # do stuff
    end
  end
end

Мой текущий подход заключается в том, чтобы заблокировать вызов метода внутри блока, чтобы поднять исключение ActiveRecord::Rollback, а затем проверить, действительно ли база данных изменилась. Но это означает, что если по какой-то причине реализация внутри блока изменилась, тогда тест сломался бы.

Как бы вы протестировали это?

4b9b3361

Ответ 1

Вы должны посмотреть на проблему с другой точки зрения. Тестирование того, использует ли функция транзакцию, бесполезно с поведенческой точки зрения. Он не дает вам никакой информации о том, работает ли функция BEHAVES, как ожидалось.

То, что вы должны проверить, - это поведение, то есть ожидаемый результат правильный. Для ясности можно сказать, что вы выполняете операцию A и операцию B внутри функции (выполняемой в рамках одной транзакции). Операция A кредитует пользователя в размере 100 долларов США в вашем приложении. Операция B делит кредитную карту пользователей на 100 долларов США.

Теперь вы должны предоставить недопустимую входную информацию для теста, чтобы сбой кредитной карты пользователей не удался. Оберните весь вызов функции в expect { ... }.not_to change(User, :balance).

Таким образом, вы проверяете ожидаемое ПОВЕДЕНИЕ - если сбой по кредитной карте терпит неудачу, не кредитуйте пользователя с суммой. Кроме того, если вы просто реорганизуете свой код (например, вы прекратите использовать транзакции и откаты вручную), тогда результат вашего тестового примера не должен быть затронут.

Таким образом, вы должны по-прежнему тестировать обе операции изолированно, как упоминалось в @luacassus. Кроме того, совершенно правильно, что ваш тестовый пример должен завершиться неудачей, если вы сделали "несовместимое" изменение (т.е. Измените поведение) на исходный код, упомянутый как @rb512.

Ответ 2

Необходимо отметить большой вопрос: при тестировании транзакции необходимо отключить транзакционные_комментарии. Это связано с тем, что тестовая структура (например, Rspec) завершает тестовый пример в транзакционном блоке. After_commit никогда не вызывается, потому что ничего действительно не сделано. Ожидание отката внутри транзакции не работает, даже если вы используете: require_new = > true. Вместо этого транзакция откатывается после запуска теста. Ref http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html вложенные транзакции.

Ответ 3

Как правило, вы должны использовать "чистый" тест rspec для проверки фрагментов приложения (классов и методов) в изоляции. Например, если у вас есть следующий код:

class Model 
  def method
   Model.transaction do
     first_operation
     second_operation
   end
end

вы должны протестировать first_operation и second_operation в отдельных тестовых сценариях, в идеале, без попадания в базу данных. Позже вы можете написать тест для Model#method и искупить эти два метода.

На следующем шаге вы можете написать тест интеграции на высоком уровне с https://www.relishapp.com/rspec/rspec-rails/docs/request-specs/request-spec и проверить, как этот код повлияет на базу данных в разных условиях, например, когда second_method завершится с ошибкой.

По-моему, это самый прагматичный подход к тестированию кода, который создает сложные запросы к базе данных.

Ответ 4

Во-первых, вам нужно заключить блок Model.transaction do ... end с блоками begin rescue end.

Единственный способ проверки отката транзакций - создать исключение. Таким образом, ваш нынешний подход хорош. Что касается вашей обеспокоенности, изменение в реализации всегда означает изменение тестовых случаев. Я не думаю, что возможно иметь общий unit test случай, который не требует изменений, даже если реализация метода изменяется.

Я надеюсь, что это поможет!

Ответ 5

Я делал то же самое, но теперь я думаю, возможно, все, что вам нужно сделать, это проверить, что метод транзакции был вызван в модели в одном spec, а затем проверял тело блока в других отдельных спецификациях. Хотя это не гарантирует, что транзакция обертывает ваши вызовы методов, как ваш текущий тест, а не какой-то другой код, который может быть там.

Ответ 6

Попробовать тестирование В режиме консоли в режиме rails

rails console --sandbox