我有各种 PHP 脚本,它们使用mysqli
这套函数来访问数据库。我编写了这些脚本来处理各种错误情况(例如mysqli_stmt_execute
返回 false)。
是否有一种简单的方法可以伪造这些错误条件以验证用户收到的输出是否适合这些条件?
例如:
<?php
$mysqli = new mysqli("localhost", "user", "password", "db");
$query = "INSERT INTO some_table (some_field) VALUES ('some_vale')";
$stmt = $mysqli->prepare($query);
if (false === $stmt->execute()) {
echo "Oops! some_value for same_field in some_table returned false";
}
<?php
class my_mysqli extends mysqli
{
public function prepare ($query)
{
return new my_mysqli_stmt();
}
}
class my_mysqli_stmt extends mysqli_stmt {
public function execute ()
{
return false;
}
}
为了让每个执行方法都返回 false,您需要更改的是:
<?php
$mysqli = new my_mysqli("localhost", "user", "password", "db");
例如:
<?php
$mysqli = mysqli_connect("localhost", "user", "password", "db");
$query = "INSERT INTO some_table (some_field) VALUES ('some_vale')";
$stmt = mysqli_prepare($mysqli, $query);
if (false === mysqli_stmt_execute($stmt)) {
echo "Oops! some_value for same_field in some_table returned false";
}
<?php
namespace my_faking_namespace {
function mysqli_stmt_execute($stmt)
{
return false;
}
$mysqli = mysqli_connect("localhost", "user", "password", "db");
$query = "INSERT INTO some_table (some_field) VALUES ('some_vale')";
$stmt = mysqli_prepare($mysqli, $query);
if (false === mysqli_stmt_execute($stmt)) {
echo "Oops! some_value for same_field in some_table returned false";
}
}
您的脚本将调用my_faking_namespace\mysqli_stmt_execute()
而不是 php 版本。
注意:如果您调用函数,例如\mysqli_stmt_execute()
此版本显式调用全局命名空间,这将不起作用。
这些修改调用堆栈级别的函数,通过使用自定义扩展模块(需要安装)在内部更改 php。
override_function()
和rename_function()
runkit_function_copy
, runkit_function_redefine
, runkit_function_remove
,runkit_function_rename
并且可以改变你想要的任何东西。开心!
如果您正在寻找自动化测试,最好的方法(使用 PHP)是PHPUnit。
使用 PHPUnit,您可以编写单元测试(尤其是断言),它允许您验证用户接收到的输出是否适合各种不同的条件。
如果您不在生产环境中,最好的方法是更改 PHP 中的各种数据库参数。
也许你问错了问题:为什么你的核心业务逻辑依赖于第三方库/API?你听说过所谓的洋葱架构吗?将您的应用程序更多地沿着这些线分层——域、业务、基础设施/测试——让您更轻松地测试不会改变的事物:您的业务逻辑。
然而,由于 PHP 是一种动态类型语言,所以 mocking 本质上是免费的。只要您使用的是面向对象特性而不是全局函数,您创建可测试代码所需要做的就是将假数据库句柄注入代码中。一个好方法是将$db
参数添加到您的函数/方法中。像这样微不足道的事情:
function updateStuff($rowIds, $newValues, $db) { ... }
然后,在你覆盖的单元测试中updateStuff
,传入一个模拟对象作为$db
参数。