20251029

2025/10/29

Faker

https://fakerjs.dev/

フェイクデータを生成できるツール。

  • 単体テスト用のデータ
  • デモ表示用のデータ
  • バックエンドのスキーマに基づいた仮データ

などに使用できる。

ただの文字列や数値だけでなく、意味を持ったデータ(人名、食べ物名、場所名など)を生成できる。

使用方法

npm install @faker-js/faker --save-dev
import { faker } from '@faker-js/faker';

console.log(faker.person.fullName()); // Amanda Schmeler
console.log(faker.food.dish());       // Katsu Curry
console.log(faker.location.city());   // Liamland

実行するたびにランダムで生成される。

const sex = faker.person.sexType();
const firstName = faker.person.firstName(sex);

console.log(sex);       // female
console.log(firstName); // Lynette

データ間に関連性のある場合も、ある程度対応可能。この場合は先に生成された性別をもとに、性別に合った名前が返される。

日本語対応

import { fakerJA } from '@faker-js/faker';

console.log(fakerJA.person.fullName()); // 小川 美代子
console.log(fakerJA.food.dish());       // Pasta Carbonara
console.log(fakerJA.location.city());   // 東浩市

fakerJA を使用すれば日本語を取得できる。(一部未対応の模様)

microCMS用のAPIモックデータ生成

// mocks/generators/microcmsContent.ts

import { faker } from "@faker-js/faker";
import { MicroCMSContentId, MicroCMSDate } from "microcms-js-sdk";

export default function generateMicroCMSContentMockData(): MicroCMSContentId & MicroCMSDate {
  const createdAt = faker.date.anytime().toISOString();
  const updatedAt = faker.date.soon({refDate: createdAt}).toISOString();
  const publishedAt = faker.date.anytime().toISOString();
  const revisedAt = faker.date.soon({refDate: publishedAt}).toISOString();

  return {
    id: faker.string.alpha({length: 10}),
    createdAt,
    updatedAt,
    publishedAt,
    revisedAt,
  };
}

コンテンツに共通で付与されるIDと日時の生成。

// mocks/generators/tag.ts

import { Tag } from "@/lib/microcms"
import { fakerJA } from "@faker-js/faker";
import { MicroCMSContentId, MicroCMSDate } from "microcms-js-sdk";
import generateMicroCMSContent from "./microcmsContent";

export default function generateTagList(): (MicroCMSContentId & MicroCMSDate & Tag)[] {
  return Array.from({ length: 30 }, generateTagDetail);
}

function generateTagDetail(): MicroCMSContentId & MicroCMSDate & Tag {
  return {
    name: fakerJA.lorem.word(),
    ...generateMicroCMSContent(),
  }
}

タグAPI用のモックデータの生成。

// mocks/generators/blog.ts

import { Blog, Tag } from "@/lib/microcms"
import { fakerJA } from "@faker-js/faker";
import { MicroCMSContentId, MicroCMSDate } from "microcms-js-sdk";
import generateMicroCMSContent from "./microcmsContent";

export default function generateBlogList(tagList: (MicroCMSContentId & MicroCMSDate & Tag)[]): (MicroCMSContentId & MicroCMSDate & Blog)[] {
  return Array.from({ length: 30 }, () => generateBlogDetail(tagList));
}

function generateBlogDetail(tagList: (MicroCMSContentId & MicroCMSDate & Tag)[]): MicroCMSContentId & MicroCMSDate & Blog {
  const tagNum = fakerJA.number.int({ min: 0, max: 3 });
  const tags = fakerJA.helpers.arrayElements(tagList, tagNum);

  return {
    title: fakerJA.lorem.words({min:3, max:5}),
    body: generateBodyHtml(),
    tags,
    ...generateMicroCMSContent(),
  }
}

const p = () => `<p>${fakerJA.lorem.paragraph({ min: 2, max: 4 })}</p>`;
const h2 = (t: string) => `<h2>${t}</h2>`;
const h3 = (t: string) => `<h3>${t}</h3>`;

