FactoryGirl 回调函数

#ruby #rails #factorygirl

FactoryGirl 回调函数

factorygirl中有三种钩子函数after_build,after_create,after_stub,其中,当build一个object之后就会调用after_build这个回调函数。其他两个调用的情况类似。

基本的has_many的关联

Model:

class Article < ActiveRecord::Base
  has_many :comments
end

class Comment < ActiveRecord::Base
  belongs_to :article
end

Fatories:

factory :article do
  body 'password'

  factory :article_with_comment do
    after_create do |article|
      create(:comment, article: article)
    end
  end
end

factory :comment do
  body 'Great article!'
end

然后可以这样使用回调函数了

article = create(:article_with_comment)

而不是

article = create(:article)
create(:comment, article: article)

多态关联

随着类之间的关系越来越复杂,节省的时间会越来越多

Models:

class User < ActiveRecord::Base
  has_many :interests, as: :interested
  has_many :topics, through: :interests
end

class Interest < ActiveRecord::Base
  belongs_to :topic
  belongs_to :interested, polymorphic: true
end

构件的factories:

factory :user
  email
  password 'password'

  factory :email_confirmed_user do
    email_confirmed { true }
  end
end

factory :topic do
  name 'topic_name'
end

factory :interest
  topic
  interested factory: :user
end

factory :music_interest, class: 'Interest' do
  topic.association(:topic, name: 'Music')
end

factory :sports_interest, class: 'Interest' do
  topic.association(:topic, name: 'Sports')
end

最后的部分:

factory :musical_user, parent: :email_confirmed_user do
  after_create { |user| create(:music_interest, interested: user)
end

factory :sporty_user, parent: :email_confirmed_user do
  after_create { |user| create(:sports_interest, interested: user) }
end

所以你可以这样调用:

user = create(:musical_user)

而不是

user = create(:email_confirmed_user)
create(:music_interest, interested: user)

使用factorygirl的build_stubbed生成更快的测试用例

使用built_stubbed与使用build和create相比,可以减少很多在数据库中持久化了的对象。下面的例子中,我们创建一个OrderProcessor,它接受orderCreditCard的实例。然后调用Braintree的API用来返回一个布尔值,判断是否真正发生了付款。

class OrderProcessor
  def initialize(order, credit_card)
    @order = order
    @credit_card = credit_card
  end

  def process
    charge_successful? charge_customer
  end

  private

  def charge_successful?(result)
    # processes the result to determine if the result is valid,
    # operating on the order and credit_card instance variables to
    # add errors, send emails, or track Braintree's transaction id
  end

  def charge_customer
    # runs Braintree::Customer.sale() with the appropriate options
  end
end

没有数据访问了数据库,所以我们可以这样写测试

describe OrderProcessor do
  let(:transaction_id) { '1234' }
  let(:order) { build_stubbed(:order) }
  let(:credit_card) { build_stubbed(:credit_card) }

  subject { OrderProcessor.new(order, credit_card) }

  context 'when the Braintree result is valid' do
    before do
      MockBraintree.stub_successful_customer_sale(transaction_id: transaction_id)
    end

    it 'assigns the transaction id to the order' do
      subject.process
      order.transaction_id.should == transaction_id
    end

    it 'returns true for #process' do
      subject.process.should be
    end

    it 'does not assign any errors to the credit card' do
      subject.process
      credit_card.errors.should be_empty
    end
  end

  context 'when the Braintree result is invalid' do
    before do
      MockBraintree.stub_unsuccessful_customer_sale
    end

    it 'does not assign the transaction id to the order' do
      subject.process
      order.transaction_id.should be_nil
    end

    it 'returns false for #process' do
      subject.process.should_not be
    end

    it 'assigns errors to the credit card' do
      subject.process
      credit_card.errors.should_not be_empty
    end
  end
end

Factory Girl Callbacks

Use Factory Girl’s build_stubbed for a Faster Test Suite

comments powered by Disqus