ChromaticのFlaky testへの対応、一部の文字(Font)差分問題解決


※この記事は自分が所属する組織で書いた以下の記事のコピーです。投稿した記事は個人の著作物として自ブログにコピーして良いルールとしています。

https://tech-blog.mitsucari.com/entry/2025/04/14/091232


こんにちは、ミツカリCTOの塚本こと、つかびー(@tsukaby0) です。

ミツカリ開発部ではVisual Regression Testing(VRT)を行っています。VRTを実現するためのSaaSにChromaticというものがあり、これを利用しています。

Chromaticを使うとUI変更の差分を画像で出すことができ、PRによってどのような変更が発生したかを可視化し、視覚的にレビューすることができます。

今回はその運用中に発生したとあるFlakyなTestの問題について説明します。

Chromaticの実例

以下は弊社で導入しているChromatic(VRT)の一例です。

PRが作られた場合にChromaticが自動で動き、Storybookのbase branchとの差分を検出してくれます。検出したうえでスクリーンショットを撮り、違いがある部分を教えてくれるという仕組みです。上記の画像の例では、画面内の特定のコンポーネントを変更したため、その部分の差分が表示されています。

このような仕組みがない場合、以下のような手間や問題が発生します。

  • PR作成者がbefore/afterのスクショを撮る必要があり面倒
  • PR作成者がスクショを用意しない場合、レビュワーがlocal上などで動作確認する必要があり面倒
  • 一昔前に流行ったスナップショットテストではHTMLとしての差分は見れるがビジュアル的な差分は分からない

Storybookを必要な分だけ作らないといけないという手間はありますが、それに見合うリターンはあるので、VRTはオススメです。

ミツカリでは基本的に全てのコンポーネントに対してStorybookを作成しているのでコンポーネント単体での動作確認が簡単にできるだけでなく、視覚的なチェック、レビューはすべてVRTできるという良い環境が作れていると思っています。

Chromaticの唯一の欠点を挙げるとすると、料金です。開発組織の規模やComponentにどの程度頻繁に変更が入るか(変更があるBuild数がどの程度か)次第ではありますが、弊社では毎月50,000円〜100,000円ほどかかっています。開発部内のインフラコスト的には上位に食い込んでいます・・・。

2025年現在でVRTをやるならいろいろな選択肢があるため、もしこれから始める方は以下の記事をご覧になると良いかもしれません。

https://zenn.dev/micin/articles/2024-03-15_rikson_vrt-tool-comparison

https://tech.smarthr.jp/entry/2023/09/25/120209

https://qiita.com/suke083/items/3e286ae7f6b5a5bb08e9

https://zenn.dev/acn_jp_sdet/articles/74fbda5f9142b6

予算に余裕があるならば品質的にも機能的にもまだまだChromatic一択な状況かと思います。

ChromaticのFlakyな例

Chromaticに限らない話だとは思いますが、VRTを行う上でFlaky testに気をつける必要があります。Flaky testは実行結果が不安定なテストであり、誰しも経験があると思います。

以下はFlaky testの例です。

# 擬似コード
def getCurrentYear
  return Time.current.year;
end

# 疑似テストコード
it 'returns current year' do
  expect(getCurrentYear).to eq(2025)
end

現在の年を返すような関数の単体テストを作り、これで良い!と思っていたら年を跨いだ瞬間にテストがFailし始める、というものです。これは極端な例ですが、このようなFlaky test経験は誰しもあると思います。

VRTも同様で、例えばアニメーションがあるコンポーネント、時刻に依存したコンポーネント、iframeやHTTP GETを使っており外部リソースに依存したコンポーネントなどはFlaky testになりやすいです。

これに対してChromaticは以下のようなドキュメントを用意しています。

https://www.chromatic.com/docs/delay/

まずはDelayです。例えば5秒で終わるアニメーションを含むコンポーネントがあるならば、Delayを適当に設定してアニメーションが終わったタイミングのスナップショットを撮れば差分は無くなります。

しかし、これで対応できないケースもあります。

https://www.chromatic.com/docs/ignoring-elements/

次はignoreです。例えばとあるコンポーネントがiframeを使って外部サイトを読み込んでいるとします。その場合、外部サイトのデザインが変わったり、外部サイトの読み込みが遅いとスナップショットに差分がでてしまいます。ここで考えたいのは、本当にそのiframeの中身までVRTしたいのか?ということです。おそらくはそのようなケースではそこは別に見なくて良い、と考えるエンジニアが多いと思います。

そのようなケースでignoreは力を発揮します。コンポーネント内の特定の要素に対してignoreすることで、その部分の差分は検知されなくなります。

あまりよくないですが謎のFlakyが発生した場合は、とりあえずignoreを付けて逃げるということも手ではあります。Flakyに対する王道としては、一旦消して書き直す、ということだと思います。ただ、Storybookの場合はそうも行かないかもしれません。消してしまったら他のエンジニアがそのコンポーネントを動作確認したくてもできなくなってしまいます(消すだけじゃなくて書き直せばよいだけの話ではあるが)。そういうケースではignoreを付けてお茶を濁すのもありかもしれません。

https://www.chromatic.com/docs/disable-snapshots/

