Все, кто разрабатывает на php, да и вообще на любом другом языке программирования с нечеткой типизацией, сталкивались с подобной проблемой:
Или даже такой:
В ходе разработки одного крупного проекта пришлось заняться тестированием написанных методов на правильно подданные параметры.
В моем случае я тестировал класс и контракт для него, но идеология тестирования одна и та же.
Допустим, есть функция которая привыкла принимать входной параметр массив, обрабатывать некоторые элементы.
Например:
Я написал простенький класс, который помогает в тестировании функций, он генерирует рандомные данные, начиная от int закачивая null-ом.
Вот его код:
Он очень простенький, но зато сохраняет очень много времени.
Пример использования:
Резюмируя, могу сказать, что метод тестирования который я «навелосипедил» называется Fuzzing.
Пара ссылок по теме:
www.vr-online.ru/?q=content/fuzzing-tehnologija-ohoty-za-bagami-752
wiki.xakep.ru/Default.aspx?Page=fuzzing&AspxAutoDetectCookieSupport=1
ps Мой первый топик на хабре, немного сумбурный и наверное много ошибок, прошу сильно не пинать, все замечания учту и сделаю его лучше.
upd добавил ссылку на vr-online.ru
upd2 немного переписал класс, учитывая замечания из коментариев
PHP Notice: Undefined index: test in /var/www/Testing.php on line 171
PHP Notice: Undefined index: test2 in /var/www/Testing.php on line 171
Или даже такой:
PHP Fatal error: Cannot use object of type stdClass as array in /var/www/Testing.php on line 171
В ходе разработки одного крупного проекта пришлось заняться тестированием написанных методов на правильно подданные параметры.
В моем случае я тестировал класс и контракт для него, но идеология тестирования одна и та же.
Допустим, есть функция которая привыкла принимать входной параметр массив, обрабатывать некоторые элементы.
Например:
PHP Warning: mysql_affected_rows() expects parameter 1 to be resource, string given in /var/www/Testing.php on line 177
Я написал простенький класс, который помогает в тестировании функций, он генерирует рандомные данные, начиная от int закачивая null-ом.
Вот его код:
class tester
{
public static $useRandom = true;
public static $randMin = 1;
public static $randMax = 50;
public static $stringDefaultPreffix = 'string_';
public static $mode = 'real';
const T_INTEGER = 0;
const T_STRING = 1;
const T_ARRAY = 2;
const T_OBJECT = 3;
const T_BOOLEAN = 4;
const T_FUNCTION = 5;
const T_NULL = 6;
/**
* Настройка "рандомайзера"
* чтобы не переписывать код по несколько раз для разных тестов
*/
public static $allowedTytes = array(self::T_INTEGER,self::T_STRING);
public static function getRand()
{
$dice = array_rand(self::$allowedTytes, 1);
/*
* если честно то какой то топорный механизм декларации методов =(
* мне не нравится
*/
switch ($dice)
{
/**
* пусть будет int
*/
case self::T_INTEGER :
return self::getInt();
break;
/**
* а это строка
*/
case self::T_STRING:
return self::getString();
break;
/**
* массив
*/
case self::T_ARRAY:
return self::getArray();
break;
/**
* ну ладно еще и объект
*/
case self::T_OBJECT:
return self::getObject();
break;
/**
* Еще и були!
*/
case self::T_BOOLEAN:
return self::getBoolean();
break;
/**
* функция, чем черт не шутит?
*/
case self::T_FUNCTION:
return self::getFunction();
break;
/**
* null?
*/
case self::T_NULL:
return self::getNull();
break;
default:
return true;
break;
}
}
/**
* получение null
* @static
* @return null
*/
public static function getNull()
{
return null;
}
/**
* получение массива
* @static
* @return array
*/
public static function getArray()
{
$array = array();
$arrayLength = mt_rand(self::$randMin, self::$randMax);
while (count($array) < $arrayLength)
{
/**
* self::getRand(); почему то ошибка сегментирования =(
* а как хотелось бы чтоб все влезало в память=)
*/
$array[] = mt_rand(self::$randMin, self::$randMax);
}
return $array;
}
/**
* получение объекта
* @static
* @return stdClass
*/
public static function getObject()
{
return new stdClass();
}
/**
* получение булевого значения
* @static
* @param bool $default
* @return bool
*/
public static function getBoolean($default = true)
{
if (self::$useRandom) {
return (mt_rand(0, 1) ? true : false);
} else
{
return $default;
}
}
/**
* получение целого числа
* @static
* @param int $default
* @return int
*/
public static function getInt($default = 42)
{
if (self::$useRandom) {
return mt_rand(self::$randMin, self::$randMax);
} else
{
return $default;
}
}
/**
* генерация рандомной строки
* @static
* @param string $default
* @return string
*/
public static function getString($default = 'string_1234567')
{
if (self::$useRandom) {
return uniqid(self::$stringDefaultPreffix);
} else
{
return $default;
}
}
/**
* создаем функцию
* @static
* @return string
*/
public static function getFunction()
{
/**
* честно, копипаст с
* http://php.net/manual/en/function.create-function.php
*/
return create_function('$a,$b', 'return "ln($a) + ln($b) = " . log($a * $b);');
}
/**
* наш еррор хендлер, можно отнаследоваться и поставить свой
* @param $errno
* @param $errstr
* @param $errfile
* @param $errline
* @return bool
*/
public function errorHandler($errno, $errstr, $errfile, $errline)
{
echo '-----ERROR----', PHP_EOL;
echo $errstr, PHP_EOL;
echo 'Line:', $errline, ' in file: ', $errfile, PHP_EOL;
echo '-------------', PHP_EOL;
return true;
}
/**
* активация встроенное ерорхендлера
* @static
* @return void
*/
public static function setErrorHandler()
{
set_error_handler(array(new self(), "errorHandler"));
}
/**
* тестирование класса
* @static
* @param string $className
* @param null $method
* @return void
*/
public static function test($className = 'stdClass', $method = null)
{
if (class_exists($className, true)) {
$obj = new ReflectionClass($className);
/**
* берем список всех публичных методов которые будем вызывать
*/
$methods = $obj->getMethods(ReflectionMethod::IS_PUBLIC);
foreach ($methods as $method)
{
$argsCount = $method->getNumberOfParameters();
$args = array();
for ($i = 0; $i < $argsCount; $i++)
{
$args[] = self::getRand();
}
if ($method->isStatic()) {
$result = call_user_func(array($className, $method->getName()), $args);
}
else {
$obj = new $className();
$result = call_user_func(array($obj, $method->getName()), $args);
}
}
}
}
}
Он очень простенький, но зато сохраняет очень много времени.
Пример использования:
class badWritenClass
{
public function testFunc1($param)
{
echo count($param),PHP_EOL;
}
public function testFunc2($param)
{
$calcVar = $param['test'] / $param['test2'] * 50;
echo $calcVar ,PHP_EOL;
}
public static function goodWritenFunc($param)
{
if (is_array($param) && isset($param['name'])) {
echo 'Hello ', $param['name'],PHP_EOL;
} else
{
echo 'Hello Guest!',PHP_EOL;
}
}
}
function testFunc1($param)
{
echo count($param) ,PHP_EOL;
}
function testFunc2($param)
{
$calcVar = $param['test'] + $param['test2'] * 50;
echo $calcVar ,PHP_EOL;
}
/**
* установка хендлера встроенного в класс
*/
tester::setErrorHandler();
tester::$allowedTytes = array(tester::T_INTEGER, tester::T_STRING, tester::T_ARRAY, tester::T_OBJECT, tester::T_BOOLEAN, tester::T_FUNCTION, tester::T_NULL);
/**
* тестим какие функции
*/
testFunc1(tester::getRand());
testFunc2(tester::getRand());
/**
* тестим целый класс
*/
tester::test('badWritenClass');
Резюмируя, могу сказать, что метод тестирования который я «навелосипедил» называется Fuzzing.
Пара ссылок по теме:
www.vr-online.ru/?q=content/fuzzing-tehnologija-ohoty-za-bagami-752
wiki.xakep.ru/Default.aspx?Page=fuzzing&AspxAutoDetectCookieSupport=1
ps Мой первый топик на хабре, немного сумбурный и наверное много ошибок, прошу сильно не пинать, все замечания учту и сделаю его лучше.
upd добавил ссылку на vr-online.ru
upd2 немного переписал класс, учитывая замечания из коментариев