FactoryGirl 使用总结

Jul 26, 2015 10:39 · 1388 words · 3 minute read rails tech

什么是 FactoryGirl ?

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

FactoryGirl 可以做什么?

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

如何使用 FactoryGirl

1. 配置FactoryGirl

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

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

# 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定义在下面的位置,这样可以自动加载这些文件

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

定义factories的文件内容

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 使用最多,也是最方便清晰的。

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
# 将 callback 放在定义中即可
factory :user do
  after(:build) { |user| user.generate_token }
end

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

使用 factories

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

# 创建一个 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 ,并且使用方法基本是一致的,只是配置可能略有不同。官方教程 也写得很清晰,可以参考。

tweet Share