# VoltTest Documentation > Performance testing platform for PHP and Laravel. Scale from 1K to 10M+ concurrent users. This file contains all documentation content in a single document following the llmstxt.org standard. ## Cloud Examples In this section, we'll cover examples of running load tests on VoltTest Cloud using the PHP SDK. Ex: - [Basic Cloud Test](#basic-cloud-test) - Simplest cloud run with constant VUs - [Staged Load Profile](#staged-load-profile) - Ramp up, spike, and ramp down on cloud - [Multi-Region Distribution](#multi-region-distribution) - Distribute load across geographic regions - [Multi-Scenario with Weights](#multi-scenario-with-weights) - Multiple user flows with weighted traffic - [Custom Conflict Handler](#custom-conflict-handler) - Programmatic control when a test name already exists - [Error Handling](#error-handling) - Catch cloud-specific exceptions ## Basic Cloud Test The simplest cloud example — a fixed number of virtual users for a set duration. ```php cloud('vt_your_api_key'); $test->setVirtualUsers(100); $test->setDuration('2m'); $scenario = $test->scenario('Homepage'); $scenario->step('Load page') ->get('https://example.com') ->validateStatus('success', 200); $test->run(); ``` When you run this script, the SDK submits the test to VoltTest Cloud and prints the run ID, dashboard URL, and status automatically: ``` Cloud test submitted (run: a1b2c3d4-...) Dashboard → https://volt-test.com/runs/a1b2c3d4-... Waiting for cloud infrastructure... ✓ Test completed ``` ## Staged Load Profile Use stages to create dynamic load profiles — ramp up, hold, spike, and ramp down. ```php cloud('vt_your_api_key') ->stage('1m', 100) // Ramp to 100 VUs ->stage('5m', 100) // Hold at 100 ->stage('30s', 500) // Spike to 500 ->stage('2m', 500) // Hold spike ->stage('1m', 0); // Ramp down $scenario = $test->scenario('API Check'); $scenario->step('Health') ->get('https://example.com') ->validateStatus('health', 200); $test->run(); ``` Stages are chained on the `cloud()` call since it returns the `VoltTest` instance. You can also call them separately: ```php $test->cloud('vt_your_api_key'); $test->stage('1m', 100); $test->stage('5m', 100); ``` ## Multi-Region Distribution Distribute virtual users across multiple geographic regions. Weights must sum to 100. ```php cloud('vt_your_api_key'); $test->setVirtualUsers(1000); $test->setDuration('5m'); $test->regions([ 'us-east-1' => 60, // 600 VUs in US East 'eu-west-1' => 40, // 400 VUs in EU West ]); $scenario = $test->scenario('API Health Check'); $scenario->step('Check API') ->get('https://example.com') ->validateStatus('health', 200); $scenario->step('Fetch Data') ->get('https://example.com/api/data') ->header('Accept', 'application/json') ->validateStatus('data', 200); $test->run(); ``` :::info Region distribution requires cloud mode — `cloud()` must be called before `regions()`. ::: ## Multi-Scenario with Weights Simulate realistic traffic by splitting virtual users across different user flows. ```php cloud('vt_your_api_key') ->stage('1m', 200) ->stage('5m', 200) ->stage('1m', 0); // 70% of virtual users run this scenario $login = $test->scenario('User Login Flow'); $login->setWeight(70); $login->step('Get Login Page') ->get('https://example.com/login') ->validateStatus('login_page', 200); $login->step('Submit Login') ->post('https://example.com/login', '{"email": "user@test.com", "password": "secret"}') ->header('Content-Type', 'application/json') ->extractFromJson('token', 'data.token') ->validateStatus('login_submit', 200); $login->step('Get Profile') ->get('https://example.com/profile') ->header('Authorization', 'Bearer ${token}') ->validateStatus('profile', 200); // 30% of virtual users run this scenario $browse = $test->scenario('Browse Products'); $browse->setWeight(30); $browse->step('List Products') ->get('https://example.com/products') ->header('Accept', 'application/json') ->validateStatus('products', 200); $browse->step('View Product') ->get('https://example.com/products/1') ->header('Accept', 'application/json') ->validateStatus('product_detail', 200); $test->run(); ``` With 200 VUs, approximately 140 will execute the Login Flow and 60 will execute Browse Products. Weights must sum to 100 across all scenarios. ## Custom Conflict Handler When a test with the same name already exists in your account, VoltTest prompts you to choose. Use `setOnConflictPrompt()` to handle this programmatically — useful in CI/CD where there's no interactive terminal. ```php cloud('vt_your_api_key') ->stage('1m', 50) ->stage('3m', 50) ->stage('1m', 0); $scenario = $test->scenario('Homepage'); $scenario->step('Load page') ->get('https://example.com') ->validateStatus('success', 200); // Always update the most recent existing test $test->setOnConflictPrompt(function (array $existingTests) { return $existingTests[0]['id']; }); $test->run(); ``` The callback receives an array of existing tests. Return values: - **A test ID** — update that existing test - **`null`** — create a new test - **`'cancel'`** — abort the run ## Error Handling Cloud mode throws specific exceptions for different failure scenarios. Wrap `run()` in a try/catch to handle them. ```php cloud('vt_your_api_key'); $test->setVirtualUsers(100); $test->setDuration('5m'); $scenario = $test->scenario('Test'); $scenario->step('Home')->get('https://example.com'); try { $test->run(); } catch (AuthenticationException $e) { echo "Invalid API key: " . $e->getMessage() . "\n"; } catch (PlanLimitException $e) { echo "Plan limit exceeded: " . $e->getMessage() . "\n"; } catch (CloudConnectionException $e) { echo "Connection failed: " . $e->getMessage() . "\n"; } catch (CloudTimeoutException $e) { echo "Timed out: " . $e->getMessage() . "\n"; } catch (RunFailedException $e) { echo "Run failed: " . $e->getMessage() . "\n"; } ``` | Exception | Cause | Fix | |-----------|-------|-----| | `AuthenticationException` | Invalid or expired API key | Regenerate your key at [volt-test.com](https://volt-test.com) | | `PlanLimitException` | VU count or duration exceeds your plan | Reduce load or upgrade your plan | | `CloudConnectionException` | Cannot reach VoltTest servers | Check your network connection | | `CloudTimeoutException` | Provisioning exceeded timeout | Increase with `setCloudTimeout()` | | `RunFailedException` | Test execution failed or was stopped | Check target and test configuration | All cloud exceptions extend `CloudException`, which extends `VoltTestException`. --- ## HTML Form Examples Here are some examples of testing HTML forms using VoltTest PHP SDK. Ex: - [Login Form Testing](#login-form-testing) - Login form testing with CSRF token extraction - [Registration Form Example](#registration-form-example) - Registration form testing with CSRF token extraction - [Multi-Step Form Example](#multi-step-form-example) - Multi-step form testing with session management - [Data-Driven Form Testing](#data-driven-form-testing) - Use CSV file as data source - [Form Validation Testing](#form-validation-testing) - Test form validation with different scenarios ## Login Form Testing ```php $test = new VoltTest('Login Form Test'); $test->setVirtualUsers(10); $scenario = $test->scenario('Login Form Test'); // Get login page and extract CSRF token $scenario->step('Get Login Page') ->get('https://example.com/login') ->extractFromHtml('csrf_token', 'input[name="_token"]', 'value') ->validateStatus('page_load', 200); // Submit login form $scenario->step('Submit Login') ->post('https://example.com/login', '_token=${csrf_token}&email=user@example.com&password=secret') ->header('Content-Type', 'application/x-www-form-urlencoded') ->validateStatus('login_success', 302); // Run the test and get the result $result = $test->run(); ``` In the example above, we create a new test named "Login Form Test" and set the number of virtual users to 10. We then create a scenario named "Login Form Test" and add two steps to it. The first step gets the login page, extracts the CSRF token, and validates the page load status. The second step submits the login form with the CSRF token, email, and password, and validates the login success status. ## Registration Form Example ```php $test = new VoltTest('Registration Form'); $test->setVirtualUsers(100); $test->setDuration('5s'); $scenario = $test->scenario('Registration Form'); // Load registration page $scenario->step('Load Register Page') ->get('https://example.com/register') ->extractFromHtml('csrf_token', 'input[name="_token"]', 'value') ->validateStatus('page_load', 200); // Submit registration with form data $scenario->step('Submit Registration') ->post('https://example.com/register', '_token=${csrf_token}&name=${name}&email=${email}&password=${password}&password_confirmation=${password}') ->header('Content-Type', 'application/x-www-form-urlencoded') ->validateStatus('registration_success', 302); // Run the test and get the result $result = $test->run(); ``` In the example above, we create a new test named "Registration Form" and set the number of virtual users to 100 and the test duration to 5 seconds. We then create a scenario named "Registration Form" and add two steps to it. The first step loads the registration page, extracts the CSRF token, and validates the page load status. The second step submits the registration form with the CSRF token, name, email, password, and password confirmation, and validates the registration success status. ## Multi-Step Form Example ```php $test = new VoltTest('Multi-Step Form'); $test->setVirtualUsers(50); $scenario = $test->scenario('Multi-Step Form'); // Step 1: Personal Info $scenario->step('Personal Info') ->get('https://example.com/form/step1') ->extractFromHtml('form_token', 'input[name="_token"]', 'value') ->validateStatus('step1_load', 200); $scenario->step('Submit Step 1') ->post('https://example.com/form/step1', 'form_token=${form_token}&name=${name}&email=${email}') ->header('Content-Type', 'application/x-www-form-urlencoded') ->validateStatus('step1_success', 302) ->extractFromCookie('session_id', 'session_id'); // Step 2: Address Info $scenario->step('Address Info') ->get('https://example.com/form/step2') ->header('Cookie', 'session_id=${session_id}') ->validateStatus('step2_load', 200); $scenario->step('Submit Step 2') ->post('https://example.com/form/step2', 'address=${address}&city=${city}&country=${country}') ->header('Cookie', 'session_id=${session_id}') ->validateStatus('step2_success', 302); // Run the test and get the result $result = $test->run(); ``` In the example above, we create a new test named "Multi-Step Form" and set the number of virtual users to 50. We then create a scenario named "Multi-Step Form" and add four steps to it. The first two steps handle the personal info form, and the last two steps handle the address info form. Each step extracts data from the response and validates the status of the form submission. ## Data-Driven Form Testing ```php $test = new VoltTest('Data-Driven Form Test'); $test->setVirtualUsers(10); // Configure data source $dataConfig = new DataSourceConfiguration( __DIR__ .'/test_users.csv', // CSV with test data actual file path 'sequential', // Sequential selection true // Has header row ); $scenario = $test->scenario('Registration Test') ->setDataSourceConfiguration($dataConfig); // Registration process using data from CSV $scenario->step('Submit Registration') ->post('https://example.com/register','name=${name}&email=${email}&phone=${phone}') ->header('Content-Type', 'application/x-www-form-urlencoded') ->validateStatus('registration_success', 200); // Run the test and get the result $result = $test->run(); ``` :::info The CSV file should have columns for name, email, and phone with corresponding values for each test case. ::: Data source configuration is used to provide test data for data-driven testing. In the example above, we create a new test named "Data-Driven Form Test" and set the number of virtual users to 10. We then configure a data source using a CSV file with test data and set it to sequential selection. We create a scenario named "Registration Test" and set the data source configuration. The scenario step submits the registration form using data from the CSV file and validates the registration success status. ## Form Validation Testing ```php $scenario = $test->scenario('Form Validation'); // Test required fields $scenario->step('Submit Empty Form') ->post('https://example.com/submit','') ->header('Content-Type', 'application/x-www-form-urlencoded') ->validateStatus('validation_error', 422); // Test invalid email $scenario->step('Submit Invalid Email') ->post('https://example.com/submit','email=invalid-email') ->header('Content-Type', 'application/x-www-form-urlencoded') ->validateStatus('validation_error', 422); // Test password mismatch $scenario->step('Password Mismatch') ->post('https://example.com/submit','password=test123&password_confirmation=test456') ->header('Content-Type', 'application/x-www-form-urlencoded') ->validateStatus('validation_error', 422); ``` In the example above, we create a new scenario named "Form Validation" and add three steps to it. Each step submits a form with different validation errors and validates the status code to ensure proper form validation handling. --- ## JSON API Examples In this section, we'll cover some examples of testing JSON APIs using VoltTest PHP SDK. Ex: - [API Authentication Testing](#authentication-api-testing) - Test an API that requires authentication - [CRUD Operations](#crud-operations) - Test a JSON API that supports CRUD operations - [Data Driven API Testing](#data-driven-api-testing) - Test an API with different sets of data from a CSV file ## Authentication API Testing Authentication is a common use case for APIs. In this example, we'll test an authentication API that requires a login request to get an access token and then use that token in subsequent requests. ```php $test = new VoltTest('API Authentication Test'); $test->setVirtualUsers(10); $scenario = $test->scenario('API Authentication'); // Login Request $scenario->step('Login') ->post('https://api.example.com/auth/login', json_encode([ 'email' => 'some@mail.com', 'password' => 'secret' ])) ->header('Content-Type', 'application/json') ->validateStatus('login_success', 200) ->extractFromJson('access_token', 'data.token'); // Use Token in Subsequent Requests $scenario->step('Get Profile') ->get('https://api.example.com/profile') ->header('Authorization', 'Bearer ${access_token}') ->validateStatus('profile_success', 200); // Run the test and get the result $result = $test->run(); echo $result->getRawOutput(); ``` In the example above, we create a new test named "API Authentication Test" and set the number of virtual users to 10. We then create a scenario named "API Authentication" and add two steps to it. The first step sends a POST request to the login endpoint with the email and password, validates the login success status, and extracts the access token from the JSON response. The second step sends a GET request to the profile endpoint with the access token in the Authorization header and validates the profile success status. ## CRUD Operations CRUD (Create, Read, Update, Delete) operations are fundamental to APIs. In this example, we'll test a JSON API that supports CRUD operations for managing users. ```php $test = new VoltTest('CRUD Operations Test'); $test->setVirtualUsers(100); $test->setDuration('1m'); $scenario = $test->scenario('CRUD Operations'); // Create $scenario->step('Create User') ->post('https://api.example.com/users', json_encode([ 'name' => 'John Doe', 'email' => 'some@mail.com', 'role' => 'user', ])) ->header('Content-Type', 'application/json') ->validateStatus('creation_success', 201) ->extractFromJson('user_id', 'data.id'); // Read $scenario->step('Get User') ->get('https://api.example.com/users/${user_id}') ->validateStatus('read_success', 200); // Update $scenario->step('Update User') ->put('https://api.example.com/users/${user_id}', json_encode([ 'name' => 'Jane Doe', 'email' => 'update@mail.com', ])) ->header('Content-Type', 'application/json') ->validateStatus('update_success', 200); // Delete $scenario->step('Delete User') ->delete('https://api.example.com/users/${user_id}') ->validateStatus('delete_success', 204); // Run the test and get the result $result = $test->run(); echo $result->getRawOutput(); ``` In the example above, we create a new test named "CRUD Operations Test" and set the number of virtual users to 100 and the test duration to 1 minute. We then create a scenario named "CRUD Operations" and add four steps to it. The first step sends a POST request to create a new user, validates the creation success status, and extracts the user ID from the JSON response. The second step sends a GET request to retrieve the user details. The third step sends a PUT request to update the user details, and the fourth step sends a DELETE request to delete the user. ## Data Driven API Testing Data-driven testing involves running the same test scenario with different sets of data. In this example, we'll test an API that requires different user data for registration. ```php $test = new VoltTest('Data-Driven API Test'); $test->setVirtualUsers(50); // Configure data source $dataConfig = new DataSourceConfiguration( __DIR__ . '/test_users.csv', // CSV with test data 'random', // Random selection true // Has header row ); $scenario = $test->scenario('Registration Test') ->setDataSourceConfiguration($dataConfig); // Registration process using data from CSV $scenario->step('Submit Registration') ->post('https://api.example.com/register', json_encode([ 'name' => '${name}', 'email' => '${email}', 'phone' => '${phone}', ])) ->header('Content-Type', 'application/json') ->validateStatus('registration_success', 200); // Run the test and get the result $result = $test->run(); echo $result->getRawOutput(); ``` In the example above, we create a new test named "Data-Driven API Test" and set the number of virtual users to 50. We then configure a data source using a CSV file with test data and set it to random selection. We create a scenario named "Registration Test" and set the data source configuration. The scenario step submits the registration form using data from the CSV file and validates the registration success status. :::info The CSV file should have columns for name, email, and phone with corresponding values for each test case. ::: --- ## Result # Test Results VoltTest provides comprehensive performance metrics and statistics through the TestResult class. ## Basic Usage ```php $result = $test->run(); // Get key metrics echo "Success Rate: " . $result->getSuccessRate() . "%\n"; echo "Total Requests: " . $result->getTotalRequests() . "\n"; ``` ## Available Metrics ### Basic Metrics ```php // Test duration $result->getDuration(); // Returns: "24.000873057s" // Request counts $result->getTotalRequests(); // Returns: 5000 $result->getSuccessRequests(); // Returns: 4148 $result->getFailedRequests(); // Returns: 852 // Performance metrics $result->getSuccessRate(); // Returns: 82.96 $result->getRequestsPerSecond(); // Returns: 208.33 ``` ### Response Time Statistics ```php // Time measurements $result->getMinResponseTime(); // Returns: "7.388011ms" $result->getMaxResponseTime(); // Returns: "18.179649581s" $result->getAvgResponseTime(); // Returns: "3.848391356s" $result->getMedianResponseTime(); // Returns: "8.997304894s" $result->getP95ResponseTime(); // Returns: "16.74641748s" $result->getP99ResponseTime(); // Returns: "17.552319263s" ``` ### Raw Data Access ```php // Get all metrics as array $metrics = $result->getAllMetrics(); // Access raw output $rawOutput = $result->getRawOutput(); ``` ## Metrics Format The TestResult class parses and formats the following output structure: ```plaintext Test Metrics Summary: =================== Duration: 24.000873057s Total Reqs: 5000 Success Rate: 82.96% Req/sec: 208.33 Success Requests: 4148 Failed Requests: 852 Response Time: ------------ Min: 7.388011ms Max: 18.179649581s Avg: 3.848391356s Median: 8.997304894s P95: 16.74641748s P99: 17.552319263s ``` ## Response Time Units Results are provided in appropriate units: - Milliseconds (ms) - Seconds (s) - Minutes (m) - Hours (h) ## Complete Example ```php $result = $test->run(); printf("Test Summary:\n"); printf("Duration: %s\n", $result->getDuration()); printf("Total Requests: %d\n", $result->getTotalRequests()); printf("Success Rate: %.2f%%\n", $result->getSuccessRate()); printf("Requests/sec: %.2f\n", $result->getRequestsPerSecond()); printf("Success Requests: %d\n", $result->getSuccessRequests()); printf("Failed Requests: %d\n", $result->getFailedRequests()); printf("\nResponse Times:\n"); printf("Min: %s\n", $result->getMinResponseTime()); printf("Max: %s\n", $result->getMaxResponseTime()); printf("Avg: %s\n", $result->getAvgResponseTime()); printf("Median: %s\n", $result->getMedianResponseTime()); printf("P95: %s\n", $result->getP95ResponseTime()); printf("P99: %s\n", $result->getP99ResponseTime()); ``` ## Metrics Reference Table | Metric | Method | Return Type | Description | |--------|---------|-------------|-------------| | Duration | getDuration() | string | Total test duration | | Total Requests | getTotalRequests() | int | Total number of requests | | Success Rate | getSuccessRate() | float | Percentage of successful requests | | Requests/sec | getRequestsPerSecond() | float | Average throughput | | Success Requests | getSuccessRequests() | int | Number of successful requests | | Failed Requests | getFailedRequests() | int | Number of failed requests | | Min Response Time | getMinResponseTime() | string | Fastest response time | | Max Response Time | getMaxResponseTime() | string | Slowest response time | | Avg Response Time | getAvgResponseTime() | string | Average response time | | Median Response Time | getMedianResponseTime() | string | Median response time | | P95 Response Time | getP95ResponseTime() | string | 95th percentile response time | | P99 Response Time | getP99ResponseTime() | string | 99th percentile response time | --- ## Scenarios Scenarios in VoltTest represent user flows that will be executed during the performance test. Each scenario contains a sequence of steps and can be configured with specific behaviors. ## Creating a Scenario Create scenarios through the VoltTest instance: ```php use VoltTest\VoltTest; $test = new VoltTest('API Test'); $scenario = $test->scenario( 'Login Flow', // Scenario name 'User authentication' // Optional description ); ``` ## Scenario Configuration ### Weight The weight of a scenario determines the execution probability relative to other scenarios ```php $scenario->setWeight(75); // 75% weight ``` Let's say you have two scenarios, A and B, with weights 30 and 70, respectively. and you have 100 virtual users. Scenario A will be executed by approximately 30 virtual users, and scenario B will be executed by 70 virtual users. ```php $regitrationScenario = $test->scenario( 'Registration Flow' ); $browseCategoryScenario = $test->scenario( 'Browse Category Flow' ); $browseCategoryScenario->setWeight(70); // 70% weight $regitrationScenario->setWeight(30); // 30% weight ``` :::warning If you have only one scenario, ignore the weight configuration. ::: #### Weight Validation - Weights must be integers between 1 and 100 - The sum of all scenario weights must be 100 ### Think Time Let's say you have a scenario with multiple steps. and you want to add a delay between each step. Think time is the delay between steps in a scenario. The Default think time is 0 seconds. Adding a delay between steps: ```php $scenario->setThinkTime('2s'); // 2-second delay $scenario->setThinkTime('1m'); // 1-minute delay ``` This configuration adds a delay of (2 seconds or 1 minute) between each step in the scenario. :::info You can configure think time at the step level as well, and it will override the scenario-level think time. ::: ### Cookie Management Cookie management is disabled by default in VoltTest. You can enable it if needed. ```php $scenario->autoHandleCookies(); ``` ### Data Source Configuration Assuming you have a CSV file with test data, you can configure data-driven testing in VoltTest. The data source configuration allows you to specify the data file path, mode, and whether the file has a header row. Then you can use the data in your scenario steps. Configure data-driven testing: ```php use VoltTest\DataSourceConfiguration; $dataConfig = new DataSourceConfiguration( __DIR__ . '/test-data.csv', // Full file path 'random', // Mode: sequential/random/unique true // Has header row ); $scenario->setDataSourceConfiguration($dataConfig); ``` So Each virtual user will get a row from the data file and use it in the scenario steps. Each Scenario will have its own data source configuration. For more information on data-driven testing, see the [HTML Form Examples](/docs/Examples/html-form-examples) and [JSON API Examples](/docs/Examples/JSON-API-Examples) pages. ### Data Iteration Modes When using data sources, you can choose from three iteration modes: - `sequential`: Iterate through records in order - `random`: Select records randomly - `unique`: Use each record only once ## Properties Reference | Property | Description | Default | |----------|-------------|---------| | name | Scenario name | Required | | description | Scenario description | Empty string | | weight | Execution probability | 100 | | thinkTime | Delay between steps | None | | autoHandleCookies | Cookie management | false | | DataSourceConfiguration | Data source configuration | None | Next We will see how to add steps to a scenario. --- ## Steps Steps define individual HTTP requests and their associated configurations within a scenario. Scenario can contain multiple steps, each representing a unique request to be executed during the test. ## Creating Steps Creating step from a scenario instance: ```php use VoltTest\VoltTest; $test = new VoltTest('API Test'); $scenario = $test->scenario('Login Flow'); $scenario->step('Login') // Step name is required ->post('https://api.example.com/login'); ``` ## HTTP Methods ### GET Request ```php $scenario->step('Get Users') ->get('https://api.example.com/users') ->header('Accept', 'application/json'); ``` ### POST Request Json body is required for POST requests: ```php $scenario->step('Create User') ->post('https://api.example.com/users', '{"name": "John"}') ->header('Content-Type', 'application/json'); ``` Html form data can be sent as an array: ```php $scenario->step('Submit Form') ->post('https://api.example.com/users', 'email=${email}&password=${password}') ->header('Content-Type', 'application/x-www-form-urlencoded'); ``` ### PUT Request ```php $scenario->step('Update User') ->put('https://api.example.com/users/1', '{"name": "Updated"}'); ``` ### PATCH Request ```php $scenario->step('Patch User') ->patch('https://api.example.com/users/1', '{"status": "active"}'); ``` ### DELETE Request ```php $scenario->step('Delete User') ->delete('https://api.example.com/users/1'); ``` ### HEAD Request ```php $scenario->step('Check Status') ->head('https://api.example.com/status'); ``` ### OPTIONS Request ```php $scenario->step('Get Options') ->options('https://api.example.com/users'); ``` ## Headers Add custom headers to requests: ```php $scenario->step('Create User') ->post('https://api.example.com/users', '{"name": "John"}') ->header('Authorization', 'Bearer token') ->header('Accept', 'application/json') ->header('X-Custom-Header', 'value'); ``` ## Data Extraction Data can be extracted from responses and used in subsequent requests. Let's say you have a login request that returns a token in the response. You can extract the token and use it in subsequent requests. ### JSON Response Extract data from a JSON response: ```php $scenario->step('Login') ->post('https://api.example.com/login', '{"name": "John"}') ->header('Content-Type', 'application/json') ->extractFromJson('token', 'meta.token'); ``` Support for array Indexing in JSON path You can extract data from JSON arrays using the array index. ```php $scenario->step('Login') ->post('https://api.example.com/login', '{"name": "John"}') ->header('Content-Type', 'application/json') ->extractFromJson('email', 'data[0].users[0].email'); ``` Here, email will store the value of attribute from the first object in the data array. Example usage in subsequent steps: ```php $scenario->get('https://api.example.com/profile') ->header('Authorization', 'Bearer ${token}'); ``` ### HTML Response Extract data from an HTML response: ```php $scenario->step('Get Login Page') ->get('https://example.com/login') ->extractFromHtml('csrf', 'input[name="_token"]', 'value'); ``` The above code will extract the value of the input field with the name `_token` and store it in the `csrf` variable. You can also specify the form action URL to extract data from a specific form: ```php $scenario->step('Get Login Page') ->get('https://example.com/login') ->extractFromHtml('csrf', 'form[action="http://localhost/login"] input[name="_token"]', 'value'); ``` Here, the `csrf` variable will store the value of the input field with the name `_token` from the form with the action URL `http://localhost/login`. ```php $scenario->step('Get Login Page') ->get('https://example.com/login') ->extractFromHtml('csrf', '.login-form input[name="_csrf"]', 'value'); ``` Here, the `csrf` variable will store the value of the input field with the name `_csrf` from the form with the class `login-form`. ```php $scenario->step('Get Page') ->get('https://example.com/login') ->extractFromHtml('csrf', 'div#test', 'data-test'); ``` Here, the `csrf` variable will store the value of the data attribute `data-test` from the div with the id `test`. ### Headers Extract data from response headers: ```php $scenario->step('Login') ->post('https://api.example.com/login', '{"name": "John"}') ->header('Content-Type', 'application/json') ->extractFromHeader('token', 'Authorization'); ``` Here, `token` is the variable name, and `Authorization` is the header name to extract the value from the response headers. So, the extracted value will be stored in the `token` variable and can be used in subsequent steps. ### Cookies Extract data from cookies: ```php $loginScenario->step('submit_login') ->post( 'https://example.com/login', '_token=${csrf_token}&email=tes11t1v@mai1l.com&password=12345678' ) ->extractFromCookie('session', 'laravel_session') ->extractFromCookie('XSRF-TOKEN', 'XSRF-TOKEN') ->header('Content-Type', 'application/x-www-form-urlencoded'); ``` ### Regular Expressions Extract data using regular expressions: ```php $scenario->step('Get Login Page') ->get('https://example.com/login') ->extractFromRegex('csrf', 'name="_token" value="(.+?)"'); ``` Here, `csrf` is the variable name, and `name="_token" value="(.+?)"` is the regular expression to extract the value from the response. ## Response Validation For now, VoltTest php sdk supports validating the response status code only. ### Validate Status ```php $scenario->step('Login') ->post('https://api.example.com/login', '{"name": "John"}') ->validateStatus('success', 200); ``` Here, `success` is the validation name, and `200` is the expected status code. So if the response status code is not 200, This will add an error to the test report. ## Think Time Think time is the delay after a step finishes executing. The default think time is 0 seconds. Adding a delay after step execution: ```php $scenario->step('Get Page') ->get('https://example.com') ->setThinkTime('2s'); // 2-second delay after this step ``` This overrides the scenario-level think time for this specific step. ## Using Variables Reference extracted variables in subsequent steps: ```php // Extract token from login response $scenario->step('Login') ->post('/login', '{"username": "user", "password": "pass"}') ->extractFromJson('token', 'data.token'); // Use token in next request $scenario->step('Get Profile') ->get('/profile') ->header('Authorization', 'Bearer ${token}'); ``` ## Complete Example ```php $scenario->step('Get Form') ->get('https://example.com/form') ->header('Content-Type', 'application/x-www-form-urlencoded') ->extractFromRegex('csrf', 'name="_token" value="(.+?)"') ->validateStatus('success', 200) ->setThinkTime('1s'); $scenario->step('Process Result') ->post('https://example.com/form', 'name=John') ->header('X-CSRF-TOKEN', '${csrf}') ->validateStatus('success', 200); ``` This example demonstrates a scenario with two steps: 1. Get Form: Sends a GET request to `https://example.com/form`, extracts the CSRF token from the response, and validates the status code. 2. After a 1-second delay, the Process Result step sends a POST request to `https://example.com/form` with the CSRF token extracted from the previous step. ## Validation Rules - Step name is required - URLs must be valid HTTP/HTTPS URLs - Headers must follow RFC 7230 requirements - JSON paths must be valid expressions - Regex patterns must be valid - Think time must use valid time units (s/m/h) --- ## VoltTest The `VoltTest` class is the entry point for creating and running performance tests. It holds the test configuration and scenarios. ## Creating a Test ```php use VoltTest\VoltTest; $test = new VoltTest('My API Test', 'Optional description'); ``` | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | name | string | Yes | Test name | | description | string | No | Test description (default: empty) | ## Configuration Methods ### Virtual Users Set the number of concurrent virtual users: ```php $test->setVirtualUsers(100); ``` - Must be at least 1 - Cannot be used together with `stage()` ### Duration Set the total test duration: ```php $test->setDuration('30s'); // 30 seconds $test->setDuration('5m'); // 5 minutes $test->setDuration('1h'); // 1 hour ``` - Format: `[s|m|h]` - Cannot be used together with `stage()` ### Ramp-Up & Staged Load Profiles Control how virtual users are distributed over time — gradual ramp-up, staged profiles with spikes, step-up stress tests, and more. ```php $test->setRampUp('10s'); // Gradual start over 10 seconds // Or use stages for dynamic profiles: $test->stage('2m', 100); // Ramp to 100 VUs $test->stage('10m', 100); // Hold $test->stage('2m', 0); // Ramp down ``` See the [Load Profiles](/docs/load-profiles) page for the full guide — constant load, staged patterns (spike, stress, soak), and when to use each. ### HTTP Timeout Set the per-request timeout (default: 30s): ```php $test->setHttpTimeout('60s'); // 60-second timeout per request ``` ### HTTP Debug Enable HTTP debug output for troubleshooting: ```php $test->setHttpDebug(true); ``` ### Idle Timeout Configure the connection idle timeout: ```php $test->setTarget('30s'); // 30-second idle timeout (default) ``` ## Creating Scenarios Create scenarios through the VoltTest instance: ```php $scenario = $test->scenario('Login Flow', 'User authentication test'); ``` See the [Scenarios](/docs/Scenarios) page for scenario configuration. ## Running Tests ### Local Execution ```php $result = $test->run(); echo "Success Rate: " . $result->getSuccessRate() . "%\n"; echo "Total Requests: " . $result->getTotalRequests() . "\n"; ``` Pass `true` to stream output in real time: ```php $result = $test->run(true); ``` Returns a `TestResult` object. See the [Results](/docs/Result) page for available metrics. ### Cloud Execution Run tests on VoltTest's managed cloud infrastructure instead of locally: ```php $test->cloud('vt_your_api_key'); $test->run(); ``` In cloud mode, the run ID, dashboard URL, and status are printed automatically. `run()` also returns a `CloudRun` object for programmatic access. See the [Cloud Mode](/docs/cloud-mode) page for the full guide — setup, configuration, region distribution, error handling, and more. ## Complete Example ```php setVirtualUsers(50); $test->setDuration('2m'); $test->setRampUp('10s'); $scenario = $test->scenario('CRUD Operations'); $scenario->step('Create User') ->post('https://api.example.com/users', '{"name": "John"}') ->header('Content-Type', 'application/json') ->validateStatus('created', 201) ->extractFromJson('user_id', 'data.id'); $scenario->step('Get User') ->get('https://api.example.com/users/${user_id}') ->header('Accept', 'application/json') ->validateStatus('success', 200); $result = $test->run(); printf("Duration: %s\n", $result->getDuration()); printf("Success Rate: %.2f%%\n", $result->getSuccessRate()); printf("Requests/sec: %.2f\n", $result->getRequestsPerSecond()); printf("P95: %s\n", $result->getP95ResponseTime()); ``` ## Configuration Reference | Method | Description | Default | |--------|-------------|---------| | setVirtualUsers(int) | Number of concurrent VUs | 1 | | setDuration(string) | Total test duration | None | | setRampUp(string) | Ramp-up time for VUs | None | | stage(string, int) | Add a staged load profile step | None | | setHttpTimeout(string) | Per-request timeout | 30s | | setHttpDebug(bool) | Enable HTTP debug output | false | | setTarget(string) | Connection idle timeout | 30s | | cloud(string) | Enable cloud execution with API key | None | | setCloudTimeout(int) | Cloud execution timeout in seconds | 1800 | | regions(array) | Region distribution for cloud runs | None | --- ## Cloud Mode VoltTest Cloud lets you run load tests on managed infrastructure instead of your local machine. Tests scale to thousands of concurrent virtual users across multiple regions, with results stored in the VoltTest dashboard. ## Setup ### Prerequisites - VoltTest PHP SDK installed ([Installation Guide](/docs/installation)) - A VoltTest account ([Register](https://volt-test.com/register)) ### Get Your API Key 1. Log in to [volt-test.com](https://volt-test.com) 2. Go to **Settings** 3. Copy your API key (starts with `vt_`) ### Enable Cloud Mode ```php $test = new VoltTest('My Load Test'); $test->cloud('vt_your_api_key'); ``` That's it — when you call `run()`, the test will execute on VoltTest Cloud instead of locally. ## Basic Usage ```php cloud('vt_your_api_key'); $test->setVirtualUsers(500); $test->setDuration('5m'); $scenario = $test->scenario('Checkout Flow'); $scenario->step('Login') ->post('https://api.example.com/login', '{"email": "user@test.com", "password": "secret"}') ->header('Content-Type', 'application/json') ->extractFromJson('token', 'data.token'); $scenario->step('Add to Cart') ->post('https://api.example.com/cart', '{"product_id": 1}') ->header('Authorization', 'Bearer ${token}') ->validateStatus('success', 200); $test->run(); ``` In cloud mode, `run()` submits the test to VoltTest Cloud. The run ID, dashboard URL, and status are automatically printed to the terminal — no need to echo them manually. `run()` also returns a `CloudRun` object for programmatic access (useful in CI/CD or automation). ## CloudRun API The `CloudRun` object returned by `run()` provides programmatic access to the run details: | Method | Return Type | Description | |--------|-------------|-------------| | `getRunId()` | string | Unique run identifier | | `getTestId()` | string | Test definition identifier | | `getStatus()` | string | Run status | | `getDashboardUrl()` | string | URL to view results in the dashboard | | `isSuccessful()` | bool | `true` if status is `completed` | ### Status Values | Status | Description | |--------|-------------| | `completed` | Test finished successfully | | `running` | Test is currently executing | | `failed` | Test encountered an error | | `stopped` | Test was manually stopped | ## Configuration Options ### Cloud Timeout Set the maximum time to wait for the cloud run to be provisioned and started (default: 30 minutes): ```php $test->cloud('vt_your_api_key'); $test->setCloudTimeout(3600); // 1 hour ``` The minimum timeout is 60 seconds. ### Staged Load Profiles Stages work in cloud mode the same as locally: ```php $test->cloud('vt_your_api_key'); $test->stage('1m', 100); // Ramp to 100 VUs over 1 minute $test->stage('5m', 100); // Hold at 100 VUs for 5 minutes $test->stage('2m', 500); // Ramp to 500 VUs over 2 minutes $test->stage('10m', 500); // Hold at 500 VUs for 10 minutes $test->stage('1m', 0); // Ramp down to 0 ``` ### Region Distribution Distribute load across multiple geographic regions: ```php $test->cloud('vt_your_api_key'); $test->setVirtualUsers(1000); $test->regions([ 'us-east-1' => 60, // 600 VUs in US East 'eu-west-1' => 40, // 400 VUs in EU West ]); ``` - Weights must be integers greater than 0 - Weights must sum to exactly 100 - Requires cloud mode (`cloud()` must be called first) ### Available Regions #### North America | Code | Location | |------|----------| | `us-east-1` | US East (N. Virginia) | | `us-east-2` | US East (Ohio) | | `us-west-1` | US West (N. California) | | `us-west-2` | US West (Oregon) | | `ca-central-1` | Canada (Central) | #### South America | Code | Location | |------|----------| | `sa-east-1` | South America (São Paulo) | #### Europe | Code | Location | |------|----------| | `eu-west-1` | Europe (Ireland) | | `eu-west-2` | Europe (London) | | `eu-west-3` | Europe (Paris) | | `eu-central-1` | Europe (Frankfurt) | | `eu-north-1` | Europe (Stockholm) | #### Asia Pacific | Code | Location | |------|----------| | `ap-southeast-1` | Asia Pacific (Singapore) | | `ap-southeast-2` | Asia Pacific (Sydney) | | `ap-southeast-3` | Asia Pacific (Jakarta) | | `ap-southeast-4` | Asia Pacific (Melbourne) | | `ap-southeast-5` | Asia Pacific (Malaysia) | | `ap-south-1` | Asia Pacific (Mumbai) | | `ap-south-2` | Asia Pacific (Hyderabad) | | `ap-northeast-1` | Asia Pacific (Tokyo) | | `ap-northeast-2` | Asia Pacific (Seoul) | | `ap-northeast-3` | Asia Pacific (Osaka) | | `ap-east-1` | Asia Pacific (Hong Kong) | | `ap-east-2` | Asia Pacific (Taipei) | #### Africa | Code | Location | |------|----------| | `af-south-1` | Africa (Cape Town) | :::info Region availability may change. If a region is temporarily unavailable, the API will return an error with details. ::: ## Conflict Handling When you run a test with a name that already exists in your account, VoltTest will prompt you to choose: 1. **Update** an existing test (reuse its configuration) 2. **Create new** test with the same name 3. **Cancel** the run ### Interactive Mode In a terminal (TTY), you'll see an interactive prompt: ``` 2 test(s) named 'API Load Test' already exist: [1] ID: a1b2c3d4... Target: https://api.example.com VUs: 100 Updated: 2026-05-01 [2] ID: e5f6g7h8... Target: https://api.example.com VUs: 500 Updated: 2026-05-15 [3] Create new test [4] Cancel Choice [1]: ``` ### Non-Interactive Mode In non-interactive environments (CI/CD), VoltTest defaults to updating the most recently used test. ### Custom Conflict Handler For programmatic control, use `setOnConflictPrompt()`: ```php $test->setOnConflictPrompt(function (array $existingTests) { // Return a test ID to update that test return $existingTests[0]['id']; // Return null to create a new test // return null; // Return 'cancel' to abort // return 'cancel'; }); ``` Each entry in `$existingTests` contains: | Key | Description | |-----|-------------| | `id` | Test UUID | | `name` | Test name | | `target_url` | Target URL | | `virtual_users` | Configured VUs | | `updated_at` | Last update timestamp | ## Local vs Cloud Comparison | | Local | Cloud | |--|-------|-------| | **Execution** | Your machine | VoltTest managed infrastructure | | **Result type** | `TestResult` (immediate metrics) | `CloudRun` (async, view on dashboard) | | **Max VUs** | Limited by local hardware | Thousands (plan-dependent) | | **Results storage** | Console output only | Stored in dashboard with charts | | **Real-time metrics** | CLI streaming | Live dashboard with graphs | | **Multi-region** | No | Yes, via `regions()` | | **Requires** | PHP + SDK | PHP + SDK + API key | | **Staged load** | Yes | Yes | ## Error Handling Cloud mode can throw specific exceptions for different failure scenarios: ```php use VoltTest\VoltTest; use VoltTest\Exceptions\AuthenticationException; use VoltTest\Exceptions\PlanLimitException; use VoltTest\Exceptions\CloudConnectionException; use VoltTest\Exceptions\CloudTimeoutException; use VoltTest\Exceptions\RunFailedException; $test = new VoltTest('Load Test'); $test->cloud('vt_your_api_key'); $test->setVirtualUsers(100); $test->setDuration('5m'); $scenario = $test->scenario('Test'); $scenario->step('Home')->get('https://example.com'); try { $test->run(); } catch (AuthenticationException $e) { echo "Invalid API key: " . $e->getMessage() . "\n"; } catch (PlanLimitException $e) { echo "Plan limit exceeded: " . $e->getMessage() . "\n"; } catch (CloudConnectionException $e) { echo "Connection failed: " . $e->getMessage() . "\n"; } catch (CloudTimeoutException $e) { echo "Timed out: " . $e->getMessage() . "\n"; } catch (RunFailedException $e) { echo "Run failed: " . $e->getMessage() . "\n"; } ``` ### Exception Reference | Exception | Cause | Fix | |-----------|-------|-----| | `AuthenticationException` | Invalid or expired API key | Regenerate your key at [volt-test.com/settings](https://volt-test.com/settings) | | `PlanLimitException` | VU count or duration exceeds your plan | Reduce load or upgrade your plan | | `CloudConnectionException` | Cannot reach VoltTest servers | Check your network connection | | `CloudTimeoutException` | Provisioning exceeded `cloudTimeout` | Increase timeout with `setCloudTimeout()` | | `RunFailedException` | Test execution failed or was stopped | Check target availability and test configuration | All cloud exceptions extend `CloudException`, which extends `VoltTestException`. --- ## Core Concepts and Architecture ## Introduction VoltTest is a powerful, easy-to-use performance testing SDK for PHP applications. Powered by a high-performance Golang engine running behind the scenes, it combines the ease of use of PHP with the raw power and concurrency capabilities of Go. This unique architecture enables you to create, run, and analyze performance tests with a fluent, intuitive API while leveraging Go's superior performance characteristics for the actual load generation. ## How Does VoltTest Work? VoltTest PHP SDK works as a bridge between your PHP application and the VoltTest Engine (written in Go). When you run a test: Your PHP code defines the test scenarios and configurations The SDK transforms these into a format the Go engine understands The Go engine executes the actual load testing Results are streamed back to your PHP application for analysis This architecture provides several benefits: Write tests in PHP while getting Go's performance benefits True parallel execution of virtual users Minimal resource footprint during test execution Accurate timing and metrics collection ## What Are the Core Concepts? ### Test - A test represents a complete performance testing session. It contains: - Configuration settings (virtual users, duration, etc.) - One or more scenarios - Global settings like HTTP debugging ### Scenario A scenario represents a sequence of steps that virtual users will execute. Features: - Independent flow of HTTP requests - Cookie handling - Data extraction and reuse - Custom think time - Weight-based execution distribution ### Step A step represents a single HTTP request within a scenario. Features: - HTTP method (GET, POST, etc.) - URL - Headers and body - Validation - Data extraction ### Result A result represents the outcome of a test run. It contains: - Metrics like response time, throughput, and success rate - Raw output for debugging - Error messages - min, max, med, p95, p99 - Print all the metrics --- ## Getting Started After installing the VoltTest SDK, you can start writing performance tests for your PHP applications. This guide will help you get started with the basics of VoltTest and show you how to create and run your first performance test. You can install the VoltTest SDK Here: [VoltTest SDK](/docs/installation) ## How Do I Write My First Test? Here's a minimal example to get started: ```php title="example.php" scenario('Basic Scenario'); // Add a step to the scenario $scenario->step('Register') ->get('https://google.com') ->header('Content-Type', 'text/html'); // Run the test $result = $voltTest->run(true); // Echo the result echo $result->getRawOutput(); ``` Then run the script using the following command in your terminal: ```bash title="Run the script" php example.php ``` The Output will be something like this: ```bash title="Output" Test Metrics Summary: =================== Duration: 254.804339ms Total Reqs: 1 Success Rate: 100.00% Req/sec: 3.93 Success Requests: 1 Failed Requests: 0 Response Time: ------------ Min: 252.587118ms Max: 252.587118ms Avg: 252.587118ms Median: 252.587118ms P95: 252.587118ms P99: 252.587118ms ``` This is just a simple example to get started with VoltTest. The metrics summary provides insights into the request's performance, including response time distribution and success rate. You can create more complex scenarios, simulate concurrent requests, and monitor real-time results to stress-test your application under different loads. With VoltTest, you can: - Easily define multiple steps in a scenario. - Add headers, parameters, or custom data for each request. - Measure critical performance metrics like request rates, latency percentiles, and throughput. - Use the raw output for further analysis or logging. Start writing and testing your performance scenarios today! --- ## Installation ## What Are the Requirements? - **PHP 8.0 or higher** - **Composer** - **ext-json** PHP extension - **ext-curl** PHP extension - **ext-pcntl** PHP extension (Required for Unix-like systems, not available on Windows) ⚠️ **Note:** - `pcntl` is **not available on Windows** as it only works in CLI environments on Unix-based systems. - If you're using Windows, see the [**Running on Windows**](#how-do-i-run-volttest-on-windows) section below. --- ## How Do I Install the VoltTest SDK? There are two ways to install the VoltTest SDK: 1. Install the SDK inside your project using Composer. 2. Clone the SDK repository. ### Install the SDK using Composer Run the following command inside your project directory: ```bash composer require volt-test/php-sdk ``` That's it! You have successfully installed the VoltTest SDK in your project. See the [Getting Started](/docs/getting-started) guide to create your first test script. --- ### Clone the SDK Repository If you prefer to clone the repository and install dependencies manually: 1. Clone the Repository: ```bash git clone git@github.com:volt-test/php-sdk.git ``` 2. Navigate to the project directory: ```bash cd php-sdk ``` 3. Install dependencies: ```bash composer install ``` Now, let's [get started](/docs/getting-started) by creating a simple test script. --- ## How Do I Run VoltTest on Windows? Since `pcntl` is not supported on Windows, you need to run VoltTest in a Linux environment. Here are your options: ### **Option 1: Use Windows Subsystem for Linux (WSL) (Recommended)** 1. Install WSL by running this command in **PowerShell (Admin)**: ```powershell wsl --install ``` 2. Restart your PC and open **Ubuntu** from the Start Menu. 3. Install PHP inside WSL: ```bash sudo apt update sudo apt install php-cli php-curl php-json ``` 4. Run VoltTest commands inside WSL: ```bash php your-script.php ``` ### **Option 2: Use Docker** Run VoltTest inside a **Docker container**: ```bash docker run --rm -v $(pwd):/app -w /app php:8-cli php your-script.php ``` For Alpine-based PHP: ```bash docker run --rm -v $(pwd):/app -w /app php:8-alpine sh -c "apk add php-cli php-curl php-json && php your-script.php" ``` ### **Option 3: Use a Linux Virtual Machine** - Install **VirtualBox** or **VMware**. - Set up an Ubuntu-based VM. - Install PHP and required extensions. - Run your VoltTest scripts inside the VM. --- --- ## Introduction # VoltTest PHP SDK The VoltTest PHP SDK is a PHP library that lets you write performance, load, and stress tests for any PHP application using familiar PHP syntax. It connects to a high-performance Go engine that simulates hundreds to millions of concurrent virtual users, then returns structured metrics including response times, throughput, error rates, and percentile distributions (p95, p99). You can run tests locally during development or scale to production-grade load on VoltTest Cloud. ## What Can the VoltTest PHP SDK Do? Here are some of the key features of the VoltTest PHP SDK: ### Performance Testing: - Concurrent virtual user simulation - Configurable test duration and ramp-up periods - Real-time metrics collection - Custom think time simulation ### HTTP(S) Testing: - All standard HTTP methods (GET, POST, PUT, etc.) - Header and body customization - Cookie and session management - Response validation - Variable extraction and reuse (JSON,header, cookies, regex.) ### Data Management: - CSV data source support - Multiple data iteration modes(sequential, random, unique) - Dynamic data extraction and reuse - Automatic Cookie handling ### Result Analysis: - Success/failure rates - Request throughput - Response time statistics (min, max, med, p95, p99) ### Platform Support: - Cross-platform compatibility (Windows, Linux, MacOS) - Automatic binary download and installation - PHP 8.0 or higher Let's [install the SDK](/docs/installation) and get started with writing your first test script. --- ## API Testing Test JSON APIs with authentication, token extraction, and CRUD flows. ## Authentication Flow A typical API test: log in, extract the token, use it in subsequent requests. ```php public function define(VoltTestManager $manager): void { $manager->target('http://localhost:8000'); $scenario = $manager->scenario('Authenticated API'); $scenario->step('Login') ->post('/api/login', [ 'email' => 'user@example.com', 'password' => 'password', ], ['Content-Type' => 'application/json']) ->expectStatus(200) ->extractJson('token', 'data.token'); $scenario->step('Get Profile') ->get('/api/profile') ->header('Authorization', 'Bearer ${token}') ->header('Accept', 'application/json') ->expectStatus(200); $scenario->step('Update Profile') ->put('/api/profile', [ 'name' => 'Updated Name', ], [ 'Content-Type' => 'application/json', 'Authorization' => 'Bearer ${token}', ]) ->expectStatus(200); } ``` ## CRUD Operations ```php public function define(VoltTestManager $manager): void { $manager->target('http://localhost:8000'); $scenario = $manager->scenario('User CRUD'); $scenario->step('Create User') ->post('/api/users', [ 'name' => 'John Doe', 'email' => 'john@example.com', 'role' => 'editor', ], ['Content-Type' => 'application/json']) ->expectStatus(201) ->extractJson('user_id', 'data.id'); $scenario->step('Read User') ->get('/api/users/${user_id}') ->header('Accept', 'application/json') ->expectStatus(200); $scenario->step('Update User') ->put('/api/users/${user_id}', [ 'name' => 'Jane Doe', ], ['Content-Type' => 'application/json']) ->expectStatus(200); $scenario->step('Delete User') ->delete('/api/users/${user_id}') ->expectStatus(204); } ``` ## Data Extraction ### Extract from JSON Use dot notation to extract values from JSON responses: ```php ->extractJson('token', 'data.token') ->extractJson('user_id', 'data.id') ->extractJson('first_item', 'data.items[0].name') ``` ### Extract from Headers ```php ->extractHeader('request_id', 'X-Request-Id') ->extractHeader('rate_limit', 'X-RateLimit-Remaining') ``` ### Using Extracted Variables Reference extracted values with `${variable_name}`: ```php $scenario->step('Login') ->post('/api/login', [...]) ->extractJson('token', 'data.token'); $scenario->step('Protected Endpoint') ->get('/api/protected') ->header('Authorization', 'Bearer ${token}'); ``` ## Status Validation Validate expected HTTP status codes: ```php ->expectStatus(200) // OK ->expectStatus(201) // Created ->expectStatus(204) // No Content ->expectStatus(302) // Redirect ->expectStatus(422) // Validation Error ``` You can provide a custom name for the validation: ```php ->expectStatus(200, 'profile_loaded') ``` If the status doesn't match, it's recorded as a failed request in the test report. --- ## Performance Assertions Assert performance thresholds in your PHPUnit tests. All assertions are available on `PerformanceTestCase` via the `VoltTestAssertions` trait. ## Success Rate ### assertVTSuccessful Assert that the success rate meets a minimum threshold. ```php $this->assertVTSuccessful($result); // Default: 95% $this->assertVTSuccessful($result, 99.0); // Custom: 99% ``` ### assertVTErrorRate Assert that the error rate stays below a maximum threshold. ```php $this->assertVTErrorRate($result, 5.0); // Max 5% errors $this->assertVTErrorRate($result, 1.0); // Max 1% errors ``` ## Response Times All response time assertions use **milliseconds**. ### assertVTMaxResponseTime Assert that the maximum (slowest) response time doesn't exceed the threshold. ```php $this->assertVTMaxResponseTime($result, 5000); // Max 5 seconds ``` ### assertVTAverageResponseTime ```php $this->assertVTAverageResponseTime($result, 500); // Avg under 500ms ``` ### assertVTMedianResponseTime ```php $this->assertVTMedianResponseTime($result, 300); // P50 under 300ms ``` ### assertVTP95ResponseTime ```php $this->assertVTP95ResponseTime($result, 1000); // P95 under 1 second ``` ### assertVTP99ResponseTime ```php $this->assertVTP99ResponseTime($result, 2000); // P99 under 2 seconds ``` ### assertVTMinResponseTime Assert that the minimum (fastest) response time is at least a given value. Useful for detecting suspiciously fast responses (e.g., cached/mocked responses when you expect real ones). ```php $this->assertVTMinResponseTime($result, 10); // Min at least 10ms ``` ## Throughput ### assertVTMinimumRequests Assert that the total number of requests meets a minimum. ```php $this->assertVTMinimumRequests($result, 1000); // At least 1000 total requests ``` ### assertVTMinimumRPS Assert a minimum requests-per-second throughput. ```php $this->assertVTMinimumRPS($result, 50); // At least 50 req/s ``` ### assertVTMaximumRPS Assert a maximum requests-per-second. Useful for rate-limited endpoints. ```php $this->assertVTMaximumRPS($result, 100); // No more than 100 req/s ``` ## Complete Example ```php runVoltTest(new ApiTest(), [ 'virtual_users' => 50, 'duration' => '2m', ]); // SLA: 99% success rate $this->assertVTSuccessful($result, 99.0); // SLA: P95 under 500ms $this->assertVTP95ResponseTime($result, 500); // SLA: P99 under 2 seconds $this->assertVTP99ResponseTime($result, 2000); // SLA: At least 100 requests per second $this->assertVTMinimumRPS($result, 100); // No suspiciously fast responses $this->assertVTMinResponseTime($result, 5); } } ``` ## Assertion Reference | Assertion | Parameter | Description | |-----------|-----------|-------------| | `assertVTSuccessful($result, $min)` | float (default: 95.0) | Success rate ≥ threshold | | `assertVTErrorRate($result, $max)` | float | Error rate ≤ threshold | | `assertVTMinResponseTime($result, $ms)` | int (ms) | Min response time ≥ threshold | | `assertVTMaxResponseTime($result, $ms)` | int (ms) | Max response time ≤ threshold | | `assertVTAverageResponseTime($result, $ms)` | int (ms) | Average ≤ threshold | | `assertVTMedianResponseTime($result, $ms)` | int (ms) | Median (P50) ≤ threshold | | `assertVTP95ResponseTime($result, $ms)` | int (ms) | P95 ≤ threshold | | `assertVTP99ResponseTime($result, $ms)` | int (ms) | P99 ≤ threshold | | `assertVTMinimumRequests($result, $n)` | int | Total requests ≥ threshold | | `assertVTMinimumRPS($result, $rps)` | float | Requests/sec ≥ threshold | | `assertVTMaximumRPS($result, $rps)` | float | Requests/sec ≤ threshold | --- ## Artisan Commands The Laravel package provides two Artisan commands for creating and running performance tests. ## volttest:make Generate a new test class. ```bash php artisan volttest:make {name} [options] ``` ### Arguments | Argument | Description | |----------|-------------| | `name` | Test class name (e.g., `LoginTest`, `CheckoutFlowTest`) | ### Options | Option | Description | |--------|-------------| | `--routes` | Scaffold test from your Laravel routes | | `--filter=` | Filter routes by URI pattern (e.g., `api/*`, `admin/*`) | | `--method=` | Filter routes by HTTP method (`GET`, `POST`, etc.) | | `--auth` | Only include routes with auth middleware | | `--select` | Interactively select which routes to include | ### Examples Basic test: ```bash php artisan volttest:make LoginTest ``` From API routes: ```bash php artisan volttest:make ApiTest --routes --filter=api/* ``` Only authenticated POST routes: ```bash php artisan volttest:make FormTest --routes --method=POST --auth ``` Interactive route selection: ```bash php artisan volttest:make CustomTest --routes --select ``` Generated tests are placed in `app/VoltTests/` by default (configurable via `test_paths` in config). --- ## volttest:run Execute a performance test. ```bash php artisan volttest:run [test] [options] ``` ### Arguments | Argument | Description | |----------|-------------| | `test` | Test class name or URL (optional — runs all tests if omitted) | ### Load Options | Option | Description | Default | |--------|-------------|---------| | `--users=` | Number of virtual users | Config value (10) | | `--duration=` | Test duration (e.g., `30s`, `2m`, `1h`) | Config value | | `--stage=*` | Staged load profile as `duration:target` (repeatable) | None | | `--stream` | Stream output to console in real time | false | | `--debug` | Enable HTTP debug output | false | ### URL Testing Options | Option | Description | Default | |--------|-------------|---------| | `--url` | Treat `test` argument as a URL | false | | `--method=` | HTTP method for URL test | GET | | `--headers=` | JSON string of headers | None | | `--body=` | Request body | None | | `--content-type=` | Content-Type header | None | | `--code-status=` | Expected HTTP status code | 200 | | `--scenario-name=` | Custom scenario name | None | ### Cloud Options | Option | Description | Default | |--------|-------------|---------| | `--target=` | Target base URL (overrides config `base_url`) | None | | `--cloud` | Run on VoltTest Cloud | false | | `--region=*` | Region distribution as `region:weight` (repeatable) | None | ### Examples Run a specific test: ```bash php artisan volttest:run LoginTest ``` Run with custom load: ```bash php artisan volttest:run LoginTest --users=100 --duration=5m --stream ``` Staged load profile: ```bash php artisan volttest:run LoginTest --stage=1m:50 --stage=5m:100 --stage=1m:0 ``` Direct URL test: ```bash php artisan volttest:run https://api.example.com/health --url --users=50 --duration=1m ``` URL test with POST and headers: ```bash php artisan volttest:run https://api.example.com/login \ --url \ --method=POST \ --body='{"email":"test@example.com","password":"secret"}' \ --content-type=application/json \ --code-status=200 \ --users=20 ``` Cloud execution with regions: ```bash php artisan volttest:run LoginTest --cloud --region=us-east-1:60 --region=eu-west-1:40 ``` Run all tests in the default path: ```bash php artisan volttest:run ``` Search a custom path: ```bash php artisan volttest:run --path=tests/Performance ``` --- ## Cloud Execution Run Laravel performance tests on VoltTest Cloud instead of your local machine. ## Setup Add your API key to `.env`: ```env VOLTTEST_API_KEY=vt_your_api_key ``` ## Three Ways to Enable Cloud Mode ### 1. CLI Flag ```bash php artisan volttest:run LoginTest --cloud ``` ### 2. Config File In `.env`: ```env VOLTTEST_CLOUD_ENABLED=true ``` Or in `config/volttest.php`: ```php 'cloud' => [ 'enabled' => true, 'api_key' => env('VOLTTEST_API_KEY'), ], ``` ### 3. Facade / Programmatic ```php use VoltTest\Laravel\Facades\VoltTest; VoltTest::target('https://api.example.com'); VoltTest::cloud(); $result = VoltTest::run(); ``` ## Staged Load via CLI ```bash php artisan volttest:run LoginTest --cloud --stage=1m:50 --stage=5m:100 --stage=1m:0 ``` Format: `--stage=duration:target` (repeatable). ## Region Distribution ### Via CLI ```bash php artisan volttest:run LoginTest --cloud --region=us-east-1:60 --region=eu-west-1:40 ``` Format: `--region=region_code:weight` (repeatable, weights must sum to 100). ### Via Config In `config/volttest.php`: ```php 'regions' => [ 'us-east-1' => 60, 'eu-west-1' => 40, ], ``` ### Via Code ```php use VoltTest\Laravel\Facades\VoltTest; VoltTest::target('https://api.example.com'); VoltTest::cloud(); VoltTest::regions([ 'us-east-1' => 60, 'eu-west-1' => 40, ]); $result = VoltTest::run(); ``` ### Available Regions #### North America | Code | Location | |------|----------| | `us-east-1` | US East (N. Virginia) | | `us-east-2` | US East (Ohio) | | `us-west-1` | US West (N. California) | | `us-west-2` | US West (Oregon) | | `ca-central-1` | Canada (Central) | #### South America | Code | Location | |------|----------| | `sa-east-1` | South America (São Paulo) | #### Europe | Code | Location | |------|----------| | `eu-west-1` | Europe (Ireland) | | `eu-west-2` | Europe (London) | | `eu-west-3` | Europe (Paris) | | `eu-central-1` | Europe (Frankfurt) | | `eu-north-1` | Europe (Stockholm) | #### Asia Pacific | Code | Location | |------|----------| | `ap-southeast-1` | Asia Pacific (Singapore) | | `ap-southeast-2` | Asia Pacific (Sydney) | | `ap-southeast-3` | Asia Pacific (Jakarta) | | `ap-southeast-4` | Asia Pacific (Melbourne) | | `ap-southeast-5` | Asia Pacific (Malaysia) | | `ap-south-1` | Asia Pacific (Mumbai) | | `ap-south-2` | Asia Pacific (Hyderabad) | | `ap-northeast-1` | Asia Pacific (Tokyo) | | `ap-northeast-2` | Asia Pacific (Seoul) | | `ap-northeast-3` | Asia Pacific (Osaka) | | `ap-east-1` | Asia Pacific (Hong Kong) | | `ap-east-2` | Asia Pacific (Taipei) | #### Africa | Code | Location | |------|----------| | `af-south-1` | Africa (Cape Town) | :::info Region availability may change. If a region is temporarily unavailable, the API will return an error with details. ::: ## CloudRun Result In cloud mode, `run()` returns a `CloudRun` object: ```php VoltTest::cloud()->run(); ``` The run ID, dashboard URL, and status are printed automatically. `run()` also returns a `CloudRun` object for programmatic access (useful in CI/CD). ## Conflict Handling When a test with the same name already exists, VoltTest prompts interactively (in a terminal) or defaults to updating the most recent test (in non-interactive environments like CI). For custom logic: ```php VoltTest::setOnConflictPrompt(function (array $existingTests) { return $existingTests[0]['id']; // Update most recent }); ``` ## PHPUnit + Cloud ```php class CloudPerformanceTest extends PerformanceTestCase { public function test_cloud_checkout(): void { $result = $this->runVoltTest(new CheckoutTest(), [ 'virtual_users' => 500, 'duration' => '10m', ]); // CloudRun returned — check dashboard for detailed metrics $this->assertTrue($result->isSuccessful()); } } ``` :::info When running in cloud mode, `assertVT*` response time assertions are not available since `CloudRun` doesn't contain local metrics. Use the dashboard for detailed analysis. ::: ## Error Handling Cloud-specific exceptions are thrown when issues occur. See the [Cloud Mode](/docs/cloud-mode#error-handling) page for the full exception reference. ```php use VoltTest\Exceptions\AuthenticationException; use VoltTest\Exceptions\PlanLimitException; try { $result = VoltTest::cloud()->run(); } catch (AuthenticationException $e) { // Invalid API key } catch (PlanLimitException $e) { // VU or duration exceeds plan } ``` --- ## Configuration Reference All settings are in `config/volttest.php`. Publish with: ```bash php artisan vendor:publish --tag=volttest-config ``` ## Test Configuration | Key | Env Var | Default | Description | |-----|---------|---------|-------------| | `name` | `VOLTTEST_NAME` | `Laravel Application Test` | Default test name | | `description` | `VOLTTEST_DESCRIPTION` | `Performance test for Laravel application` | Default description | ## Load Configuration | Key | Env Var | Default | Description | |-----|---------|---------|-------------| | `virtual_users` | `VOLTTEST_VIRTUAL_USERS` | `10` | Number of concurrent virtual users | | `duration` | `VOLTTEST_DURATION` | `null` | Test duration (e.g., `30s`, `2m`, `1h`) | | `ramp_up` | `VOLTTEST_RAMP_UP` | `null` | Ramp-up time for VUs | ## Stages ```php 'stages' => [ ['duration' => '1m', 'target' => 50], ['duration' => '5m', 'target' => 100], ['duration' => '1m', 'target' => 0], ], ``` When stages are set, `virtual_users`, `duration`, and `ramp_up` are ignored. ## Region Distribution ```php 'regions' => [ 'us-east-1' => 60, 'eu-west-1' => 40, ], ``` Weights must sum to 100. Leave empty for single-region default. Requires cloud mode. ## Debug | Key | Env Var | Default | Description | |-----|---------|---------|-------------| | `http_debug` | `VOLTTEST_HTTP_DEBUG` | `false` | Enable HTTP debug output | ## Paths | Key | Default | Description | |-----|---------|-------------| | `test_paths` | `app_path('VoltTests')` | Directory for test classes | | `reports_path` | `storage_path('volttest/reports')` | Directory for saved reports | ## Reports | Key | Env Var | Default | Description | |-----|---------|---------|-------------| | `save_reports` | `VOLTTEST_SAVE_REPORTS` | `true` | Save test reports to disk | ## Base URL | Key | Env Var | Default | Description | |-----|---------|---------|-------------| | `use_base_url` | `VOLTTEST_USE_BASE_URL` | `true` | Prefix relative paths with base URL | | `base_url` | `VOLTTEST_BASE_URL` | `http://localhost:8000` | Base URL for your application | When `use_base_url` is `true`, relative paths like `/api/users` become `http://localhost:8000/api/users`. Full URLs are used as-is. ## CSV Data Source ```php 'csv_data' => [ 'path' => storage_path('volttest/data'), 'validate_files' => true, 'default_distribution' => 'unique', 'default_headers' => true, ], ``` | Key | Default | Description | |-----|---------|-------------| | `csv_data.path` | `storage/volttest/data` | Default directory for CSV files | | `csv_data.validate_files` | `true` | Check CSV files exist before running | | `csv_data.default_distribution` | `unique` | Default distribution mode (`unique`, `random`, `sequential`) | | `csv_data.default_headers` | `true` | Whether CSV files have a header row | ## Cloud | Key | Env Var | Default | Description | |-----|---------|---------|-------------| | `cloud.enabled` | `VOLTTEST_CLOUD_ENABLED` | `false` | Enable cloud execution by default | | `cloud.api_key` | `VOLTTEST_API_KEY` | `null` | VoltTest API key (starts with `vt_`) | ## Minimal .env Example ```env VOLTTEST_VIRTUAL_USERS=20 VOLTTEST_DURATION=1m VOLTTEST_BASE_URL=http://localhost:8000 VOLTTEST_SAVE_REPORTS=true ``` ## Cloud .env Example ```env VOLTTEST_API_KEY=vt_your_api_key VOLTTEST_CLOUD_ENABLED=true VOLTTEST_VIRTUAL_USERS=500 VOLTTEST_DURATION=10m ``` --- ## Creating Tests ## Test Structure Every VoltTest test class implements `VoltTestCase` and defines scenarios in the `define()` method: ```php target('http://localhost:8000'); $scenario = $manager->scenario('Checkout Flow'); $scenario->step('Browse Products') ->get('/products') ->expectStatus(200); $scenario->step('Add to Cart') ->post('/cart', ['product_id' => 1, 'quantity' => 2], [ 'Content-Type' => 'application/json', ]) ->expectStatus(200); } } ``` ## Scenarios Create scenarios through the `$manager->scenario()` method. Each scenario represents a user flow. ### Multiple Scenarios Use weights to control how virtual users are distributed across scenarios: ```php public function define(VoltTestManager $manager): void { $manager->target('http://localhost:8000'); $browse = $manager->scenario('Browse Only'); $browse->weight(70); $browse->step('Home')->get('/')->expectStatus(200); $browse->step('Products')->get('/products')->expectStatus(200); $purchase = $manager->scenario('Purchase Flow'); $purchase->weight(30); $purchase->step('Home')->get('/')->expectStatus(200); $purchase->step('Checkout')->post('/checkout', [...])->expectStatus(200); } ``` 70% of virtual users will browse, 30% will purchase. ### Think Time Add delays between steps to simulate real user behavior: ```php $scenario->step('View Product') ->get('/products/1') ->expectStatus(200) ->thinkTime('2s'); $scenario->step('Add to Cart') ->post('/cart', ['product_id' => 1]) ->expectStatus(200); ``` ## Steps Each step represents an HTTP request. Create steps with `step()` then chain the HTTP method: ```php $scenario->step('Step Name') ->get('/path') ->expectStatus(200); ``` ### HTTP Methods ```php $scenario->step('Get')->get('/users'); $scenario->step('Create')->post('/users', $data, $headers); $scenario->step('Update')->put('/users/1', $data, $headers); $scenario->step('Patch')->patch('/users/1', $data, $headers); $scenario->step('Delete')->delete('/users/1'); ``` ### Headers Add headers inline or with the `header()` method: ```php // Inline headers (second/third parameter) $scenario->step('Create') ->post('/api/users', ['name' => 'John'], [ 'Content-Type' => 'application/json', 'Accept' => 'application/json', ]); // Chained headers $scenario->step('Get Profile') ->get('/api/profile') ->header('Authorization', 'Bearer ${token}') ->header('Accept', 'application/json'); ``` ### Auto-JSON Encoding When you pass an array as the request body with a JSON `Content-Type` header, the package automatically encodes it to JSON: ```php $scenario->step('Create User') ->post('/api/users', ['name' => 'John', 'email' => 'john@example.com'], [ 'Content-Type' => 'application/json', ]); ``` ### Base URL By default, relative paths (e.g., `/login`) are prefixed with the configured `base_url` (`http://localhost:8000`). Full URLs are used as-is. ```php $scenario->step('Local')->get('/api/health'); // → http://localhost:8000/api/health $scenario->step('External')->get('https://api.example.com/health'); // → https://api.example.com/health ``` Configure in `.env`: ```env VOLTTEST_BASE_URL=http://localhost:8000 ``` ## Route Discovery Generate test scaffolds from your existing Laravel routes: ```bash php artisan volttest:make ApiTest --routes --filter=api/* ``` This creates a test class with steps for each matching route: ```php class ApiTest implements VoltTestCase { public function define(VoltTestManager $manager): void { $manager->target('http://localhost:8000'); $scenario = $manager->scenario('API Routes'); $scenario->step('GET /api/users') ->get('/api/users') ->expectStatus(200); $scenario->step('POST /api/users') ->post('/api/users') ->expectStatus(200); // ... more routes } } ``` ### Filtering Routes ```bash # By URI pattern php artisan volttest:make Test --routes --filter=api/v1/* # By HTTP method php artisan volttest:make Test --routes --method=GET # Only authenticated routes php artisan volttest:make Test --routes --auth # Interactive selection php artisan volttest:make Test --routes --select ``` ## Cookie Handling The Laravel package automatically enables cookie handling for all scenarios. This means Laravel session cookies, CSRF tokens, and other cookies are preserved across steps — matching how a real browser works. --- ## Data-Driven Testing Load test data from CSV files so each virtual user gets different data. ## Basic Setup 1. Place your CSV file in `storage/volttest/data/` (configurable): ```csv email,password,name alice@test.com,secret123,Alice bob@test.com,secret456,Bob charlie@test.com,secret789,Charlie ``` 2. Reference it in your scenario: ```php public function define(VoltTestManager $manager): void { $manager->target('http://localhost:8000'); $scenario = $manager->scenario('Registration with CSV Data'); $scenario->dataSource('users.csv'); $scenario->step('Register') ->post('/register', 'name=${name}&email=${email}&password=${password}') ->header('Content-Type', 'application/x-www-form-urlencoded') ->expectStatus(302); } ``` Each virtual user gets a row from the CSV. Column headers become variable names accessible via `${column_name}`. ## Distribution Modes Control how CSV rows are assigned to virtual users: ```php $scenario->dataSource('users.csv', 'unique'); // Each row used once (default) $scenario->dataSource('users.csv', 'sequential'); // Rows in order, cycling when exhausted $scenario->dataSource('users.csv', 'random'); // Random row selection ``` | Mode | Behavior | |------|----------| | `unique` | Each row assigned to one VU only. Fails if more VUs than rows. | | `sequential` | Rows assigned in order; wraps around when exhausted. | | `random` | Random row per VU on each iteration. | ## File Location By default, CSV files are resolved relative to `storage/volttest/data/`. You can also use absolute paths: ```php // Relative to configured path $scenario->dataSource('users.csv'); // Absolute path $scenario->dataSource('/path/to/custom/data.csv'); ``` ### Configure Default Path In `config/volttest.php`: ```php 'csv_data' => [ 'path' => storage_path('volttest/data'), 'validate_files' => true, 'default_distribution' => 'unique', 'default_headers' => true, ], ``` ## Headers If your CSV has a header row (default: `true`), the first row defines column names. Disable if your CSV has no headers: ```php $scenario->dataSource('data.csv', 'sequential', false); ``` ## File Validation By default, the package validates that CSV files exist before running. Disable in config if needed: ```php 'csv_data' => [ 'validate_files' => false, ], ``` ## Example: API Testing with CSV ```php public function define(VoltTestManager $manager): void { $manager->target('http://localhost:8000'); $scenario = $manager->scenario('API with Test Data'); $scenario->dataSource('api_users.csv', 'random'); $scenario->step('Login') ->post('/api/login', [ 'email' => '${email}', 'password' => '${password}', ], ['Content-Type' => 'application/json']) ->expectStatus(200) ->extractJson('token', 'data.token'); $scenario->step('Get Profile') ->get('/api/profile') ->header('Authorization', 'Bearer ${token}') ->expectStatus(200); } ``` CSV file (`storage/volttest/data/api_users.csv`): ```csv email,password admin@test.com,admin123 editor@test.com,editor456 viewer@test.com,viewer789 ``` --- ## Installation(Laravel) ## Requirements - **PHP 8.2** or higher - **Laravel 11, 12, or 13** - **ext-pcntl** PHP extension (Unix-like systems only) - **Composer** ## Install the Package ```bash composer require volt-test/laravel-performance-testing ``` The package uses Laravel's auto-discovery, so the service provider and facade are registered automatically. ## Publish Configuration ```bash php artisan vendor:publish --tag=volttest-config ``` This creates `config/volttest.php` with all default settings. ## Environment Variables Add these to your `.env` file as needed: ```env # Test defaults VOLTTEST_VIRTUAL_USERS=10 VOLTTEST_DURATION=1m VOLTTEST_RAMP_UP=10s # Base URL (defaults to http://localhost:8000) VOLTTEST_BASE_URL=http://localhost:8000 # Cloud execution (optional) VOLTTEST_API_KEY=vt_your_api_key VOLTTEST_CLOUD_ENABLED=false # Debug VOLTTEST_HTTP_DEBUG=false # Reports VOLTTEST_SAVE_REPORTS=true ``` ## Verify Installation Run the following to confirm the package is installed: ```bash php artisan volttest:make --help ``` You should see the command's help output with available options. ## Next Steps - [Quick Start](/docs/laravel/laravel-quick-start) — Create and run your first test - [Configuration Reference](/docs/laravel/laravel-configuration) — All config options explained --- ## PHPUnit Integration Run performance tests inside your PHPUnit test suite with automatic server management and performance assertions. ## Setup Extend `PerformanceTestCase` instead of the standard Laravel `TestCase`: ```php runVoltTest(new LoginTest(), [ 'virtual_users' => 10, 'duration' => '30s', ]); $this->assertVTSuccessful($result); $this->assertVTMaxResponseTime($result, 2000); } } ``` ## Running Tests ### Execute a VoltTestCase ```php $result = $this->runVoltTest(new CheckoutTest(), [ 'virtual_users' => 20, 'duration' => '1m', 'ramp_up' => '10s', ]); ``` ### Options | Key | Type | Default | Description | |-----|------|---------|-------------| | `virtual_users` | int | 5 | Number of concurrent VUs | | `duration` | string | null | Test duration (e.g., `30s`, `2m`) | | `ramp_up` | string | null | Ramp-up time | | `stages` | array | null | Staged load profile (overrides VUs/duration) | | `stream` | bool | false | Stream output to console | | `http_debug` | bool | false | Enable HTTP debug output | ### Staged Load in PHPUnit ```php $result = $this->runVoltTest(new ApiTest(), [ 'stages' => [ ['duration' => '30s', 'target' => 10], ['duration' => '2m', 'target' => 10], ['duration' => '30s', 'target' => 0], ], ]); ``` ## Quick Helpers ### Load Test a URL ```php $result = $this->loadTestUrl('http://localhost:8000/api/health', [ 'virtual_users' => 50, 'duration' => '30s', ]); $this->assertVTSuccessful($result); $this->assertVTP95ResponseTime($result, 500); ``` ### Load Test an API Endpoint ```php $result = $this->loadTestApi('/api/users', 'GET', [], [ 'virtual_users' => 20, 'duration' => '1m', ]); $this->assertVTMinimumRPS($result, 100); ``` ```php $result = $this->loadTestApi('/api/users', 'POST', [ 'name' => 'John', 'email' => 'john@example.com', ], [ 'virtual_users' => 10, ]); ``` ## Server Management `PerformanceTestCase` can automatically start and stop `php artisan serve` for your tests. ### Enable Auto Server ```php class ApiPerformanceTest extends PerformanceTestCase { protected static bool $enableServerManagement = true; protected static ?int $preferredPort = 8000; } ``` The server starts in `setUpBeforeClass()` and stops in `tearDownAfterClass()`. If the preferred port is busy, an available port is found automatically. ### Custom Base URL ```php class ExternalApiTest extends PerformanceTestCase { protected function setUp(): void { parent::setUp(); $this->setBaseUrl('https://staging.example.com'); } } ``` ### Debug Server ```php $this->debugServer(); // Print server info $stats = $this->getServerStats(); // Get registry statistics ``` ## Complete Example ```php runVoltTest(new CheckoutTest(), [ 'virtual_users' => 50, 'duration' => '2m', 'ramp_up' => '15s', ]); $this->assertVTSuccessful($result, 95.0); $this->assertVTAverageResponseTime($result, 500); $this->assertVTP95ResponseTime($result, 1500); $this->assertVTMinimumRPS($result, 20); } public function test_health_endpoint(): void { $result = $this->loadTestUrl($this->getBaseUrl() . '/api/health', [ 'virtual_users' => 100, 'duration' => '30s', ]); $this->assertVTSuccessful($result, 99.0); $this->assertVTMaxResponseTime($result, 1000); } } ``` Run with PHPUnit: ```bash php artisan test --filter=CheckoutPerformanceTest # or vendor/bin/phpunit tests/Performance/CheckoutPerformanceTest.php ``` --- ## Quick Start Get your first performance test running in under 5 minutes. ## 1. Generate a Test ```bash php artisan volttest:make LoginTest ``` This creates `app/VoltTests/LoginTest.php`: ```php target('http://localhost:8000'); $scenario = $manager->scenario('Login Flow'); $scenario->step('Visit Login Page') ->get('/login') ->expectStatus(200); } } ``` ## 2. Define Your Scenario Edit the test to add a realistic flow: ```php public function define(VoltTestManager $manager): void { $manager->target('http://localhost:8000'); $scenario = $manager->scenario('Login Flow'); $scenario->step('Get Login Page') ->get('/login') ->expectStatus(200) ->extractCsrfToken(); $scenario->step('Submit Login') ->post('/login', [ '_token' => '${csrf_token}', 'email' => 'user@example.com', 'password' => 'password', ]) ->expectStatus(302); $scenario->step('View Dashboard') ->get('/dashboard') ->expectStatus(200); } ``` ## 3. Run the Test ```bash php artisan volttest:run LoginTest --users=10 --duration=30s ``` ## 4. Read the Results ``` Test Metrics Summary: =================== Duration: 30.05s Total Reqs: 1,250 Success Rate: 98.40% Req/sec: 41.58 Response Time: ------------ Min: 12.3ms Max: 892.1ms Avg: 145.7ms Median: 102.4ms P95: 456.2ms P99: 721.8ms ``` ## Quick URL Test Test any URL without creating a test class: ```bash php artisan volttest:run https://api.example.com/health --url --users=50 --duration=1m ``` ## Next Steps - [Creating Tests](/docs/laravel/laravel-creating-tests) — Test structure and scenarios in depth - [CLI Commands](/docs/laravel/laravel-cli-commands) — All command options - [API Testing](/docs/laravel/laravel-api-testing) — Test JSON APIs with token extraction --- ## Web Testing Test HTML forms, CSRF-protected pages, and multi-step web flows. ## CSRF Token Extraction Laravel forms require a CSRF token. Use the built-in `extractCsrfToken()` helper: ```php public function define(VoltTestManager $manager): void { $manager->target('http://localhost:8000'); $scenario = $manager->scenario('Login Form'); $scenario->step('Get Login Page') ->get('/login') ->expectStatus(200) ->extractCsrfToken(); $scenario->step('Submit Login') ->post('/login', '_token=${csrf_token}&email=user@example.com&password=secret') ->header('Content-Type', 'application/x-www-form-urlencoded') ->expectStatus(302); } ``` `extractCsrfToken()` extracts the value from `input[name=_token]` and stores it as `${csrf_token}`. You can customize the variable name, selector, and attribute: ```php ->extractCsrfToken('my_token', 'meta[name=csrf-token]', 'content') ``` ## Registration Form ```php public function define(VoltTestManager $manager): void { $manager->target('http://localhost:8000'); $scenario = $manager->scenario('Registration'); $scenario->step('Get Register Page') ->get('/register') ->expectStatus(200) ->extractCsrfToken(); $scenario->step('Submit Registration') ->post('/register', '_token=${csrf_token}&name=John&email=john@test.com&password=secret123&password_confirmation=secret123') ->header('Content-Type', 'application/x-www-form-urlencoded') ->expectStatus(302); $scenario->step('View Dashboard') ->get('/dashboard') ->expectStatus(200); } ``` ## Multi-Step Forms ```php public function define(VoltTestManager $manager): void { $manager->target('http://localhost:8000'); $scenario = $manager->scenario('Multi-Step Wizard'); $scenario->step('Step 1: Personal Info') ->get('/wizard/step-1') ->expectStatus(200) ->extractCsrfToken(); $scenario->step('Submit Step 1') ->post('/wizard/step-1', '_token=${csrf_token}&name=John&email=john@test.com') ->header('Content-Type', 'application/x-www-form-urlencoded') ->expectStatus(302) ->thinkTime('1s'); $scenario->step('Step 2: Address') ->get('/wizard/step-2') ->expectStatus(200) ->extractCsrfToken('csrf_token_2'); $scenario->step('Submit Step 2') ->post('/wizard/step-2', '_token=${csrf_token_2}&address=123+Main+St&city=Springfield') ->header('Content-Type', 'application/x-www-form-urlencoded') ->expectStatus(302); } ``` ## HTML Data Extraction Extract any value from the HTML response using CSS selectors: ```php // Extract input value ->extractHtml('field_value', 'input[name=email]', 'value') // Extract data attribute ->extractHtml('item_id', 'div.product', 'data-id') // Extract text content (omit attribute) ->extractHtml('page_title', 'h1.title') // Extract from a specific form ->extractHtml('action_url', 'form#checkout', 'action') ``` ## Regex Extraction For complex patterns, use regex: ```php ->extractRegex('csrf', 'name="_token" value="(.+?)"') ->extractRegex('order_id', 'Order #(\d+) confirmed') ``` ## Cookie Handling The Laravel package automatically handles cookies for all scenarios. Session cookies, CSRF cookies, and any other cookies set by your application are preserved across steps in the same scenario — just like a real browser session. No configuration needed — this is enabled by default. --- ## Load Profiles Load profiles control how virtual users are distributed over time during a test. VoltTest supports two approaches: **constant load** and **staged load profiles**. ## How Do I Set Up a Constant Load Test? The simplest approach — a fixed number of virtual users for a set duration. ```php $test = new VoltTest('Constant Load Test'); $test->setVirtualUsers(100); $test->setDuration('5m'); ``` This runs 100 VUs for 5 minutes. All users start immediately by default. ### Ramp-Up Gradually start virtual users over a period instead of all at once. This avoids a thundering herd hitting your server at second zero. ```php $test = new VoltTest('Ramped Load Test'); $test->setVirtualUsers(200); $test->setDuration('10m'); $test->setRampUp('30s'); // Spread user starts over 30 seconds ``` With ramp-up, VUs start linearly over the specified time: - At 0s: 0 VUs active - At 15s: ~100 VUs active - At 30s: all 200 VUs active - At 10m: test ends ### When to Use Constant Load - **Baseline testing** — measure steady-state performance under known load - **Smoke tests** — quick sanity check with low VU count - **Endurance tests** — sustained load over a long duration to find memory leaks ## How Do I Configure Staged Load Profiles? Stages let you define a dynamic load profile where VU count changes over time. Each stage linearly ramps from the previous target to a new target over a given duration. ```php $test = new VoltTest('Staged Test'); $test->stage('1m', 50); // Ramp up to 50 VUs over 1 minute $test->stage('5m', 50); // Hold at 50 VUs for 5 minutes $test->stage('30s', 200); // Spike to 200 VUs over 30 seconds $test->stage('5m', 200); // Hold at 200 VUs for 5 minutes $test->stage('1m', 0); // Ramp down to 0 over 1 minute ``` ### Stage Parameters | Parameter | Type | Description | |-----------|------|-------------| | duration | string | How long this stage lasts (`[s\|m\|h]`) | | target | int | Target VU count at the end of this stage | Each stage starts from wherever the previous stage ended. The first stage starts from 0. ### Common Patterns #### Ramp Up → Hold → Ramp Down The standard load test pattern. Gradually increase load, sustain it, then wind down. ```php $test->stage('2m', 100); // Ramp up $test->stage('10m', 100); // Hold $test->stage('2m', 0); // Ramp down ``` #### Spike Test Test how your system handles sudden traffic surges. ```php $test->stage('1m', 50); // Normal load $test->stage('5m', 50); // Hold normal $test->stage('10s', 500); // Sudden spike $test->stage('1m', 500); // Hold spike $test->stage('10s', 50); // Drop back to normal $test->stage('5m', 50); // Hold normal $test->stage('1m', 0); // Ramp down ``` #### Step-Up (Stress Test) Incrementally increase load to find the breaking point. ```php $test->stage('1m', 100); // Step 1 $test->stage('3m', 100); // Hold $test->stage('1m', 200); // Step 2 $test->stage('3m', 200); // Hold $test->stage('1m', 400); // Step 3 $test->stage('3m', 400); // Hold $test->stage('1m', 800); // Step 4 $test->stage('3m', 800); // Hold $test->stage('2m', 0); // Ramp down ``` #### Soak Test (Endurance) Sustained load over a long period to detect memory leaks and degradation. ```php $test->stage('5m', 100); // Ramp up $test->stage('2h', 100); // Hold for 2 hours $test->stage('5m', 0); // Ramp down ``` ### When to Use Staged Profiles - **Realistic traffic simulation** — real users don't all arrive at once - **Spike testing** — simulate flash sales, marketing campaigns, or viral events - **Stress testing** — find breaking points by stepping up load incrementally - **Soak testing** — long-running tests to find slow memory leaks or connection pool exhaustion ## Constant vs Staged | | Constant Load | Staged Profiles | |--|---------------|-----------------| | **VU count** | Fixed for entire test | Changes over time | | **Setup** | `setVirtualUsers()` + `setDuration()` | `stage()` calls | | **Ramp-up** | Optional via `setRampUp()` | Built into first stage | | **Complexity** | Simple | Flexible | | **Best for** | Baseline, smoke tests | Spike, stress, soak tests | :::warning Constant load and staged profiles are **mutually exclusive**. Using `stage()` prevents using `setVirtualUsers()`, `setDuration()`, and `setRampUp()` — and vice versa. ::: ## Cloud Mode Both constant and staged profiles work identically in cloud mode: ```php $test = new VoltTest('Cloud Staged Test'); $test->cloud('vt_your_api_key'); $test->stage('2m', 500); $test->stage('10m', 500); $test->stage('2m', 0); $test->run(); ``` See the [Cloud Mode](/docs/cloud-mode) page for details on cloud execution. --- ## What is VoltTest? VoltTest is a cloud-based performance testing platform that lets you simulate 1,000 to 10,000,000+ concurrent virtual users against your application. Write tests in PHP, run them locally or on managed cloud infrastructure, and analyze results in a real-time dashboard. ## How It Works ``` Your PHP Code → VoltTest SDK → Go Engine → Your Application ↓ Metrics & Results ``` 1. **You write tests in PHP** using the SDK or Laravel package 2. **The Go engine executes the load** — thousands of concurrent virtual users with minimal resource usage 3. **Results come back** as structured metrics (response times, throughput, error rates, percentiles) Run locally for development, or on VoltTest Cloud for production-scale tests. ## What Features Does VoltTest Offer? ### Write Tests in PHP No new language to learn. Define test scenarios using the same PHP you write your application in. ```php $test = new VoltTest('API Load Test'); $test->setVirtualUsers(100); $test->setDuration('5m'); $scenario = $test->scenario('User Flow'); $scenario->step('Login')->post('/api/login', $credentials)->expectStatus(200); $scenario->step('Dashboard')->get('/api/dashboard')->expectStatus(200); $test->run(); ``` ### Scale to Millions of Users The Go-powered engine handles massive concurrency with minimal memory. Run locally for hundreds of VUs, or scale to thousands across multiple cloud regions. ### Real-Time Dashboard When running on VoltTest Cloud, view live metrics — response times, throughput, error rates, and percentile distributions — as your test executes. ### Multi-Region Testing Distribute load across geographic regions to test your application from where your real users are. ```php $test->regions([ 'us-east-1' => 60, 'eu-west-1' => 40, ]); ``` ### Laravel Integration First-class Laravel support with Artisan commands, PHPUnit integration, CSRF handling, route discovery, and performance assertions. ```bash php artisan volttest:run LoginTest --users=50 --duration=2m ``` ### Flexible Load Profiles Constant load, staged ramp-up/down, spike tests, stress tests, and soak tests. ```php $test->stage('2m', 100); // Ramp up $test->stage('10m', 100); // Hold $test->stage('1m', 500); // Spike $test->stage('5m', 500); // Hold spike $test->stage('2m', 0); // Ramp down ``` ## What Are the Platform Components? | Component | Description | |-----------|-------------| | **PHP SDK** | Write and run performance tests in PHP | | **Laravel Package** | Artisan commands, PHPUnit integration, assertions | | **Go Engine** | High-performance load generation engine | | **VoltTest Cloud** | Managed infrastructure for large-scale tests | | **Dashboard** | Real-time metrics visualization | ## How Do I Get Started? Choose your path: - **[PHP SDK →](/docs/introduction)** — Install the SDK and write your first test - **[Laravel →](/docs/laravel/laravel-installation)** — Add performance testing to your Laravel app ### Cloud Execution Both the PHP SDK and Laravel package support cloud execution: - **[PHP SDK Cloud Mode →](/docs/cloud-mode)** — Cloud execution with the PHP SDK - **[Laravel Cloud Mode →](/docs/laravel/laravel-cloud-mode)** — Cloud execution with Artisan commands and config