Slide 1

Slide 1 text

単体テストを書かない技術 PHPカンファレンス小田原2024 Hideki Kinjyo GitHub: o0h / X: @o0h_ [公開用] v1.0.0

Slide 2

Slide 2 text

自己紹介 • 金城秀樹 / きんじょうひでき • GitHub:@o0h / Twitter:@o0h_ • 好きなFWはCakePHP • アイコンは 美味しい鮭親子丼の写真です

Slide 3

Slide 3 text

今日のお話!

Slide 4

Slide 4 text

たのしみどころ!👏 ぺちこん小田原トークのたのしみどころ【全31本】|PHPカンファレンス小田原 https://note.com/phpcon_odawara/n/n10e3291b5725

Slide 5

Slide 5 text

ンッ!?

Slide 6

Slide 6 text

分かる

Slide 7

Slide 7 text

同じことを思ったんですよね • 六本木某社、とある新規PJ • 謎にすごい人がデザインをリード • クリーンアーキテクチャ、モジュラモノリス─── • そうした技術選定が社内では(多分)初 • 機が熟した〜感?とか、まぁ諸々あったんだと思います

Slide 8

Slide 8 text

同じことを思ったんですよね 会話: • 私「どうっスカ!新しいPJ!」 • 謎にすごい人「大変だけど、頑張りながらやってるよ〜」

Slide 9

Slide 9 text

同じことを思ったんですよね 会話: • 私「やっぱりすげ〜んスカ!アーキテクチャ!違い、どッスカ!」 • 私「クラスは増えるけど、コードも読みやすくて、テストも書きやす いんだろうな〜。いいな〜、憧れるッス!!」

Slide 10

Slide 10 text

同じことを思ったんですよね 謎にすごい人 「1周して、テストとか必要なくなる感じがしてきてる」

Slide 11

Slide 11 text

同じことを思ったんですよね 謎にすごい人 「1周して、テストとか必要なくなる感じがしてきてる」 ʂ

Slide 12

Slide 12 text

─そこからn年

Slide 13

Slide 13 text

今日に至る

Slide 14

Slide 14 text

今回の『「ンッ!?」と目を引くタイトル』は 原体験に立ち返るもの

Slide 15

Slide 15 text

おしながき 1.ドクトリン: テストに大事なこと、なぜ書くのか 2.戦略: 「書かない」ために必要なこと 3.戦術: 「書かない」ために使うもの 4.大事なおしらせ

Slide 16

Slide 16 text

1. ドクトリン: テストに大事なこと、なぜ書くのか 2. 戦略: 「書かない」ために必要なこと 3. 戦術: 「書かない」ために使うもの 4. 大事なおしらせ

Slide 17

Slide 17 text

「単体テストを書かない」 stands for 書かない ≠ サボる ⇓ 「十分な」状態を保ちつつ、削る

Slide 18

Slide 18 text

書かない ≠ サボる ⇓ 「十分な」状態を保ちつつ、削る 「単体テストを書かない」 stands for 「十分な」状態を保ちつつ、削る 「何ができれば」を知る必要がある 「何のために」が決める

Slide 19

Slide 19 text

「単体テスト」 is for それでは、単体テストをすることで何を成し遂げたいのでしょうか? その答えは、ソフトフェア開発プロジェクトの成長を持続可能なもの にする、ということです。 (୯ମςετʹؔ͢Δهड़) [ग़య] ᴷ Vladimir Khorikov (ஶ), ਢాஐ೭ (຋༁) ୯ମςετͷߟ͑ํ/࢖͍ํ P29 ※ ڧௐ͸εϥΠυஶऀʹΑΔ

Slide 20

Slide 20 text

「単体テスト」 is for プログラムをテストするときは、そのプログラムになんらかの価値を 付加することになる. テストによって価値を付加するとは、プログラ ムの品質・信頼性を向上させることである. プログラムの信頼性を向 上させるとは、エラーをみつけ, それをとりのぞくことである. (ιϑτ΢ΣΞςετʹؔ͢Δهड़) [ग़య] ᴷ Glenford J. Myers (ஶ), দඌ ਖ਼৴ (຋༁) ιϑτ΢ΣΞɾςετͷٕ๏ ୈ2൛ P6 ※ ิ଍͸εϥΠυஶऀʹΑΔ

Slide 21

Slide 21 text

コードは「自然と腐る」

Slide 22

Slide 22 text

単体テストが何故「寿命を伸ばす」のか 期待する挙動の明瞭化 発見を助ける

Slide 23

Slide 23 text

「単体テスト」 is for

Slide 24

Slide 24 text

What it is for is NOT「単体テスト」 1番のコアはここであり

Slide 25

Slide 25 text

What it is for is NOT「単体テスト」 コアを支える 方針が存在し

Slide 26

Slide 26 text

What it is for is NOT「単体テスト」 技術詳細は教条ではない

Slide 27

Slide 27 text

