探索

php测试

时间:2010-12-5 17:23:32  作者:百科   来源:焦点  查看:  评论:0
内容摘要:打开终端,敲下 php -v,屏幕回显 PHP 8.3.6。这个版本跑一个空循环一百万次大约 0.02 秒,比 7.4 快了将近一倍。JIT 不是摆设,但很多人根本没用上——因为默认配置里 opcac

打开终端,敲下 php -v,屏幕回显 PHP 8.3.6。这个版本跑一个空循环一百万次大约 0.02 秒,比 7.4 快了将近一倍。JIT 不是摆设,但很多人根本没用上——因为默认配置里 opcache.jit 是关的。改一行 php.ini,把 opcache.jit=tracing 和 opcache.jit_buffer_size=64M 填进去,再重启 PHP-FPM,同样的代码立刻不一样。测性能这件事,不先确认环境,后面全是白忙。

写测试代码,最怕的是自己骗自己。比如测一个字符串拼接,有人写 $str = $str . 'a'; 循环一万次,然后说 PHP 拼接慢。实际上 PHP 的字符串是 copy-on-write,这种写法每次都在重新分配内存。换成 $parts[] = 'a'; 最后 implode,时间能差出三倍。测试用例本身写错了,跑出来的数字再精确也没意义。我习惯在测试文件头部加一行 declare(strict_types=1);,至少让类型转换的坑少一点。

PHPUnit 是绕不过去的。装它最简单的方式是 composer require --dev phpunit/phpunit。但别急着写 assertEquals,先想清楚要测什么。一个计算折扣的函数,输入 100 返回 90,这太表面了。边界呢?输入 0 呢?负数呢?浮点数精度呢?我见过有人写了几十个测试用例,覆盖率报告 100%,结果线上一个 intdiv 除零错误直接崩掉——因为测试里从来没传过 0。测试不是给领导看的,是给自己兜底的。

说到覆盖率,phpunit --coverage-html ./report 生成的报告挺好看,红红绿绿的。但覆盖率 100% 不代表没 bug,它只代表每一行都执行过。一个 if 分支里两个条件,你只测了 true 的情况,覆盖率照样算你过了。更实在的做法是 mutation testing,用 Infection 这个工具,它会把你的代码随机改掉——比如把 + 改成 -,把 true 改成 false——然后看你的测试能不能抓住。抓不住,说明测试太弱。跑一次 Infection 比看十遍覆盖率报告有用得多。

数据库测试是另一个泥潭。用 SQLite 内存库跑单元测试很快,但 SQLite 和 MySQL 的 SQL 方言不一样,group by 的宽松程度也不一样。本地全绿,上了 CI 用 MySQL 就挂。我的做法是:单元测试用 SQLite,集成测试必须连真实 MySQL,而且每个测试用例跑完要回滚事务。Laravel 的 RefreshDatabase trait 做得不错,但如果你用的是裸 PDO,记得手动 beginTransaction 和 rollBack。别偷懒用 truncate,外键约束会教你做人。

最后说一个容易被忽略的:时间。测试里千万别用 sleep(1) 来等异步操作。CI 机器负载一高,1 秒变 3 秒,测试随机失败。用 usleep 加轮询,或者干脆把时间抽象成接口,测试时注入一个假的时钟。PHP 的 hrtime 函数精度到纳秒,测性能时比 microtime 靠谱。但别在循环里频繁调 hrtime,它本身也有开销。测一个函数跑 10 万次,取平均,比单次测量准得多。

跑完测试,终端输出 OK (12 tests, 34 assertions)。别急着关窗口。把 --testdox 加上,看看每个测试的名字是不是人话。testDiscountCalculation 比 test1 强,但 testDiscountForVipUserWithCoupon 更好。测试名字写清楚了,失败的时候你一眼就知道哪块逻辑出了问题,不用去翻代码。这习惯我坚持了三年,省下的调试时间够写好几个新功能了。

最近更新
热门排行
copyright © 2026 powered by 力不从心网   sitemap