1. コードカバレッジについて
コード カバレッジの測定には、行カバレッジ、関数/メソッド カバレッジ、クラス カバレッジ、ブランチ カバレッジなど、さまざまなレベルがあります。コード カバレッジは、テストの品質を測定するための重要な基準でもあります。ブラック ボックス テストの場合、テスト ケースがシステム内のコードのすべての行を実際に実行したかどうかがわからない場合は、常にテストの整合性を無視する必要があります。 。したがって、業界では、ほぼすべてのプログラミング言語に対して独自のコード カバレッジ ソリューション セットを用意しています。世界で最も美しい言語である PHP も例外ではありません。 PHPUnit と Spike PHPCoverage は、xdebug に基づいたコード カバレッジ テスト ソリューションを提供します。この記事では、私が遭遇した特定のビジネス シナリオに基づいて、PHP コード関数カバレッジ テストのための私独自のソリューションについて説明します。
2. 事業背景
オンラインで Web サイトを開発し、それを機能テストのためにビジネス テストの同僚に引き渡すとします。では、どうやってテストしたのでしょうか?通常、これは開発者が Web サイトを展開するだけで、その後テスターが異常な使用状況を含むすべてのオンライン機能を試します。ビジネステストの場合、すべての機能ポイントをテストし、異常な使用状態をすべて検出できれば完了です。しかし、開発に関しては、私が書いたすべてのコードが実行されたかどうかのほうが気になります。非常に特殊な状況でのみトリガーできるコードがあり、そのような状況を測定したことがないのでしょうか?この時点で、コード カバレッジが役立つ必要がある場合があります。
実際、私は最初にカバレッジをテストするために xdebug を考えました。これに必要な関数は次のように 2 つまたは 3 つだけです。
xdebug_start_code_coverage(); //コード行カバレッジの収集を開始します
xdebug_get_code_coverage(); //これまでに実行されたコード ファイル名と行番号を取得します
xdebug_stop_code_coverage(); //コード行カバレッジの収集を停止します
xdebug が提供するインターフェイスは、ライン カバレッジをテストするために使用できます。これは要件を満たしていますか?実際、行カバレッジの粒度は、実際のプロジェクトでは、開発者がコードを微調整する可能性があります。たとえば、このテストでは A.php ファイルの 10 行目までを実行しましたが、ある日、A.php を微調整して、A.php の 9 行目と 10 行目にさらに 2 行のコードを追加しました。その結果、元の10行目は12行目になってしまい、xdebugの行カバレッジ情報には行番号しか記録されていませんでした…前のデータは不正確ではないでしょうか? 。 。慎重に検討した結果、機能範囲は適切な粒度であると思います。 比較的成熟したプロジェクトでは、大規模な機能変更が行われることはほとんどありません。しかし、問題は、xdebug が関数カバレッジのためのインターフェイスを提供していないことです。
つまり、私たちが今遭遇しているシーンは次のとおりです:
【1】あるテストでカバーされているすべての機能のリストを測定し、このプロジェクトにどれだけの機能があるかを知り、カバー率が十分に高いかどうかを計算したいと考えています。
【2】テストが完了したら、コードカバレッジを視覚化するためにカバレッジレポートを生成する必要があります。
【3】完全なテストプロセスは次のとおりです:
インストルメンテーションとは、テスト実行前の準備作業を意味します。
3. 機能カバレッジソリューション
(1) 原則
xdebug は本質的にライン カバレッジのサポートを提供するため、関数カバレッジを自分で計算する必要があります。関数カバレッジには 2 つのデータが必要です。1 つは実行される関数、もう 1 つはファイル内の関数の総数です。
ファイル内の関数の総数。すべての関数を実行することは不可能であるため、この部分はコードの静的スキャンによってのみ実現できます。 C++ や Java の場合は、字句解析ツールが必要になるかもしれませんが、最も美しい言語である PHP の前では、それほど複雑なことはまったく必要ありません。 PHP4.3 以降、PHP Zend Engine には、開発者がソース コードの字句解析を実行できるようにするトークナイザー機能が組み込まれています。 PHP で関数を定義するときに、対応する字句規則を見つけるだけでよく、指定された PHP ファイル内のすべての関数を簡単に取得できます。
トークナイザーによって定義されたインターフェースも非常にシンプルです:
配列 token_get_all (文字列 $source)
この関数はファイル解析を実行し、PHP ソース コードをトークンで構成される配列に分割します。
文字列 トークン名 (int $token)
整数形式のトークンを文字列形式に変換します。 C言語のstrerror関数に似ています。 トークナイザーを使用すると、PHP 関数で定義されたルールと形式に従って有限状態マシンを設計し、すべての関数の分析を完了できます。コードのこの部分の比較的単純なバージョンを作成し、参照用に別に取り出しました: PHPFunctionParser
関数カバレッジを見つける際のもう 1 つの困難は、実行された関数のリストを取得することです。この場所は私たちをいくつかの回り道に連れて行きました。最初の最も単純な方法は、xdebug を通じて実行された行を取得するので、行番号を使用してこの行がどの関数に属しているかを推測することです。ただし、1 回のリクエストで取得する行番号情報は非常に多く、1,000 行のリクエストを実行すると 1,000 回の判定が行われることになり、効率が悪くなります。調べてみると、xdebugにはリクエスト内の関数呼び出し関係を取得できる関数トレース機能が用意されていることが分かりましたが、関数名は取得できただけで、それが置かれているファイルは取得できませんでした。そこで、再度調べた結果、メソッド名とクラス名があれば、それがどのファイルに定義されているかが推測できることが分かりました。そこで、関数トレースを使用して関数の呼び出し関係を一時ファイルに一時的に保存し、そのファイルを解析して実行された関数名を取得します (クラスメソッドの場合、「クラス名::関数名」の形式になります)。 )、リフレクションを使用します。このメカニズムにより、この関数を定義するファイルをバックアウトできます。世界で一番美しい言語の力を改めて実感しました。
(2) 計測
使用の敷居を下げるために、PHP のソースコードの変更は最小限に抑えます。 xdebug の情報収集の原理は、xdebug_start_code_coverage と xdebug_stop_code_coverage をそれぞれ呼び出してカバレッジ情報収集の開始と終了を制御することであるため、ソース コードの変更は避けられません。ここでの解決策は、PHP プログラムが終了する前に実行する必要があるプログラム (C 言語の atexit 関数と同様) として register_shutdown_function を介して xdebug_stop_code_coverage を登録し、それをファイルにカプセル化し、ソース コードの最初の行でこれを要求することです。ファイルするだけです。 PHP フレームワークが CodeIgniter のようなフレームワークで、すべてのリクエストに統一されたエントリ Index.php がある場合、この 1 つのファイルを変更するだけで済み、ソース コードも 1 行変更するだけです。実際、基本的に現在のすべての PHP フレームワークは、すべてのリクエストのエントリ ポイントとしてindex.php ファイルを使用します。
ソース コードに加えた唯一の変更は、エントリ ファイルindex.php の最初の行に次の文を追加することでした。
<ol class="dp-j"><li class="alt"><span><span><?php require_once </span><span class="string">"/file/path/to/phpcoverage.php"</span><span>; ?> </span></span></li></ol>
phpcoverage.php のコア コード ロジックはおおよそ次のとおりです:
<ol class="dp-j"> <li class="alt"><span><span><?php </span></span></li> <li><span>…… </span></li> <li class="alt"><span>function xdebugPhpcoverageBeforeShutdown(){ </span></li> <li><span>…… </span></li> <li class="alt"><span>$lineCovData = xdebug_get_code_coverage(); </span></li> <li><span>xdebug_stop_code_coverage(); </span></li> <li class="alt"><span>…… </span></li> <li><span>xdebug_stop_trace(); </span></li> <li class="alt"><span>…… </span></li> <li><span>} </span></li> <li class="alt"><span>register_shutdown_function(‘xdebugPhpcoverageBeforeShutdown’); </span></li> <li><span>…… </span></li> <li class="alt"><span>xdebug_start_trace(……); </span></li> <li><span>xdebug_start_code_coverage(); </span></li> <li class="alt"><span><span class="comment">//备注:上面省略号表示非关键代码,这里就不展示了</span><span> </span></span></li> </ol>
(3) 情報の保管
関数カバレッジテストのアイデアとして、xdebug の関数トレースを使用して、リクエスト内のすべての関数の呼び出し関係を取得し、すべての実行された関数を取得してファイルに出力し、ファイルの解析とリフレクションを通じて実行された関数を取得します。 . 名前と関数が配置されているファイル。この情報をデータベースまたはファイルに保存するだけです。
以前 Spike を試したとき、情報は XML 形式でファイルに保存されており、データの冗長性が非常に高いことがわかりました。その結果、いくつかのテストの後、ファイルはすでに非常に大きくなっていました。これは明らかに私たちが見たいものではありません。そのため、データを保存する際には、データを直接 json 形式でシリアル化し、文字列形式でファイルに保存することで、ファイル サイズを大幅に削減します。同時に、リクエスト元のIPと日付を区切りとして、異なるファイルを別々に保存します。これにより、各マシンからの毎日のリクエスト データが一目でわかるようになり、「精度」がさらに向上し、テスターの各リクエストを正確に監視できます。下の写真は、ビジネス実務で収集したいくつかのデータ ファイルのスクリーンショットです:
このようにして、任意の IP からのすべての Web リクエスト、それがカバーする行および機能情報がファイルに記録されます。一般的なプロジェクトのテストでは、少数のテスターだけがそれを使用しているため、パフォーマンスの問題を考慮する必要はありません。
4. レポートの生成
カバレッジ データを生成する原理は上で説明しましたが、これまでに取得したのはデータ ファイルだけであり、それらを完全なレポートにまとめるにはどうすればよいでしょうか。そのためには、生成されたデータ ファイルを解析するためのスクリプトを自分で記述する必要があります。私たちのアプローチは、オープンソース ツールのスパイク phpcoverage のテンプレートを借用し、独自のコード ロジック、特にこのツールにはない関数カバレッジ統計を追加することです。私たちがテストした Web ページによって生成されたレポートは次のとおりです:
図では、各ファイルの行カバレッジ、関数カバレッジ、および合計カバレッジ統計を確認できます。より正確なデータが必要な場合は、ファイル リンクをクリックして、どのコード行がカバーされているかを確認できます (青はカバーされている、赤はカバーされていないことを意味します):
5. 概要
ビジネス テストで Web テストを行う場合、コード カバレッジはテストの品質を示す重要な指標です。テストの実行後に、どの行が実行され、どの行が実行されなかったかを正確に確認できるように、この方法を使用することを期待しています。テストケースが実行されなかった原因を分析し、テストケースを改善します。このツールを使用するプロセスも非常に簡単です。インストルメンテーション => テスト => データの収集 => レポートの作成です。また、このソリューションはビジネス コードへの影響を最小限に抑え、コードを 1 行変更するだけで済みます。途中で問題が発生しても、すぐにコードを元の状態に戻すことができます。テストを安心して行うことができ、開発も安心して行えます。
ただし、最後に強調しておく必要があるのは、すべてのコードをカバーしてもテストが完了したことを意味するわけではないということです。しかし、それがカバーされていない場合、それは不完全でなければなりません。したがって、このソリューションの最大の意義は、テスト中に欠落しているコードを発見し、問題を発見できることです。実際、これは新入社員がプロジェクトのコード構造全体を理解するのにも役立ちます。ブラウザー要求ごとにサーバー上のどのコードが実行されているかを明確に知ることができます。