function generateBodyHtml(): string {
  const sections = fakerJA.number.int({ min: 2, max: 4 });
  let html = "";

  for (let i = 0; i < sections; i++) {
    const h2Title = fakerJA.lorem.sentence();
    html += h2(h2Title);

    // h2直下の段落を1〜3個
    const paraUnderH2 = fakerJA.number.int({ min: 1, max: 3 });
    for (let j = 0; j < paraUnderH2; j++) html += p();

    // 小見出し(h3)を1〜3個、それぞれに1〜3段落
    const subCount = fakerJA.number.int({ min: 1, max: 3 });
    for (let k = 0; k < subCount; k++) {
      const h3Title = fakerJA.lorem.sentence();
      html += h3(h3Title);

      const paraUnderH3 = fakerJA.number.int({ min: 1, max: 3 });
      for (let m = 0; m < paraUnderH3; m++) html += p();
    }
  }

  return html;
}

ブログAPI用のモックデータ生成。なんちゃってリッチテキストを生成している。

APIスキーマはこのブログのスキーマに合わせる。

タグの整合性を保つためにタグの配列を引数で受け取り、それを使用する。

生成スクリプト

// scripts/generate-mock.ts

import fs from 'node:fs';
import path from 'node:path';
import generateBlogList from '@/mocks/generators/blog';
import generateTagList from '@/mocks/generators/tag';

const outDir = path.resolve(process.cwd(), 'mocks/fixtures');

const tagList = generateTagList();
const blogList = generateBlogList(tagList);

fs.mkdirSync(outDir, { recursive: true });
fs.writeFileSync(path.join(outDir, 'tag.json'), JSON.stringify(tagList, null, 2));
fs.writeFileSync(path.join(outDir, 'blog.json'), JSON.stringify(blogList, null, 2));

上記の生成関数を使ってデータを生成し、結果をファイルに保存。

// package.json
...
  "scripts": {
    ...
    "gen:mocks": "tsx ./scripts/generate-mocks.ts"
  },
...
$ npm run gen:mocks

tsxで上記を実行。実行しやすいようにnpm scriptsに追加する。

以下、生成されたデータ。

// mocks/fixtures/tag.json

[
  {
    "name": "かぐ",
    "id": "RgGDcSSPxv",
    "createdAt": "2025-05-26T16:07:40.785Z",
    "updatedAt": "2025-05-26T20:47:16.230Z",
    "publishedAt": "2025-06-24T13:07:19.192Z",
    "revisedAt": "2025-06-25T13:03:15.152Z"
  },
  {
    "name": "左右",
    "id": "zKsFhWSWLj",
    "createdAt": "2024-11-17T01:39:22.434Z",
    "updatedAt": "2024-11-17T15:34:35.765Z",
    "publishedAt": "2026-06-17T13:48:28.032Z",
    "revisedAt": "2026-06-17T17:03:13.568Z"
  },
  ...
]
// mocks/fixtures/blog.json

