如何测试某个函数在Rails和rspec 2中使用事务

时间:2012-05-09 07:01:00

标签: ruby-on-rails ruby rspec transactions rails-activerecord

我有一个模型功能,我想确保使用一个事务。例如:

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

我当前的方法是在块内部存根方法调用以引发ActiveRecord::Rollback异常,然后检查数据库是否实际已更改。但这意味着如果由于某种原因,块内的实现发生了变化,那么测试就会中断。

你会如何测试?

6 个答案:

答案 0 :(得分:30)

您应该从不同的角度来看问题。 从行为角度测试函数是否使用事务是无用的。它没有提供有关函数BEHAVES是否符合预期的任何信息。

您应该测试的是行为,即预期结果是正确的。为清楚起见,假设您在函数内执行操作A和操作B(在一个事务中执行)。操作A将用户100美元记入您的应用程序。 B行动用100美元借记用户信用卡。

您现在应该为测试提供无效的输入信息,以便从用户信用卡中扣款。将整个函数调用包装在expect { ... }.not_to change(User, :balance)

这样,您可以测试预期的行为 - 如果信用卡借记失败,请不要将该金额记入用户。此外,如果您只是重构代码(例如,您手动停止使用事务和回滚),那么测试用例的结果不应受到影响。

话虽这么说,你仍然应该像@luacassus提到的那样孤立地测试这两个操作。此外,如果您将所谓的“不兼容”更改(即您更改行为)更改为源代码@ rb512,那么您的测试用例应该失败是完全正确的。

答案 1 :(得分:14)

需要提到一个重要问题:在测试事务时,您需要关闭transactional_fixtures。这是因为测试框架(例如Rspec)将测试用例包装在事务块中。永远不会调用after_commit,因为没有真正提交。即使您使用以下内容,期望在事务内部回滚也不起作用:requires_new =>真正。相反,事务在测试运行后回滚。参考http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html嵌套事务。

答案 2 :(得分:5)

通常,您应该使用“纯”rspec测试来测试隔离中的应用程序块(类和方法)。例如,如果您有以下代码:

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

您应该在单独的测试方案中测试first_operationsecond_operation,最好不要访问数据库。稍后您可以为Model#method编写测试并模拟这两种方法。

在下一步中,您可以使用https://www.relishapp.com/rspec/rspec-rails/docs/request-specs/request-spec编写高级集成测试,并检查此代码在不同条件下如何影响数据库,例如second_method失败时。

在我看来,这是测试生成复杂数据库查询的代码最实用的方法。

答案 3 :(得分:2)

首先,您需要将Model.transaction do ... end块与begin rescue end块一起包围。

检查事务回滚的唯一方法是引发异常。所以你目前的方法都很好。至于您的关注,实施方式的改变总是意味着相应地改变测试用例。我不认为有一个通用的单元测试用例,即使方法实现发生变化也不需要改变。

我希望有所帮助!

答案 4 :(得分:1)

我一直在做同样的事情,但现在我想也许你需要做的就是测试在一个规范中在模型上调用'transaction'方法,然后在其他单独的规范中测试块的主体。虽然这不能确保事务将您的方法调用包装为当前测试,而不是其他可能存在的代码。

答案 5 :(得分:-4)

尝试在rails控制台沙盒模式下进行测试

rails console --sandbox