Flood.io and Tricentis Integration: Enterprise Load Testing
Tricentis acquired Flood.io in 2021 and has been integrating it into its testing platform since. If you're in an enterprise environment already using Tricentis products — NeoLoad, qTest, Tosca — there are specific integration points worth knowing about. And even if you're not in the Tricentis ecosystem, Flood's REST API makes it straightforward to trigger load tests from any CI/CD pipeline.
Flood in the Tricentis Ecosystem
Tricentis positions Flood as the cloud-scale load testing layer in its platform. The complementary products:
NeoLoad — Tricentis's primary load testing tool. GUI-based, enterprise-focused, with its own protocol support and enterprise license model. NeoLoad runs on-premise or in Tricentis's managed cloud. Flood and NeoLoad serve overlapping use cases but different audiences — NeoLoad for teams deeply invested in Tricentis tooling, Flood for teams with JMeter/Gatling scripts who want cloud infrastructure.
qTest — Tricentis's test management platform. Flood results can be linked to qTest test runs for traceability from load test execution back to test plans.
Tosca — Tricentis's no-code test automation platform. Tosca doesn't directly integrate with Flood at the script level (Tosca tests aren't JMeter/Gatling scripts), but enterprise implementations often run Tosca functional tests alongside Flood load tests in CI/CD pipelines as separate stages.
The Tricentis integration matters most for enterprises where procurement and tooling decisions are already made. If your organization bought a Tricentis enterprise license, Flood is likely included, and the integration with qTest for reporting is worth setting up. For smaller teams or teams choosing tools independently, the Tricentis-specific integrations are less relevant than Flood's standalone API.
The Flood API
Everything you can do in the Flood UI, you can do via the Flood API. This is the foundation of any CI/CD integration.
Authentication
Generate an API token in your Flood account settings (Account → API Access). All API requests use HTTP Basic Auth with your token as the username and empty password:
curl -u your_api_token: https://api.flood.io/api/v1/accountStarting a Flood via API
The core API call to launch a flood:
curl -u $FLOOD_API_TOKEN: \
-X POST https://api.flood.io/api/v1/floods \
-H "Content-Type: application/json" \
-d '{
"flood": {
"tool": "jmeter",
"threads": 100,
"duration": 600,
"rampup": 60,
"grids": [
{
"infrastructure": "demand",
"instance_quantity": 2,
"region": "us-east-1",
"instance_type": "m5.xlarge",
"stop_after_iteration": false
}
],
"tag_list": "ci-build,staging",
"name": "Sprint 23 Load Test"
},
"flood_files": [
{
"url": "https://your-storage.example.com/tests/my-test.jmx"
}
]
}'This returns a flood object with a UUID. Use that UUID to poll status.
Polling Flood Status
FLOOD_UUID="abc123-..."
curl -u $FLOOD_API_TOKEN: \
https://api.flood.io/api/v1/floods/$FLOOD_UUIDResponse includes:
{
"uuid": "abc123-...",
"status": "executing",
"started_at": "2024-01-15T10:00:00Z",
"stopped_at": null,
"current_sample": {
"requests": 45230,
"response_time": 187,
"concurrency": 98,
"transaction_rate": 75.4,
"error_rate": 0.02
}
}Status values: queued, provisioning, executing, finished, failed, stopped.
Retrieving Results
After a flood finishes:
curl -u $FLOOD_API_TOKEN: \
https://api.flood.io/api/v1/floods/$FLOOD_UUID/reportThis returns aggregate results including percentile response times, total requests, error rate, and transaction breakdown.
For raw time-series data:
curl -u $FLOOD_API_TOKEN: \
https://api.flood.io/api/v1/floods/$FLOOD_UUID/result/timeseriesJenkins Integration
Here's a complete Jenkins pipeline stage that runs a Flood load test and fails the build if performance thresholds are exceeded:
pipeline {
agent any
environment {
FLOOD_API_TOKEN = credentials('flood-api-token')
FLOOD_TEST_URL = "https://your-file-storage.example.com/tests/my-load-test.jmx"
}
stages {
stage('Deploy to Staging') {
steps {
// your deployment steps
sh './deploy.sh staging'
}
}
stage('Load Test') {
steps {
script {
// Launch flood
def startResponse = sh(
script: """
curl -sf -u ${FLOOD_API_TOKEN}: \
-X POST https://api.flood.io/api/v1/floods \
-H 'Content-Type: application/json' \
-d '{
"flood": {
"tool": "jmeter",
"threads": 200,
"duration": 600,
"rampup": 60,
"grids": [{
"infrastructure": "demand",
"instance_quantity": 2,
"region": "us-east-1",
"instance_type": "m5.xlarge"
}],
"name": "Build #${BUILD_NUMBER} Load Test"
},
"flood_files": [{"url": "${FLOOD_TEST_URL}"}]
}'
""",
returnStdout: true
).trim()
def floodData = readJSON text: startResponse
def floodUuid = floodData.uuid
echo "Flood started: ${floodUuid}"
env.FLOOD_UUID = floodUuid
// Poll until complete
def maxWait = 900 // 15 minutes
def waited = 0
def interval = 30
while (waited < maxWait) {
sleep(interval)
waited += interval
def statusResponse = sh(
script: "curl -sf -u ${FLOOD_API_TOKEN}: https://api.flood.io/api/v1/floods/${floodUuid}",
returnStdout: true
).trim()
def floodStatus = readJSON text: statusResponse
echo "Status: ${floodStatus.status} | RPS: ${floodStatus.current_sample?.transaction_rate} | P95: ${floodStatus.current_sample?.response_time}ms | Errors: ${floodStatus.current_sample?.error_rate}%"
if (floodStatus.status == 'finished' || floodStatus.status == 'failed' || floodStatus.status == 'stopped') {
break
}
}
}
}
}
stage('Evaluate Results') {
steps {
script {
def reportResponse = sh(
script: "curl -sf -u ${FLOOD_API_TOKEN}: https://api.flood.io/api/v1/floods/${env.FLOOD_UUID}/report",
returnStdout: true
).trim()
def report = readJSON text: reportResponse
def errorRate = report.summary.errors_rate as Double
def p95ResponseTime = report.summary.response_time_percentile_95 as Integer
echo "Final Results:"
echo " Total Requests: ${report.summary.requests}"
echo " Error Rate: ${errorRate}%"
echo " P95 Response Time: ${p95ResponseTime}ms"
// Fail build on threshold breach
if (errorRate > 1.0) {
error("Load test FAILED: Error rate ${errorRate}% exceeds 1% threshold")
}
if (p95ResponseTime > 2000) {
error("Load test FAILED: P95 response time ${p95ResponseTime}ms exceeds 2000ms threshold")
}
echo "Load test PASSED: All thresholds met"
}
}
}
}
post {
always {
script {
// Archive results link
echo "Results: https://app.flood.io/floods/${env.FLOOD_UUID}"
}
}
}
}Azure DevOps Integration
For Azure DevOps pipelines, the same pattern applies using Azure DevOps pipeline tasks:
# azure-pipelines.yml
trigger:
- main
variables:
FLOOD_API_TOKEN: $(floodApiToken) # Set in pipeline secret variables
stages:
- stage: Deploy
jobs:
- job: DeployToStaging
steps:
- script: ./deploy.sh staging
displayName: 'Deploy to staging'
- stage: LoadTest
dependsOn: Deploy
jobs:
- job: RunFloodTest
timeoutInMinutes: 30
steps:
- script: |
# Start flood
RESPONSE=$(curl -sf -u $(FLOOD_API_TOKEN): \
-X POST https://api.flood.io/api/v1/floods \
-H 'Content-Type: application/json' \
-d '{
"flood": {
"tool": "gatling",
"threads": 500,
"duration": 600,
"rampup": 120,
"grids": [{
"infrastructure": "demand",
"instance_quantity": 3,
"region": "eu-west-1",
"instance_type": "m5.2xlarge"
}],
"name": "Release $(Build.BuildNumber)"
},
"flood_files": [{
"url": "$(GATLING_SCRIPT_URL)"
}]
}')
FLOOD_UUID=$(echo $RESPONSE | python3 -c "import sys,json; print(json.load(sys.stdin)['uuid'])")
echo "##vso[task.setvariable variable=FLOOD_UUID]$FLOOD_UUID"
echo "Flood UUID: $FLOOD_UUID"
displayName: 'Launch load test'
- script: |
# Poll for completion
while true; do
STATUS=$(curl -sf -u $(FLOOD_API_TOKEN): \
https://api.flood.io/api/v1/floods/$(FLOOD_UUID) | \
python3 -c "import sys,json; print(json.load(sys.stdin)['status'])")
echo "Current status: $STATUS"
if [ "$STATUS" = "finished" ] || [ "$STATUS" = "failed" ] || [ "$STATUS" = "stopped" ]; then
break
fi
sleep 30
done
if [ "$STATUS" != "finished" ]; then
echo "##vso[task.logissue type=error]Flood ended with status: $STATUS"
exit 1
fi
displayName: 'Wait for flood to complete'
- script: |
# Evaluate thresholds
REPORT=$(curl -sf -u $(FLOOD_API_TOKEN): \
https://api.flood.io/api/v1/floods/$(FLOOD_UUID)/report)
ERROR_RATE=$(echo $REPORT | python3 -c "import sys,json; print(json.load(sys.stdin)['summary']['errors_rate'])")
P95=$(echo $REPORT | python3 -c "import sys,json; print(json.load(sys.stdin)['summary']['response_time_percentile_95'])")
echo "Error rate: ${ERROR_RATE}%"
echo "P95 response time: ${P95}ms"
python3 -c "
error_rate = float('${ERROR_RATE}')
p95 = int('${P95}')
if error_rate > 1.0:
raise SystemExit(f'FAIL: Error rate {error_rate}% > 1% threshold')
if p95 > 2000:
raise SystemExit(f'FAIL: P95 {p95}ms > 2000ms threshold')
print('PASS: All thresholds met')
"
displayName: 'Evaluate performance thresholds'GitHub Actions Integration
name: Load Test on Release
on:
release:
types: [published]
jobs:
load-test:
runs-on: ubuntu-latest
steps:
- name: Launch Flood
id: launch
run: |
RESPONSE=$(curl -sf -u ${{ secrets.FLOOD_API_TOKEN }}: \
-X POST https://api.flood.io/api/v1/floods \
-H 'Content-Type: application/json' \
-d '{
"flood": {
"tool": "jmeter",
"threads": 300,
"duration": 900,
"rampup": 120,
"grids": [{
"infrastructure": "demand",
"instance_quantity": 2,
"region": "us-east-1",
"instance_type": "m5.xlarge"
}],
"name": "Release ${{ github.ref_name }}"
},
"flood_files": [{
"url": "${{ vars.FLOOD_SCRIPT_URL }}"
}]
}')
UUID=$(echo $RESPONSE | python3 -c "import sys,json; print(json.load(sys.stdin)['uuid'])")
echo "flood_uuid=$UUID" >> $GITHUB_OUTPUT
- name: Wait and evaluate
run: |
UUID=${{ steps.launch.outputs.flood_uuid }}
for i in $(seq 1 30); do
sleep 60
STATUS=$(curl -sf -u ${{ secrets.FLOOD_API_TOKEN }}: \
https://api.flood.io/api/v1/floods/$UUID | \
python3 -c "import sys,json; print(json.load(sys.stdin)['status'])")
echo "Status: $STATUS"
[ "$STATUS" = "finished" ] && break
[ "$STATUS" = "failed" ] && exit 1
done
REPORT=$(curl -sf -u ${{ secrets.FLOOD_API_TOKEN }}: \
https://api.flood.io/api/v1/floods/$UUID/report)
echo "Results: https://app.flood.io/floods/$UUID"
python3 << 'EOF'
import json, sys
report = json.loads("""$REPORT""")
summary = report['summary']
error_rate = float(summary['errors_rate'])
p95 = int(summary['response_time_percentile_95'])
print(f"P95: {p95}ms, Error rate: {error_rate:.2f}%")
if error_rate > 1.0 or p95 > 2000:
sys.exit(1)
EOFExporting Results to External Systems
Flood results can be pushed to external systems for long-term storage and trending.
Pushing to InfluxDB/Grafana
import requests
import json
from influxdb_client import InfluxDBClient, Point
from influxdb_client.client.write_api import SYNCHRONOUS
FLOOD_TOKEN = "your-flood-api-token"
FLOOD_UUID = "your-flood-uuid"
# Fetch time series from Flood
response = requests.get(
f"https://api.flood.io/api/v1/floods/{FLOOD_UUID}/result/timeseries",
auth=(FLOOD_TOKEN, "")
)
data = response.json()
# Write to InfluxDB
client = InfluxDBClient(url="http://your-influxdb:8086", token="influx-token", org="your-org")
write_api = client.write_api(write_options=SYNCHRONOUS)
for sample in data['timeseries']:
point = (
Point("load_test_results")
.tag("flood_uuid", FLOOD_UUID)
.tag("flood_name", data['name'])
.field("requests_per_second", sample['transaction_rate'])
.field("response_time_mean", sample['mean_response_time'])
.field("response_time_p95", sample['response_time_percentile_95'])
.field("error_rate", sample['error_rate'])
.field("concurrency", sample['concurrency'])
.time(sample['timestamp'])
)
write_api.write(bucket="load-tests", record=point)
print(f"Wrote {len(data['timeseries'])} data points to InfluxDB")Linking to Tricentis qTest
If your organization uses qTest for test management, Flood results can be linked to qTest test runs via qTest's API. The typical integration:
- Before launching a flood, create a qTest test run for the load test scenario
- After the flood completes, update the test run with PASS/FAIL status based on thresholds
- Attach the Flood results URL as a test run attachment
This gives you traceability: a qTest test plan references a Flood test run, which links back to the actual flood results. This satisfies audit requirements in regulated industries where test evidence needs to be attached to a test management system.
The qTest API integration uses their REST API — consult qTest's API documentation for the specific endpoints, as they vary by qTest version.
Flood Agent for On-Premise Testing
If your application isn't reachable from Flood's cloud infrastructure, Flood provides an agent that runs on your own hardware. The agent connects outbound to Flood's control plane, registers itself as an available grid node, and executes floods on command.
Agent setup:
# Download the Flood agent for Linux
curl -O https://flood.io/downloads/flood-agent-linux-amd64.tar.gz
tar -xzf flood-agent-linux-amd64.tar.gz
# Configure with your API token and grid name
./flood-agent \
--token your-api-token \
--grid your-private-grid-name \
--tags env:internal,network:privateOnce registered, your private grid appears in the Flood UI alongside hosted grids. You can launch floods targeting your private grid using the same API calls — just specify the grid name instead of an infrastructure type.
This model is the most common integration for enterprises with internal applications: Flood handles orchestration and results aggregation, your own hardware handles the actual traffic generation.