Slide 1

Slide 1 text

phpunit/php-code-coverage って何をしてるんだ PHPerKaigi 2024 Hideki Kinjyo GitHub: o0h / X: @o0h_ [発表用]

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

このお話のねらい • 何となく耳にするし目にしたこともあるかも知れない 「カバレッジ」という概念への、なるほど!の提供 • 実際に「カバレッジを計測する」といった時に、 内部で何が起きているのか 基礎的な仕組みについて理解をする • その結果として、「なんとなく気持ちいいな!!」となる • もしかしたら何か素敵な実用方法を思いつくかも? • (それは皆さんの心の中に──

Slide 4

Slide 4 text

おしながき 1.おさらい!カバレッジ 〜それってそーいや何なのさ〜 2.PHPはどうやってカバレッジを出しているのか 3.応用的な話 〜coverageの仕組みを知っておくとできること〜

Slide 5

Slide 5 text

おしながき 1.おさらい!カバレッジ 〜それってそーいや何なのさ〜 2.PHPはどうやってカバレッジを出しているのか 3.応用的な話 〜coverageの仕組みを知っておくとできること〜 (時間見つつ駆け足で)

Slide 6

Slide 6 text

1. おさらい!カバレッジ 〜それってそーいや何なのさ〜 2. PHPはどうやってカバレッジを出しているのか 3. 応用的な話

Slide 7

Slide 7 text

よく見る(聞く)カバレッジ

Slide 8

Slide 8 text

カバレッジ? • 「網羅(cover)されている」割合 • 網羅する = 「実際に使われた(実行された)」状態 • コードがどのくらい網羅されている? ➡ コードカバレッジ • テストによるコードカバレッジ ➡ テストカバレッジ

Slide 9

Slide 9 text

Slide 10

Slide 10 text

実行される

Slide 11

Slide 11 text

カバレッジの種類 • 一言で「カバレッジ」といっても、 観点によりいくつかの種類がある • PHPUnitのドキュメントでは 5種類のカバレッジに言及している • Line Coverage • Branch Coverage • Path Coverage • Function and Method Coverage • Class and Trait Coverage 7. Code Coverage — PHPUnit 11.0 Manual https://docs.phpunit.de/en/11.0/code-coverage.html

Slide 12

Slide 12 text

カバレッジの種類 • 一言で「カバレッジ」といっても、 観点によりいくつかの種類がある • PHPUnitのドキュメントでは 5種類のカバレッジに言及している • Line Coverage • Branch Coverage • Path Coverage • Function and Method Coverage • Class and Trait Coverage 7. Code Coverage — PHPUnit 11.0 Manual https://docs.phpunit.de/en/11.0/code-coverage.html ココだけ おさらい してみましょう

Slide 13

Slide 13 text

Line Coverage 行単位で通ったコード(の割合) 実行される

Slide 14

Slide 14 text

Branch Coverage 「分岐」単位で通ったコード(の割合) 実行される 実行されない

Slide 15

Slide 15 text

Branch Coverage 「分岐」単位で通ったコード(の割合) 実行される 実行される

Slide 16

Slide 16 text

Path Coverage 「経路」単位で通ったコード(の割合) 4つのパス(=分岐の組み合わせ)が生まれる

Slide 17

Slide 17 text

比較すると? Line Coverageを100%にするのは、どれでもOK OR OR OR

Slide 18

Slide 18 text

Branch Coverageを100%にするのは、$cの判定まで行く必要がある それさえあればOK 比較すると? OR

Slide 19

Slide 19 text

Path Coverageを100%にするのは、すべて必要 (※1番下って必要?については説明しません!) 比較すると? AND AND AND

Slide 20

Slide 20 text

※興味のある人向けの情報(説明しない)

Slide 21

Slide 21 text

1. おさらい!カバレッジ 2. PHPはどうやってカバレッジを出しているのか 3. 応用的な話

Slide 22

Slide 22 text

PHPでよく出会うカバレッジの出し方 計測(データの収集) 収集データの出力 集計/レポート作成 プログラムの実行 • 色々な仕組みでリレーをしていく

Slide 23

Slide 23 text

PHPでよく出会うカバレッジの出し方 計測(データの収集) 収集データの出力 集計/レポート作成 プログラムの実行 PHPUnitとか

Slide 24

Slide 24 text

PHPでよく出会うカバレッジの出し方 計測(データの収集) 収集データの出力 集計/レポート作成 プログラムの実行 Xdebugとか pcovとか phpdbgとか

Slide 25

Slide 25 text

PHPでよく出会うカバレッジの出し方 計測(データの収集) 収集データの出力 集計/レポート作成 プログラムの実行 Xdebugとか pcovとか phpdbgとか

Slide 26

Slide 26 text

PHPでよく出会うカバレッジの出し方 計測(データの収集) 収集データの出力 集計/レポート作成 プログラムの実行 phpunit/ php-code-coverage

Slide 27

Slide 27 text

カバレッジデータの収集〜出力 計測(データの収集) 収集データの出力 集計/レポート作成 プログラムの実行

Slide 28

Slide 28 text

簡単な例 • Xdebugを利用し、カバレッジデータを収集する例を簡単に紹介します

Slide 29

Slide 29 text

こんなコードがあったとします

Slide 30

Slide 30 text

足しました

Slide 31

Slide 31 text

注目すべき点

Slide 32

Slide 32 text

注目すべき点 xdebug_start_code_coverage() コールされた箇所からカバレッジ データの収集を開始 xdebug_get_code_coverage() 収集したカバレッジデータを取得 ※ 他に、 収集を停止する xdebug_stop_code_coverage()も

Slide 33

Slide 33 text

実行します (Xdebugを「カバレッジモード」に指定します)

Slide 34

Slide 34 text

でました

Slide 35

Slide 35 text

ファイル名 行番号 実行されたら`1` 実行されていない行はデータなし

Slide 36

Slide 36 text

Line Coverageのデータは・・・ • 単純に、ファイル名と行番号ごとにフラグを立てている • とっても単純!!

Slide 37

Slide 37 text

少し踏み込んだ例 • より便利なデータを収集することもできます

Slide 38

Slide 38 text

こんなコードがあったとします

Slide 39

Slide 39 text

足しました

Slide 40

Slide 40 text

フラグ

Slide 41

Slide 41 text

フラグ XDEBUG_CC_UNUSED カバレッジデータに 未実行の行を含める XDEBUG_CC_DEAD_CODE カバレッジデータに 到達不能な行(デッドコード)を含める

Slide 42

Slide 42 text

実行します (実行方法は同じ)

Slide 43

Slide 43 text

でました

Slide 44

Slide 44 text

1度も実行されていない行は`-1` 到達不能な行は `-2`

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

新しいフラグ XDEBUG_CC_BRANCH_CHECK ブランチごとの実行状態を収集する 単独では利用できず、 他のフラグと合わせて指定する

Slide 50

Slide 50 text

実行します (実行方法は同じ)

Slide 51

Slide 51 text

でました !?

Slide 52

Slide 52 text

全体の骨格

Slide 53

Slide 53 text

全体の骨格 ファイルごとのkeyになるのは コレまでと同じ

Slide 54

Slide 54 text

全体の骨格 ファイルごとのkeyになるのは コレまでと同じ `lines` `functions` に分かれる `lines` は行ごとのデータ。 ここまでに見てきたモノが そのまま格納される

Slide 55

Slide 55 text

全体の骨格 今から注目するのはココ

Slide 56

Slide 56 text

このコードは 3つのブランチからなると解釈される

Slide 57

Slide 57 text

ブランチってどういう風に作られるの? ところで!

Slide 58

Slide 58 text

ブランチってなんだっけ • ブランチ = 分岐 • コードを上から処理して行って、 「共通して通る箇所」「条件によって分岐するところ」で分ける • 分けて出来上がったのがブランチ

Slide 59

Slide 59 text

このコードで言えば・・ ① 引数$xに値をセットする ② 引数$yに値をセットする ③ $xをチェックして、 真ならJUMP→⑤へ ④ $yをチェックする ⑤ おしまい! 細 か い ス テ プ に 分 解

Slide 60

Slide 60 text

このコードで言えば・・ ① 引数$xに値をセットする ② 引数$yに値をセットする ③ $xをチェックして、 真ならJUMP→⑤へ ④ $yをチェックする ⑤ おしまい! ブランチその1 細 か い ス テ プ に 分 解

Slide 61

Slide 61 text

このコードで言えば・・ ① 引数$xに値をセットする ② 引数$yに値をセットする ③ $xをチェックして、 真ならJUMP→⑤へ ④ $yをチェックする ⑤ おしまい! ブランチその2 細 か い ス テ プ に 分 解

Slide 62

Slide 62 text

このコードで言えば・・ ① 引数$xに値をセットする ② 引数$yに値をセットする ③ $xをチェックして、 真ならJUMP→⑤へ ④ $yをチェックする ⑤ おしまい! ブランチその3 細 か い ス テ プ に 分 解

Slide 63

Slide 63 text

それ、OpCode見れば分かるよ • PHPのソースコードをさらに分解して、 単純化された命令の組み合わせに変換したのが「OpCode」 • CPUが実行可能なくらい単純、というイメージをしてもらえれば • ブランチやパスの分析には、OpCodeの情報を利用している

Slide 64

Slide 64 text

PHPコード 実際のOpCode 1. 引数$xに値をセットする 2. 引数$yに値をセットする 3. $xをチェックして、 真ならJUMP→⑤へ 4. $yをチェックする 5. おしまい! 見比べてみておくれ 見比べてみておくれ

Slide 65

Slide 65 text

それ、OpCode見れば分かるよ ・・・という話をしていると、うまく20分にまとまらなかったので!! 別の資料を展開します!! (個人的には1番面白い部分だったので、ぜひ・・! https://speakerdeck.com/o0h/phperkaigi-2024-omake

Slide 66

Slide 66 text

Xdebugで分析するとブランチも教えてくれる • 簡単に言うと、 「OpCodeでいうと、ここからここまでが1つのブランチ」 の情報が出てくる

Slide 67

Slide 67 text

Source -> OpCode 実際のOpCode コレ自体は PHPの機能 (Xdebugの機能ではない) お役立ち: opcodeダンプするのにvldもphpdbgも要らなくなってた #PHP - Qiita https://qiita.com/hnw/items/352c5030d6729343a49e

Slide 68

Slide 68 text

OpCode -> Branch 1つのブランチ

Slide 69

Slide 69 text

Xdebugで分析するとブランチも教えてくれる • 簡単に言うと、 「OpCodeでいうと、ここからここまでが1つのブランチ」 の情報が出てくる • 「『ラベル1〜10で1つのブランチ』で、このブランチは『ブランチ3, 6,11のどれかにジャンプします』」という感じ

Slide 70

Slide 70 text

Branch(op_start / out)

Slide 71

Slide 71 text

再:カバレッジデータの収集〜出力 計測(データの収集) 収集データの出力 集計/レポート作成 プログラムの実行

Slide 72

Slide 72 text

ブランチごとの到達(実行)回数 hit: ブランチに到達した回数

Slide 73

Slide 73 text

未到達の場合も記録されている ブランチに到達しなかった例

Slide 74

Slide 74 text

パスカバレッジの例 • パスカバレッジのデータは どうやって取るのでしょうか?どうなっているでしょうか?

Slide 75

Slide 75 text

とりかたは同じ

Slide 76

Slide 76 text

全体の骨格

Slide 77

Slide 77 text

全体の骨格 今から注目するのはココ

Slide 78

Slide 78 text

このコードは 20のパスからなると解釈される

Slide 79

Slide 79 text

通るブランチの番号 (=op_start)の列挙

Slide 80

Slide 80 text

ブランチとパスのデータ(一部、フォーマット済み) 0を起点に、全てのoutを辿っていけば全パスを網羅できる

Slide 81

Slide 81 text

Branch(op_start / out)

Slide 82

Slide 82 text

Branch -> path

Slide 83

Slide 83 text

Branch -> path

Slide 84

Slide 84 text

hit: パスに到達した回数

Slide 85

Slide 85 text

カバレッジの集計 計測(データの収集) 収集データの出力 集計/レポート作成 プログラムの実行

Slide 86

Slide 86 text

PHPUnitのカバレッジレポート機能 PHPUnitのテスト実行時にオプションを付与する • coverage-html=$path: HTMLレポートを$pathに生成する • その他に、coverage-xml・coverage-textなどの形式 • coverage-filter: カバレッジレポートに含める対象を指定する • 通常の運用ではphpunit.xml上で指定することが多い

Slide 87

Slide 87 text

気になること • Xdebug等のカバレッジ収集機能、どこで管理してるの? • テストされていないファイルや、 読み込まれていないファイルも取り扱っているよね?

Slide 88

Slide 88 text

気になること • Xdebug等のカバレッジ収集機能、どこで管理してるの? • テストされていないファイルや、 読み込まれていないファイルも取り扱っているよね?

Slide 89

Slide 89 text

PHPUnitの起動〜テスト実行までの流れ テストケースの実行 個別のテストケースの管理 テスト一式の管理 全体の管理 PHPUnit\TextUI\ Application::run() vendor/bin/phpunit -> phpunit.php PHPUnit\TextUI\ TestRunner::run() PHPUnit\Framework\ TestSuite::run() PHPUnit\Framework\ TestCase::run() PHPUnit\Framework\ TestRunner::run() PHPUnit\Framework\ TestCase::runBare() PHPUnit\Framework\ TestCase::runTest() PHPUnit\Framework\ TestCase::{ケース}() `phpunit`コマンドの実行から個別のテストケースが実行されるまでは、 ざっくりとこうした流れとなる

Slide 90

Slide 90 text

PHPUnitの起動〜テスト実行までの流れ テストケースの実行 個別のテストケースの管理 テスト一式の管理 全体の管理 PHPUnit\TextUI\ Application::run() vendor/bin/phpunit -> phpunit.php PHPUnit\TextUI\ TestRunner::run() PHPUnit\Framework\ TestSuite::run() PHPUnit\Framework\ TestCase::run() PHPUnit\Framework\ TestRunner::run() PHPUnit\Framework\ TestCase::runBare() PHPUnit\Framework\ TestCase::runTest() PHPUnit\Framework\ TestCase::{ケース}() 「カバレッジ収集の初期設定」「(終了後)レポートデータの生成」を実行する

Slide 91

Slide 91 text

PHPUnit\Runner\CodeCoverage • PHPUnitの内部のクラスで、 コードカバレッジの収集・レポート生成等の機能を調整する

Slide 92

Slide 92 text

PHPUnitの起動〜テスト実行までの流れ テストケースの実行 個別のテストケースの管理 テスト一式の管理 全体の管理 PHPUnit\TextUI\ Application::run() vendor/bin/phpunit -> phpunit.php PHPUnit\TextUI\ TestRunner::run() PHPUnit\Framework\ TestSuite::run() PHPUnit\Framework\ TestCase::run() PHPUnit\Framework\ TestRunner::run() PHPUnit\Framework\ TestCase::runBare() PHPUnit\Framework\ TestCase::runTest() PHPUnit\Framework\ TestCase::{ケース}() 「カバレッジ収集の開始」「終了」を実行する

Slide 93

Slide 93 text

PHPUnit\Runner\CodeCoverage • 具体的に計測の開始・終了の操作を担うのもCodeCoverageクラス • `start()` `stop()` Methodを介して行う

Slide 94

Slide 94 text

SebastianBergmann\CodeCoverage\Driver • `CodeCoverage::start()`, `::stop()`メソッドは、 `SebastianBergmann\CodeCoverage\Driver` Interfaceを 内部的に利用し、PHP拡張の機能を呼び出す • `PcovDriver`,`XdebugDriver`といった具象クラスが存在 ※`phpunit/php-code-coverage` パッケージのクラス。 パッケージ名と名前空間のvendorが一致していないので少し注意

Slide 95

Slide 95 text

SebastianBergmann\CodeCoverage\Driver • `XdebugDriver::start()`であれば、 `xdebug_start_code_coverage()` を呼び出す

Slide 96

Slide 96 text

SebastianBergmann\CodeCoverage\Driver • `PcovDriver`であれば、 `\pcov\start()`を呼び出す

Slide 97

Slide 97 text

という感じで、 カバレッジ収集が管理されている

Slide 98

Slide 98 text

気になること • Xdebug等のカバレッジ収集機能、どこで管理してるの? • テストされていないファイルや、 読み込まれていないファイルも取り扱っているよね?

Slide 99

Slide 99 text

実行されていないファイル情報の取得 • XdebugやPcovのもたらす実行情報は、 「読み込まれたファイル」に関するもののみ • しかし、カバレッジレポートは 「プロジェクト全体(etc)」について出力されるのが一般的 • そこで出てくるのが、 カバレッジの「分母」に関する情報

Slide 100

Slide 100 text

SebastianBergmann\CodeCoverage\Filter • `phpunit/php-code-coverage` のクラス • `phpunit.xml`に含まれる `source` > `include/exclude` に 相当する情報を扱う • ここで集計対象となるファイルの列 挙・管理が行われる

Slide 101

Slide 101 text

PHPUnitの起動〜テスト実行までの流れ テストケースの実行 個別のテストケースの管理 テスト一式の管理 全体の管理 PHPUnit\TextUI\ Application::run() vendor/bin/phpunit -> phpunit.php PHPUnit\TextUI\ TestRunner::run() PHPUnit\Framework\ TestSuite::run() PHPUnit\Framework\ TestCase::run() PHPUnit\Framework\ TestRunner::run() PHPUnit\Framework\ TestCase::runBare() PHPUnit\Framework\ TestCase::runTest() PHPUnit\Framework\ TestCase::{ケース}() タイミングとしては、ここで行われる (`CodeCoverage::instance()->init()`と同じ箇所からの起動)

Slide 102

Slide 102 text

1. おさらい!カバレッジ 2. PHPはどうやってカバレッジを出しているのか 3. 応用的な話 〜coverageの仕組みを知っておくとできること〜

Slide 103

Slide 103 text

「テストカバレッジ」以外の活用例 • ここまでで、 カバレッジのデータの収集・出力の仕組みが分かりました • 代表的なユースケースは やはり「テストカバレッジデータの集計」だと思います • が、それ以外にも色々ある予感がする • せっかくなので、いくつか「他の使い道」を考えてみましょう!

Slide 104

Slide 104 text

phpunit/phpcov

Slide 105

Slide 105 text

phpunit/phpcov • PHPUnitの作者(@sebastianbergmann)により作成されたツール • コードカバレッジの取得・レポート作成を行う • `phpunit/php-code-coverage` を使うためのUI、という位置づけ (“CLI frontend for php-code-coverage”) • 要するに、PHPUnitを抜きにして && テストではなく何でも自由にカバレッジを測れる

Slide 106

Slide 106 text

使い方 (execute) • `phpcov execute $PHP_SCRIPT` で、 その$PHP_SCRIPTの実行によって、 カバーされたコードを分析する • 仕組みはシンプルで、 `\SebastianBergmann\CodeCoverage\CodeCoverage::start()` を実行した後に $PHP_SCRIPTをproxy、実行後に `CodeCoverage::stop()`してレポートを生成 • Options • `—include`: 集計対象に含めるパス • `—html` `—text` `—php` `—clover` (など): 出力するレポート形式・出力先 • `—path-coverage`: パスカバレッジ分析の実行

Slide 107

Slide 107 text

実行サンプル ↑ CLIからの実行 ← 生成されたレポート

Slide 108

Slide 108 text

使い方 (merge) • `phpcov merge $DETA_DIR_PATH` で、 $DETA_DIR_PATHに含まれている 複数のカバレッジデータを合成した 解析結果レポートを出力する • 合成のものとになるのは `—php`の形式で、拡張子を`.cov`で揃えたもの • PHP形式=`SebastianBergmann\CodeCoverage\CodeCoverage`オブジェクトの serialized文字列 • Options • `—html` `—text` `—php` `—clover` (など): 出力するレポート形式・出力先

Slide 109

Slide 109 text

実行サンプル ← 与える入力値を変え、同一スクリプトに関して `phpcov execute`を複数回実行している。 データ出力先となるファイル名はユニークに ↓ マージしたレポートの生成を実行

Slide 110

Slide 110 text

使い方 (patch-coverage) • この話は移動しました • 資料の最後にあります!!(302)

Slide 111

Slide 111 text

EndToEnd、 その他の実行形式のカバレッジ計測

Slide 112

Slide 112 text

プロダクションの「使われていないコード」検知 • Xdebugの作者が、自身のYoutubeに挙げている例 • https://www.youtube.com/watch?v=T9rwW-uySBE • 例えば、廃止した機能の安全な削除を考える際に有効 • パフォーマンスの問題と向き合いながらの活用方法になるので注意

Slide 113

Slide 113 text

シナリオテストとの合体 • `k2tzumi/laravel-coverage-middleware` は、 シナリオテストツール runnと組み合わせて使うカバレッジ収集機構 • 「テスト」といっても単体テストだけではない • 結合テストやE2Eなど、 テストが「広く」なればなるほど コード内部との結合はゆるく出来る この後のLTで楽しみにしているやつです!! →

Slide 114

Slide 114 text

他にも色々とアイディアはあるはず・・ • テストコード自体のカバレッジ • 「実は動いていないテストコード」があったら消したい • Property based testingやMutation testingとの組み合わせ • 肝要なのは、 「どうやって起動・終了させるか」「集めたデータがどんな値を持っ ているか」「それを組み立てるツールの存在と使い方」 を知ること • 基本さえ掴んでしまえば、自身のニーズに合わせて活用が効く!

Slide 115

Slide 115 text

まとめ

Slide 116

Slide 116 text

自分の目で確かめると面白いし応用が効く! • 今回は、「気になったから突っ込んでみた」「そしたら納得感があっ たし面白かった!!」という体験を基に、お話しました • 「カバレッジレポート」は何か派手で凄い!なイメージでしたが、 調べてみたら凄く地道で単純なデータの集合なんだなぁと知りました • 普段の開発においては知らなくてもいい話だろうな〜とも思います • でも、知っておくと・・・気持ちいいんすわ。 • 皆さんも、身の回りの「実はよく知らないかも!!」に気づいて ズブズブと足を踏み入れてみましょう!

Slide 117

Slide 117 text

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

Slide 118

Slide 118 text

おまけ: phpcov patch-coverage

Slide 119

Slide 119 text

使い方 (patch-coverage) • コードの変更差分に対して、 行カバレッジの統計を出す機能 • `phpcov patch-coverage --path-prefix $INCLUDE_PATH_PREFIX $COVERAGE_DATA $PATCH`で実行 • 出力はテキスト形式のみで、高機能なレポートは提供しない • その代わり、include/exclude等の詳細なオプションを必要としない

Slide 120

Slide 120 text

使い方 (patch-coverage) • ざっくり言うと 「patchファイルと、カバレッジデータ(.cov)を比べて」 「差分がある箇所について、カバーされている/されていない」 を出力するコマンド • patchは `git diff` とかから取ってくる!

Slide 121

Slide 121 text

利用例 • 例えば こんなコードがあり

Slide 122

Slide 122 text

利用例 • 利用するコードもあって • ちなみに `main3.php`とい うスクリプトです。名前は適 当

Slide 123

Slide 123 text

利用例 • カバレッジは こんな状況になっています • ちなみに、これは `execute --text` で出しています

Slide 124

Slide 124 text

利用例 • PHP形式で、カバレッジ データを出力しておきます

Slide 125

Slide 125 text

利用例 • 新しいメソッドを 足しました

Slide 126

Slide 126 text

利用例 • `git diff` の結果です • これを `main3.text` と いうファイルに保存してお きます(ファイル名は適当)

Slide 127

Slide 127 text

利用例 • path-coverageを実行

Slide 128

Slide 128 text

• 実行可能なコードが 1行分増加している 利用例

Slide 129

Slide 129 text

• 増加したコードの内で、 0行がカバーされている (割合でいうと0.00%) 利用例

Slide 130

Slide 130 text

• 変更されたがカバーされて いない箇所の詳細 利用例

Slide 131

Slide 131 text

例えば、の使い方 • CIとは相性良さそう • GitHub Actionsに組み込めば 「カバーされていない行にコメント」とか出来そう? • 「diffにある指定の行に指摘をする」は、提供されているAPI経由で行える • 過去に書いた記事(情報が古い可能性があります、確認していません!!) > PHPUnitの実行結果(失敗したテスト)をProblem MatcherでPRのdiffに示す https://daisuki.nichiyoubi.land/entry/phperkaigi-2022-lt-hosoku

Slide 132

Slide 132 text

おまけ: PHPUnitで吐き出せるカバレッジ5種

Slide 133

Slide 133 text

カバレッジの種類 • 一言で「カバレッジ」といっても、 観点によりいくつかの種類がある • PHPUnitのドキュメントでは 5種類のカバレッジに言及している • Line Coverage • Branch Coverage • Path Coverage • Function and Method Coverage • Class and Trait Coverage 7. Code Coverage — PHPUnit 11.0 Manual https://docs.phpunit.de/en/11.0/code-coverage.html

Slide 134

Slide 134 text

Line Coverage • 行単位で通ったコードの割合 • 右のスニペットは、 カバレッジが100%となる

Slide 135

Slide 135 text

Slide 136

Slide 136 text

Function and Method Coverage • 関数・メソッドの内で 実行された割合 • 右のスニペットは、 カバレッジが50%となる • 定義されている関数が2つ • 実行はhoge()のみ

Slide 137

Slide 137 text

Slide 138

Slide 138 text

Class and Trait Coverage • クラス・トレイトの内で 1メソッド以上実行された割合 • 自前のメソッドの実行が条件 • 継承元への転送ではダメ • コンストラクタも除く • 右のスニペットは、 カバレッジが2/3になる • クラス`C`は カバーされていないとみなされる

Slide 139

Slide 139 text

Slide 140

Slide 140 text

`C::getI()`を検証しても 「クラスCのメソッド」とは みなされないまま

Slide 141

Slide 141 text

Branch Coverage • 分岐(ブランチ)を考慮し 通ったコードの割合 • 同一行にある分岐も 区別できる • 右のスニペットは、 カバレッジが50%程度となる

Slide 142

Slide 142 text

$aの値に関わらず、必ず$aは評価される ($a=true || $b=true)は$aを無視しない ($a=false || $b=true)も$aを無視しない $aの値によって、$bは評価を省略される ($a=true || $b=true)は$bを通らない ($a=false || $b=true)は$bを通る

Slide 143

Slide 143 text

1つのコードに対して、 「場合分け」をした上での カバー範囲を判定している様子

Slide 144

Slide 144 text

「行」と「ブランチ」でカバレッジが異なる例 5行目は実行されている(カバーされている)が、 $aの短絡評価の結果により、$bの評価まで処理が行っていない(カバーされていない)

Slide 145

Slide 145 text

Path Coverage • 経路(=分岐の組み合わせ)で 通ったコードの割合 • 右のスニペットは、 カバレッジが1/8になる • boolの変数3つの組み合わせで 2^3=8通りのパスが存在 • テストできているのは1パス

Slide 146

Slide 146 text

ブランチの観点から カバレッジを満たしても パスではカバレッジが低いまま