コードカバレッジの測定には、行カバレッジ、関数/メソッドカバレッジ、クラスカバレッジ、ブランチカバレッジなど、さまざまなレベルがあります。コード カバレッジは、テストの品質を測定するための重要な基準でもあります。ブラック ボックス テストの場合、テスト ケースがシステム内のコードのすべての行を実際に実行したかどうかがわからない場合は、常にテストの整合性を無視する必要があります。 。したがって、業界では、ほぼすべてのプログラミング言語に対して独自のコード カバレッジ ソリューション セットを用意しています。世界で最も美しい言語である PHP も例外ではありません。 PHPUnit と Spike PHPCoverage は、xdebug に基づいたコード カバレッジ テスト ソリューションのセットを提供します。この記事では、私が遭遇した特定のビジネス シナリオに基づいて、PHP コード関数カバレッジ テストのための私独自のソリューションについて説明します。
オンラインで 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】完全なテストプロセスは次のとおりです:
インストルメンテーションとは、テスト実行前の準備作業を意味します。
xdebug は本質的にライン カバレッジをサポートしており、関数カバレッジを自分で計算する必要があります。関数カバレッジには 2 つのデータが必要です。1 つは実行される関数、もう 1 つはファイル内の関数の総数です。
ファイル内の関数の総数。すべての関数を実行することは不可能であるため、この部分は静的コード スキャンによってのみ実現できます。 C++ や Java の場合は、字句解析ツールが必要になるかもしれませんが、最も美しい言語である PHP の前では、それほど複雑なことはまったく必要ありません。 PHP4.3 以降、PHP Zend Engine には、開発者がソース コードの字句解析を実行できるようにするトークナイザー機能が組み込まれています。 PHP で関数を定義するときに、対応する字句規則を見つけるだけでよく、指定された PHP ファイル内のすべての関数を簡単に取得できます。
tokenizer によって定義されるインターフェイスも非常に単純です。
array token_get_all (string $source)
この関数はファイル解析を実行し、PHP ソース コードをトークンで構成される配列に分割します。
string token_name (int $token)
整数形式のトークンを文字列形式に変換します。 C言語のstrerror関数に似ています。 トークナイザーを使用すると、PHP 関数で定義されたルールと形式に従って有限状態マシンを設計し、関数全体の分析を完了できます。コードのこの部分の比較的単純なバージョンを作成し、参照用に別に取り出しました: PHPFunctionParser
求函数覆盖率的另外一个难点在于获取被执行的函数列表。这地方让我们走了一些弯路。一开始一个最简单的办法,我们既然通过xdebug拿到被执的 行,可以通过行号来反推此行属于哪一个函数。然而每一次的请求获取的行号信息量是非常大的,如果一个求情执行了1000行,那就要进行1000次判断,效 率上会比较差。调研了一番之后,发现xdebug提供了function trace的功能,可以把一次请求中的函数调用关系获取到,只不过拿到了函数名字,却没办法得到它所在的文件。于是,再次调研一番,发现了 Reflection,给定方法名和类名,可以反推出来它在哪个文件中定义。于是我们使用function trace把函数调用关系暂存在一个临时文件中,然后通过文件解析,拿到执行的函数名(如果是类方法,则是“类名::函数名”的形式),再通过 reflection机制反推出定义这个函数的文件即可。再次体会到了世界上最美语言的强大之处。
为了降低使用门槛,我们尽可能少地改变PHP源代码为好。xdebug收集信息的原理是分别调用 xdebug_start_code_coverage和xdebug_stop_code_coverage来控制覆盖率信息收集的开始和结束,因此不 可避免地要改变源代码。此处我们的解决办法是,将xdebug_stop_code_coverage通过 register_shutdown_function注册为php程序结束前必须要跑的一段程序(类似C语言的atexit函数),将其封装到一个文件 中,然后在源代码第一行require这个文件即可。如果你的PHP框架是CodeIgniter这种所有请求都有一个统一入口index.php的框 架,那就只需要改变这一个文件即可,对源代码只有一行的改动!实际上,目前基本上所有的PHP框架,都是以一个index.php文件作为所有请求的入 口。
我们对源代码的改动只有入口文件index.php的第一行加入了一句话:
<?php require_once "/file/path/to/phpcoverage.php"; ?>
而phpcoverage.php核心代码逻辑大致如下:
<php …… function xdebugPhpcoverageBeforeShutdown(){ …… $lineCovData = xdebug_get_code_coverage(); xdebug_stop_code_coverage(); …… xdebug_stop_trace(); …… } register_shutdown_function(‘xdebugPhpcoverageBeforeShutdown’); …… xdebug_start_trace(……); xdebug_start_code_coverage(); //备注:上面省略号表示非关键代码,这里就不展示了
我们的函数覆盖率测试有了思路,使用xdebug的function trace获取一次请求中所有函数的调用关系,得到执行过的所有函数,输出到文件中,通过文件解析和reflection获得被执行的函数名和该函数所在文件。将这些信息存入数据库或文件即可。
之前试用Spike的时候,我们发现这些信息以xml格式存入文件,数据冗余度很高,导致几个测试下来,文件已经非常大了。这显然不是我们想看到的。因此在数据存储的时候,我们直接将数据做json格式的序列化,字符串形式存在文件中,大大减少了文件大小。与此同时,我们再通过请求来源的IP和日期作为分隔,分别存储不同的文件。这样,来自每个机器每天的请求数据都能一目了然,向着“精准”的方向又迈进了一步,可以对测试人员的每个请求做精确的监控。下图是我们在业务实践中搜集的部分数据文件截图:
这样,来自任何一个IP的每一次Web请求,它所覆盖的行和函数信息,都会被记录到文件中。对于一般的项目测试中,也就只有几个测试人员在使用,所以不需要考虑一些性能问题。
上面讲了生成覆盖率数据的原理,不过我们至此获得的只是一份份的数据文件,如何汇总成一份完整的报告呢?这就需要我们自己来写一段脚本解析刚才生成的数据文件了。我们的做法是借鉴了开源工具spike phpcoverage的模版,并加入自己的代码逻辑,特别是加入了该工具所不具有的函数覆盖率统计数据。我们自己测试的web页面生成的报告如下:
图中可以看到每个文件的行覆盖率,函数覆盖率,还有总的覆盖率统计数据。如果需要更精确的数据,可以点进文件连接,查看到底覆盖的是哪些代码行(蓝色为覆盖,红色为未覆盖):
业务测试中做Web测试时,对代码的覆盖率是衡量测试质量的重要指标。我们希望通过此方法做到尽量地“精准”,测试执行完后可以精确看到哪一行代码被执行过,哪一行没被执行过。分析没被执行过的原因,从而改进测试用例。使用工具的流程也很简单,插桩=>测试=>搜集数据=>出报告。并且此解决方案最大化地减少了对业务代码的影响,只需要改一行代码即可。即便中间出现了问题,也可以快速将代码恢复为原来的样子。让测试放心,让开发也放心。
ただし、最後に強調しておく必要があるのは、すべてのコードをカバーしてもテストが完了したことを意味するわけではないということです。しかし、それがカバーされていない場合、それは不完全でなければなりません。したがって、このソリューションの最大の意義は、テスト中に欠落しているコードを発見し、問題を発見できることです。実際、これは新入社員がプロジェクトのコード構造全体を理解するのにも役立ちます。ブラウザー要求ごとにサーバー上のどのコードが実行されているかを明確に知ることができます。