Как проверить, что определенная функция использует транзакцию в Rails и rspec 2
У меня есть функция модели, которую я хочу убедиться, что использует транзакцию. Например:
class Model
def method
Model.transaction do
# do stuff
end
end
end
Мой текущий подход заключается в том, чтобы заблокировать вызов метода внутри блока, чтобы поднять исключение ActiveRecord::Rollback
, а затем проверить, действительно ли база данных изменилась. Но это означает, что если по какой-то причине реализация внутри блока изменилась, тогда тест сломался бы.
Как бы вы протестировали это?
Ответы
Ответ 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