php测试代码
写PHP测试代码,试代最怕的试代就是“看起来能跑”。我见过太多人本地测一遍就上线,试代结果数据库字段长度少写个零,试代用户提交个长文本直接炸了。试代测试不是试代走形式,是试代拿代码去撞墙,看它哪里先裂开。试代
先看一段典型的试代反面教材。假设有个用户注册函数,试代接收邮箱和密码,试代插入数据库。试代很多人会这么写:
function register($email,试代 $password) { $db = new PDO('mysql:host=localhost;dbname=test', 'root', ''); $stmt = $db->prepare("INSERT INTO users (email, password) VALUES (?, ?)"); $stmt->execute([$email, $password]); return true; } 这段代码能跑通,前提是试代你手动填了合法邮箱和短密码。但测试要做的试代,是往里面塞垃圾。邮箱传空字符串会怎样?传一个没有@的字符串呢?密码传一个超过字段长度的字符串呢?我试过把密码字段设成varchar(20),然后传了21个字符,PDO默认不报错,MySQL静默截断。用户注册时密码是21位,登录时输入完整21位,反而登不上——因为数据库里只存了前20位。这种bug不测根本发现不了。
所以测试代码得覆盖边界。写个简单的测试脚本,不用PHPUnit那么重,直接写断言函数:
function assertTrue($condition, $message) { if (!$condition) { echo "FAIL: $message\n"; return false; } echo "PASS: $message\n"; return true; } // 测试空邮箱 $result = register('', '123456'); assertTrue($result === false, '空邮箱应该被拒绝'); // 测试超长密码 $longPassword = str_repeat('a', 100); $result = register('test@example.com', $longPassword); assertTrue($result === false, '超长密码应该被拒绝'); 但上面的register函数根本没做这些校验,所以测试会失败。失败是好事,说明测试有效。然后你去改register,加filter_var校验邮箱,加strlen判断密码长度。改完再跑,通过了。这个过程比看十篇教程都管用。
另一个坑是数据库状态。测试插入操作时,如果直接往生产库或开发库写,跑几次测试就多一堆垃圾数据。我习惯用SQLite内存数据库做测试,每次连接都是全新的:
$db = new PDO('sqlite::memory:'); $db->exec("CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT, password TEXT)"); 这样测完就销毁,不污染任何环境。而且SQLite启动快,跑几百个测试用例也就几秒。有人非要用MySQL测试,那就得在setUp里清表,tearDown里再清一次,麻烦且容易漏。
测试代码本身也要保持简单。我见过有人写测试比业务代码还长,各种mock、stub、依赖注入容器,最后测试跑不起来,因为mock对象没配好。测试的第一原则是能跑,第二是跑得快,第三才是覆盖全。为了覆盖率写一堆花哨的测试,结果自己都维护不动,不如不写。
还有一点,测试要能重复跑。同一个测试用例跑两遍,结果必须一致。如果第一遍插入成功,第二遍因为主键冲突失败,那测试就有问题。要么用随机邮箱,要么每次重建数据库。我倾向后者,干净利落。
最后说个真实案例。之前有个项目,图片上传功能。测试时用一张2MB的jpg,通过。上线后用户传了张10MB的png,PHP默认上传限制是2MB,直接报错。但前端没做提示,用户看到白屏。后来补测试,专门测各种大小、各种格式的文件,甚至传了个.txt改后缀为.jpg的假图片。测试代码里用file_get_contents读文件头,判断是不是真正的图片。这种测试写起来不优雅,但能拦住真实问题。
PHP测试代码不需要多高深,关键是敢往代码里扔脏数据、边界值、异常输入。写测试的人得有点“破坏欲”,别总想着证明代码是对的,要想着怎么证明它是错的。错得越多,上线后错得越少。