0

0

Web端PHP代码函数覆盖率测试解决方案_PHP教程

php中文网

php中文网

发布时间:2016-07-12 09:05:13

|

968人浏览过

|

来源于php中文网

原创

Web端PHP代码函数覆盖率测试解决方案

1. 关于代码覆盖率

衡量代码覆盖率有很多种层次,比如行覆盖率,函数/方法覆盖率,类覆盖率,分支覆盖率等等。代码覆盖率也是衡量测试质量的一个重要标准,对于黑盒测 试来说,如果你不确定自己的测试用例是否真正跑过了系统里面的每一行代码,在测试的完整性上总要打些折扣。因此,业界几乎对各种编程语言都有自己的一套代 码覆盖率解决方案。世界上最美的语言PHP当然也不例外。PHPUnit和Spike PHPCoverage提供了一套基于xdebug的代码覆盖率测试方案。在本文中,我将针对自己碰到的特定业务场景,讲述一下自己进行PHP代码函数覆 盖率测试的解决方案。

2. 业务背景

假设我们在线开发了一个网站,交给业务测试的同事去进行功能测试。那他们是怎么测试的呢?通常情况下,无非是开发人员把网站部署好了,然后测试人员 把网上所有功能都试用一遍,包括一些异常使用情况。对于业务测试来说,只要我把所有的功能点都测了,把所有异常使用情况也测到了,那就完成了。但是对于开 发来说,我比较好奇的是,你是否把我写的所有代码都跑到了?会不会存在一些代码,只有在很特殊的情况下才能触发,而你从来没有测到过这些情况?这时,可能 就需要代码覆盖率来出马了。 

其实我首先想到了xdebug来测试覆盖率,只需要两三个函数即可,如下:

立即学习PHP免费学习笔记(深入)”;

xdebug_start_code_coverage(); //开始收集代码行覆盖情况

xdebug_get_code_coverage();
//获取截至目前所跑过的代码文件名和行号

xdebug_stop_code_coverage();
//停止收集代码行覆盖情况

xdebug提供的接口可以用于测试行覆盖率,这是否能满足要求呢?其实,行覆盖率颗粒度有点细,实际项目中,开发人员可能会对代码进行微调。比 如,这次测试,你跑过了A.php文件的第10行,但是我有一天对A.php进行了微调,在A.php第9行和第10行之间又加了两行代码。于是,原来的 第10行变为了第12行,而xdebug的行覆盖信息只记录了行号……这样之前的数据岂不是不准确了么。。。考虑再三,我觉得函数覆盖是个不错的颗粒度。 在相对成熟的项目中,很少有大规模函数变动的情况。不过问题是,xdebug并没有提供函数覆盖的接口。

于是,我们现在碰到的场景是:

1】希望测到某次测试中所覆盖的所有函数列表,知道这个项目总共有多少个函数,计算一下覆盖率是否足够高。

2】测试完成之后,要生成一份覆盖率报告,将代码的覆盖情况可视化。

3】完整测试的流程如下:

其中插桩的意思是在测试执行之前的一些准备工作。

3. 函数覆盖率解决方案

1)原理

xdebug天生提供了对行覆盖率的支持,我们要自己计算出函数覆盖率。函数覆盖率需要两点数据,一个是哪些函数被执行,一个是文件中总共有多少个函数。

文件中总共的函数量,由于我们不可能把所有函数都执行一遍,因此这部分只能通过代码静态扫描来实现。如果是在C++或者Java中,可能就需要词法 分析工具了,然而在最美的语言PHP面前,我们完全不需要那么复杂。从PHP4.3开始,PHP Zend Engine中内置了tokenizer功能,帮助开发者做源码词法分析。我们只需要找到PHP中定义函数时所对应的词法规律,就可以轻松得到指定PHP 文件中的全部函数了。

tokenizer定义的接口也十分简单:

array token_get_all (string $source)

该函数进行文件解析,将php源代码拆成由token组成的数组。

AITDK
AITDK

