我有一个模型功能,我想确保使用一个事务。例如:
class Model
def method
Model.transaction do
# do stuff
end
end
end
我当前的方法是在块内部存根方法调用以引发ActiveRecord::Rollback
异常,然后检查数据库是否实际已更改。但这意味着如果由于某种原因,块内的实现发生了变化,那么测试就会中断。
你会如何测试?
答案 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_operation
和second_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