FactoryGirl 使用总结

什么是 FactoryGirl ?

FactoryGirl 是一个取代 fixtures 的工具,应用于测试中,以很简洁的方式生成你需要的测试数据。

FactoryGirl 可以做什么?

FactoryGirl 使用直白的语法生成多种数据,并且可以使用不同的生成策略,例如 已保存的实例、未保存的实例、包含实例属性的hash、stubbed object(表现为已保存的实例,实际未写入数据库,可以提高测试速度)。
FactoryGirl 可以自动匹配与自身文件同名的类,可以继承一个 factory 。
FactoryGirl 让你能以简单并且灵活的方式生成大量测试数据,而且你并不需要一一指定数据的类。

如何使用 FactoryGirl

1. 配置FactoryGirl

1
2
3
4
5
6
# Gemfile
group :test do
gem 'factory_girl_rails', '~> 4.0'
# 每次测试后清理数据库所需的gem
gem 'database_cleaner'
end

测试框架使用 RSpec,如果你使用其它测试框架请参照GettingStarted

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# spec/support/factory_girl.rb
RSpec.configure do |config|
# 加入FactoryGirl辅助方法,如果不加这一行,调用FactoryGirl时需要在方法前加 FactoryGirl
config.include FactoryGirl::Syntax::Methods
# Linting Factories,factory在build时会调用 valid? 方法,如果返回false,会报错,保证factory的正确性
# DatabaseCleaner,每次测试后清理数据库
config.before(:suite) do
begin
DatabaseCleaner.start
FactoryGirl.lint
ensure
DatabaseCleaner.clean
end
end
end

2. 基本用法

定义 factories

一般factories定义在下面的位置,这样可以自动加载这些文件

1
2
3
4
# 模型较少时可以放到一个文件中
spec/factories.rb
# 模型较多时最好一个模型对应一个文件
spec/factories/*.rb

定义factories的文件内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
FactoryGirl.define do
# FactoryGirl会自动匹配User模型
factory :user do
# 一般属性
username "cloudy9101"
admin false
# lazy attribute block
email { "#{username}@example.com".downcase }
# 可以继承 factory
factory :admin do
# 其它属性同 user ,以下属性覆盖或添加到 user
admin true
end
end
# 可以指定使用的模型
factory :admin, class: User do
username "admin"
admin true
end
# 可以指定factory的别名
factory :post, aliases: [:topic, :article] do
title "post title"
# 定义关联 - FactoryGirl 会自动根据 factory 名称进行关联实例的 build
# 如果关联名称和factory同名
user
# 如果关联名称和factory不同名(同时,可以指定属性进行覆盖)
association :author, factory: :user, username: 'other_name'
# 定义 sequence ,创建多个post时避免属性内容重复
sequence(:content) { |n| "content-#{n}" }
end
end

当你的factories 有多种类型时,譬如 user 有的时普通用户,有的是管理员用户,同时可能用户有等级高低的分别,这时如果每种用户建立一个 factory 太麻烦,使用继承也是可以的,但是可能不够清晰,这时我们可以用 trait ,我觉得 trait 是定义 factories 使用最多,也是最方便清晰的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
FactoryGirl.define do
factory :user do
username 'username'
admin false
trait :admin do
admin true
end
trait :vip do
role 'vip'
end
trait :male do
username 'male_user'
gender 'male'
end
end
end

FactoryGirl 中有四个 callback

  • after(:build) - after FactoryGirl.build, after FactoryGirl.create
  • before(:create) - before FactoryGirl.create
  • after(:create) - after FactoryGirl.create
  • after(:stub) - after FactoryGirl.build_stubbed
1
2
3
4
# 将 callback 放在定义中即可
factory :user do
after(:build) { |user| user.generate_token }
end

以上这些在定义 factories 时都可以使用,并且可以自由的组合使用,这样,你就可以定义出各种不同的 factory 供你的测试使用。

使用 factories

当你在配置好 FactoryGirl 之后,就可以在测试框架中使用它了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 创建一个 User 的实例,但是不保存,属性是在 factories 中默认的定义
user = build(:user)
# 创建一个已保存的 User 实例
user = create(:user)
# 获得 user 的全部属性的一个 hash 格式的内容,在测试建立 User 时很方便
attrs = attributes_for(:user)
# 创建一个 User 的实例,表现为已保存,实际并不插入数据库
stub = build_stubbed(:user)
# 可以在创建 factory 时定义属性,会覆盖默认属性值,包括普通属性和关联
user = build(:user, username: 'other_name')
post = build(:post, title: 'other_title', user: user)
# 选择 trait
admin = build(:user, :admin)
male = build(:user, :male)
male_admin = build(:user, :admin, :male)
# 创建多个对象,保存到一个数组
users = create_list(:user, 3, :admin, username: 'admin')

FactoryGirl 使用起来是不是非常简单方便呢,以上只是 FactoryGirl 的一些基本的功能,它还有一些进阶的使用方法,如定义创建的策略,修改定义,自定义 callback 等,如果需要可以自行研究。

3. 配合工具

FactoryGirl 可以用于多种测试框架,以及多种 web 框架,并不局限于 rails 或者 RSpec ,并且使用方法基本是一致的,只是配置可能略有不同。官方教程 也写得很清晰,可以参考。