単体テスト(あるいはそれ以外の何か)のドクトリン 「エントロピーの増大」に抵抗する ⇑ 複雑さや曖昧さを減らす

Slide 28

Slide 28 text

単体テスト(あるいはそれ以外の何か)のドクトリン ここを削ぎ落とせればOK 単体テスト以外で(も)・・ね!

Slide 29

Slide 29 text

1. ドクトリン: テストに大事なこと、なぜ書くのか 2. 戦略: 「書かない」ために必要なこと 3. 戦術: 「書かない」ために使うもの 4. 大事なおしらせ

Slide 30

Slide 30 text

「単体テストを書かない」ために必要なこと 単体テストがなくても「十分」なコードにする 単体テスト以外の方法で「十分」を得る

Slide 31

Slide 31 text

「単体テストを書かない」ために必要なこと 単体テストの仕事は 「入力」に対して「出力」を試し、欠陥を暴くこと ⇓ 「試さなければならないような入力」を減らせば 無用な仕事となる

Slide 32

Slide 32 text

「単体テストを書かない」ために必要なこと 単体テストの仕事は 「入力」に対して「出力」を試し、欠陥を暴くこと ⇓ 「試さなければならないような入力」を減らせば 無用な仕事となる この「数」「広さ」が可能性の大きさ コード自体の持つ可能性を狭める 「入力」に対して「出力」 ⇓ 「試さなければならないような入力」を減らせ

Slide 33

Slide 33 text

日常会話で例えるなら 曖昧なプリンの例 • 「冷蔵庫にプリンあるよ〜」 • 「えっ!食べていいの?」 // 権限チェック • 「そのプリンは賞味期限まだ過ぎてない?」 // バリデーション • 「夕食の直前にも食べて良いプリン?」 // バリデーション • 「最近、カロリー気にしてるんだけど食事制限内?」 // バリデーション • いただきま〜す!

Slide 34

Slide 34 text

日常会話で例えるなら 自己チェック機能を備えたプリンの例 • 「冷蔵庫にゼロカロリーで、食べてもお腹に溜まらない、新鮮なプリ ンあるよ〜」 • 「えっ!食べていいの?」 // 権限チェック • いただきま〜す!

Slide 35

Slide 35 text

可能性について: shin1x1さんの「制約の力」 https://speakerdeck.com/shin1x1/restricting-states

Slide 36

Slide 36 text

可能性について: shin1x1さんの「制約の力」 https://speakerdeck.com/shin1x1/restricting-states

Slide 37

Slide 37 text

曖昧さについて: t-wadaさんの「予防に勝る防御なし」 https://speakerdeck.com/twada/growing-reliable-code-phperkaigi-2022

Slide 38

Slide 38 text

曖昧さについて: t-wadaさんの「予防に勝る防御なし」 https://speakerdeck.com/twada/growing-reliable-code-phperkaigi-2022

Slide 39

Slide 39 text

コードから可能性を減らすために 自分自身が「何者であるか」を 熟知したコードを追求する

Slide 40

Slide 40 text

• Userを取得できてい ること • Userのuuidプロパ ティが指定した値と 同じであること • レコードが存在しな いuuidにはfalseを返 すこと どんなテストを書きたい?

Slide 41

Slide 41 text

• $uuidがstringである • $uuidが16進数表記と ハイフンしか含まな いこと • $uuidが規定された文 字数の規定されたグ ループからなること • $uuidが大文字でも小 文字でも同じとみな せること どんなテストを書きたい?

Slide 42

Slide 42 text

• Userを取得できてい ること • Userのuuidプロパ ティが指定した値と 同じであること 返り値の型宣言がつい ていれば、テストしな くてもErrorになる(保 証される)

Slide 43

Slide 43 text

• $uuidがstringである • $uuidが16進数表記とハ イフンしか含まないこと • $uuidが規定された文字 数の規定されたグループ からなること • $uuidが大文字でも小文 字でも同じとみなせるこ と • 渡された時点で有効な データであることが保 証されている

Slide 44

Slide 44 text

曖昧さを減らすためのヒント「概念」

Slide 45

Slide 45 text

「名は体を表す」ようにコードを書く 本質や特性を選りすぐり、固めたものが「概念」 ⇕ 曖昧さとの対極であり、可能性を狭める

Slide 46

Slide 46 text

「名は体を表す」ようにコードを書く 本質や特性を選りすぐり、固めたものが「概念」 ⇓ 曖昧さとの対極であり、可能性を狭める 可能性が減る =「テストして否定しないといけない」事が減る

Slide 47

Slide 47 text

「名は体を表す」ようにコードを書く 本質や特性を選りすぐり、固めたものが「概念」 ⇓ 曖昧さとの対極であり、可能性を狭める ⇓ 埋もれている概念に名前を与え、システムの制約にする 可能性が減る =「テストして否定しないといけない」事が減る

Slide 48

Slide 48 text

