Factory Girl是一个方便的rails框架,可以轻松创建测试模型实例.
factory_girl允许您快速定义每个模型的原型,并询问具有对手头测试很重要的属性的实例.
一个例子(也来自主页):
Factory.sequence :email do |n|
"somebody#{n}@example.com"
end
# Let's define a factory for the User model. The class name is guessed from the
# factory name.
Factory.define :user do |f|
# These properties are set statically, and are evaluated when the factory is
# defined.
f.first_name 'John'
f.last_name 'Doe'
f.admin false
# This property is set "lazily." The block will be called whenever an
# instance is generated, and the return value of …Run Code Online (Sandbox Code Playgroud) 是否有可能做到这一点?
如果是这样,你怎么能这样做?
我有两个模型(ModelA和ModelB),每个都有FactoryGirl工厂.我希望ModelB的工厂能够(A)创建测试数据,并且(B)构建(不保存到数据库)样本数据以显示给客户.由于我的模型中的Rails验证,我无法让(A)工作.
MODELA:
class ModelA < ActiveRecord::Base
belongs_to :model_b
validates_presence_of :model_b
end
Run Code Online (Sandbox Code Playgroud)
ModelA工厂:
FactoryGirl.define do
factory :model_a do
some_attr "hello"
model_b { FactoryGirl.build :model_b }
end
end
Run Code Online (Sandbox Code Playgroud)
ModelB
class ModelB < ActiveRecord::Base
has_one :model_a
end
Run Code Online (Sandbox Code Playgroud)
ModelB的工厂
FactoryGirl.define do
factory :model_b do
some_attr "goodbye"
end
end
Run Code Online (Sandbox Code Playgroud)
如果没有验证错误,我无法从这些工厂创建对象:
ruby> FactoryGirl.create :model_a
ActiveRecord::RecordInvalid: Validation failed: ModelB can't be blank
Run Code Online (Sandbox Code Playgroud)
似乎FactoryGirl在保存其关联之前尝试保存工厂对象.我意识到我可以让ModelB的工厂创建其关联的ModelA(而不是构建它) - 然而,我将失去能够使用ModelA工厂来构建样本数据或保存测试数据的灵活性.或者,我可以删除验证; 但后来我没有验证.
还有其他选择吗?
默认情况下,FactoryGirl调用关联的工厂来创建它们.我可以将工厂的关联作为参数传递.但是我怎样才能传递一个应该在关联链中使用的对象呢?
例如:
我有一个Post,它有一个PostsManager,它有一个Account,属于current_user.
当我这样做时,Factory(:post)它会创建一个PostsManager,它创建一个不属于(存根)current_user的帐户.
因此,在使用Post工厂的规范中,我必须这样做:
account = Factory(:account, user: current_user)
post_manager = Factory(:post_manager, account: account)
post = Factory(:post, post_manager: post_manager)
Run Code Online (Sandbox Code Playgroud)
我想做的是打电话给工厂Factory(:post, user: current_user),然后current_user一直通过协会到帐户工厂.有没有办法这样做?
我需要将额外的参数传递给工厂女孩以用于回调.像这样的东西(但实际上更复杂):
Factory.define :blog do |blog|
blog.name "Blah"
blog.after_create do |blog|
blog.posts += sample_posts
blog.save!
end
end
Run Code Online (Sandbox Code Playgroud)
然后使用以下内容创建它:
Factory.create(:blog, :sample_posts => [post1, post2])
Run Code Online (Sandbox Code Playgroud)
有什么想法怎么做?
我对rails和TDD都很陌生(毫无疑问,从我的帖子中可以看出这一点)并且很难将我的大脑包裹在Rspec和FactoryGirl周围.
我正在使用Rails 3,rspec和工厂女孩:
gem 'rails', '3.0.3'
# ...
gem 'rspec-rails', '~>2.4.0'
gem 'factory_girl_rails'
Run Code Online (Sandbox Code Playgroud)
我有一个用户模型,我在开发过程中已成功运行测试,但随后需要添加一个名为"source"的属性.它用于确定用户记录最初来自何处(本地与LDAP).
在我的factories.rb文件中,我定义了几个工厂,看起来如下所示:
# An alumnus account tied to LDAP
Factory.define :alumnus, :class => User do |f|
f.first_name "Mickey"
f.last_name "Mouse"
f.username "mickeymouse"
f.password "strongpassword"
f.source "directory"
end
Run Code Online (Sandbox Code Playgroud)
我有一个宏定义(直到现在一直在工作),看起来像这样:
def login(user)
before(:each) do
sign_out :user
sign_in Factory.create(user)
end
end
Run Code Online (Sandbox Code Playgroud)
我在多个规范中调用它(例如来自users_controller_spec.rb):
describe "for non-admins or managers" do
login(:alumnus)
it "should deny access" do
get :index
response.should redirect_to(destroy_user_session_path)
end
end
Run Code Online (Sandbox Code Playgroud)
如果我没有指定"source"属性,一切正常,但是一旦我这样做,运行测试时会出现类似的错误
12) UsersController for non-admins or managers should deny access …Run Code Online (Sandbox Code Playgroud) 我有一个如此定义的Family类:
class Family < ActiveRecord::Base
after_initialize :initialize_family
belongs_to :user
validates :user,
:presence => true
validates :name,
:presence => true,
:length => { :maximum => 30 },
:format => { :with => /\A[a-zA-Z0-9\-_\s\']+\z/i}
def initialize_family
if self.name.blank? && self.user
self.name = "#{self.user.profile_full_name}'s Family"
end
end
end
Run Code Online (Sandbox Code Playgroud)
在我的工厂里.我有:
Factory.define :family do |f|
f.association :user, :factory => :user
end
Run Code Online (Sandbox Code Playgroud)
在我的family_spec.rb中,我有
let(:family) { Factory(:family) }
Run Code Online (Sandbox Code Playgroud)
但这失败了:
1) Family is valid with valid attributes
Failure/Error: let(:family) { Factory(:family) }
ActiveRecord::RecordInvalid:
Validation failed: Name can't be …Run Code Online (Sandbox Code Playgroud) 我只是想知道是否可以使用FactoryGirl创建一个名为"alias"的属性,因为别名是Ruby中的保留字.
FactoryGirl.define do
factory :blah do
name "dummy"
alias "dummy"
end
end
Run Code Online (Sandbox Code Playgroud)
我已经尝试了各种逃避事物的组合,但却无法获得任何有用的工作.
我只是好奇人们在编写RSpec规范时倾向于使用FactoryGirl.build_stubbed和使用的地方double.也就是说,是否有最佳实践,例如"仅在相应的模型规范中使用FactoryGirl方法?"
当您发现自己FactoryGirl.create(:foo)在spec/models/bar_spec.rb中使用时,它是代码味道吗?
如果你FactoryGirl.build_stubbed(:foo)在spec/models/bar_spec.rb中使用它是不是代码味道?
如果你FactoryGirl.create(:foo)在foos_controller_spec.rb中使用它是代码味道吗?
如果你FactoryGirl.build_stubbed(:foo)在foos_controller_spec.rb中使用它是不是代码味道?
如果你FactoryGirl.build_stubbed(:foo)在spec/decorators/foo_decorator_spec.rb中使用它是代码味道吗?
抱歉这么多问题!我只是想知道其他人如何在单元测试隔离和面向对象设计最佳实践中划清界限.
谢谢!
我最近升级到Rails 4,一切正常,除了我的Rspec测试.
require 'spec_helper'
describe Invoice do
before :each do
@user = FactoryGirl.create(:activated_user)
person = FactoryGirl.create(:person, :user => @user, :company => nil)
@project = FactoryGirl.create(:project, :user => @user, :person_ids => [person.id], :invoice_recipient_id => person.id)
end
it "has a valid factory" do
expect(FactoryGirl.build(:invoice, :project => @project, :user => @user)).to be_valid
end
it "is invalid without a number" do
expect(FactoryGirl.build(:invoice, :project => @project, :user => @user, :number => nil)).to have(1).errors_on(:number)
end
end
Run Code Online (Sandbox Code Playgroud)
运行这些测试时,我收到此错误:
Failure/Error: expect(FactoryGirl.build(:invoice, :project => @project, :user => @user, :number …Run Code Online (Sandbox Code Playgroud)