Проверка работы index.php инструменты и сценарии тестирования
Файл index.php часто является единственной точкой входа для PHP-приложения. От его корректной работы зависит запуск всех компонентов: загрузка конфигурации, инициализация маршрутизатора, обработка запросов. Тестирование этого файла позволяет выявить ошибки на ранних этапах и избежать простоев.
Способы тестирования index.php
Как провести полное функциональное тестирование index.php с помощью PHPUnit и встроенного сервера?
Наиболее надёжный способ - запустить реальный HTTP-сервер, отправить к нему запросы и проверить ответы. Для этого используется встроенный сервер PHP и клиент Guzzle внутри тестового класса.
class IndexPhpTest extends TestCase
{
private static $serverProcess;
private static $baseUrl;
public static function setUpBeforeClass(): void
{
$port = rand(8000, 9000);
self::$baseUrl = "http://127.0.0.1:$port";
$docRoot = realpath(__DIR__ . '/../public');
self::$serverProcess = proc_open(
"php -S 127.0.0.1:$port -t $docRoot",
[
0 => ['pipe', 'r'],
1 => ['pipe', 'w'],
2 => ['pipe', 'w']
],
$pipes
);
sleep(1); // ждём запуск сервера
}
public static function tearDownAfterClass(): void
{
if (self::$serverProcess) {
proc_terminate(self::$serverProcess);
}
}
public function testHomePageReturns200(): void
{
$client = new \GuzzleHttp\Client(['base_uri' => self::$baseUrl]);
$response = $client->get('/');
$this->assertEquals(200, $response->getStatusCode());
$this->assertStringContainsString('Главная', (string)$response->getBody());
}
}Типичные проблемы и их решения
- Порт занят - используйте случайный порт или проверку занятости.
- Сервер не запустился - увеличьте задержку sleep() или проверьте вывод ошибок.
- Зависание процесса - в tearDown используйте proc_terminate и proc_close.
Как протестировать index.php путём его прямого включения?
Метод подходит для простых приложений без сложной инициализации. В тесте устанавливаются суперглобальные переменные, затем подключается index.php. Результат перехватывается через буферизацию.
public function testDirectInclude()
{
$_SERVER['REQUEST_METHOD'] = 'GET';
$_SERVER['REQUEST_URI'] = '/test';
$_GET = ['id' => 1];
ob_start();
require __DIR__ . '/../public/index.php';
$output = ob_get_clean();
$this->assertStringContainsString('test', $output);
}Проблемы
- Отправка заголовков - используйте ob_start до require.
- Глобальное состояние - сбрасывайте суперглобальные массивы после каждого теста.
- Автозагрузка - убедитесь, что автозагрузчик подключён до index.php.
Как эмулировать HTTP-запросы с помощью Symfony BrowserKit?
Если приложение использует Symfony компоненты, можно применить BrowserKit для имитации запросов. Этот метод не требует запуска сервера.
use Symfony\Component\BrowserKit\HttpBrowser;
use Symfony\Component\HttpClient\HttpClient;
$browser = new HttpBrowser(HttpClient::create());
$crawler = $browser->request('GET', 'http://localhost/index.php/?page=about');
$this->assertCount(1, $crawler->filter('h1:contains("О нас")'));Проблемы
- Зависимости - требуется установка symfony/browser-kit и symfony/http-client.
- Ограничения - не поддерживает сессии “из коробки” без дополнительной настройки.
Как проверить производительность index.php с помощью Apache Bench?
Инструмент ab (Apache Bench) позволяет выполнить нагрузочное тестирование и оценить количество запросов в секунду.
ab -n 100 -c 10 http://localhost/index.phpРезультат покажет среднее время ответа, количество успешных запросов и пропускную способность.
Проблемы
- Требуется установленный сервер - без запущенного веб-сервера тест не выполнить.
- Не тестируется логика - ab проверяет только HTTP-статус, а не содержимое ответа.
Как найти уязвимости index.php в тестах безопасности?
Автоматическая отправка вредоносных данных (SQL-инъекции, XSS) помогает выявить слабые места обработки входных параметров.
$payload = "' OR 1=1 -- ";
$response = $client->get('/search?q=' . urlencode($payload));
$this->assertStringNotContainsString('Ошибка SQL', (string)$response->getBody());Проблемы
- Ложные срабатывания - требуется ручная проверка каждого случая.
- Сложность автоматизации - необходимо писать специфичные тесты под каждую уязвимость.
Расширенные примеры тестирования index.php
Ниже приведены подробные сценарии с полными реализациями на PHPUnit.
// Пример 1: Полный тест с встроенным сервером и проверкой POST-запроса
class IndexPhpAdvancedTest extends TestCase
{
private static $process;
private static $baseUrl;
public static function setUpBeforeClass(): void
{
$port = getenv('TEST_PORT') ?: 8081;
self::$baseUrl = "http://127.0.0.1:$port";
$dir = __DIR__ . '/../public';
self::$process = proc_open(
"php -S 127.0.0.1:$port -t $dir",
[0 => STDIN, 1 => STDOUT, 2 => STDERR],
$pipes
);
usleep(500000); // 0.5 сек
}
public static function tearDownAfterClass(): void
{
if (self::$process) {
proc_terminate(self::$process);
}
}
public function testPostFormRedirects(): void
{
$client = new \GuzzleHttp\Client(['base_uri' => self::$baseUrl]);
$response = $client->post('/submit', [
'form_params' => ['name' => 'Тест', 'email' => 'test@example.com']
]);
$this->assertEquals(302, $response->getStatusCode());
$this->assertEquals('/success', $response->getHeaderLine('Location'));
}
public function test404Page(): void
{
$client = new \GuzzleHttp\Client(['base_uri' => self::$baseUrl, 'http_errors' => false]);
$response = $client->get('/nonexistent');
$this->assertEquals(404, $response->getStatusCode());
$this->assertStringContainsString('Страница не найдена', (string)$response->getBody());
}
public function test500OnInternalError(): void
{
// Предполагается, что приложение выбрасывает исключение на определённом маршруте
$client = new \GuzzleHttp\Client(['base_uri' => self::$baseUrl, 'http_errors' => false]);
$response = $client->get('/trigger-error');
$this->assertEquals(500, $response->getStatusCode());
}
}PHPUnit 9.5.10 by Sebastian Bergmann. ... 3 / 3 (100%) Time: 0.98 seconds, Memory: 6.00 MB OK (3 tests, 5 assertions)
// Пример 2: Тестирование с помощью mock-объектов для базы данных
// Допустим, index.php использует класс Database, соединение которого нужно имитировать
use PHPUnit\Framework\TestCase;
class IndexWithMockTest extends TestCase
{
public function testDatabaseQueryMocked(): void
{
// Создаём заглушку для PDO
$mockPDO = $this->createMock(PDO::class);
$mockStmt = $this->createMock(PDOStatement::class);
$mockStmt->method('fetchAll')->willReturn([['id' => 1, 'title' => 'Мок']]);
$mockPDO->method('query')->willReturn($mockStmt);
// Подставляем заглушку в контейнер зависимостей (если используется)
// App::setDatabase($mockPDO);
// Выполняем index.php с определённым маршрутом
$_SERVER['REQUEST_URI'] = '/list';
$_SERVER['REQUEST_METHOD'] = 'GET';
ob_start();
require __DIR__ . '/../public/index.php';
$output = ob_get_clean();
$this->assertStringContainsString('Мок', $output);
}
}PHPUnit 9.5.10 . Time: 0.05 seconds OK (1 test, 2 assertions)
// Пример 3: Тестирование разных HTTP-методов (PUT, DELETE)
public function testPutMethodUpdatesResource(): void
{
$client = new \GuzzleHttp\Client(['base_uri' => self::$baseUrl]);
$response = $client->put('/resource/5', [
'json' => ['value' => 'обновлено']
]);
$this->assertEquals(200, $response->getStatusCode());
$body = json_decode((string)$response->getBody(), true);
$this->assertEquals('обновлено', $body['value']);
}
public function testDeleteMethodRemovesResource(): void
{
$client = new \GuzzleHttp\Client(['base_uri' => self::$baseUrl]);
$response = $client->delete('/resource/5');
$this->assertEquals(204, $response->getStatusCode());
}PHPUnit 9.5.10 .. Time: 0.15 seconds OK (2 tests, 2 assertions)