我可以使用 FactoryGirl 创建无效值吗?
Can I create invalid values with FactoryGirl?
我有电子邮件工厂 (spec/factories/email.rb):
FactoryGirl.define do
factory :email, class: String do
skip_create
transient do
username 'user'
subdomain 'mail'
domain_name 'example.com'
host { [subdomain, domain_name].compact.join('.') }
end
trait(:with_blank_host) { host '' }
trait(:with_blank_username) { username '' }
initialize_with { new("#{username}@#{host}") }
end
end
我有规范 (spec/models/user_spec.rb):
RSpec.describe User, type: :model do
# ...
it { is_expected.to_not allow_value(FactoryGirl.create(:email, :with_blank_host)).for(:email) }
it { is_expected.to_not allow_value(FactoryGirl.create(:email, :with_blank_username)).for(:email) }
end
这样使用FactoryGirl对吗?这是一种不良做法吗?
假设在没有 host/username 的情况下创建这些电子邮件背后的逻辑将不会在除测试之外的任何其他地方使用。为什么要留在厂里?
为什么不简单地做:
FactoryGirl.define do
factory :email, class: String do
skip_create
transient do
username 'user'
subdomain 'mail'
domain_name 'example.com'
host { [subdomain, domain_name].compact.join('.') }
end
initialize_with { new("#{username}@#{host}") }
end
end
RSpec.describe User, type: :model do
# ...
it 'should not allow blank host' do
is_expected.to_not allow_value(FactoryGirl.create(:email, host: '')).for(:email)
end
it 'should not allow blank username' do
is_expected.to_not allow_value(FactoryGirl.create(:email, username: '').for(:email)
end
end
最重要的是,有一个工厂来创建电子邮件字符串真的有意义吗?为什么不简单地使用字符串(这样更简单):
is_expected.to_not allow_value(FactoryGirl.create(:email, host: '')).for(:email)
对
is_expected.to_not allow_value('test@').for(:email)
如果你想通过测试获得一致的 :email,为什么不把它放在用户中。
FactoryGirl.define do
factory :user
transient do
username 'user'
subdomain 'mail'
domain_name 'example.com'
host { [subdomain, domain_name].compact.join('.') }
end
before(:create) do |user, evaluator|
user.email = "#{username}@#{host}"
end
end
end
但是如果逻辑属于那些特定的测试,为什么要在工厂中抽象所有这些而不是简单地使用 before/callback。 根据经验,我只将在工厂规范文件中使用的逻辑放在工厂中,其他一切都在适当的 before/after 回调中
RSpec.describe User, type: :model do
before :each do
# logic before each test of the file
end
context 'email validation'
before :each do
# logic before each test concerning the email validation
# useful to factor stuff that will be used multiple time
# e.g.
# @email_ok = 'user@example.com'
end
it 'should not allow blank host' do
# test specific setup
# useful for stuff only used in this test
# e.g
# email_no_host = 'user@'
# expectation
end
end
end
简而言之:
你所做的当然会奏效。这本身并不是坏习惯。恕我直言,这没有多大意义。
已编辑
您还可以在测试范围内添加一个助手,以免使模型太胖:
RSpec.describe User, type: :model do
context 'email validation'
def generate_email(**opts)
options = {username: 'user', subdomain: 'mail', domain_name 'example.com'}.merge(opts)
username = options[:username]
host = options[:host] || "#{options[:subdomain]}.#{options[:domain_name]}"
"#{username}@#{host}"
end
it 'should not allow blank host' do
is_expected.to_not allow_value(generate_email host: '').for(:email)
end
# Here goes 100 other use of generate_email
end
end
我有电子邮件工厂 (spec/factories/email.rb):
FactoryGirl.define do
factory :email, class: String do
skip_create
transient do
username 'user'
subdomain 'mail'
domain_name 'example.com'
host { [subdomain, domain_name].compact.join('.') }
end
trait(:with_blank_host) { host '' }
trait(:with_blank_username) { username '' }
initialize_with { new("#{username}@#{host}") }
end
end
我有规范 (spec/models/user_spec.rb):
RSpec.describe User, type: :model do
# ...
it { is_expected.to_not allow_value(FactoryGirl.create(:email, :with_blank_host)).for(:email) }
it { is_expected.to_not allow_value(FactoryGirl.create(:email, :with_blank_username)).for(:email) }
end
这样使用FactoryGirl对吗?这是一种不良做法吗?
假设在没有 host/username 的情况下创建这些电子邮件背后的逻辑将不会在除测试之外的任何其他地方使用。为什么要留在厂里?
为什么不简单地做:
FactoryGirl.define do
factory :email, class: String do
skip_create
transient do
username 'user'
subdomain 'mail'
domain_name 'example.com'
host { [subdomain, domain_name].compact.join('.') }
end
initialize_with { new("#{username}@#{host}") }
end
end
RSpec.describe User, type: :model do
# ...
it 'should not allow blank host' do
is_expected.to_not allow_value(FactoryGirl.create(:email, host: '')).for(:email)
end
it 'should not allow blank username' do
is_expected.to_not allow_value(FactoryGirl.create(:email, username: '').for(:email)
end
end
最重要的是,有一个工厂来创建电子邮件字符串真的有意义吗?为什么不简单地使用字符串(这样更简单):
is_expected.to_not allow_value(FactoryGirl.create(:email, host: '')).for(:email)
对
is_expected.to_not allow_value('test@').for(:email)
如果你想通过测试获得一致的 :email,为什么不把它放在用户中。
FactoryGirl.define do
factory :user
transient do
username 'user'
subdomain 'mail'
domain_name 'example.com'
host { [subdomain, domain_name].compact.join('.') }
end
before(:create) do |user, evaluator|
user.email = "#{username}@#{host}"
end
end
end
但是如果逻辑属于那些特定的测试,为什么要在工厂中抽象所有这些而不是简单地使用 before/callback。 根据经验,我只将在工厂规范文件中使用的逻辑放在工厂中,其他一切都在适当的 before/after 回调中
RSpec.describe User, type: :model do
before :each do
# logic before each test of the file
end
context 'email validation'
before :each do
# logic before each test concerning the email validation
# useful to factor stuff that will be used multiple time
# e.g.
# @email_ok = 'user@example.com'
end
it 'should not allow blank host' do
# test specific setup
# useful for stuff only used in this test
# e.g
# email_no_host = 'user@'
# expectation
end
end
end
简而言之:
你所做的当然会奏效。这本身并不是坏习惯。恕我直言,这没有多大意义。
已编辑
您还可以在测试范围内添加一个助手,以免使模型太胖:
RSpec.describe User, type: :model do
context 'email validation'
def generate_email(**opts)
options = {username: 'user', subdomain: 'mail', domain_name 'example.com'}.merge(opts)
username = options[:username]
host = options[:host] || "#{options[:subdomain]}.#{options[:domain_name]}"
"#{username}@#{host}"
end
it 'should not allow blank host' do
is_expected.to_not allow_value(generate_email host: '').for(:email)
end
# Here goes 100 other use of generate_email
end
end