Run VoltTest performance tests within PHPUnit test suites with automated server management and comprehensive assertions.
- Overview
- Requirements
- Quick Start
- PerformanceTestCase
- Server Management
- Performance Assertions
- Complete Examples
- Configuration
- Best Practices
- Troubleshooting
VoltTest Laravel package allowed you to:
- Run performance tests via Artisan:
php artisan volttest:run UserTest - View performance metrics in console/JSON reports
- Manually review results to determine if performance was acceptable
Now you can:
- Run performance tests with PHPUnit:
./vendor/bin/phpunit - Write assertions for performance requirements (P95 < 500ms, success rate > 95%)
- Automatically fail builds when performance degrades
- Auto-manage test servers (no manual
php artisan serve) - Mix performance tests with unit/feature tests in one suite
Before: Manual process - run tests, check results, decide if acceptable.
After: Automated - tests pass/fail based on assertions, integrated into CI/CD.
- PHP 8.2+
- PHPUnit 10.0+
- ext-pcntl (for server management) and run volt-test engine.
<?php
namespace Tests\Performance;
use VoltTest\Laravel\Contracts\VoltTestCase;
use VoltTest\Laravel\Testing\PerformanceTestCase;
use VoltTest\Laravel\VoltTestManager;
class HomePagePerformanceTest extends PerformanceTestCase
{
/**
* Test homepage performance under load.
*/
public function test_homepage_performance(): void
{
$this->loadTestUrl('/', [
'virtual_users' => 10,
'stream' => true, // make output visible in console
]);
// Assert performance metrics
$this->assertVTSuccessful($result, 95.0); // At least 95% success rate for requests
$this->assertVTP95ResponseTime($result, 500); // 95% of requests complete within 500ms or less
$this->assertVTAverageResponseTime($result, 200); // Average response time under 200ms
}
}./vendor/bin/phpunit tests/Performance/HomePagePerformanceTest.phpThe PerformanceTestCase base class provides everything needed for PHPUnit integration.
use VoltTest\Laravel\Testing\PerformanceTestCase;
class MyPerformanceTest extends PerformanceTestCase
{
public function test_api_endpoint(): void
{
$result = $this->loadTestApi('/api/data', 'GET', [], [
'virtual_users' => 15,
'duration' => '1m',
]);
$this->assertVTSuccessful($result); // Default 95% success rate
$this->assertVTP95ResponseTime($result, 400); // 95% of requests within 400ms
}
}runVoltTest(VoltTestCase $testClass, array $options = [])
Run a VoltTest performance test.
You can reuse existing VoltTestCase classes with already makes from commands php artisan volttest:make.
$result = $this->runVoltTest($testClass, [
'virtual_users' => 20,
'duration' => '1m',
'ramp_up' => '10s',
'stream' => false,
'http_debug' => false,
]);Options:
virtual_users- Number of concurrent users (default: 5)duration- Test duration (e.g., '30s', '2m', '1h')ramp_up- Gradual user ramp-up timestream- Stream output to console (default: false)http_debug- Enable HTTP debugging (default: false)
loadTestUrl(string $url, array $options = [])
Quick helper to test a single URL.
public function test_homepage_load(): void
{
$result = $this->loadTestUrl('/', [
'virtual_users' => 10,
'duration' => '30s',
]);
$this->assertVTSuccessful($result);
}loadTestApi(string $endpoint, string $method = 'GET', array $data = [], array $options = [])
Quick helper to test API endpoints.
public function test_api_performance(): void
{
$result = $this->loadTestApi('/api/users', 'POST', [
'name' => 'John Doe',
'email' => 'john@example.com',
], [
'virtual_users' => 15,
]);
$this->assertVTSuccessful($result);
$this->assertVTP99ResponseTime($result, 1000);
}**`setBaseUrl(string $url)`**
Set custom base URL for testing.
Useful if the server is running on a different host/port.
```php
protected function setUp(): void
{
parent::setUp();
$this->setBaseUrl('http://localhost:8080');
}getBaseUrl()
Get the current base URL.
$url = $this->getBaseUrl();The test base class can automatically manage a PHP development server for your tests.
Set the $enableServerManagement property:
class MyPerformanceTest extends PerformanceTestCase
{
protected static bool $enableServerManagement = true;
protected static ?int $preferredPort = 8000;
public function test_with_auto_server(): void
{
// Server is automatically started before tests
// and stopped after tests complete
$result = $this->loadTestUrl('/');
$this->assertVTSuccessful($result);
}
}Or use environment variable:
VOLTTEST_ENABLE_SERVER_MANAGEMENT=true ./vendor/bin/phpunit$enableServerManagement - Enable automatic server management (default: false)
protected static bool $enableServerManagement = true;$preferredPort - Preferred port for the test server (default: 8000)
protected static ?int $preferredPort = 9000;$enableDebugForServerManagement - Enable debug output for server management (default: false)
protected static bool $enableDebugForServerManagement = true;VOLTTEST_ENABLE_SERVER_MANAGEMENT- Enable server managementVOLTTEST_DEBUG_FOR_SERVER_MANAGEMENT- Enable server debug outputVOLTTEST_BASE_PATH- Custom Laravel application base pathVOLTTEST_HTTP_DEBUG- Enable HTTP debugging
The VoltTestAssertions trait provides specialized assertions for performance testing.
assertVTSuccessful(TestResult $result, float $minSuccessRate = 95.0)
Assert that the test was successful with minimum success rate.
$this->assertVTSuccessful($result); // >= 95%
$this->assertVTSuccessful($result, 99.0); // >= 99%assertVTErrorRate(TestResult $result, float $maxErrorRate = 5.0)
Assert that error rate is below threshold.
$this->assertVTErrorRate($result); // <= 5%
$this->assertVTErrorRate($result, 1.0); // <= 1%assertVTMinResponseTime(TestResult $result, int $minTimeMs)
Assert minimum response time (useful for detecting unrealistic/cached responses).
$this->assertVTMinResponseTime($result, 10); // Min >= 10msassertVTMaxResponseTime(TestResult $result, int $maxTimeMs)
Assert maximum response time is within threshold.
$this->assertVTMaxResponseTime($result, 2000); // Max <= 2000msassertVTAverageResponseTime(TestResult $result, int $maxAvgTimeMs)
Assert average response time is acceptable.
$this->assertVTAverageResponseTime($result, 300); // Avg <= 300msassertVTMedianResponseTime(TestResult $result, int $maxMedianTimeMs)
Assert median (P50) response time - represents typical user experience.
$this->assertVTMedianResponseTime($result, 200); // P50 <= 200msassertVTP95ResponseTime(TestResult $result, int $maxP95TimeMs)
Assert P95 response time - 95% of requests complete within this time.
$this->assertVTP95ResponseTime($result, 500); // P95 <= 500msassertVTP99ResponseTime(TestResult $result, int $maxP99TimeMs)
Assert P99 response time - 99% of requests complete within this time.
$this->assertVTP99ResponseTime($result, 1000); // P99 <= 1000msassertVTMinimumRequests(TestResult $result, int $minRequests)
Assert total requests meet minimum threshold.
$this->assertVTMinimumRequests($result, 100); // Total >= 100assertVTMinimumRPS(TestResult $result, float $minRPS)
Assert minimum requests per second.
$this->assertVTMinimumRPS($result, 10.0); // RPS >= 10assertVTMaximumRPS(TestResult $result, float $maxRPS)
Assert maximum requests per second (detect unrealistic results).
$this->assertVTMaximumRPS($result, 1000.0); // RPS <= 1000<?php
namespace Tests\Performance;
use VoltTest\Laravel\Contracts\VoltTestCase;
use VoltTest\Laravel\Testing\PerformanceTestCase;
use VoltTest\Laravel\VoltTestManager;
class HomePageTest extends PerformanceTestCase
{
protected static bool $enableServerManagement = true;
public function test_homepage_handles_moderate_load(): void
{
$testClass = new class implements VoltTestCase {
public function define(VoltTestManager $manager): void
{
$scenario = $manager->scenario('Homepage Load Test');
$scenario->step('Load Homepage')
->get('/')
->expectStatus(200);
}
};
$result = $this->runVoltTest($testClass, [
'virtual_users' => 50,
'duration' => '1m',
'ramp_up' => '10s',
]);
// Comprehensive assertions
$this->assertVTSuccessful($result, 95.0);
$this->assertVTErrorRate($result, 5.0);
$this->assertVTAverageResponseTime($result, 200);
$this->assertVTMedianResponseTime($result, 150);
$this->assertVTP95ResponseTime($result, 500);
$this->assertVTP99ResponseTime($result, 1000);
$this->assertVTMinimumRPS($result, 10.0);
}
}<?php
namespace Tests\Performance;
use VoltTest\Laravel\Contracts\VoltTestCase;
use VoltTest\Laravel\Testing\PerformanceTestCase;
use VoltTest\Laravel\VoltTestManager;
class ApiAuthenticationTest extends PerformanceTestCase
{
protected static bool $enableServerManagement = true;
public function test_api_login_performance(): void
{
$testClass = new class implements VoltTestCase {
public function define(VoltTestManager $manager): void
{
$scenario = $manager->scenario('API Login Test')
->dataSource('users.csv', 'unique');
$scenario->step('Login')
->post('/api/login', [
'email' => '${email}',
'password' => '${password}',
], [
'Content-Type' => 'application/json',
])
->expectStatus(200)
->extractJson('token', 'data.token');
$scenario->step('Get User Profile')
->get('/api/user', [
'Authorization' => 'Bearer ${token}',
])
->expectStatus(200);
}
};
$result = $this->runVoltTest($testClass, [
'virtual_users' => 20,
'duration' => '30s',
]);
$this->assertVTSuccessful($result, 98.0);
$this->assertVTP95ResponseTime($result, 400);
}
}<?php
namespace Tests\Performance;
use VoltTest\Laravel\Contracts\VoltTestCase;
use VoltTest\Laravel\Testing\PerformanceTestCase;
use VoltTest\Laravel\VoltTestManager;
class CheckoutFlowTest extends PerformanceTestCase
{
protected static bool $enableServerManagement = true;
public function test_checkout_flow_performance(): void
{
$testClass = new class implements VoltTestCase {
public function define(VoltTestManager $manager): void
{
$scenario = $manager->scenario('Checkout Flow');
// Browse products
$scenario->step('Browse Products')
->get('/products')
->expectStatus(200)
->extractHtml('product_id', '.product:first-child', 'data-id');
// Add to cart
$scenario->step('Add to Cart')
->post('/cart/add', [
'product_id' => '${product_id}',
'quantity' => 1,
])
->expectStatus(200)
->thinkTime('2s');
// Checkout
$scenario->step('Checkout')
->get('/checkout')
->expectStatus(200)
->extractCsrfToken('token');
// Complete order
$scenario->step('Complete Order')
->post('/checkout/complete', [
'_token' => '${token}',
'payment_method' => 'credit_card',
])
->expectStatus(302);
}
};
$result = $this->runVoltTest($testClass, [
'virtual_users' => 15,
'duration' => '2m',
]);
// Assert end-to-end performance
$this->assertVTSuccessful($result, 95.0);
$this->assertVTMaxResponseTime($result, 3000);
$this->assertVTAverageResponseTime($result, 500);
}
}<?php
namespace Tests\Performance;
use VoltTest\Laravel\Testing\PerformanceTestCase;
class QuickLoadTests extends PerformanceTestCase
{
protected static bool $enableServerManagement = true;
public function test_homepage_quick_load(): void
{
$result = $this->loadTestUrl('/');
$this->assertVTSuccessful($result);
}
public function test_api_users_endpoint(): void
{
$result = $this->loadTestApi('/api/users', 'GET');
$this->assertVTSuccessful($result);
$this->assertVTP95ResponseTime($result, 300);
}
public function test_api_create_user(): void
{
$result = $this->loadTestApi('/api/users', 'POST', [
'name' => 'Test User',
'email' => 'test@example.com',
]);
$this->assertVTSuccessful($result);
}
}One of the most powerful patterns is reusing your existing VoltTest classes in PHPUnit tests. This allows you to:
- Define scenarios once in
app/VoltTests/ - Run them via Artisan commands:
php artisan volttest:run RegistrationTest - Run them in PHPUnit test suites with assertions
- Maintain consistency across different test environments
VoltTest Class (app/VoltTests/RegistrationTest.php):
<?php
namespace App\VoltTests;
use VoltTest\Laravel\Contracts\VoltTestCase;
use VoltTest\Laravel\VoltTestManager;
class RegistrationTest implements VoltTestCase
{
public function define(VoltTestManager $manager): void
{
$scenario = $manager->scenario('RegistrationTest')
->dataSource('registration_users.csv', 'sequential');
// Step 1: Home
$scenario->step('Home')
->get('/')
->expectStatus(200);
// Step 2: Get Registration Page
$scenario->step('Register')
->get('/register')
->extractCsrfToken('csrf_token')
->expectStatus(200);
// Step 3: Submit Registration
$scenario->step('Submit Registration')
->post('/register', [
'_token' => '${csrf_token}',
'name' => '${name}',
'email' => '${email}',
'password' => '${password}',
'password_confirmation' => '${password}',
], [
'Content-Type' => 'application/x-www-form-urlencoded'
])
->expectStatus(302);
// Step 4: Visit Dashboard
$scenario->step('Visit Dashboard')
->get('/dashboard')
->expectStatus(200);
}
}PHPUnit Test (tests/Performance/RegistrationPerformanceTest.php):
<?php
namespace Tests\Performance;
use App\VoltTests\RegistrationTest;
use VoltTest\Laravel\Testing\PerformanceTestCase;
class RegistrationPerformanceTest extends PerformanceTestCase
{
protected static bool $enableServerManagement = true;
public function test_registration_flow_performance(): void
{
// Simply instantiate and run the existing VoltTest class
$test = new RegistrationTest();
$result = $this->runVoltTest($test, [
'virtual_users' => 50,
'duration' => '2m',
'stream' => true,
]);
// Add comprehensive assertions
$this->assertVTSuccessful($result, 95.0);
$this->assertVTErrorRate($result, 5.0);
$this->assertVTMinResponseTime($result, 10);
$this->assertVTMaxResponseTime($result, 2000);
$this->assertVTAverageResponseTime($result, 800);
$this->assertVTMedianResponseTime($result, 600);
$this->assertVTP95ResponseTime($result, 1500);
$this->assertVTP99ResponseTime($result, 1800);
}
public function test_registration_handles_high_load(): void
{
// Reuse the same test with different parameters
$test = new RegistrationTest();
$result = $this->runVoltTest($test, [
'virtual_users' => 100,
'duration' => '5m',
'ramp_up' => '1m',
]);
// More lenient thresholds for higher load
$this->assertVTSuccessful($result, 90.0);
$this->assertVTP95ResponseTime($result, 3000);
}
}Benefits of This Pattern:
- DRY Principle - Define scenarios once, use everywhere
- Consistency - Same tests run in different contexts
- Flexibility - Different virtual users, durations, and assertions per test
- Maintainability - Update scenario logic in one place
- Development Workflow - Use Artisan commands during development, PHPUnit in CI/CD
Usage:
# Run via Artisan (quick testing during development)
php artisan volttest:run RegistrationTest --users=10 --stream
# Run via PHPUnit (with assertions and in CI/CD)
./vendor/bin/phpunit tests/Performance/RegistrationPerformanceTest.phpAdd performance tests to a separate test suite:
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php">
<testsuites>
<testsuite name="Unit">
<directory>tests/Unit</directory>
</testsuite>
<testsuite name="Feature">
<directory>tests/Feature</directory>
</testsuite>
<testsuite name="Performance">
<directory>tests/Performance</directory>
</testsuite>
</testsuites>
<php>
<env name="VOLTTEST_ENABLE_SERVER_MANAGEMENT" value="true"/>
<env name="VOLTTEST_BASE_URL" value="http://localhost:8000"/>
<env name="VOLTTEST_HTTP_DEBUG" value="false"/>
</php>
</phpunit># Run only performance tests
./vendor/bin/phpunit --testsuite=Performance
# Run with custom configuration
./vendor/bin/phpunit --testsuite=Performance --configuration=phpunit.performance.xmlCreate a dedicated tests/Performance directory:
tests/
├── Feature/
├── Unit/
└── Performance/
├── Api/
│ ├── AuthenticationTest.php
│ └── UserApiTest.php
├── Web/
│ ├── HomePageTest.php
│ └── CheckoutTest.php
// Good: Gradual ramp-up with realistic user count
$result = $this->runVoltTest($testClass, [
'virtual_users' => 20,
'duration' => '2m',
'ramp_up' => '30s',
]);
// Avoid: Sudden spike without ramp-up
$result = $this->runVoltTest($testClass, [
'virtual_users' => 100,
'duration' => '10s',
]);// Different thresholds for different endpoints
public function test_homepage(): void
{
$result = $this->loadTestUrl('/');
$this->assertVTP95ResponseTime($result, 200); // Fast static page
}
public function test_complex_report(): void
{
$result = $this->loadTestUrl('/reports/analytics');
$this->assertVTP95ResponseTime($result, 2000); // Complex query
}Focus on high-traffic and business-critical endpoints:
public function test_critical_checkout_flow(): void
{
// Checkout is critical - strict performance requirements
$result = $this->runVoltTest($checkoutTest, [
'virtual_users' => 30,
]);
$this->assertVTSuccessful($result, 99.0); // High success rate
$this->assertVTP95ResponseTime($result, 500); // Fast response
}public function test_user_authentication(): void
{
$testClass = new class implements VoltTestCase {
public function define(VoltTestManager $manager): void
{
$scenario = $manager->scenario('Auth Test')
->dataSource('test-users.csv', 'unique');
$scenario->step('Login')
->post('/login', [
'email' => '${email}',
'password' => '${password}',
])
->expectStatus(200);
}
};
$result = $this->runVoltTest($testClass);
$this->assertVTSuccessful($result);
}// Each test class can manage its own server
class FastApiTests extends PerformanceTestCase
{
protected static bool $enableServerManagement = true;
protected static ?int $preferredPort = 8000;
}
class HeavyLoadTests extends PerformanceTestCase
{
protected static bool $enableServerManagement = true;
protected static ?int $preferredPort = 8001; // Different port
}/**
* Baseline performance test for homepage.
*
* Baseline metrics (as of 2025-01-15):
* - P95: 180ms
* - P99: 250ms
* - Success rate: 99.5%
*/
public function test_homepage_baseline(): void
{
$result = $this->loadTestUrl('/');
$this->assertVTSuccessful($result, 99.0);
$this->assertVTP95ResponseTime($result, 200);
$this->assertVTP99ResponseTime($result, 300);
}Problem: Server management fails to start the server.
Solutions:
- Check ext-pcntl is installed:
php -m | grep pcntl - Verify Laravel base path: Set
VOLTTEST_BASE_PATHenvironment variable - Enable debug mode:
VOLTTEST_DEBUG_FOR_SERVER_MANAGEMENT=true - Check port availability: Try a different port
protected static ?int $preferredPort = 9000; // Use different portProblem: Tests fail with "Connection refused" errors.
Solutions:
- Enable server management:
protected static bool $enableServerManagement = true;
- Check base URL matches server:
$this->setBaseUrl('http://localhost:8000');
- Verify server is running:
$this->debugServer(); // Print server stats
Problem: Performance assertions pass sometimes and fail other times.
Solutions:
- Increase test duration for more stable results:
'duration' => '2m', // Longer duration = more stable metrics
- Use appropriate thresholds:
$this->assertVTP95ResponseTime($result, 500); // Not too strict
- Add ramp-up time:
'ramp_up' => '30s', // Gradual load increase
Problem: Tests fail with out-of-memory errors.
Solutions:
- Reduce virtual users:
'virtual_users' => 10, // Start small
- Disable report saving in tests:
config(['volttest.save_reports' => false]);
- Increase PHP memory limit:
php -d memory_limit=512M vendor/bin/phpunit
Problem: Tests fail with "CSV file not found" errors.
Solutions:
- Create CSV files in correct location:
storage/volttest/data/users.csv - Use absolute paths:
->dataSource(storage_path('volttest/data/users.csv'))
- Check file permissions
Problem: Multiple test classes interfere with each other.
Solutions:
- Use different ports per test class:
// ClassA protected static ?int $preferredPort = 8000; // ClassB protected static ?int $preferredPort = 8001;
- Run tests serially:
./vendor/bin/phpunit --do-not-cache-result