Ruby on rails 如何在Rails和RSpec2中测试某个函数是否使用事务

Ruby on rails 如何在Rails和RSpec2中测试某个函数是否使用事务,ruby-on-rails,ruby,rspec,transactions,rails-activerecord,Ruby On Rails,Ruby,Rspec,Transactions,Rails Activerecord,我有一个模型函数,我想确保它使用事务。例如: class Model def method Model.transaction do # do stuff end end end 我目前的方法是在块内存根一个方法调用,以引发ActiveRecord::Rollback异常,然后检查数据库是否已实际更改。但这意味着,如果出于某种原因,块内的实现发生了更改,那么测试将中断 您将如何测试这一点?首先,您需要附上您的模型。事务处理。。。结束块,并开始救援结束块 检

我有一个模型函数,我想确保它使用事务。例如:

class Model 
  def method
    Model.transaction do
      # do stuff
    end
  end
end
我目前的方法是在块内存根一个方法调用,以引发
ActiveRecord::Rollback
异常,然后检查数据库是否已实际更改。但这意味着,如果出于某种原因,块内的实现发生了更改,那么测试将中断


您将如何测试这一点?

首先,您需要附上您的
模型。事务处理。。。结束
块,并开始救援结束块

检查事务回滚的唯一方法是引发异常。因此,您当前的方法都很好。至于您所关心的问题,实现中的更改总是意味着相应地更改测试用例。我不认为有一个通用的单元测试用例是可能的,即使方法实现发生了变化,它也不需要改变

我希望这有帮助

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

class Model 
  def method
   Model.transaction do
     first_operation
     second_operation
   end
end
您应该在单独的测试场景中测试
第一个\u操作
第二个\u操作
,理想情况下不影响数据库。稍后,您可以为
Model#method
编写一个测试,并模拟这两种方法

在下一步中,您可以使用编写高级集成测试,并检查此代码在不同条件下对数据库的影响,例如当
second\u方法
失败时


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

我一直在做同样的事情,但现在我认为您需要做的可能就是测试“transaction”方法是否已在一个规范中对模型调用,然后在其他单独的规范中测试块体。尽管这不能确保事务像当前测试那样包装您的方法调用,也不能像其他代码那样包装方法调用。

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

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

您现在应该为测试提供无效的输入信息,这样就无法从用户的信用卡中借记。将整个函数调用包装在一个
中,期望{…}。不要更改(User,:balance)

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


也就是说,您仍然应该像@luacassus提到的那样单独测试这两个操作。另外,如果您对@rb512提到的源代码进行了“不兼容”更改(即更改行为),那么测试用例应该失败,这是完全正确的。

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


rails控制台——沙盒

需要提到一个大问题:在测试事务时,需要关闭事务装置。这是因为测试框架(例如Rspec)将测试用例包装在事务块中。从不调用after_commit,因为没有真正提交任何内容。即使使用:requires_new=>true,在事务内部预期回滚也不起作用。相反,事务在测试运行后回滚。引用嵌套事务

我知道这一点。不管怎样,谢谢你的回答。这是一个巨大的难题,你在为处理AR事务的代码编写规范时需要注意。或者更好一点
expect{…}。not_to change(User,:balance)
谢谢@Andrew的评论,我一直在寻找这些小的调整:)在我的答案中改变了它。这个答案在理论上是有用的,但是没有一个很好的方法来测试像这样的并发行为。此外,假设我们相信ActiveRecord对
事务的实现是正确的,我们只需要测试特定的块是否被传递给它(顺便说一句,我也没有很好的方法来测试它。)OP询问自动化测试(请参阅标记“rspec2”和“rspec rails”),不检查它在Rails控制台中是否只工作一次。“测试块体”这是怎么可能的?好吧,你不能专门调用块,除非你将它分离到一个函数或lambda,但是你可以调用封闭函数并测试它是否完成了你想要的事情。但是正如我所说的,这不会给你任何关于这些事情是否在事务中完成的信息。对于
raise-ActiveRecord::Rollback
来说,这是不正确的,它是由事务直接处理的,不会被重新引发。