Подделка условий ошибки в библиотеке mysqli
У меня есть множество PHP-скриптов, которые используют набор функций mysqli
для доступа к базе данных. Я написал эти сценарии для обработки различных условий ошибок (например, mysqli_stmt_execute
, возвращающих false).
Существует ли простой способ подделать эти условия ошибки, чтобы проверить, что выходные данные, полученные пользователем, соответствуют этим условиям?
4 answers
Если вы используете классы mysqli.
Например:
<?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>=5.3
<?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 внутренне с помощью пользовательских модулей расширения (требуется установка).
-
Расширенный отладчик PHP имеет
override_function()
иrename_function()
-
ранкит имеет
runkit_function_copy
,runkit_function_redefine
,runkit_function_remove
,runkit_function_rename
и может измениться просто обо всем, что вы пожелаете.
НДЖой!
Если вы ищете автоматизированное тестирование, лучший способ (с PHP) - это PHPUnit.
С помощью PHPUnit вы можете писать модульные тесты (в частности, утверждения), которые позволяют проверить, что выходные данные, полученные пользователем, соответствуют любым различным условиям.
Если вы не находитесь в производственной среде, лучший способ сделать это - изменить различные параметры базы данных в вашем PHP.
- Измените пароль базы данных, чтобы узнать, как вы обрабатываете ошибки подключения
- Измените структуру таблиц, чтобы увидеть, как вы справляетесь с ошибками запроса
- Попробуйте посмотреть, что происходит со специальными символами (например: буква) в ваших запросах
Возможно, вы задаете неправильный вопрос: почему ваша основная бизнес-логика зависит от сторонних библиотек/API? Вы слышали о так называемой луковой архитектуре? Разделение вашего приложения в большей степени по этим направлениям - домен, бизнес, инфраструктура/тесты - значительно упрощает тестирование того, что не изменится: вашей бизнес-логики.
Однако, поскольку PHP является динамическим типизированным языком, насмешки, по сути, бесплатны. До тех пор, пока вы используете функции OO, а не глобальные функции, все, что вам нужно для создания тестируемого кода, - это способ внедрить в ваш код поддельный дескриптор базы данных. Отличный способ - добавить параметр $db
в ваши функции/методы. Что-то тривиальное вроде этого:
function updateStuff($rowIds, $newValues, $db) { ... }
Затем в ваших модульных тестах, которые охватывают updateStuff
, передайте макет объекта в качестве параметра $db
.