---
# System prepended metadata

title: FactoryBot筆記
tags: [FactoryBot]

---

---
title: FactoryBot筆記
description: 語法
robots: noindex, nofollow
lang: zh-tw
dir: ltr
breaks: true
tags: FactoryBot
disqus: hackmd
---
# 基礎應用
[基礎篇](https://devhints.io/factory_bot)
[Github FactoryBot Getting Started](https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md)

## Setting

Rspec 就不用再輸入`FactoryBot`直接`create(:xxx)`即可
```ruby=
RSpec.configure do |config|
  config.include FactoryBot::Syntax::Methods
end
```
Cucumber
```ruby=
# env.rb (Rails example location - RAILS_ROOT/features/support/env.rb)
World(FactoryBot::Syntax::Methods)
```

將 Factory 設定在以下路徑 `FactoryBot.find_definitions` 會自動找到此路徑加載 Factory

```ruby=
test/factories.rb
spec/factories.rb
test/factories/*.rb
spec/factories/*.rb
```

## 基礎

使用範例：
```ruby=
# This will guess the User class
FactoryBot.define do
  factory :user do
    first_name { "John" }
    last_name  { "Doe" }
    admin { false }
  end
end
```
上述所說會去猜測是否有 User 這個 class 然後依照你 Model欄位來寫attributes 最後幫你建出一筆 user。花括弧可省略

```ruby=
# This will use the User class (otherwise Admin would have been guessed)
factory :admin, class: "User"
```
意思是你也可以替factory指定其他名稱但必須指定他屬於哪個class

### 建立bot

factory_bot supports several different build strategies: build, create, attributes_for and build_stubbed:

```ruby=
# Returns a User instance that's not saved
user = build(:user)

# Returns a saved User instance
user = create(:user)

# Returns a hash of attributes that can be used to build a User instance
attrs = attributes_for(:user) # {name: 'CK', phone: '123'}

# Returns an object with all defined attributes stubbed out
stub = build_stubbed(:user)

# Passing a block to any of the methods above will yield the return object
create(:user) do |user|
  user.posts.create(attributes_for(:post))
end
```
create => 真的寫進db
build => 還沒save 且 id為 nil
build_stubbed => stub的概念創建一個假的實體而且還有id

如果不懂build、create、build_stubbed的可以看看這篇
[FactoryGirl create vs build vs build_stubbed](https://blog.gmifly.tw/2019/04/14/Rspec-FactoryGirl-vs-create-vs-build-vs-build-stubbed/)
[attributes_for看不懂看這篇](https://stackoverflow.com/questions/13150272/meaning-for-attributes-for-in-factorygirl-and-rspec-testing)

### Aliases

不常使用，用法其實就是association的概念只是縮寫
```ruby=
factory :user, aliases: [:author, :commenter] do
  first_name { "John" }
  last_name { "Doe" }
  date_of_birth { 18.years.ago }
end

factory :post do
  # 寫法等同於 association :author, factory: :user
  # 寫法等同於 association :user
  author
  title { "How to read a book effectively" }
  body { "There are five steps involved." }
end
```

不過這邊不推薦使用`association`因為他會在你建立這筆bot時也將有關聯的bot一同建立出來, 建議使用 `trait`

### Dependent Attributes

可以定義動態區塊在建立bot時在指定值

```ruby=
factory :user do
  first_name { "Joe" }
  last_name  { "Blow" }
  email { "#{first_name}.#{last_name}@example.com".downcase }
end

create(:user, last_name: "Doe").email
# => "joe.doe@example.com"
```

### Transient(目前搞不出來，測試中)

不常使用，可以定義不是attributes的值拿來判斷或是存放值

```ruby=
factory :user do
  transient do
    rockstar { true }
  end

  name { "John Doe#{" - Rockstar" if rockstar}" }
end

create(:user).name
#=> "John Doe - ROCKSTAR"

create(:user, rockstar: false).name
#=> "John Doe"
```

```ruby=
factory :car do
  transient do
    # capitalize is not an attribute of the car
    capitalize  false
  end

  name           { "Jacky" }
  purchase_price { 1000 }
  model          { "Honda" }

  after(:create) do |car, evaluator|
    car.name.upcase! if evaluator.capitalize
  end
end

car = FactoryGirl.create(:car, capitalize: true)
car.name
# => "JACKY"
```
[這邊有更詳細的講解](https://stackoverflow.com/questions/38573131/what-is-the-purpose-of-a-transient-do-block-in-factorybot-factories)

## 其他相關文件

[FactoryBot在4.11之後刪除所謂的靜態屬性](https://thoughtbot.com/blog/deprecating-static-attributes-in-factory_bot-4-11)(目前我還沒實測出來)