[
  {
    "title": "誓い 左右 靖国神社 たまご 碁",
    "body": "<h2>病院 間隔 陳列室 こころみる 襲撃 となえる 可愛い ざせき はきだす.</h2><p>辛子 おろし 会議. さわ かわさき 近視 色彩 火 かいほうする. ぞうえん え じぎする せいぞう 電話 済ます こくふくする 間接 遺失 せんじょう.</p><p>疎外 あらじお あまい ちがい 察知 子守歌 はやて. しえんする 金縛り 墓 かたみち 匿名 封筒. ごらん 図説 前 公共 せいかん 壁 きょうき まほうつかい. 親子丼 累進 こうくうぼかん.</p><h3>泥棒 しんし 原因 全日本 ざぜん 米兵.</h3><p>親子丼 かいたく 宜しく 解説 猿真似 ぞうえん 二巻 かくじっけん 空き瓶. 貨物船 はいき 辞儀する. 性病 栞 かつぐ 燃やす.</p><p>やしなう 構え しっぷう じゅうどう とうさん 頑張れ 約する 月刊 性格. のぞいて 総括 大仏 魔術 ゆれる きげんご 指定する 米兵 年額 ぞくご.</p><p>てんのう 満潮 そんざい ふかのう てんぷく 雑音 唄う びんぼう. 終点 こい ちょさくけん 洗濯屋 鋭い ひんかく 糸 がくふ.</p><h3>超音波 鎮める とふ まつ 検査.</h3><p>いく りょうど 巡回. 遮断 みつ 没落 愛国心 いか.</p><p>わかめ 電源 もくひょう まもる せんじょう そだてる じっかん あまる かんかつ. 煩い 金縛り 靖国神社 運ぶ 逆.</p><h3>雑音 羊毛 九日 さいほう 平安 問題 とうさく.</h3><p>全日本 おなか 悪霊 あれる. 安泰 拘置 ごらく ころす かくれる そあく ぎせい じょうだん りりしい. 逆 秘める しょひょう はなのあな のむ.</p><p>華道 下さい 壮年 病床 原油 ふきつ やすい きゅうりょう 原因 料理人. 封筒 左右 しめる あしくび 傑作. 溶岩 こい 同音異義語 かくじっけん いさぎよい.</p><h2>独裁 羊毛 たて のき ほんるいだ 報じる そうだん.</h2><p>じょうだん 焦がす 悲しみ 自宅. 漠然 警官 柱 漬物 病床. 頑張れ ふたたび やすい ふねんゴミ 果樹 輸出 狂う. よくあつ せんのう 不思議 所 たまご 桜色.</p><h3>じょうじゅん つくる 解説 普段 しばふ 備える しょうじょう 総括 華やか しんじゅく.</h3><p>走り回る しんし 魔術 着く 指紋 ほうき ふくせん かんそく. 並 ついたち じぞう 主に. 犠牲 全日本 柱 えんちょうする 夏 たんれん へいせい ふさい.</p><h3>みき かんしん 救急車 いち きじゅつ れいてん もくひょう いじん.</h3><p>ちあん 貨幣 あらしお 帳簿 きょうかい はなのあな. むぜい 原因 平安 約する うらぎり. あらあらしい 好奇心 ころす. けんしゅうせい 反則 みぎて はなじ 失う 開閉.</p><p>博物館 おんとう 寮生 あっとうする はんそう あしくび 象牙 かせぎ ほんそう ゆれる. 退く てんぷく 雄犬 ほ むらさきいろ さきまわり けしき. 図説 ごじゅう おんとう.</p>",
    "tags": [
      {
        "name": "碁",
        "id": "dEhPqFvbUY",
        "createdAt": "2024-11-05T16:08:03.955Z",
        "updatedAt": "2024-11-05T21:20:34.322Z",
        "publishedAt": "2025-12-01T22:04:47.806Z",
        "revisedAt": "2025-12-01T22:18:55.015Z"
      },
      {
        "name": "誘惑",
        "id": "nxboQKJgwr",
        "createdAt": "2024-12-17T03:14:55.743Z",
        "updatedAt": "2024-12-17T23:03:33.755Z",
        "publishedAt": "2026-04-21T10:57:55.213Z",
        "revisedAt": "2026-04-22T05:55:38.395Z"
      }
    ],
    "id": "GzJEqkSUgH",
    "createdAt": "2026-04-06T18:05:47.783Z",
    "updatedAt": "2026-04-06T19:08:42.797Z",
    "publishedAt": "2025-12-08T08:38:11.728Z",
    "revisedAt": "2025-12-09T00:31:52.654Z"
  },
  ...
]

長いので省略。

fakerJA を使って一応日本語の文字列が出来上がっている。文章というよりただの単語の羅列なんだけど、、、多分問題ないので目をつぶる。

これをMSWが返すようにすればAPIモックが出来上がる、、、か?

MSW

以前書いたMSWの設定で疑問が一つ生まれたので調べた。

以前の例では MswProvider コンポーネントで "use client" を指定して、コンテンツをすべて MswProvider 内に置いていた。その場合、もしかして子コンポーネントもClient componentとなってしまうのではないか?

結論から言うと子コンポーネントはServer componentとなる。

"use client" はコンポーネントツリー内の境界ではなくモジュールツリー内の境界を指定する。つまり "use client" を指定したモジュールからインポートしたモジュールはClient componentとなるが、受け取った children を子コンポーネントとして返す場合、その子コンポーネントは"use client" の影響を受けない。

MswProvider では children を引数で受け取り、MSWの設定が終わると children を返す。(MSWの設定が終わるまで null を返す。)モジュールツリー上は MswProvider が末端となり、 children は依然としてServer Componentとして解釈される。