Ruby 3.x迁移时RSpec测试中Proc代码行出现参数错误
迁移Ruby 2.7到3.0.4时ActiveRecord关联报错排查
问题背景
正在将基于Ruby 2.7的应用迁移至Ruby 3.0.4,应用使用Rails 6.1.7与rspec 5.1.2。其中CommonIssue模型与Issue模型存在has_many关联,执行测试代码时触发参数错误。
报错信息
#<ArgumentError: wrong number of arguments (given 2, expected 0..1)>
触发代码
两种写法均会触发相同错误:
写法1:
proc { CommonIssue.first.issues << Issue.first } .should raise_error("Validation failed: Issue has already been taken")
写法2:
expect { CommonIssue.first.issues << Issue.first } .to raise_error("Validation failed: Issue has already been taken")
堆栈跟踪
Failure/Error: proc { CommonIssue.first.issues << Issue.first } .should raise_error("Validation failed: Issue has already been taken") 期望捕获异常"Validation failed: Issue has already been taken",实际得到#<ArgumentError: wrong number of arguments (given 2, expected 0..1)>,调用栈如下: # /usr/local/bundle/gems/i18n-1.12.0/lib/i18n.rb:210:in `translate' # ./config/initializers/dynamic_form_monkey_patch.rb:55:in `block (2 levels) in full_messages' # ./config/initializers/dynamic_form_monkey_patch.rb:47:in `each' # ./config/initializers/dynamic_form_monkey_patch.rb:47:in `block in full_messages' # /usr/local/bundle/gems/activemodel-6.1.7/lib/active_model/errors.rb:237:in `each' # /usr/local/bundle/gems/activemodel-6.1.7/lib/active_model/errors.rb:237:in `each' # ./config/initializers/dynamic_form_monkey_patch.rb:34:in `full_messages' # /usr/local/bundle/gems/activerecord-6.1.7/lib/active_record/validations.rb:21:in `initialize' # /usr/local/bundle/gems/activerecord-6.1.7/lib/active_record/validations.rb:80:in `new' # /usr/local/bundle/gems/activerecord-6.1.7/lib/active_record/validations.rb:80:in `raise_validation_error' # /usr/local/bundle/gems/activerecord-6.1.7/lib/active_record/validations.rb:53:in `save!' # /usr/local/bundle/gems/activerecord-6.1.7/lib/active_record/transactions.rb:302:in `block in save!' # /usr/local/bundle/gems/activerecord-6.1.7/lib/active_record/transactions.rb:354:in `block in with_transaction_returning_status' # /usr/local/bundle/gems/activerecord-6.1.7/lib/active_record/connection_adapters/abstract/database_statements.rb:318:in `transaction' # /usr/local/bundle/gems/activerecord-6.1.7/lib/active_record/transactions.rb:350:in `with_transaction_returning_status' # /usr/local/bundle/gems/activerecord-6.1.7/lib/active_record/transactions.rb:302:in `save!' # /usr/local/bundle/gems/activerecord-6.1.7/lib/active_record/suppressor.rb:48:in `save!' # /usr/local/bundle/gems/activerecord-6.1.7/lib/active_record/associations/has_many_through_association.rb:80:in `save_through_record' # /usr/local/bundle/gems/activerecord-6.1.7/lib/active_record/associations/has_many_through_association.rb:31:in `insert_record' # /usr/local/bundle/gems/activerecord-6.1.7/lib/active_record/associations/collection_association.rb:437:in `block (2 levels) in concat_records' # /usr/local/bundle/gems/activerecord-6.1.7/lib/active_record/associations/collection_association.rb:462:in `replace_on_target' # /usr/local/bundle/gems/activerecord-6.1.7/lib/active_record/associations/collection_association.rb:283:in `add_to_target' # /usr/local/bundle/gems/activerecord-6.1.7/lib/active_record/associations/collection_association.rb:435:in `block in concat_records' # /usr/local/bundle/gems/activerecord-6.1.7/lib/active_record/associations/collection_association.rb:433:in `each' # /usr/local/bundle/gems/activerecord-6.1.7/lib/active_record/associations/collection_association.rb:433:in `concat_records' # /usr/local/bundle/gems/activerecord-6.1.7/lib/active_record/associations/has_many_association.rb:130:in `concat_records' # /usr/local/bundle/gems/activerecord-6.1.7/lib/active_record/associations/has_many_through_association.rb:40:in `concat_records' # /usr/local/bundle/gems/activerecord-6.1.7/lib/active_record/associations/collection_association.rb:121:in `block in concat' # /usr/local/bundle/gems/activerecord-6.1.7/lib/active_record/associations/collection_association.rb:136:in `block in transaction' # /usr/local/bundle/gems/activerecord-6.1.7/lib/active_record/connection_adapters/abstract/database_statements.rb:320:in `block in transaction' # /usr/local/bundle/gems/activerecord-6.1.7/lib/active_record/connection_adapters/abstract/transaction.rb:319:in `block in within_new_transaction' # /usr/local/bundle/gems/activesupport-6.1.7/lib/active_support/concurrency/load_interlock_aware_monitor.rb:26:in `block (2 levels) in synchronize' # /usr/local/bundle/gems/activesupport-6.1.7/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in `handle_interrupt' # /usr/local/bundle/gems/activesupport-6.1.7/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in `block in synchronize' # /usr/local/bundle/gems/activesupport-6.1.7/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in `handle_interrupt' # /usr/local/bundle/gems/activesupport-6.1.7/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in `synchronize' # /usr/local/bundle/gems/activerecord-6.1.7/lib/active_record/connection_adapters/abstract/transaction.rb:317:in `within_new_transaction' # /usr/local/bundle/gems/activerecord-6.1.7/lib/active_record/connection_adapters/abstract/database_statements.rb:320:in `transaction' # /usr/local/bundle/gems/activerecord-6.1.7/lib/active_record/transactions.rb:209:in `transaction' # /usr/local/bundle/gems/activerecord-6.1.7/lib/active_record/associations/collection_association.rb:135:in `transaction' # /usr/local/bundle/gems/activerecord-6.1.7/lib/active_record/associations/collection_association.rb:121:in `concat' # /usr/local/bundle/gems/activerecord-6.1.7/lib/active_record/associations/has_many_through_association.rb:21:in `concat' # /usr/local/bundle/gems/activerecord-6.1.7/lib/active_record/associations/collection_proxy.rb:1027:in `<<' # ./spec/models/common_issue_spec.rb:46:in `block (3 levels) in <top (required)>' # ./spec/models/common_issue_spec.rb:46:in `block (2 levels) in <top (required)>' # ./spec/spec_helper.rb:76:in `block (3 levels) in <top (required)>' # /usr/local/bundle/gems/database_cleaner-core-2.0.1/lib/database_cleaner/strategy.rb:30:in `cleaning' # /usr/local/bundle/gems/database_cleaner-core-2.0.1/lib/database_cleaner/cleaners.rb:34:in `block (2 levels) in cleaning' # /usr/local/bundle/gems/database_cleaner-core-2.0.1/lib/database_cleaner/cleaners.rb:35:in `cleaning' # ./spec/spec_helper.rb:75:in `block (2 levels) in <top (required)>' # /usr/local/bundle/gems/webmock-3.18.1/lib/webmock/rspec.rb:37:in `block (2 levels) in <top (required)>' # ./spec/models/common_issue_spec.rb:46:in `block (2 levels) in <top (required)>' # ./spec/spec_helper.rb:76:in `block (3 levels) in <top (required)>' # /usr/local/bundle/gems/database_cleaner-core-2.0.1/lib/database_cleaner/strategy.rb:30:in `cleaning' # /usr/local/bundle/gems/database_cleaner-core-2.0.1/lib/database_cleaner/cleaners.rb:34:in `block (2 levels) in cleaning' # /usr/local/bundle/gems/database_cleaner-core-2.0.1/lib/database_cleaner/cleaners.rb:35:in `cleaning' # ./spec/spec_helper.rb:75:in `block (2 levels) in <top (required)>' # /usr/local/bundle/gems/webmock-3.18.1/lib/webmock/rspec.rb:37:in `block (2 levels) in <top (required)>'
问题原因
从堆栈跟踪可以看到,错误根源在./config/initializers/dynamic_form_monkey_patch.rb第55行调用I18n.translate时传入了2个参数,但当前使用的i18n 1.12.0版本的translate方法只接受0到1个参数(Ruby 3.0对参数传递的兼容性更严格,加上i18n版本的API变化)。
这个monkey patch是针对旧版本Ruby/Rails/i18n编写的,迁移到Ruby 3.0后,参数传递方式不再兼容,导致抛出参数数量错误。
修复方案
- 打开
config/initializers/dynamic_form_monkey_patch.rb文件,定位到第55行的I18n.translate调用。 - 修改参数传递方式,符合i18n 1.12.0的API要求:
- 如果原来的调用是
I18n.translate(key, some_value),需要改为使用关键字参数,比如I18n.translate(key, default: some_value)或者其他对应的关键字参数,具体取决于代码的业务逻辑。
- 如果原来的调用是
- 保存修改后重新运行测试,验证错误是否消失。
内容的提问来源于stack exchange,提问作者Chris Root
相关产品推荐
相关产品推荐