1つ前の説明では特定の部分をignoreする説明をしましたが、そもそもコンポーネント全体がFlakyだったり、このコンポーネントは別にVRTの対象にしたくないな、というケースがあります。そのような場合はignoreよりもdisableが有効です。Storybookの設定でchromaticに対してスナップショットは不要だという指示を出しましょう。

https://www.chromatic.com/docs/threshold/

Thresholdを調整するのも一つの手かもしれません。ただ、ミツカリではこれは使っていないです。

本当に微妙な1pixelの差分がどうしてもでてしまうようなケースで、それは差分として検知したくない、というようなケースもあるかもしれません。そういう場合はThresholdを調整して画像の差分検知のロジックの許容範囲を調整するのもありでしょう。

他にもいろいろなTipsがありますので、Chromaticの公式docを見て、自分の要件に合うソリューションを見つけると良いと思います。

ChromaticのFontによるFlaky Test

今回の記事の本題です。

Chromaticを使っている中で2024年11月頃?から奇妙なFlaky testが発生するようになりました。

少し分かりづらいので、一部分だけ拡大します。

before

after

afterの一部の文字が緑がかっていますが、これはdiffがあるという表現なので、本質はそこではなく、フォントが変わっているっぽい、というのが一番の問題です。

diff表示を止めてみると・・・

明らかにフォントが変わっていますね。ただ、本当にフォントなのか?という疑問も残ります。 ログイン・ の部分は差分がないので、そこも謎が深まるばかりです。異なるフォントがロードされているなら ログイン・ の部分も変わっていそうなものです。漢字だけが別フォント?という気もしますが、 メールアドレス の部分は別のフォントになっていそうです。(そしてそもそも中華フォントっぽいような馴染のないフォントである点も気になります)

ある時から急にこのFlaky問題が起きはじめ、解決方法がわからず数ヶ月におよび苦しめられていました。本当に何もFont周りはいじっていないのに急に発生し始めたので、Chromaticの環境側の問題だろうと睨んでいましたが、誰も解決できない状態が続いていたので、私が対応することにしました。

ChromaticのFontによるFlaky Testの解決

結論から言うと原因の特定には至っていませんが、おそらくChroamtic側のSnapshotを撮っているLinux環境の問題であり、sans-serif(によって利用される実際)のフォントが壊れている、というか、異なるフォントが入ってるマシンがありそう、という結論に至りました。差分は出るPRもあれば出ないPRもあり、全くの規則性が無いので、向こうの環境、つまりはマシンガチャな状態だったのかなと予想しています。

解決までの道のりとしては詳細は省きますが、以下のような感じでした。

  1. FlakyになるケースでDOMに差分がないかを確認する -> ない
  2. font-familyの設定を調整して、どのFontが使われていそうか、フォールバックしていそうか特定する -> sans-serifが使われていそう
  3. Chromaticのサポートに問い合わせて現象を説明、はじめはThresholdを調整してねと言われるも、しきい値を相当大きくしないと差分検知してしまうため、リスクがあると判断、使えないと伝える
  4. Chromaticのサポートから別のFontをDLするかWeb fontを使ってFontを固定(fallback)してみてはと言われるも、Web fontは使いたくない。ChromaticのLinuxには何のJPフォントなら入っているのか?と聞いたところIPAGothic, IPAPGothic, Noto Sans JPなら入っていると教えてもらう
  5. Noto Sans JPを実際のプロダクションでは使わずに、単にfont-familyに書くだけでChromatic環境では読み込ませる方針へ。これによって壊れているであろうsans-serifは使われないのでFlakyは無くなった

ちなみに残念ながらsans-serifが壊れているかどうかは指摘しましたが深く調査はしてもらえませんでした。

もし同じ現象で困っている人がいたらsans-serifよりも前にfallback fontとしてNoto Sans JPなどを入れるとFontが固定化されて解決すると思います。

正直解決できるか自信はありませんでしたし、同僚には本当にFontの問題なのか?現象的に違うのではないか(cssではないか)?と言われていました。確かにFontと言えなくもないですが、そこは無事検討が当たっており良かったです。

なんとか問題を解決できて、開発部としてはストレスの種が1つ減りました。

そもそもの問題

そもそも今回の問題が発生した背景には、ミツカリにおけるFontの戦略および設定が甘い点に問題があります。

例えばとあるサイトがMacにはヒラギノで、Windowsには遊ゴシックで表示させたいという要件で構築されていたとします。残念ながらこれらのフォントはそれらのOS特有のものなので、ChromaticのLinux環境にはインストールされていません。そのため、一見してちゃんとVRTできているように見えてFontが異なるから完璧にはVRTできていないことになります。

そのため、Fontをstatic resourceとして用意してChromatic環境でも読み込めるようにする必要があります。(もちろんこのケースの場合、Win表示の場合とMac表示の場合で2パターンのVRTを用意するのがベスト)

Web Fontであればその作業は不要で手軽ですが、Web Fontにはまた別のDLやレイアウトシフトの問題があるため、そもそもプロダクションで使いたくないという人もいると思います。

理想的にはChromatic(というかWinやMac以外のOS)まで踏まえたうえでFontに対する戦略や設計を行う必要があります。


現在、ミツカリではITエンジニアを募集しています。興味のある方はぜひお気軽にご連絡ください!

https://herp.careers/v1/mitsucari