免费AI SEO工具,SEO的AI生成器

下载

string token_name (int $token)

将整数形式的token转变为字符串形式。类似于C语言中的strerror函数。有了tokenizer,自己再根据php函数定义的规律和格式设计一个有限状态机,即可完成全量函数的解析。这部分代码,本人写了个比较简陋的,把它单独拿出来,仅供大家参考:PHPFunctionParser

求函数覆盖率的另外一个难点在于获取被执行的函数列表。这地方让我们走了一些弯路。一开始一个最简单的办法,我们既然通过xdebug拿到被执的 行,可以通过行号来反推此行属于哪一个函数。然而每一次的请求获取的行号信息量是非常大的,如果一个求情执行了1000行,那就要进行1000次判断,效 率上会比较差。调研了一番之后,发现xdebug提供了function trace的功能,可以把一次请求中的函数调用关系获取到,只不过拿到了函数名字,却没办法得到它所在的文件。于是,再次调研一番,发现了 Reflection,给定方法名和类名,可以反推出来它在哪个文件中定义。于是我们使用function trace把函数调用关系暂存在一个临时文件中,然后通过文件解析,拿到执行的函数名如果是类方法,则是“类名::函数名”的形式),再通过 reflection机制反推出定义这个函数的文件即可。再次体会到了世界上最美语言的强大之处。

2)插桩

为了降低使用门槛,我们尽可能少地改变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的第一行加入了一句话:

<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>&hellip;&hellip; </span></li><li class="alt"><span>function xdebugPhpcoverageBeforeShutdown(){ </span></li><li><span>&hellip;&hellip; </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>&hellip;&hellip; </span></li><li><span>xdebug_stop_trace(); </span></li><li class="alt"><span>&hellip;&hellip; </span></li><li><span>} </span></li><li class="alt"><span>register_shutdown_function(&lsquo;xdebugPhpcoverageBeforeShutdown&rsquo;); </span></li><li><span>&hellip;&hellip; </span></li><li class="alt"><span>xdebug_start_trace(&hellip;&hellip;); </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的function trace获取一次请求中所有函数的调用关系,得到执行过的所有函数,输出到文件中,通过文件解析和reflection获得被执行的函数名和该函数所在文件。将这些信息存入数据库或文件即可。

之前试用Spike的时候,我们发现这些信息以xml格式存入文件,数据冗余度很高,导致几个测试下来,文件已经非常大了。这显然不是我们想看到的。因此在数据存储的时候,我们直接将数据做json格式的序列化,字符串形式存在文件中,大大减少了文件大小。与此同时,我们再通过请求来源的IP和日期作为分隔,分别存储不同的文件。这样,来自每个机器每天的请求数据都能一目了然,向着“精准”的方向又迈进了一步,可以对测试人员的每个请求做精确的监控。下图是我们在业务实践中搜集的部分数据文件截图:

这样,来自任何一个IP的每一次Web请求,它所覆盖的行和函数信息,都会被记录到文件中。对于一般的项目测试中,也就只有几个测试人员在使用,所以不需要考虑一些性能问题。

4. 报告生成

上面讲了生成覆盖率数据的原理,不过我们至此获得的只是一份份的数据文件,如何汇总成一份完整的报告呢?这就需要我们自己来写一段脚本解析刚才生成的数据文件了。我们的做法是借鉴了开源工具spike phpcoverage的模版,并加入自己的代码逻辑,特别是加入了该工具所不具有的函数覆盖率统计数据。我们自己测试的web页面生成的报告如下:

图中可以看到每个文件的行覆盖率,函数覆盖率,还有总的覆盖率统计数据。如果需要更精确的数据,可以点进文件连接,查看到底覆盖的是哪些代码行蓝色为覆盖,红色为未覆盖):

5. 总结

