Upgrade to Pro — share decks privately, control downloads, hide ads and more …

スタートアップだからこそ考えるフロントエンドのテスト戦略

 スタートアップだからこそ考えるフロントエンドのテスト戦略

2023-03-21-saitama.js

Ibuki Yoshinaga

March 21, 2023
Tweet

More Decks by Ibuki Yoshinaga

Other Decks in Technology

Transcript

  1. スタートアップだからこそ考える
    フロントエンドのテスト戦略
    2023/3/21 Saitama.js vol.5

    View Slide

  2. 自己紹介
    ・名前: Ibuki Yoshinaga
    ・誕生日: 2001/02/24
    ・Twitter: @__GGEasy
    ・埼玉要素:
    埼玉県飯能市で爆誕。一時静岡で育つも、小学生
    の時埼玉に戻ってきた。
    ・その他:
    昨日沖縄旅行から帰ってきました。

    View Slide

  3. 所属している会社
    Misson
    e
    スポーツで 一人ひとりの可能性が ひろがる教育機会を
    e
    スポーツとは
    「e
    スポーツ」とは、「エレクトロニック・スポーツ(Electronic
    Sports
    )」の略で、ビデオゲームを使った対戦をスポーツ競技として
    捉える際の名称を指す。
    つい数年前まではオタクの趣味または「ゲームは悪」と捉えられてい
    たが、現在では海外での人気のみならず、日本においてもさいたまス
    ーパーアリーナを埋め尽くすほどの観客動員数を誇り始めている。
    ゲシピ株式会社

    View Slide

  4. 最近開発しているサービス
    主力サービスをさらに発展させるため、9
    月より1
    人目の正社員エンジニアとして僕がジョインしました。
    実は弊社に関わり始めたのは高校生の頃なので、超待望のリリースって感じです。
    概要
    主に小学生~
    大人向けの英会話レッスンを提供するtoC
    サービ

    特徴
    ネイティブスピーカーの講師陣とFortnite
    やマイクラを遊びな
    がら、英会話を教えてもらえる。
    ゲーム内でのやり取りは基本全て英語、ゲームを用いたカリ
    キュラムが組まれているため子供達が自主的に取り組みやす
    く英語に対する苦手意識も克服できる。
    ニュース
    最近リスキリングe
    スポーツ英会話がリリース🎉大人向けの
    英会話レッスンがゲームを通した会話で学べます。
    e
    スポーツ英会話

    View Slide

  5. 開発初期に採用した主な技術
    ・Next.js + TypeScript
    ・react-hook-form
    ・Recoil
    フロントエンド
    ・Nest.js + TypeScript
    ・Prisma
    ・OpenApi
    ・PostgreSQL
    バックエンド
    ・Heroku
    ・Docker
    インフラ
    ・GitHub Actions
    CI/CD

    View Slide

  6. 現在の開発体制
    会社全体で正社員
    10
    人未満のスタートアップ
    現在の開発組織
    (2022/09~)
    CTO(
    設計 +
    他プロダクト +
    マネージメント)
    ワイ(
    設計 +
    開発)
    フルタイム
    業務委託(
    設計 +
    開発)
    業務委託(
    設計 +
    開発)
    業務委託(
    デザイナー)
    その他
    ・全体的に若い組織、CTO
    以外の開発者は全員20

    ・全プロダクトでTS
    を採用している
    ・新規開発が多いフェーズ
    ・正社員で開発に関わるエンジニアは自分だけ
    ・よほど大きなものでない限りはフロントエンド・バックエンドは一人でやる
    ・テストを書いた経験は全員少ない
    開発組織の特徴

    View Slide

  7. クオリティの担保は誰がする?
    恐怖がつきまとう開発体験は、とても心理的に厳しい
    長期的に見た結果、テストがないことが何よりもの負債になることは目に見えているし、組織のスケールもしにくい
    とはいえ...
    ・現状テストの知見は0
    ・テストに工数を取られて、開発速度が落ちたら元も子もない
    ・クオリティをCTO
    に任せきりの開発体制で良いのか?
    ・プロダクト数や、コード量が増えることに対する恐怖心、オフショア委託も始めました
    ・事業の成長に伴う、デグレ時の被害の増大
    ・普通に動作確認が大変
    芽生えた危機感
    レベル1:
    バグの発生にすぐ気がつける
    レベル2: atom
    レベルの単体テストが充実する
    レベル3: API
    のモック、atom
    要素以外の単体・結合テストが充実する
    優先度

    View Slide

  8. Testing Trophy
    によれば
    上にいけば行くほど実装コストが高くなるが信頼性が上がる。体積
    が大きいほど重点が置かれている。
    各項目の説明
    実際の環境でアプリケーションを動かすテスト
    End to End
    関数(hooks)
    やコンポーネントを組み合わせた時のテスト
    ここを一番厚く書くことが推奨される
    Integration
    atoms
    レベルのコンポーネントや、関数(hooks)
    単体のテスト
    Unit
    静的テスト、lint
    とか
    Static

    View Slide

  9. まずはテストピラミッドを目指す
    ・どのテストに力を入れるかをピラミッド型で示している
    ・上に行けば行くほどテストが少なく、費用対効果が低い
    ・結合テストを重視するトロフィとは違い、高速で回せる
    Unit
    テスト多め
    テストピラミッドとは
    ・最初は簡単なものから始めたい
    ・unit
    テストは範囲が限られているので調査・実装コストが
    とても軽く素振りしやすい
    何故
    Unit
    テストの配分を増やしたのか

    View Slide

  10. 結果的に導入したもの
    静的な物(eslint/prettier)
    に関しては最初に導入してました。
    strictNullChecks
    は有名ですが、後からtrue
    にすると大量のエラーと戦うハメになります。
    PR
    立ち上げ時に動作
    ・eslint
    静的
    ・React Testing Library
    単体
    /
    結合
    STG
    デプロイ時に動作
    ・Cypress
    ・Storybook
    ・Chromatic(
    消した)
    UI
    ・Sentry
    常時

    View Slide

  11. 各種ライブラリの使い分け

    View Slide

  12. React Testing Library
    ・コンポーネント・カスタムフックのテスト
    ・Storybook
    との連携
    ・1hooks
    、component
    毎にテストが作られる
    ・PR
    内のCI
    で怒られたい
    期待した役割
    ・ロジックに関わる部分は基本的にRTL
    に集約
    ・期待通りの結果が表示・返却されているかの確認だけで、スタイルの崩れなどはここでは見ない
    ・ビジネスロジックに関わらなそうな部分のテストコード作成は、たまにChatGPT
    に投げている
    現状の運用方法
    ・結合テストの充実
    ・MSW
    を入れたい(
    現在はAPI
    を使う場合型と同じ形の値をmock
    してしまっている)
    今後の期待

    View Slide

  13. 実装例
    1
    口座の情報をprops
    から受け取って、それが整形されて返ってくるhooks
    のテスト
    describe(__dirname, () => {
    it('should return the correct text for a domestic bank transfer', async () => {
    const bankAccount: any = {
    transfer_method: 'domestic',
    bank_name: 'Test Bank',
    branch_name: 'Test Branch',
    account_type: 'checking',
    account_number: '123456789',
    account_holder_name: 'Test User',
    };
    const { result } = renderHook(() => useRemittanceText({ bankAccount }));
    await waitFor(() => {
    expect(result.current.get).toEqual({
    section1: 'Test Bank Test Branch',
    section2: '
    当座 123456789
     Test User',
    });
    });
    });
    });

    View Slide

  14. 実装例
    2
    コンポーネントのテスト with Storybook
    余談:
    最近play
    という関数を知った。フォームコンポーネントなどの結合テストを書く場合、RTL
    主導にするよりもStorybook
    内でテ
    ストを書いて、それをRTL
    で結果を見て判断する方法に変えていくのもありだなと思った。
    const { $Example } = composeStories(stories);
    describe(__dirname, () => {
    test(`${CLASS_SCHEDULE_STATUS.COMPLETED}
    の時${CLASS_SCHEDULE_STATUS_TEXT.completed}
    が表示される`, () => {
    const { getByText } = render(
    $Example lessonStatus={CLASS_SCHEDULE_STATUS.COMPLETED} >
    );
    expect(getByText(CLASS_SCHEDULE_STATUS_TEXT.completed)).toBeInTheDocument();
    });
    test(`${CLASS_SCHEDULE_STATUS.IMPLEMENTATION}
    の時${CLASS_SCHEDULE_STATUS_TEXT.implementation}
    が表示される`, () => {
    const { getByText } = render(
    $Example lessonStatus={CLASS_SCHEDULE_STATUS.IMPLEMENTATION} >
    );
    expect(
    getByText(CLASS_SCHEDULE_STATUS_TEXT.implementation)
    ).toBeInTheDocument();
    });
    });

    View Slide

  15. Storybook

    Chromatic
    ・Storybook
    駆動開発がしたい(
    願望)
    ・コンポーネント毎のカタログが欲しい
    ・1
    コンポーネントにつき1
    つのstory
    ファイルが作られる
    ・スナップショットテストやビジュアルリグレッションテスト
    期待した役割
    ・atom
    レベルのコンポーネントは必ずstory
    ファイルを作成する
    ・スタイル崩れなどはここで検知する
    現状の運用方法
    ・Chromatic
    に代わる何かを導入する(
    後述)
    ・play
    などを使って、結合テストをより充実させる
    今後の期待

    View Slide

  16. 実装例
    アイコンを表示するコンポーネント
    type Component = typeof BaseIcon;
    type Meta = ComponentMeta;
    export default {
    title: 'frontend/src/components/common/FixSb/BaseIcon',
    component: BaseIcon,
    argTypes: {
    icon: {
    options: Object.keys(ICONS),
    control: { type: 'radio' },
    defaultValue: 'lined_reward',
    },
    },
    } as Meta;
    const Template: Story = (args) => ;
    export const $Example = Template.bind({});
    $Example.args = {
    icon: 'lined_reward',
    };

    View Slide

  17. Sentry
    Cypress
    ・バグった状態に気が付かないは避けたい
    ・バグを防ぐと言うより、バグにすぐ気がつく環境整備
    期待した役割
    ・効果を明確に感じるまではとりあえず無料アカウントで運用(
    人が増えたら有料に移行)
    ・朝確認しつつ、ヤバそうな通知が飛んできたら都度確認
    現状の運用方法
    ・最初期に作った部分が正常に動いているかを確かめたい
    ・異常系よりも正常系を優先(
    そもそも壊れていないかを知りたかった)
    期待した役割
    ・STG
    デプロイ後に動作
    ・デプロイ時にこけていた場合は怒られるので、該当箇所を確認して修正する
    現状の運用方法

    View Slide

  18. 採用を見送った・消した技術
    便利な技術には予算をかけるスタンスは維持しつつ、使われずに腐る可能性も考慮した上で選定することが大事
    ・スケールした場合のお値段が高い、予算をかけずにミニマムに進めたい
    ・現状Admin
    ページとコーチページしかリリースしていないため、最悪表示が壊れているだけならごめんなさいでギリ許される
    (
    許されない)
    ・無料枠を使い切らずにどうにか回せるなら採用するかも
    結果
    : Storycap + reg-suit
    あたりを検討しています
    Chromatic
    ・物によるがちょっと高い
    ・ノーコードとはいえそのツールのお作法を覚えることに、今コストをかけたくない
    ・契約して腐りましたは絶対に嫌だ
    結果
    : Cypress
    を採用
    ノーコード系の
    E2E
    テストツール

    View Slide

  19. テストを導入してどうだったか・まとめ
    現在はノルマ・目標などは設けておらず、atom
    レベルのロジックを持ったコンポーネントにはStorybook
    、新しく発行したカスタム
    フックにはテストを書いてねくらいのゆるさにしている。
    現在弊社ではある一定の期間でリファクタウィークを設けて、そこでテストを含めた負債の解消をしようとしています。
    すぐに全てのテストを作れはしない
    (
    モチベ的にも
    )
    ので、継続して対応することが必要。
    ・デグレによる不安が少し解消された
    ・古くからある機能や壊れやすい部分をE2E
    で担保することで、該当機能を充実させるまでの時間稼ぎができている
    ・エラーが起こった時に、ある程度規模感を検知・場合によってはrevert
    できる
    ・知見が溜まってない頃に書いたコードは普通に汚いので、ある意味強制的なリファクタリングが発生する
    ・現状一人で開発している僕にとって、ある一定の心理的安全性が保たれる
    よかった点
    ・テストの粒度を決めきれていない、網羅しきれていない
    ・知見・時間等の理由で明確なシナリオを用意できていないため、正常系が主体になってしまっている
    ・一時的だがChromatic
    を消してしまった今、明確にStorybook
    に登録するメリットを感じきれていない
    ・デザイナーを正社員として雇っていないこともあり、動機的なStorybook
    駆動開発をするにはまだ時間がかかりそう
    懸念点・もっと頑張る

    View Slide

  20. ご清聴ありがとうございました!
    正社員や副業も募集してます!
    CTO
    含めて若手多めです!

    View Slide