FactoryGirl 技巧

简洁地设置 Association

1
2
3
factory :post do
association :author, factory: :author
end

我们可以通过以上来设置相关的 association,但如果 association 的名字与它 factory 的名称是相同的,我们就可以省略其 factory 的设置。

1
2
3
factory :post do
auther
end

继承

1
2
3
4
5
6
7
8
9
10
11
factory :post do
title "A title"

factory :approved_post do
approved true
end
end

approved_post = create(:approved_post)
approved_post.title # => "A title"
approved_post.approved # => true

也能明确指定 parent。

1
2
3
4
5
6
7
factory :post do
title "A title"
end

factory :approved_post, parent: :post do
approved true
end

使用 Trait

以 Order 为例,它有多个状态。为了设置不同状态的 Order,我们可以通过写多个 Order 的 factory 来完成。

1
2
3
4
5
6
7
8
9
10
11
factory :order_paid do
customer
state :paid
note 'This is a paid order'
end

factory :order_unpaid do
customer
state :unpaid
note 'This is an unpaid order'
end

但如果 Order 的属性很多,这两个 factory 大部分都是重复的。可以使用 trait 来简化类似的 factory。

1
2
3
4
5
6
7
8
9
10
11
12
factory :order od
customer
note 'This is a order'
end

trait :paid do
state :paid
end

trait :unpaid do
state :unpaid
end

要创建不同状态的 Order 时,使用 create :order, :paidcreate :order, :unpaid

使用 Callback

当 Model 有多个 association 时,而且多个 association 还存在一定的关系时,我们就不能简单地设置 association,这会使 association 的关系遭到破坏。
例如一个订单有多个子订单,而每个子订单都有一个物流信息。

1
2
3
4
5
6
7
8
9
10
11
12
class Order
has_many :shipments
end

class SubOrder
has_one :shipment
end

class Shipment
belongs_to :order
belongs_to :sub_order
end

若要创建 Shipment 的 factory,需注意到 order、sub_order 是有一定的关系的。
可以通过 FactoryGirl 的 before(:create) 回调来实现。

1
2
3
4
5
6
7
8
9
factory :shipment do
...
before :create do |shipment|
order = create :order
sub_order = create :sub_order, order: order
shipment.order = order
shipment.sub_order = sub_order
end
end

除 before(:create) 外,还有 after(:build)、after(:create)、after(:stub)。

设置 Transient Attributes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
FactoryGirl.define do
factory :comment do
author
body 'Post body'
approved_at Date.new(2016, 12, 18)
post
end

factory :post do
title 'New post'
end

trait :with_comments do
after :create do |post|
create_list :comment, 3, :post => post
end
end
end

create :post, :with_comments 会创建带有 3 条 comment 的 post。但若我们只想要有 2 条 comment 呢?通过 transient attributes 可以在 create 时动态地更改 comment 的数量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
FactoryGirl.define do
factory :comment do
author
body 'Post body'
approved_at Date.new(2016, 12, 18)
post
end

factory :post do
title 'New post'
end

trait :with_comments do
transient do
comments_count 3
end
after :create do |post, evaluator|
create_list :comment, evaluator.comments_count, :post => post
end
end
end

调用 create :post, :with_comments, comments_count: 2 即可。

Lazy Attributes

1
2
3
4
5
factory :product do
...
name Faker::Name.name
...
end

这里使用 Faker 来动态生成名字。但是调用 create :product 多次后你会发现它们的名字都是相同的。若想在每次创建实例时重新执行 Faker::Name.name

1
2
3
4
5
factory :product do
...
name { Faker::Name.name }
...
end

这样生成实例时都会调用 Faker::Name.name

参考