Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

組み合わせ爆発にのまれない - 責務分割 x テスト

Avatar for HAL HAL
December 09, 2025

組み合わせ爆発にのまれない - 責務分割 x テスト

開発者がどのようにユニットテストや統合テストを書き、組み合わせ爆発を回避しているのかをQA向けに解説するスライドです。テストパターンが少ないと起きる漏れ、多すぎると発生する管理コストの増大というジレンマを示し、責務分割によるテスト設計の最適化やRomiでの実例を紹介します。

Avatar for HAL

HAL

December 09, 2025
Tweet

More Decks by HAL

Other Decks in Programming

Transcript

  1. ⾃⼰紹介 2 • 信田 春満(のぶた はるみつ) @halhorn 
 • 大学


    ◦ 〜2012: 京都大学大学院情報学研究科
 ▪ 認知発達ロボティクス
 ▪ X(Twitter) 会話 bot 「にせほるん」
 • MIXI
 ◦ 2013〜2016: SNS mixi
 ◦ 2017〜2025: Romi - 雑談対話ロボットの新規事業
 ▪ Romi 最初のエンジニア
 ▪ 現エンジニアリングマネージャー
 ◦ 2026〜: みてね AI チーム予定
 • 趣味
 ◦ ホルン
 ◦ クライミング
 ◦ 写真
 ◦ 鳥

  2. • テストパターンを減らしたら ◦ 思わぬ漏れや抜けパターンが発覚 ◦ バグ再現時に「なぜ気づかなかった…?」となる ◦ “テストが通っているのに壊れていた”状態に陥る • テストパターンを増やしたら

    ◦ 組み合わせが膨⼤になり、テスト実⾏時間が激増 ◦ 細かい仕様変更のたびにテスト修正が雪だるま式に増える ◦ 「修正よりテスト直す⽅が⼤変」になってしまう ◦ 結果、誰もテストを触れなくなる こんなことありませんか? 4
  3. • Romi: 雑談会話 AI ロボット ◦ バグが有っても⼈が死んだり⼤きな⾦銭的損失が起きたりはしにくい ◦ 先端的で不確実な要素が多い分野 ◦

    スピードと柔軟性が⼤事! ◦ 網羅性よりも変更に強いテストが⼤事 • 医療とか⾦融とか ◦ (知らないけど)バグが重⼤な影響を起こす ◦ 網羅性が⼤事 テストの網羅性とプロダクトの機動性のトレードオフ 5 とはいえ、できるだけ網羅的かつ変更に強いテストをかきたい
  4. 例:サブスク登録している Romi は会話できる 組み合わせ爆発 8 • API Key がある •

    API Key が無い • サブスク登録済み • サブスク未登録 • API Key が正しい • API Key が不正 • 無料期間中 • 課⾦期間中 • ルールベース会話 • AI 会話 • 決済⼿段がクレカ • 決済⼿段がキャリア • 天気を話せる • ニュースを話せる • しりとりできる • etc… • ⻑期記憶がある場合 • ⻑期記憶がない場合 テストパターンを 出そう
  5. 組み合わせ爆発 9 API Key有 API Key 正 サブスク登録済み 無料期間 クレカ決裁予定

    ルール会話 天気 - 話せる API Key有 API Key 正 サブスク登録済み 無料期間 クレカ決裁予定 ルール会話 ニュース - 話せる API Key有 API Key 正 サブスク登録済み 無料期間 クレカ決裁予定 ルール会話 しりとり - 話せる API Key有 API Key 正 サブスク登録済み 無料期間 クレカ決裁予定 AI 会話 - 長期記憶有 話せる API Key有 API Key 正 サブスク登録済み 無料期間 クレカ決裁予定 AI 会話 - 長期記憶無 話せる API Key有 API Key 正 サブスク登録済み 無料期間 キャリア決裁予定 ルール会話 天気 - 話せる API Key有 API Key 正 サブスク登録済み 無料期間 キャリア決裁予定 ルール会話 ニュース - 話せる 2 x 2 x 2 x 2 x 2 x 2 x 3 x 2 = 384 • 組み合わせは指数的に増える ◦ 「2つの選択肢」が10個あると、そのすべての組み合わせは 2^10 = 1024 とおり • 1つの仕様が変更されると、⼤量のテストを変更する必要がある ◦ 変更に弱い! ◦ ⼤量のテストを更新していると雑になる => ミスする
  6. 分割統治 11 • API Key がある • API Key が無い

    • サブスク登録済み • サブスク未登録 • API Key が正しい • API Key が不正 • 無料期間中 • 課⾦期間中 • ルールベース会話 • AI 会話 • 決済⼿段がクレカ • 決済⼿段がキャリア • 天気を話せる • ニュースを話せる • しりとりできる • etc… • ⻑期記憶がある場合 • ⻑期記憶がない場合 例:サブスク登録している Romi は会話できる
  7. • ⼤きな問題を、独⽴した⼩さな部品に階層的に分けていく ◦ 分け⽅がエンジニアの腕の⾒せどころ ◦ 「責務」できれいに分けていく 分割統治:部品に分ける 12 例:サブスク登録している Romi

    は会話できる クライアントの認可 • API Key がある • API Key が無い • API Key が正しい • API Key が不正 サブスク 会話 • サブスク登録済み • サブスク未登録 • 無料期間中 • 課⾦期間中 決済⼿段 • 決済⼿段がクレカ • 決済⼿段がキャリア ルールベース会話 AI 会話 • 天気を話せる • ニュースを話せる • しりとりできる • etc… • ⻑期記憶がある場合 • ⻑期記憶がない場合 責務で分割!
  8. 会話 ルールベース会話 AI 会話 • 天気を話せる • ニュースを話せる • しりとりできる

    • etc… • ⻑期記憶がある場合 • ⻑期記憶がない場合 • 分割した「部品」の中でテストをする ◦ 「使う側」と「内部の仕様」に着⽬ ▪ 使う側から⾒た世界:認可成功、認可失敗 ▪ 内部の世界: API Key がある、ない‧API Key が正しい、不正 分割統治:末端の部品からテスト 13 例:サブスク登録している Romi は会話できる クライアントの認可:成功/失敗 • API Key がある • API Key が無い • API Key が正しい • API Key が不正 サブスク:有効/無効 • サブスク登録済み • サブスク未登録 • 無料期間中 • 課⾦期間中 決済⼿段:⽀払いができる • 決済⼿段がクレカ • 決済⼿段がキャリア 3 3 2 3 2 2 2 1 1 < 使う側 > 認可という意味では 「成功」「失敗」 の2パターンしかない
  9. 会話 ルールベース会話 AI 会話 • 天気を話せる • ニュースを話せる • しりとりできる

    • etc… • ⻑期記憶がある場合 • ⻑期記憶がない場合 • 分割した「部品」の中でテストをする ◦ 「使う側」と「内部の仕様」に着⽬ ▪ 使う側から⾒た世界:認可成功、認可失敗 ▪ 内部の世界: API Key がある、ない‧API Key が正しい、不正 分割統治:末端の部品からテスト 14 例:サブスク登録している Romi は会話できる クライアントの認可:成功/失敗 • API Key がある • API Key が無い • API Key が正しい • API Key が不正 サブスク:有効/無効 • サブスク登録済み • サブスク未登録 • 無料期間中 • 課⾦期間中 決済⼿段:⽀払いができる • 決済⼿段がクレカ • 決済⼿段がキャリア 3 3 2 3 2 2 2 1 1
  10. 会話 ルールベース会話 AI 会話 • 天気を話せる • ニュースを話せる • しりとりできる

    • etc… • ⻑期記憶がある場合 • ⻑期記憶がない場合 • 各部品の「外から⾒た状態」のみ考えて組み合わせのテスト ◦ 各部品の「内部の状態」までは踏み込まない 分割統治:部品の組み合わせのテスト 15 例:サブスク登録している Romi は会話できる クライアントの認可:成功/失敗 • API Key がある • API Key が無い • API Key が正しい • API Key が不正 サブスク:有効/無効 • サブスク登録済み • サブスク未登録 • 無料期間中 • 課⾦期間中 決済⼿段:⽀払いができる • 決済⼿段がクレカ • 決済⼿段がキャリア 3 3 2 3 2 2 2 1 1 全体:2 x 2 x 1 = 4パターン
  11. 会話 ルールベース会話 AI 会話 • 天気を話せる • ニュースを話せる • しりとりできる

    • etc… • ⻑期記憶がある場合 • ⻑期記憶がない場合 • 「掛け算」を「⾜し算」にできた! ◦ 384パターン → 17パターン に圧縮 分割統治:パターン数が減った! 16 例:サブスク登録している Romi は会話できる クライアントの認可:成功/失敗 • API Key がある • API Key が無い • API Key が正しい • API Key が不正 サブスク:有効/無効 • サブスク登録済み • サブスク未登録 • 無料期間中 • 課⾦期間中 決済⼿段:⽀払いができる • 決済⼿段がクレカ • 決済⼿段がキャリア 3 3 2 3 2 2 2 1 全体:2 x 2 x 1 = 4 部品:3 + 3 + 2 + 3 + 2 = 13 合計:17パターン 1
  12. • 部品の詳細が変わっても部品を「使う側」のテストは影響を受けない ◦ 仕様の変更に強い! ◦ 決済⼿段の詳細は、実装上もテスト上も「決済⼿段」の責務の中に閉じている 分割統治:変更に強い! 17 サブスク:有効/無効 •

    サブスク登録済み • サブスク未登録 • 無料期間中 • 課⾦期間中 決済⼿段:⽀払いができる • 決済⼿段がクレカ • 決済⼿段がキャリア • 決済⼿段が銀⾏振込 New 3 3 2 1 決済⼿段が増えても、 「⽀払いができる」 さえ満たせてれば OK サブスクの挙動には 影響しない
  13. • 原則:独⽴した責務ごとにわける ◦ c.f. 単⼀責任の原則 • 企画仕様を、どれだけ独⽴した責務にきれいに分けられるかが鍵 • そもそもの企画仕様が「例外」的なのが多いとメンテ性が下がる ◦

    ユーザーの利便性 vs メンテのしやすさ ◦ エンジニアとして仕様に意⾒することも 分割統治:部品の分け⽅ 18 サブスク:有効/無効 • サブスク登録済み • サブスク未登録 • 無料期間中 • 課⾦期間中 決済⼿段:⽀払いができる • 決済⼿段がクレカ • 決済⼿段がキャリア • 銀⾏振込 「銀⾏振込の場合、会話の⼀部 機能が制限される」 みたいな仕様が⼊ると⾟い‧‧ 例外の少ない 仕様のほうが ユーザーフレンドリー (個⼈の感想です)
  14. • 問題を独⽴した部品に分けていく ◦ 階層的に分けていこう。例: 全体 / サブスク / 決済⼿段 •

    末端の部品から順にテスト ◦ その部品の「外から⾒た挙動」と「内部の状態」に着⽬ ◦ 「内部の状態」ごとに、「外から⾒た挙動」がどう変わるかをテスト • 部品の外側のテストは、各部品の「内部の状態」までは気にしない • パターン数が掛け算(累乗)から⾜し算に ◦ 部品が増えてもパターン数が爆発的には増えなくなる • 部品のテストを増やしても組み合わせ爆発しない! 分割統治:まとめ 19 サブスク:有効/無効 • サブスク登録済み • サブスク未登録 • 無料期間中 • 課⾦期間中 決済⼿段:⽀払いができる • 決済⼿段がクレカ • 決済⼿段がキャリア • 決済⼿段が銀⾏振込 New 3 3 2 1
  15. 関連:テストピラミッド 20 gihyo.jp 「テストピラミッド」より https://gihyo.jp/dev/serial/01/savanna-letter/0005 部品ほどたくさんのテストを。使う側はテストを少なく。 • E2E:UIなど含めた全体 ◦ 「サブスク登録している

    Romi は会話できる」 ◦ ↑を Romi 実機を介して動作確認 • インテグレーション:複数の部品の組み合わせ • ユニットテスト:単⼀の機能を持った部品 ◦ 「無料期間ならサブスクは有効」
  16. • 企画意図に対し、部品の分け⽅が間違っているリスク ◦ 部品から作るとどうしても視野が狭くなる ◦ 気づいたら企画意図と違うものがテスト含めできていた‧‧ • 部品内部のパターンに漏れがあるリスク • 複数の部品に隠れた依存があるケース

    ◦ 「複数の部品を連続して呼ぶと死ぬ」とか ◦ 例:キャッシュ関連 • etc. 重要なユースケースは E2E テストをすべき 22 サブスク:有効/無効 • サブスク登録済み • サブスク未登録 • 無料期間中 • 課⾦期間中 決済⼿段:⽀払いができる • 決済⼿段がクレカ • 決済⼿段がキャリア
  17. • E2E テストであらゆるパターンを網羅すると組み合わせ爆発 ◦ パターン数多すぎ ◦ 変更に弱い • 分割統治 ◦

    問題を独⽴した部品に分けていく ◦ 末端の部品から順にテスト ◦ 部品の外側のテストは、各部品の「内部の状態」までは気にしない ◦ この考えを適⽤すると「テストピラミッド」が⾃然にできる • 重要なユースケースはちゃんと E2E テストしよう まとめ 23 サブスク:有効/無効 • サブスク登録済み • サブスク未登録 • 無料期間中 • 課⾦期間中 決済⼿段:⽀払いができる • 決済⼿段がクレカ • 決済⼿段がキャリア • 決済⼿段が銀⾏振込 New 3 3 2 1