関連する特性が集合し意味を持つ所に名前を与える 自己チェック機能を備えたプリンの例 • 「冷蔵庫にゼロカロリーで、食べてもお腹に溜まらない、新鮮なプリ ンあるよ〜」 • 「えっ!食べていいの?」 // 権限チェック • いただきま〜す! 命名「アンチ・ギルティ・プリン」 流通可能で一意な概念となる(ユビキタス)

Slide 49

Slide 49 text

単体テストは「本質」に意識を向ける 単体テストでは 対象が「何をしたいか」を見抜き、表現する

Slide 50

Slide 50 text

• 価格通りの商品を1つ 取得するメソッド • 期待するProductが取 得できているかを検 査している

Slide 51

Slide 51 text

• 価格通りの商品を1つ 取得するメソッド • 期待するProductが取 得できているかを検 査している 価格だけが「期待する こと」であるはず それ以外は書かなくて 良いテストコード

Slide 52

Slide 52 text

提案: 新しいスタンス 「単体テストを書いた」に甘えてない? ⇓ テストコードに「君は何でココにいるんだい」と 5回は問いかけてみる姿勢を持つ

Slide 53

Slide 53 text

1. ドクトリン: テストに大事なこと、なぜ書くのか 2. 戦略: 「書かない」ために必要なこと 3. 戦術: 「書かない」ために使うもの 4. 大事なおしらせ

Slide 54

Slide 54 text

1. ドクトリン: テストに大事なこと、なぜ書くのか 2. 戦略: 「書かない」ために必要なこと 3. 戦術: 「書かない」ために使うもの 4. 大事なおしらせ

Slide 55

Slide 55 text

コード自体の「セルフ制約」を高めるデザイン • 足し算の発想 • 関連する要素を集めて、1つの「概念」に固める • ゼロカロリーで、食べてもお腹に溜まらない、新鮮なプリン => アンチ・ギルティ・プリン(と呼べるかもしれない) • 引き算の発想 • 既に実在し、形を持っている(ように見える)ものに引っ張られすぎず、 本質を削り出す • 「いつも親切で差し入れとかをくれる田中先輩」が好きなのではなく、 「お菓子をくれる人」ロールを必要としているだけかもしれない

Slide 56

Slide 56 text

コード自体の「セルフ制約」を高めるデザイン • 足し算の発想 => 豊富な表現力を持つ型 • 値オブジェクト • DTO • 引き算の発想 => 本質的な(=無駄を削ぎ落とした)型 • 具象クラスよりInterface • 「コードの流れ的に、実際に渡ってくるデータ」より「用のある振る舞い」 • ex) `UserEntityCollection` ではなく `Countable` なだけかもしれない

Slide 57

Slide 57 text

「チラッ」ではなく「ガチ制約」に落とし込む • 型を「制約」に • 「そういう意味を匂わせる変数名」よりも型宣言を • 静的解析による型検査 • + CIやIDE上で「真の制約となる」ことが大事 • assert()の利用 • 「コメントで説明する」ではなく「表明で落とす」ようにする

Slide 58

Slide 58 text

本質の追求を怠らない • 「埋もれている概念」の探求 • 会話の中で「意味を確認」した内容を「仕様」として • 「仕様」の集合に名前をつけて「概念」に持っていく • 静的なバリデーションは「型」化を検討 • バリデーションしている内容 ≒ 仕様 • 「AでありBでありCであるX」は、「Y」という概念が埋もれていないか? • 動的なチェック(=「DB内に重複がある」などの状態)は、概念化が難しそう

Slide 59

Slide 59 text

考え方 • 契約プログラミング • 偶発的プログラミング(を避ける) • 攻撃的プログラミング • 単一責任の原則 • セキュア・バイ・デザイン[書籍]

Slide 60

Slide 60 text

1. ドクトリン: テストに大事なこと、なぜ書くのか 2. 戦略: 「書かない」ために必要なこと 3. 戦術: 「書かない」ために使うもの 4. 大事なおしらせ

Slide 61

Slide 61 text

まとめ 「単体テストを書かない技術」とは 「コード自身に豊かに語らせる技術」

Slide 62

Slide 62 text

ふりかえり 「テストとか必要なくなる感じ」は、 確かに存在する!? ⇓ より「ちゃんと意味のあるコード」に向き合い、 目指した先にある話・・・なのかな?(感想)

Slide 63

Slide 63 text

ふりかえり 意味のあるコードを書く = 無駄をなくす、不足もなくす、十分なコードを書く

Slide 64

Slide 64 text

大事なお知らせ 埋もれている「概念」「本質」を救っていく ⇑ テストコードの「煩雑さ」は不吉な臭い 会話、要件詰めの中での「煩雑さ」は不吉な臭い

Slide 65

Slide 65 text

やっていきたいこと: たくさん話しまくる 対話を積み重ね、概念に気づいていく ⇑ ユーザーやチームなどの「人」との対話は勿論、 単体テストは「コードとの対話」という側面もある

Slide 66

Slide 66 text

おしまい! お付き合いいただき ありがとうございました!!