interfaceForgeでテスト用のモックを作る

# はじめに
これまで、フロント側のテストでユーザーなどのオブジェクトを使いたい時に、各specで欲しいオブジェクトを定義していました。その場合、あちこちに同じような定義が散らばってしまうので、良い感じに管理できるものはないかなーと探していたところ、[interfaceForge](https://goldziher.github.io/interface-forge/) というライブラリを見つけました。触ってみたところ、シンプルで使いやすいと感じたので、本記事で使い方などを簡単にまとめておこうと思います。

# 環境
- typescript 4.1.6
- jest 24.9.0
- interface-forge 1.4.1

# interfaceForgeとは
interfaceForge を使うと、テストで使うモックを一元管理できるようになります。
使い方の流れは以下になります。

1. 各オブジェクトのFactory(モックのひな形のようなもの)を定義する
1. テストでFactoryを元にモックを生成する

Rails + RSpecでテストを書いたことがある人なら、FactoryBotのようなものと思ってもらえるとイメージしやすいと思います。

オブジェクトの基本系は Factory に集約されるので、冒頭で挙げていたオブジェクトの定義があちこちに散らばってしまう問題も解消することができます。また、バリデーションのテストをしている時などで、「このテストの時だけ特定のプロパティの値を変えたい（空にするなど）」というシチュエーションもあるかと思いますが、Factory を使う時に一部のプロパティのみ上書きすることも可能です。

また、TypeScriptをサポートしているので、型の恩恵も受けられます。

# 実践
セットアップから実際の使い方について説明していきます。

## 準備
### interfaceForgeのインストール

npmの場合

```
npm install --save-dev interface-forge
```

yarnの場合

```
yarn add --dev interface-forge
```

### もろもろセットアップ
#### オブジェクトの型を定義する
Factory を作成する時に、対象のオブジェクトの型を型引数として渡すことができます。
今回はユーザー用のモックを作る想定なので、ここではユーザーの型を定義します。
また、`Type Alias`と`Interface`のどちらの型定義でも、Factory の型引数として渡すことができますが、本記事では個人的によく使う `Type Alias` で型を定義していきます。

```typescript:types.ts
export type User = {
  id: number
  firstName: string
  lastName: string
  email: string
  password: string
}
```

#### Factoryを定義する
モックを作成する時にベースとなる Factory を定義していきます。
公式ドキュメントでは、factories.ts に Factory を定義していますが、個人的には1ファイルで1オブジェクトを管理した方が使いやすそうな気がするので、factories というディレクトリを切って、ユーザー用の Factory を定義しました。ただし、ここで作成した Factory は、テスト側で import して使うのですが、ファイルを分けることで import する行も増えてしまうという欠点があります。この辺りは使っていきつつ、どちらが良さそうか検討していこうと思っています。

```typescript:factories/users.ts
import { TypeFactory } from 'interface-forge';
import { User } from 'types';

// ここで型引数を指定することで、モックを作成する時に型の恩恵を受けれる。
export const UserFactory = new TypeFactory<User>((i) => ({
  // iが0始まりなので、+1してる。もっと良いやり方がありそう。。
  id: 1 + i,
  firstName: 'hoge',
  lastName: 'huga',
  email: 'test@exmaple',
  password: 'password'
}));
```

ここまでで、準備は終わりです。

## 使い方
先ほど定義した Factory を使って、モックを作成していきます。

### モックを生成
これが基本の操作で、`buildSync`を使ってモックを生成しています。
戻り値は、Factory をベースに作られたオブジェクトです。

```typescript:hoge.ts
import { UserFactory } from '../factories/users'
...

const user = UserFactory.buildSync()
// 下記のようなモックが作成される
// {
//    id: 1,
//    firstName: 'hoge',
//    lastName: 'huga',
//    email: 'test@exmaple',
//    password: 'password'
// }

```

### 一部のプロパティを上書きしつつ、モックを生成
一部のプロパティを変えたい場合は、プロパティ名と設定したい値を引数に渡せば、渡したプロパティのみ上書きすることができます。例えば、バリデーションエラーになることを確認するテストで、「特定の項目のみ invalid な値を設定したい」なんて時に使うと便利だと思います。下記は、firstName を空文字にして、それ以外は Factory に設定してある値を使うようにしています。

```typescript:hoge.ts
import { UserFactory } from '../factories/users'
...

const user = UserFactory.buildSync({ firstName: '' })
// 下記のようなモックが作成される
// {
//   id: 1,
//   firstName: '',
//   lastName: 'huga',
//   email: 'test@exmaple',
//   password: 'password'
// }

```

ちなみに Factory を定義する時に、型引数を指定していたので、タイポがあったりするとちゃんとコンパイルエラーになります。素晴らしい🎉

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201112/03b2ee50-4135-2299-21b1-35a9272f0ec0.png)

### モックをまとめて作成する
モックをまとめて作成したい時は `batchSync` が使えます。
戻り値はオブジェクトの配列です。

```typescript:hoge.ts
import { UserFactory } from '../factories/users'
...

const user = UserFactory.batchSync(2)

// 下記のようなモックが作成される
// [
//    {
//      id: 1,
//      firstName: 'hoge',
//      lastName: 'huga',
//      email: 'test@exmaple',
//      password: 'password'
//    },
//    {
//      id: 2,
//      firstName: 'hoge',
//      lastName: 'huga',
//      email: 'test@exmaple',
//      password: 'password'
//    }
// ]
```


# まとめ
自分はこれまで Rails の案件に携わることが多かったので、テストを書く時によく FactoryBot を使っていました。interfaceForge を使うことで、フロント側のテストを書く時も同じように Factory を管理できるようになるので、個人的には結構使いやすいと感じています。interfaceForge は faker.js と組み合わせて使うこともできるみたいなので、試したら別途記事にしようと思います。

# 参考
- https://goldziher.github.io/interface-forge/