這是 Test Prescription 這本書部分內容的筆記整理,如果你對這篇文章有興趣,強烈建議你去讀讀這本書。
spec/rails_helper.rb
RSpec.configure do |config|
# ...
# Setup FactoryGirl
config.include FactoryGirl::Syntax::Methods
# ...
end
可以動態定義欄位數值、可以參照已經定義過的欄位、如果factory名稱與model不同需要指定class。
spec/factories/projects.rb
FactoryGirl.define do
factory :project do
name "Project Runway"
due_date Date.parse("2014-01-12")
slug { "#{name.downcase.gsub(" ", "_")}" }
end
factory :big_project, class: Project do
name: "Big Project"
due_date { Date.today - rand(50) }
end
end
sequence可以用來自動建立序號,另外也支援定義在factory的外面讓多個factory可以共用或是使用generate如果欄位名稱不一樣。
spec/factories/tasks.rb
FactoryGirl.define do
factory :task do
sequence(:title) { |n| "Task #{n}" }
end
end
spec/factories/users.rb
FactoryGirl.define do
sequence :email do
|n| "user_#{n}@test.com"
end
factory :user do
name "Fred Flintstone"
email
end
factory :task do
title "Finish Chapter"
user_email { generate(:email) }
end
end
spec/factories/tasks.rb
FactoryGirl.define do
factory :task do
sequence(:title) { |n| "Task #{n}" }
factory :big_task do
size 5
end
factory :small_task do
size 1
end
end
end
可以將一些會共用的attributes包成一個trait。
FactoryGirl.define do
factory :task do
sequence(:title) { |n| "Task #{n}" }
trait :small do
size 1
end
trait :large do
size 5
end
trait :soon do
due_date { 1.day.from_now }
end
trait :later do
due_date { 1.month.from_now }
end
factory :trivial do
small
later
end
factory :panic do
large
soon
end
end
end
上面的trivial與panic factory可以寫成一行:
factory :trivial, traits: [:small, :later]
factory :panic, traits: [:large, :soon]
使用trait的優點除了可以共用外,將attribute用trait命名也可以增加可讀性,但缺點就是讓factory變複雜,而且要多打一些字。
factory_girl還有提供額外的功能:包括建立時可以呼叫callback、客製化建立的流程或是建立dummy attributes。更多有關factory_girl的資料請參考:factory_girl - Getting Started
在測試中可以使用FactoryGirl提供的method來建立假資料,例如:
it "uses factory girl slug block" do
project = FactoryGirl.create(:project, name: "Book To Write")
expect(project.slug).to eq("book_to_write")
end
而建立的方式有下面4種:
另外在建立時可以接一個block做額外設定:
project = FactoryGirl.build_stubbed(:project) do |p|
p.tasks << FactoryGirl.build_stubbed(:task)
end
還可以在method後面加上_pair或是_list來同時建立多個測試資料:
create_pair(:project)
build_stubbed_pair(:project)
create_list(:project, 5)
在factory中可以使用association來建立關聯,建立的時候可以支援association的名稱與model名稱不同,如下面的例子:
FactoryGirl.define do
factory :task do
title: "To Something"
size: 3
project
association :doer, factory: :user, name: "Task Doer"
end
end
在測試中使用具有關聯的factory要特別注意立時的行為,以task有一個project的assoication為例:
有另一種方式也可以避免assoication產生的create問題,就是在factory中明確指定建立的方式,如下面範例所示:
FactoryGirl.define do
factory :task do
title: "To Something"
size: 3
project, strategy: :build
end
end
不過這種做法有一個很大的缺點,因為使用strategy: :build表示build出來的project並沒有id,而task的project_id也不會有值,沒有意識到這樣的情況會在測試association上出錯。
其實處理關聯最好的做法是不要在factory中定義assoication,取而代之的是在測試中明確的設定所需的association,理由如下: