Snowfakery を使用した Salesforce のテストデータ作成

[Snowfakery](https://github.com/SFDO-Tooling/Snowfakery) は、[Salesforce.org](https://www.salesforce.org/homepage-new/) のエンジニアリングチームが提供する、テストデータ生成のためのツールです。Snowfakery を使用すると、オブジェクト間のリレーションを保ちながら、ランダムなテストデータを生成することができます。生成したデータは Salesforce に直接ロードしたり、 CSV や JSON など様々な形式で出力することができます。この記事では、Snowfakery の基本的な使用方法について説明します。

## インストール
Python 3 および [pipx](https://github.com/pipxproject/pipx) をインストール後、

```
$ pipx install snowfakery
```

を実行してください。なお、Snowfakery は Salesforce プロジェクト・パッケージの自動化ツールの1つである [Cumulus CI](https://github.com/SFDO-Tooling/CumulusCI) に含まれています。

## サンプルファイルの実行
YAML でレシピファイルを作成し、これに基づいてデータセットを生成します。はじめに、簡単なファイルでデータ生成を試してみましょう。

```simple_account.yaml
- object: Account
  fields:
    Name: 東京株式会社
    Phone: 03-1234-5667
```

以下のコマンドを実行します。

```
$ snowfakery simple_account.yaml
Account(id=1, Name=東京株式会社, Phone=03-1234-5667)
```

YAML ファイルの構成がオブジェクトとその項目値のセットであること、また、実行すると Id が自動的に付与されることが分かります。

静的なレコードだけだとあまり旨味がないので、続けて、日本語のフェイクデータ生成を試してみましょう。以下のサンプルファイルを実行してみましょう。

```contacts.yml
- var: snowfakery_locale
  value: ja_JP
- object: Contact
  count: 5
  fields:
    LastName:
      fake: last_name
    FirstName:
      fake: first_name
    Address__c:
      fake: address
    Age__c:
      random_number:
        min: 20
        max: 65

```

```
$ snowfakery contacts.yml
Contact(id=1, LastName=中村, FirstName=加奈, Address__c=熊本県横浜市中区九段南9丁目10番20号 シャルム細竹339, Age__c=55)
Contact(id=2, LastName=鈴木, FirstName=拓真, Address__c=富山県印旛郡印旛村前弥六南町11丁目13番20号, Age__c=22)
Contact(id=3, LastName=鈴木, FirstName=あすか, Address__c=鹿児島県荒川区芝浦28丁目22番13号, Age__c=58)
Contact(id=4, LastName=池田, FirstName=翔太, Address__c=石川県府中市鶴ヶ丘4丁目3番14号 コーポ台場501, Age__c=55)
Contact(id=5, LastName=石井, FirstName=里佳, Address__c=岐阜県台東区日本堤16丁目21番6号 東大和町クレスト309, Age__c=47)
```

それらしいランダムデータが出力されているはずです。出力フォーマットを変更するには `--output-format csv` のようにオプションを指定してください。

Snowfakery は [Faker](https://github.com/joke2k/faker) のプロバイダを使用してフェイクデータを生成します。サンプルファイルでは、はじめにロケールを日本に設定後、姓・名・住所のプロバイダを指定しています。使用できるプロバイダは [Faker の ja_JP のプロバイダドキュメント](https://faker.readthedocs.io/en/master/locales/ja_JP.html) を参照してください。例えば、ダミーの企業名は `` fake: company `` のように指定します。 また、`random_number` のようにランダムで数値・選択肢を設定するこもできます。

繰り返しデータを生成するには、`--target-number レコード数 オブジェクト名` のようにオプションを指定します。

```
snowfakery contacts.yml --target-number 100 Contact
```

とすると、レシピが 20 回実行され、100 レコード分の取引先責任者が生成されます。

## 項目値の設定
`fake` 以外に項目値の設定に使用できる主要なキーワードを紹介します。

### ランダム値
数値の場合は、先のサンプルファイルのように、`random_number` を使用します。

選択肢の場合は、`randam_choice` を使用します。

```yaml
- object: Contact
  fields:
    FirstName:
      fake: first_name
    LastName:
      fake: last_name
    Salutation:
      random_choice:
        Mr.: 40%
        Ms.: 40%
        Dr.: 10%
        Prof.: 10%
```

日付の場合は、`date_between` を使用します。

```yml
- object: Opportunity
  fields:
    CloseDate:
      date_between:
        start_date: 2000-01-01
        end_date: today
```

### 条件分岐

`if` キーワードを使用します。

```yml
- var: snowfakery_locale
  value: ja_JP
- object: Person_c
  count: 5
  fields:
    Gender__c:
      random_choice:
        - choice:
            probability: 40%
            pick: 女性
        - choice:
            probability: 40%
            pick: 男性
        - choice:
            probability: 20%
            pick: その他
    FirstNameKana__c:
      if:
        - choice:
            when: ${{Gender__c=='女性'}}
            pick:
              fake: first_kana_name_female
        - choice:
            when: ${{gender=='男性'}}
            pick:
              fake: first_kana_name_male
        - choice:
            pick:
              fake: first_kana_name
```

```
Person_c(id=1, Gender__c=女性, FirstNameKana__c=ハルカ)
Person_c(id=2, Gender__c=女性, FirstNameKana__c=ユミコ)
Person_c(id=3, Gender__c=女性, FirstNameKana__c=アスカ)
Person_c(id=4, Gender__c=男性, FirstNameKana__c=クミコ)
Person_c(id=5, Gender__c=その他, FirstNameKana__c=ユウタ)

```

## オブジェクト間のリレーション

### 子レコード (1対多)
1対多のデータセットを生成するには、親オブジェクトの項目定義の後、`friends:` を使用します。子オブジェクト側は `reference: 親オブジェクト名` で参照します。

```account_contacts.yaml
- var: snowfakery_locale
  value: ja_JP
- object: Account
  count: 2
  fields:
    Name:
      fake: company
  friends:
    - object: Contact
      count: 3
      fields:
        LastName:
          fake: last_name
        FirstName:
          fake: first_name
        Account_ExternalId__c:
          reference: Account
```

```
$ snowfakery account_contacts.yml
Account(id=1, Name=藤田ガス株式会社)
Contact(id=1, LastName=高橋, FirstName=花子, Account_ExternalId__c=Account(1))
Contact(id=2, LastName=中村, FirstName=拓真, Account_ExternalId__c=Account(1))
Contact(id=3, LastName=林, FirstName=くみ子, Account_ExternalId__c=Account(1))
Account(id=2, Name=藤井情報有限会社)
Contact(id=4, LastName=福田, FirstName=学, Account_ExternalId__c=Account(2))
Contact(id=5, LastName=松本, FirstName=陽一, Account_ExternalId__c=Account(2))
Contact(id=6, LastName=山田, FirstName=洋介, Account_ExternalId__c=Account(2))
```

### 親レコードへの参照
レコード毎に親レコードを設定するには、`object` をネストします。例えば商談毎に取引先を生成する場合、以下のようにします。数式項目のように親レコードの項目値を参照することもできます。

```opp_account.yml
- var: snowfakery_locale
  value: ja_JP
- object: Opportunity
  fields:
    AccountId:
      - object: Account
        fields:
          Name:
            fake: company
    Name: ${{AccountId.Name}}の商談
```

```
$ snowfakery opp_account.yml
Account(id=1, Name=有限会社加藤水産)
Opportunity(id=1, AccountId=Account(1), Name=有限会社加藤水産の商談)
Account(id=2, Name=斉藤印刷株式会社)
Opportunity(id=2, AccountId=Account(2), Name=斉藤印刷株式会社の商談)
Account(id=3, Name=株式会社藤原印刷)
Opportunity(id=3, AccountId=Account(3), Name=株式会社藤原印刷の商談)
```


## Salesforce へのデータ投入
ちょっとした一度切りのデータ投入であれば、テキストで出力した結果を元に匿名実行で insert したり、生成された CSV をデータローダやインポートウィザードで取り込んでも良いかもしれませんが、Snowfakery は [Cumulus CI](https://github.com/SFDO-Tooling/CumulusCI) と統合されており、コマンドベースでデータ投入を自動化することができます。
Cumulus CI 自体は非常に多くの機能を有しているため、この記事では詳細は割愛しますが、Cumulus CI をインストールし組織に接続後、以下のようなコマンドで接続先の組織にデータセットをインポートすることができます。Cumulus CI の概要や設定手順については [Trailhead](https://trailhead.salesforce.com/ja/content/learn/modules/cumulusci-setup) を参照してください。

``` 
cci task run generate_and_load_from_yaml \
-o generator_yaml YOUR_RECIPE_FILE.yml \
-o num_records 10000 -o num_records_tablename Contact --org qa
```

## おまけ
Apex から直接フェイクデータを生成するようなコミュニティライブラリがないか探してみました。[Salesforce Faker](https://github.com/igormatkovic/salesforce-faker) や [Apex Faker](https://github.com/dshahin/apex-faker) を見つけましたが、メンテナンスはされていなさそうです…。

## 参考
* [Snowfakery の公式ドキュメント (英語)](https://snowfakery.readthedocs.io/en/stable/)
    * この記事では紹介しきれませんが、他にも多くの機能があります。