我有电子邮件工厂(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(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是否正确?这是一种不好的做法吗?
答案 0 :(得分:1)
假设创建没有主机/用户名的电子邮件背后的逻辑不会在测试之外的任何地方使用。你为什么要把它留在工厂里?
为什么不简单地做:
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)
VS
is_expected.to_not allow_value('test@').for(: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。 根据经验,我只会在工厂中放置将在spec文件中使用的逻辑,其他所有内容都在回调之前/之后
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
简而言之:
您正在做的事情当然会奏效。这本身并不坏。它只是没有多大意义恕我直言。
<强> EDITED 强>
您还可以在测试范围内添加帮助程序,以免使模型过于胖:
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