Push Jenkins Test Results to QTest with the REST API
Automate QTest test-run creation and result ingestion from Jenkins using bearer tokens and the HTTP Request plugin.
01 Sept 2026, 09:50 UTC

The Problem: Manual QTest Updates Slow Down CI Feedback
When a Jenkins pipeline finishes a Selenium or API test suite, someone still opens QTest, finds the matching test run, and marks cases as passed or failed by hand. That manual step delays feedback, creates transcription errors, and means QTest does not reflect the actual state of the latest build.
The practical takeaway is that QTest exposes a REST API for creating test runs and updating results. Jenkins can call that API directly, so execution status flows from CI into QTest without a person copying report data.
Assumptions and Required Access
This approach assumes QTest version 10 or later, which supports bearer token authentication over REST. Earlier versions may use different endpoints or basic authentication. You need:
- A QTest API key with permission to create test runs and update test results.
- Jenkins with the HTTP Request plugin installed, providing the
httpRequeststep. - Network access from the Jenkins controller or agent to the QTest API host.
Store the API key as a Jenkins Secret text credential, not as plain text in the job. Reference it in pipelines so the token does not appear in console logs or version control.
Worked Example: Declarative Jenkins Pipeline
This pipeline creates a test run in QTest, executes the automation suite, then sends the results back to QTest. Replace every placeholder value with your own IDs and host.
pipeline {
agent any
environment {
QTEST_HOST = 'https://your-qtest-instance.com'
QTEST_API_BASE = "${QTEST_HOST}/api/v3"
PROJECT_ID = 'your-project-id'
QTEST_TOKEN = credentials('qtest-api-token')
}
stages {
stage('Create QTest Test Run') {
steps {
script {
def createResponse = httpRequest(
url: "${QTEST_API_BASE}/projects/${PROJECT_ID}/test-runs",
httpMethod: 'POST',
contentType: 'APPLICATION_JSON',
requestBody: '{"name": "Jenkins Build ${BUILD_NUMBER}", "description": "Automated run from CI"}',
customHeaders: [[name: 'Authorization', value: "Bearer ${QTEST_TOKEN}"]],
validResponseCodes: '200:201'
)
echo "QTest create response: ${createResponse.content}"
}
}
}
stage('Run Automation Suite') {
steps {
// Replace with your test command, for example a Maven or Gradle test task
sh 'mvn test -Dtest=MySuite'
}
}
stage('Update QTest Results') {
steps {
script {
// Parse your report and build the QTest result payload here
def resultsPayload = '{"results": []}'
httpRequest(
url: "${QTEST_API_BASE}/projects/${PROJECT_ID}/test-runs/${TEST_RUN_ID}/results",
httpMethod: 'POST',
contentType: 'APPLICATION_JSON',
requestBody: resultsPayload,
customHeaders: [[name: 'Authorization', value: "Bearer ${QTEST_TOKEN}"]],
validResponseCodes: '200:201'
)
}
}
}
}
}
Where to run: In a Jenkins job or multibranch pipeline. Permissions: The Jenkins credential must map to a QTest API key that can create test runs and write results; otherwise expect HTTP 403 responses. Expected check: The Jenkins console should show HTTP 200 or 201 from the create and update calls. Risk: If the QTest host is behind a firewall, the job will time out unless Jenkins can reach the endpoint.
Trade-offs and Limitations
- QTest commonly enforces API rate limits, often around 60 requests per minute. Bursty pipelines can receive HTTP 429 responses; check the
Retry-Afterheader and back off before retrying. - Test case IDs in QTest must map to identifiers used by your automation framework. That mapping is not maintained automatically and can drift as tests are added or renamed.
- Network latency between Jenkins and QTest adds time to every pipeline run, especially if you call the API once per test case instead of batching results.
Verification and Rollback
Verify the integration with one small test run before rolling it out to all suites:
- Generate a QTest API key through Settings → API Keys and copy the token.
- Add the token to Jenkins as a Secret text credential named
qtest-api-token. - Run the pipeline above and confirm the Jenkins console shows successful API responses.
- Open the corresponding test run in QTest and confirm the status and attached results match the Jenkins outcome.
- Review the console for HTTP 401 (authentication) or 429 (rate limit) errors.
Because this pipeline creates and updates test runs in QTest, it changes state outside Jenkins. To roll back, revoke the API key in QTest, remove the Jenkins credential, and delete any test runs created during testing. Disabling the Jenkins job stops future updates but does not remove data already written to QTest.
Actionable Closing
Start with a single test suite and one Jenkins job. Confirm that results appear in QTest automatically, then expand coverage. Treat the QTest API key like a production secret, monitor for rate-limit responses, and keep your test-case ID mapping under version control so the integration stays reliable as your suite grows.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.