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

テストコードを導入して変わったこと 〜秘伝のタレ状態からの脱却〜

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
Avatar for uiui uiui
November 24, 2025
270

テストコードを導入して変わったこと 〜秘伝のタレ状態からの脱却〜

2025年11月24日PHPカンファレンス香川にて

Avatar for uiui

uiui

November 24, 2025
Tweet

Transcript

  1. 株式会社クレアヴォイアンス SWE 
 エンジニア歴4年 
 自己紹介
 uiui
 X: @U_Okuyama_Devlp work:最近はAWS/Laravel/Vue あたりでやっています。

    また生成AI系をWebアプリの機能に組み込むみたいなのが 多いです。 それ以外:元々ヴァイオリン弾きです。最近は日曜音楽家と名乗ることにしました。 あと辛い物が好き!
  2. コストをかけられる人だ けが書くもの じゃないの? テスト書くの コスト高いよね? なくても動くから後 回しでいい? すごく高い信頼性を 求められている ものだけ?

    テスト書くより 実装を進めたい… 難しそう… 時間かかりそう めんどくさそう テストを書かないチームで抱きがちな印象
  3. $shipping = ($item->isFreeShipping()) ? 0 : 1000; if (!is_null($item->customShippingFee())) {

    if ($user->livingInSpecialPlace()) { //特殊地域 if ($item->customShippingFee() + self::SPECIAL_PLACE > 3000) { //3000円以上だったら割引をする $shipping = $item->customShippingFee() + self::SPECIAL_PLACE - self::HIGHER_DISCOUNT; } else { $shipping = $item->customShippingFee() + self::SPECIAL_PLACE; } } else { $shipping = $item->customShippingFee(); } } if ($user->isPremium()) { if ($user->livingInSpecialPlace()) { $shipping -= 500; } else { $shipping = 0; } } *実際のコードではありませんが形はこんな感じ 🙃仕様の割にコードが長くて複雑 (ネストの深いif文もよく見られる) もっとスマートに書けるはずなのに どうしてこう書かれてるのかわからない
  4. … $shipping = ($item->isFreeShipping()) ? 0 : 1000; if (!is_null($item->customShippingFee()))

    { if ($user->livingInSpecialPlace()) { //特殊地域 if ($item->customShippingFee() + self::SPECIAL_PLACE > 3000) { //3000円以上だったら割引をする $shipping = $item->customShippingFee() + self::SPECIAL_PLACE - self::HIGHER_DISCOUNT; } else { $shipping = $item->customShippingFee() + self::SPECIAL_PLACE; } } else { $shipping = $item->customShippingFee(); } } if ($user->isPremium()) { if ($user->livingInSpecialPlace()) { $shipping -= 500; } else { $shipping = 0; } } … 🙃責務や粒度が適切でないコード構造 メソッドに切り出さないがゆえに 複雑になってることも多々あり メソッドに切り出すべき …
  5. … $shipping = ($item->isFreeShipping()) ? 0 : 1000; if (!is_null($item->customShippingFee()))

    { if ($user->livingInSpecialPlace()) { //特殊地域 if ($item->customShippingFee() + self::SPECIAL_PLACE > 3000) { //3000円以上だったら割引をする $shipping = $item->customShippingFee() + self::SPECIAL_PLACE - self::HIGHER_DISCOUNT; } else { $shipping = $item->customShippingFee() + self::SPECIAL_PLACE; } } else { $shipping = $item->customShippingFee(); } } if ($user->isPremium()) { if ($user->livingInSpecialPlace()) { $shipping -= 500; } else { $shipping = 0; } } … 時がたつと… 🙃どのパターンをテストすればいいかわからない
  6. public function sendNotification(array $user, bool $isEmail) { … //メッセージの整形など様々処理 if

    ($isEmail) { // メール用処理 … mail($user['email'], "通知", $message); } else { // SMS用処理 … smsSend($user['phone'], $message); } … //メッセージ送信後の様々な事後処理 Log::info("送信完了: " . ($isEmail ? 'Email' : 'SMS') . "\n"); } 🙃共通化すべきでない処理の共通化
  7. … $shipping = ($item->isFreeShipping()) ? 0 : 1000; … 事例①

    *実際のコードではありません *むちゃくちゃ手続型でした 最初はシンプルだった
  8. … $shipping = ($item->isFreeShipping()) ? 0 : 1000; if ($user->isPremium())

    { $shipping = 0; } … 仕様が追加されていく 事例① *実際のコードではありません
  9. … $shipping = ($item->isFreeShipping()) ? 0 : 1000; if (!is_null($item->customShippingFee()))

    { $shipping = $item->customShippingFee(); } if ($user->isPremium()) { $shipping = 0; } … 仕様が追加されていく 事例① *実際のコードではありません
  10. … $shipping = ($item->isFreeShipping()) ? 0 : 1000; if (!is_null($item->customShippingFee()))

    { if ($user->livingInSpecialPlace()) { $shipping = $item->customShippingFee() + self::SPECIAL_PLACE; } else { $shipping = $item->customShippingFee(); } } if ($user->isPremium()) { if ($user->livingInSpecialPlace()) { $shipping -= 500; } else { $shipping = 0; } } 仕様が追加されていく 事例① *実際のコードではありません
  11. … $shipping = ($item->isFreeShipping()) ? 0 : 1000; if (!is_null($item->customShippingFee()))

    { if ($user->livingInSpecialPlace()) { if ($item->customShippingFee() + self::SPECIAL_PLACE > 3000) { //3000円以上だったら割引をする $shipping = $item->customShippingFee() + self::SPECIAL_PLACE - self::HIGHER_DISCOUNT; } else { $shipping = $item->customShippingFee() + self::SPECIAL_PLACE; } } else { $shipping = $item->customShippingFee(); } } if ($user->isPremium()) { if ($user->livingInSpecialPlace()) { $shipping -= 500; } else { $shipping = 0; } } … 現在に至る
  12. public function sendNotification(array $user) { // メール処理 mail($user['email'], "通知", $message);

    } 事例② *実際のコードではありません 最初はシンプルだった
  13. public function sendNotification(array $user) { … //メッセージの整形など様々処理 // メール用処理 …

    mail($user['email'], "通知", $message); … //メッセージ送信後の様々な事後処理 Log::info("送信完了: " . ($isEmail ? 'Email' : 'SMS') . "\n"); } 事例② *実際のコードではありません 例によって複雑化し
  14. public function sendNotification(array $user, bool $isEmail) { … //メッセージの整形など様々処理 if

    ($isEmail) { // メール用処理 … mail($user['email'], "通知", $message); } else { // SMS用処理 … smsSend($user['phone'], $message); } … //メッセージ送信後の様々な事後処理 Log::info("送信完了: " . ($isEmail ? 'Email' : 'SMS') . "\n"); } 事例② *実際のコードではありません 仕様の追加により 
 切り出しや抽象化が必要になるが 
 変更できない 

  15. function calcShipping(int $amount): int { // 3000円以上の購入なら送料無料 if ($amount >=

    3000) { return 0; } return 500; } //3000円以上が送料無料になるか // ① 準備(Arrange) $amount = 3500; // ② 実行(Act) $shipping = calcShipping($amount); // ③ 検証(Assert) if ($shipping === 0) { echo "OK: 3000円以上は送料無料"; } else { echo "NG: 送料無料になっていない "; } ざっくり言うと 決めた入力を与え、期待した値になっているかを確かめるもの すごくシンプル テスト対象
  16. テストがない時の開発 
 󰢨(初心者) 2〜3行書く→デバッグ 󰟻(ちょっと慣れた開発者 ) ほとんど書いてからデバッグ 一発で動いたらラッキー。 しかしほとんどの場合は、どこでバグが出てるのか調査に時間を費やす ことになる

    🤓(だいぶ慣れた開発者) 数十行書く→デバッグを繰り返しながら開発する。 ある程度コードを書くと、デバッグしてバグが出た時に調査に時間がかかりそう だという直 感が働くようになる。 しかしデバッグにも時間がかかる のでバランスを意識し始める *個人的な所感によるものです TDDが短期的にも効果的な理由①
  17. 対処法:仮説検証を早く回す 
 TDDはインターフェースから考えることになる #[TestDox("要約の取得")] public function test_summary(): void { $frame

    = new TextMessageFrame(text: TextMessageFrame::LINK_TAG . "Hello! Test!"); $expected_summary = "[リンク]Hello! Test!"; $this->assertEquals($expected_summary, $frame->getSummary()); } 先にメソッド名や引数・期待する戻り値などを決める その後中身を実装する TDDはインターフェースから考えることになる TDDが短期的にも効果的な理由②
  18. 😎TDDはインターフェースから考えることになる② 
 • 「作るものは何か」から考えることになる • まず使いやすく・誤りにくいメソッドの外観を考える 習慣がつく(名前・入 力パラメーター・戻り値など) また、テストを書くことで実装よりも インターフェースに集中することに

    なります(これは常に良いことで す)。 p.91 開発者はまず「作るものは何か」から始める べきだ。 〜 実装についての知識がシステムの他の部 分に漏れるのを防ぐ・コードの焦点を集中さ せられるし、カプセル化もできるようになる。 p.175
  19. テストコード・TDDのメリットまとめ 
 • リファクタリングができる • 想定するテストケースが残っている • 適切な責務・粒度の指標になる • 変更をする勇気を与えてくれる

    • エラーを早期発見できデバッグ時間が短くなる • 仕様や使い方も示すことができる • TDDで着実に前進できるようになる • TDDでインターフェースをより意識できる • TDDでモチベが高い状態でテストコードを書ける
  20. (補足)テストが書ける環境だったのか? 
 この段階では • 自身のローカルだけでもいい ので書けそうなところを探す • 自身が書く新規コード があれば、テストを先に書きつつ実装すると良い •

    関数に切り出されていて外部のAPI等に依存してなければ書けそう • プロダクションコードに影響はないのでチームメンバーもスルーしていた • DBの依存はあったが、とりあえずは後でデータも消すようにテストコードを書けば いいので試してみるくらいであれば問題ない
  21. 意識すること 
 • やることが決まっても積極的にサポートをしなければ廃れてしまう (人間は元の やり方で続けたくなるものだ) • 現状維持バイアス 的な意見が出ることも覚悟し、それを否定しない •

    各メンバーがテストコードによる恩恵を自分で発見 できるように、サポートする (機会をつぶさない・誘導する) • 書くことで改善できそうな場面を見つけたら、すかさず&さりげなくサポートする