2
0

More than 1 year has passed since last update.

JS/TS GraphQL環境でFactoryBot Fakerのような環境を作った

Last updated at Posted at 2022-10-31
1 / 17

テスト書くとき、mock定義面倒ですよね!!!


storybookでも同じコード書いて...
mockだけで100行超えてしまうと本来書くコードが何か分からなくなったりしますよね😇😇😇😇😇


RailsのFactoryBotのような環境を作りましたので紹介します


紹介するもの完成形(使用方法)


index.stories.tsx
import { createList } from '../factory_helper'
import { newUser, newUserEmail } from '../factories'

Default.args = {
  fragment: {
    users: createList(newUser, 3), // [{__typename: 'User', id: 'u:1', nickname: 'Jerrell49', phone: '(574) 708-9283', userEmails: Array(1)}, ...]
    email: newUserEmail() // {__typename: 'UserEmail', id: 'ue:1', email: 'Lucile.Hagenes@gmail.com'}
  },
}


カスタマイズしたい場合、newUser({ nickname: '山田' })のように書きます

> newUser({ nickname: '山田' })
{__typename: 'User', id: 'u:1', nickname: '山田', phone: '(574) 708-9283', userEmails: Array(1)}

今回使用例のschema


schema.graphql
type User {
  id: ID!
  userEmails: [UserEmail]!
  nickname: String!
  phone: String!
}

type UserEmail {
  id: ID!
  email: String
}

@faker-js/faker をいれる

呼び出されるたびに結果が変わる

$ yarn add --dev @faker-js/faker

node
> faker.internet.userName()
'Abdiel_Volkman53'
> faker.internet.userName()
'Jerrell49'

/factoriesにfactoryを定義する


/factories/UserEmail.ts
import { faker } from '@faker-js/faker'
import { UserEmail } from '../graphql/generated'
import { nextFactoryId } from '../factory_helper'

export interface UserEmailOptions {
  __typename?: 'UserEmail'
  id?: UserEmail['id']
  email?: UserEmail['email']
}

export const newUserEmail = (options: UserEmailOptions = {}): UserEmail => {
  const o = options as UserEmail
  o.__typename = 'UserEmail'
  o.id = options.id ?? nextFactoryId('UserEmail')
  o.email = options.email ?? faker.internet.email()

  return o
}


/factories/User.ts
import { faker } from '@faker-js/faker'
import { User, UserEmail } from '../graphql/generated'
import { newUserEmail } from '.'
import { nextFactoryId } from '../factory_helper'

export interface UserOptions {
  __typename?: 'User'
  id?: User['id']
  nickname?: User['nickname']
  phone?: User['phone']
  userEmails?: Array<UserEmail>
}

export const newUser = (options: UserOptions = {}): User => {
  const o = options as User
  o.__typename = 'User'
  o.id = options.id ?? nextFactoryId('User')
  o.nickname = options.nickname ?? faker.internet.userName()
  o.phone = faker.phone.number()
  o.userEmails = [newUserEmail({ user: o })]

  return o
}


こんなようにindex.tsにとめると便利ですね

/factories/index.ts
export { newUser, type UserOptions } from './User'
export { newUserEmail, type UserEmailOptions } from './UserEmail'

FactoryBotによくあるhelperを作る


createListを作りました (npm packageにしたいね)

/factory_helper.ts
import { newUser, UserOptions, newUserEmail, UserEmailOptions } from './factories'

export type FactoryFunction = typeof newUser | typeof newUserEmail

export type NodeOptions =
  | UserOptions
  | UserEmailOptions

export const createList = (fn: FactoryFunction, i: number, options: NodeOptions = {}) => [...Array(i)].map(() => fn(options as any))

let nextFactoryIds: Record<string, number> = {};
const taggedIds: Record<string, string> = {};

export const nextFactoryId = (objectName: string): string => {
  const nextId = nextFactoryIds[objectName] || 1;
  nextFactoryIds[objectName] = nextId + 1;
  const tag = taggedIds[objectName] ?? objectName.replace(/[a-z]/g, "").toLowerCase();
  return tag + ":" + nextId;
}


参考

https://github.com/homebound-team/graphql-typescript-factories
scalarが読み込めなかったり、optionsをコミットしたかったので使用せず、ファイルを作成する形にした。
associationにて親子ともにを定義した場合重くなるので、方法模索中...(メソッド叩くまでクエリ呼ばれないとか、letとかrailsすごかった)
案としてはfragmentからfacotryを生成できるやつをどうにか頑張る(resoverに適当させる)

余談(Rails環境でのFactoryの便利さ)

とあるページがみたいが、userデータが必要なとき

User.create!(email: 'test@example.com', address: '東京都' ...大量のvalidates項目を入力)
# => nicknameが入力されていません。😡
FactoryBot.create(:user)
# => 簡単!!

herokuなどのpreview app環境でもfactoryからユーザーを大量に作ったりしています
User.create!()のようにheroku preview環境のテストコードのデータを書いた場合、validatesの変更で動かなくなり、メンテナンスされてないことを防げます。

Rails.envでproductionを使いながら、gemを追加する方法はこちら→productionで動かしながら、サーバーによって専用Gemを追加

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0