本文转载自公众号《核心阅读》(ID:AI_Discovery)。测试用例之间的陈旧数据是RSpec中竞争条件的主要原因之一,包括数据库Redis、文件等。本文将讨论如何清理数据库中的陈旧数据。RailsRspec后台默认事务如果你使用rails-rspec,在spec/rails_helper.rb中默认启用如下配置:RSpec.configuredo|config|config.use_transactional_fixtures=trueend,意思是“在一个事务中运行每个示例”,即,在示例结束时,将回滚所有数据库更改。如何获得“事务夹具”以实现“在事务中运行每个实例”?在深入研究Rails4代码库以查看其实际工作原理后,我发现了以下内容。在setup_fixtures函数中,Rails为每个数据库连接调用begin_transaction。Rails4的setup_fixtures在teardown_fixtures函数中,Rails为每个数据库连接调用rollback_transaction。Rails4的teardown_fixtures还意味着如果在应用程序中使用多个数据库,那么应用程序将为所有数据库创建事务。在实例外部创建的数据库记录不会回滚由于数据库事务围绕着每个实例,任何在实例外部创建的数据库记录都不会回滚,即before(:all),before(:context)或before(:suite)块将不会回滚。这可能会导致实例组之间而不是同一组实例之间的竞争条件,因此请小心处理挂钩。context'context1'dobefore(:context)docreate(:user)#WON'TBEROLLED-BACKendbeforedocreate(:user)#willberolled-backend#...endcontext'context2'dobefore(:context)docreate(:user)#WON'TBEROLLED-BACKend#...end#BYNOW,THEREARE2USERRECORDSCOMMITEDTODATABASE手动设置数据库事务你也可以选择使用钩子来手动设置数据库事务。RSpec.configuredo|config|config.use_transactional_fixtures=false#DISABLEDEFAULTTRANSACTIONSendbefore(:example)doActiveRecord::Base.connection.begin_transactionendafter(:example)doconn=ActiveRecord::Base.connectionconn.rollback_transactionifconn.transaction_open?end#ORaround(:example)做|example|ActiveRecord::Base.transactiondoexample.run#ROLLBACKaftertheexamplefinishes.#ThisexceptionissilentlyswallowedbyActiveRecord.raiseActiveRecord::Rollbackendend[Rails4&Rails5.0.x]数据库事务由线程执行。Rails4中的数据库连接从上面可以看出,ActiveRecord数据库连接是通过线程执行的。因此,Rails通过use_transactional_fixtures管理的默认数据库事务只能在主线程上使用。从技术上讲,根据事务回滚策略,一个线程的数据库记录将独立于其他线程。当您需要从另一个线程(例如Selenium)中的一个线程访问数据库数据时,请注意这一点。[Rails4&Rails5.0.x]JavaScript驱动程序(Selenium)和CapybaraWebkit的验收测试问题。Selenium在另一个线程上运行,因此它不能与运行RSpec的主线程共享事务。为了让客户端应用程序访问数据库中的数据,RSpec需要提交更改。在这种情况下,可以允许提交数据,然后手动清理数据。[Rails4&Rails5.0.x]DatabaseCleaner-回滚策略要解决上述问题,您首先需要通过将config.use_transactional_fixtures设置为false来禁用Rails派生的事务,或者干脆将其删除。DatabaseCleaner是一个gem,它提供了清理数据库的高级策略,例如修剪、事务处理或删除。这是一个众所周知的要点,它利用DatabaseCleaner来处理上述JS驱动程序问题:[SinceRails5.1]Databasetransactionsaresharedbetweentestthreads。添加了lock_thread为测试启用了lock_thread此更新允许将启用JS的验收测试包装在RSpec的默认事务中,这消除了对DatabaseCleaner的需要。希望本文能帮助您更好地理解RSpec中的数据库事务。
