
[英]Rspec blows up because of a failing check in an untested class initializer. Is this because of Rails autoloading classes?
[英]Re-instancing a class after autoloading with Rails and RSpec
我有这个 Class,我在其中根据数据库表创建了一些属性:
class Form
include ActiveAttr::Model
attribute :type, type: String
attribute :default_name, type: String
Languages.all.each do |lang|
attribute :"name_#{lang}", type: String
end
end
这工作正常,但后来我用 RSpec 做了两个测试:
单元测试:
require 'rails_helper'
RSpec.describe Form, type: :class do
...
end
端到端测试:
require 'rails_helper'
RSpec.describe 'forms', type: :system do
let!(:languages) do
create(:language, name: 'es')
create(:language, name: 'en')
end
scenario 'accessing the page where I can see all the attributes of the Form' do
@form = create(:form, :with_languages)
visit form_page(@form)
end
...
当我运行rspec
时,Rails 自动加载所有内容,并且创建的表单 class 还没有数据库中的任何语言,因此它没有任何name_
属性。 第一个测试工作正常,但第二个测试失败,因为加载了表单 Class 时没有模拟语言:
undefined method `name_en' for #<Form:0x000000014766c4f0>
这是因为为了加载视图,我们在 controller 中执行@view = Form.new(@form)
。显然它不会创建新的 object。
如果我只运行第二个测试,它就像一个魅力,我尝试使用 DatabaseCleaner,但它是一样的。
有没有办法在不禁用自动加载的情况下执行此操作? 我尝试使用config.autoload_paths
禁用它,但它给了我数千个错误,而且它是一个巨大的应用程序。
我尝试了几种解决方案,但都没有用,我需要的是重新创建 class。
@view = Form.new(@form)
确实创建了一个新的 object 但它不会重新加载 class,因此不会再次定义属性。 attribute
是一个 class 方法,它在加载Form
class 时运行,在调用Form.new
时不运行。
我不确定您仅使用Form
class 做什么,通常每个表格都有一个特定的 class。 您可以稍后添加属性,但这是对Form
class 的永久更改:
>> Form
# Form class is autoloaded and all attributes are defined
Language Load (0.9ms) SELECT "languages".* FROM "languages"
=> Form(default_name: String, type: String)
>> Language.create(name: :en)
>> Form
# will not add language attributes
=> Form(default_name: String, type: String)
>> Form.attribute :name_en, type: String
=> attribute :name_en, :type => String
>> Form
=> Form(default_name: String, name_en: String, type: String)
您可以在初始化新的 object 时更改Form
class:
class Form
include ActiveAttr::Model
attribute :type, type: String
attribute :default_name, type: String
def self.lang_attributes languages = Language.pluck(:name)
Array.wrap(languages).each do |lang|
attribute :"name_#{lang}", type: String
end
end
lang_attributes
def initialize(...)
self.class.lang_attributes
super
end
end
>> Language.create(name: :en)
>> Form # first call to Form autoloads it
Language Pluck (0.8ms) SELECT "languages"."name" FROM "languages"
=> Form(default_name: String, name_en: String, type: String)
>> Language.create(name: :es)
>> Form.lang_attributes
>> Form
=> Form(default_name: String, name_en: String, name_es: String, type: String)
>> Language.create(name: :fr)
>> my_form = Form.new
Language Pluck (0.6ms) SELECT "languages"."name" FROM "languages"
=> #<Form default_name: nil, name_en: nil, name_es: nil, name_fr: nil, type: nil>
# NOTE: but this changes the class itself
如果您希望实际的 class 是动态的,那么 class 的定义必须是动态的:
class FormClass
def self.new langs: Language.pluck(:name)
Class.new do
include ActiveAttr::Model
attribute :type, type: String
attribute :default_name, type: String
Array.wrap(langs).each do |lang|
attribute :"name_#{lang}", type: String
end
end
end
end
>> form_class = FormClass.new # aka Form
Language Load (0.5ms) SELECT "languages".* FROM "languages"
=> (default_name: String, name_en: String, name_es: String, name_fr: String, type: String)
>> form_object = form_class.new # aka Form.new
=> #< default_name: nil, name_en: nil, name_es: nil, name_fr: nil, type: nil>
>> Language.destroy_all
>> form_object = FormClass.new.new
Language Load (0.7ms) SELECT "languages".* FROM "languages"
=> #< default_name: nil, type: nil>
# no more language attributes ^
# and you can override language attribute(s)
>> FormClass.new(langs: :en).new
=> #< default_name: nil, name_en: nil, type: nil>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.