业务测试中做Web测试时,对代码的覆盖率是衡量测试质量的重要指标。我们希望通过此方法做到尽量地“精准”,测试执行完后可以精确看到哪一行代码被执行过,哪一行没被执行过。分析没被执行过的原因,从而改进测试用例。使用工具的流程也很简单,插桩=>测试=>搜集数据=>出报告。并且此解决方案最大化地减少了对业务代码的影响,只需要改一行代码即可。即便中间出现了问题,也可以快速将代码恢复为原来的样子。让测试放心,让开发也放心。

不过,最后还需要强调的一点是,并不是说覆盖了所有的代码,就证明测试已经完整了。只不过没被覆盖的话,一定是不完整的。所以这个方案最大的意义在于能够发现测试中一些遗漏的代码,找到一部分问题。其实,它也可以帮助新来的员工理解整个项目代码结构,我们可以清晰的知道,自己的每一次浏览器请求,到底在运行服务器上的哪些代码。

 



www.bkjia.comtruehttp://www.bkjia.com/PHPjc/1069984.htmlTechArticleWeb端PHP代码函数覆盖率测试解决方案 1. 关于代码覆盖率 衡量代码覆盖率有很多种层次,比如行覆盖率,函数/方法覆盖率,类覆盖率,分支...

相关文章

PHP速学教程(入门到精通)
PHP速学教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

php

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

26

2026.03.13

Python异步编程与Asyncio高并发应用实践
Python异步编程与Asyncio高并发应用实践

本专题围绕 Python 异步编程模型展开,深入讲解 Asyncio 框架的核心原理与应用实践。内容包括事件循环机制、协程任务调度、异步 IO 处理以及并发任务管理策略。通过构建高并发网络请求与异步数据处理案例,帮助开发者掌握 Python 在高并发场景中的高效开发方法,并提升系统资源利用率与整体运行性能。

46

2026.03.12

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

178

2026.03.11

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

51

2026.03.10

Kotlin Android模块化架构与组件化开发实践
Kotlin Android模块化架构与组件化开发实践

本专题围绕 Kotlin 在 Android 应用开发中的架构实践展开,重点讲解模块化设计与组件化开发的实现思路。内容包括项目模块拆分策略、公共组件封装、依赖管理优化、路由通信机制以及大型项目的工程化管理方法。通过真实项目案例分析,帮助开发者构建结构清晰、易扩展且维护成本低的 Android 应用架构体系,提升团队协作效率与项目迭代速度。

92

2026.03.09

JavaScript浏览器渲染机制与前端性能优化实践
JavaScript浏览器渲染机制与前端性能优化实践

本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

102

2026.03.06

Rust内存安全机制与所有权模型深度实践
Rust内存安全机制与所有权模型深度实践

本专题围绕 Rust 语言核心特性展开,深入讲解所有权机制、借用规则、生命周期管理以及智能指针等关键概念。通过系统级开发案例,分析内存安全保障原理与零成本抽象优势,并结合并发场景讲解 Send 与 Sync 特性实现机制。帮助开发者真正理解 Rust 的设计哲学,掌握在高性能与安全性并重场景中的工程实践能力。

227

2026.03.05

PHP高性能API设计与Laravel服务架构实践
PHP高性能API设计与Laravel服务架构实践

本专题围绕 PHP 在现代 Web 后端开发中的高性能实践展开,重点讲解基于 Laravel 框架构建可扩展 API 服务的核心方法。内容涵盖路由与中间件机制、服务容器与依赖注入、接口版本管理、缓存策略设计以及队列异步处理方案。同时结合高并发场景,深入分析性能瓶颈定位与优化思路,帮助开发者构建稳定、高效、易维护的 PHP 后端服务体系。

532

2026.03.04

AI安装教程大全
AI安装教程大全

2026最全AI工具安装教程专题:包含各版本AI绘图、AI视频、智能办公软件的本地化部署手册。全篇零基础友好,附带最新模型下载地址、一键安装脚本及常见报错修复方案。每日更新,收藏这一篇就够了,让AI安装不再报错!

171

2026.03.04

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
CSS3 教程
CSS3 教程

共18课时 | 7.1万人学习

Django 教程
Django 教程

共28课时 | 5万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号