Overview
Grafana k6 is an open-source performance testing tool that allows development and QA teams to simulate user traffic, measure application performance, and identify potential bottlenecks. It is commonly used in CI/CD pipelines to validate that applications continue to meet performance requirements as new changes are introduced.
Although k6 provides detailed performance metrics, organizations using Spira often want to centralize their performance test results alongside functional, regression, integration, and other automated tests. Doing so makes it easier to monitor release readiness, track quality over time, and establish traceability between requirements, test cases, and test execution results.
Fortunately, k6 supports exporting test results in JUnit XML format through Grafana's JUnit helper library. Inflectra provides a generic xUnit/JUnit importer that can read these results and upload them directly into Spira.
The integration consists of three stages:
- Execute performance tests using Grafana k6, including defined performance thresholds.
- Generate a JUnit XML report using Grafana's JUnit helper library.
- Import the JUnit results into Spira using Inflectra's Node.js xUnit importer.
The integration can run locally or as part of an automated CI/CD pipeline.
Prerequisites
Before configuring the integration, ensure you have the following:
- A working installation of Grafana k6.
- Access to SpiraTest, SpiraTeam, or SpiraPlan (version 6.0 or later).
- A Spira user account with permission to record automated test results.
- The user's Spira API key, available through their profile.
- Node.js and npm installed on the machine or CI/CD agent executing the importer.
- Existing test cases in the appropriate Spira project to receive the k6 test results.
You will also need to install the Inflectra xUnit importer, which is available from npm and GitHub:
https://github.com/Inflectra/spira-addons-xunit-node
Step 1: Create a k6 Performance Test
First, create a k6 JavaScript test script that performs HTTP requests against the application being tested.
For example, create a file named load-test.js:
import http from 'k6/http';
import { check, sleep } from 'k6';
import { jUnit } from 'https://jslib.k6.io/k6-summary/0.0.2/index.js';
export const options = {
vus: 20,
duration: '60s',
thresholds: {
http_req_duration: ['p(95)<500'],
http_req_failed: ['rate<0.01'],
},
};
export default function () {
const response = http.get('https://test.k6.io');
check(response, {
'HTTP status is 200': (r) => r.status === 200,
});
sleep(1);
}
export function handleSummary(data) {
const junitXml = jUnit(data);
// Add a classname to each JUnit test case for Spira mapping.
const spiraXml = junitXml.replace(
/<testcase\b/g,
'<testcase classname="k6.thresholds"'
);
return {
'junit.xml': spiraXml,
'summary.json': JSON.stringify(data, null, 2),
};
}
In this example, k6 simulates 20 concurrent virtual users accessing a website for 60 seconds.
Two performance thresholds are defined:
- Response time: The 95th percentile (p95) of HTTP request durations must remain below 500 milliseconds.
- Error rate: Fewer than 1% of HTTP requests should fail.
These thresholds act as performance acceptance criteria. When a threshold is exceeded, k6 reports a failure, allowing the CI/CD pipeline and Spira to identify that the application did not meet its expected performance requirements.
The script also includes a handleSummary() function. This function runs after the load test finishes and uses Grafana's jUnit() helper to convert the summarized threshold results into JUnit XML.
Important: The standard Grafana helper generates JUnit test cases without a classname attribute. Since Inflectra's Node.js importer uses the combination of classname and name to map results to Spira test cases, the example adds a consistent k6.thresholds classname to each generated test case.
This adjustment is intended for the structure produced by the Grafana JUnit helper shown above. If you use another JUnit reporter that already includes classnames, you should preserve its existing classnames instead.
Step 2: Execute the k6 Test and Generate the JUnit Report
Run the performance test using the k6 command-line interface:
k6 run load-test.js
When execution completes, k6 generates two files in the working directory:
junit.xml — Contains the performance threshold results in JUnit XML format, suitable for importing into Spira.
summary.json — Contains the aggregated performance statistics for additional analysis or archival.
The generated JUnit XML will have a structure similar to the following:
<?xml version="1.0"?>
<testsuites tests="2" failures="0">
<testsuite name="k6 thresholds"
tests="2"
failures="0">
<testcase
classname="k6.thresholds"
name="http_req_duration - p(95)<500" />
<testcase
classname="k6.thresholds"
name="http_req_failed - rate<0.01" />
</testsuite>
</testsuites>
This is an illustrative example of a successful execution. The actual output may vary depending on the thresholds and version of the helper library being used.
If a threshold fails, the corresponding test case will contain a JUnit failure element, for example:
<testcase
classname="k6.thresholds"
name="http_req_duration - p(95)<500">
<failure message="failed" />
</testcase>
This information can subsequently be imported into Spira as a failed automated test run.
Note that the Grafana JUnit helper primarily reports threshold outcomes. It is not intended to export every individual HTTP request or the complete performance telemetry collected by k6.
Step 3: Install the Inflectra Spira xUnit Importer
Next, install the Spira Node.js xUnit importer.
Open a command prompt or terminal in your project directory and execute:
npm install spira-addons-xunit
The importer reads standard JUnit/xUnit XML files and submits their test execution results to Spira using the Spira REST API.
It supports mapping test cases to existing Spira test cases, associating test runs with releases and test sets, and optionally creating Spira build records.
The source code and additional documentation are available at:
https://github.com/Inflectra/spira-addons-xunit-node
Step 4: Create the Corresponding Test Cases in Spira
Before importing results, create the test cases in Spira that will represent your k6 performance acceptance criteria.
For the example above, you might create the following test cases in your Spira project:
| Spira Test Case | Description |
|---|
| TC:101 — API Response Time | Verify that the 95th percentile HTTP response time remains below 500 ms |
| TC:102 — API Error Rate | Verify that the HTTP request failure rate remains below 1% |
These test cases provide a consistent place to store performance results across multiple executions and builds.
For example, if the response-time threshold passes during one build but fails during a subsequent build, both outcomes can be recorded against the same Spira test case, making it easier to identify performance regressions.
The test cases can also be linked to performance or non-functional requirements in Spira, allowing teams to establish traceability between performance acceptance criteria and their execution results.
Step 5: Configure the Spira Importer
In the same project directory, create a configuration file named spira.cfg.
The configuration identifies the target Spira instance, authentication credentials, project, and the mapping between JUnit test cases and their corresponding Spira test cases.
For example:
[credentials]
url = https://mycompany.spiraservice.net
username = automation-user
token = YOUR_SPIRA_API_KEY
project_id = 1
release_id = 5
test_set_id = 10
create_build = true
[test_cases]
k6.thresholds.http_req_duration - p(95)<500 = 101
k6.thresholds.http_req_failed - rate<0.01 = 102
The configuration settings have the following meanings:
| Setting | Description |
|---|
url | Base URL of the Spira instance, without a trailing slash |
username | Spira account used to import the automated test results |
token | Spira API key associated with that account |
project_id | Spira project that will receive the test results |
release_id | Optional release to associate with the results |
test_set_id | Optional default test set |
create_build | Whether the importer should create a build record in Spira |
[test_cases] | Mappings between JUnit test cases and Spira test case IDs |
In the [test_cases] section, each mapping uses the format:
classname.name = SpiraTestCaseID
For example:
k6.thresholds.http_req_duration - p(95)<500 = 101
This maps the k6 response-time threshold to Spira test case TC:101.
The mapping values must match the classname and name attributes in the generated XML. Since the XML represents characters such as < using XML entities, use their decoded equivalents when defining the mappings in the configuration file.
The example test case IDs, project ID, release ID, and test set ID should be replaced with the actual identifiers from your Spira installation.
Security note: The configuration file contains a Spira API token and should not be committed to a source-code repository. In CI/CD environments, store the token in your pipeline's secrets manager and generate or populate the configuration securely at runtime.
Step 6: Import the k6 Results into Spira
Once the JUnit XML report has been generated and the configuration file has been created, execute the Spira xUnit importer:
node node_modules/spira-addons-xunit/spira-xunit-reader.js junit.xml spira.cfg
The importer reads the JUnit report, identifies the Spira test case associated with each result, and submits the execution status to Spira.
For a successful import, you should see console output similar to:
Sending test results to Spira at URL
'https://mycompany.spiraservice.net'.
Successfully reported 2 test cases to Spira.
The exact console output may vary depending on the importer version.
After the import has completed, log into Spira and navigate to the Testing > Test Runs section of the relevant project.
You should see the results corresponding to the k6 performance thresholds.
A successful threshold will be recorded as a passed test run, while a failed threshold will be recorded as a failed test run.
If you specified a release or test set in the configuration file, the imported test runs will be associated with those artifacts. If create_build is enabled and a valid release is configured, the importer can also create a build record to group the execution results.
Step 7: Integrate the Process into Your CI/CD Pipeline
One of the main advantages of this approach is that the entire process can be automated as part of your software delivery pipeline.
The workflow consists of executing k6, generating the JUnit XML report, and importing the results into Spira.
For example, a simple shell-based pipeline could execute:
#!/usr/bin/env bash
# Execute the k6 load test and preserve its exit status.
set +e
k6 run load-test.js
K6_STATUS=$?
set -e
# Ensure the report was generated.
test -s junit.xml
# Import the results into Spira.
node node_modules/spira-addons-xunit/spira-xunit-reader.js \
junit.xml spira.cfg
# Preserve the original k6 result for the CI/CD quality gate.
exit "$K6_STATUS"
This script preserves the exit status of the k6 test, which is important when one or more performance thresholds have failed.
By executing the Spira import as a separate step after k6 finishes, failed performance tests can still be reported to Spira rather than being lost when the CI/CD pipeline marks a testing stage as failed.
The approach can be incorporated into GitHub Actions, GitLab CI/CD, Jenkins, Azure DevOps, or other pipeline orchestration tools.
For production use, ensure that:
Node.js dependencies are installed before running the importer.
The Spira API token is retrieved securely from pipeline secrets.
The JUnit XML and JSON summary files are retained as pipeline artifacts.
A failed k6 threshold does not prevent the result-importing step from running.
Import errors and missing test-case mappings are detected and handled appropriately.
The basic example preserves the k6 exit status, but production pipelines should also verify that the expected number of results was successfully imported. The current importer may log certain mapping or API errors without necessarily returning a failing process exit code.
Understanding What Gets Imported into Spira
The JUnit integration is designed to transfer test execution outcomes, rather than the complete performance dataset produced by k6.
For example, the integration can report that the response-time threshold passed or failed, but it does not automatically import every recorded response time, request rate, or virtual-user measurement into Spira as a separate performance metric.
For detailed performance analytics, retain the k6 JSON summary or use k6 with a supported time-series or observability backend, such as Grafana.
This combination allows teams to use Spira as the centralized system of record for test execution and release readiness, while continuing to use specialized performance monitoring tools for detailed load testing analysis.
Troubleshooting
No test results appear in Spira
First, verify that the junit.xml file contains the expected test cases. Next, confirm that the [test_cases] section of spira.cfg contains mappings that match the classname and name attributes in the XML.
If a test case does not have a matching mapping, the importer can skip it.
The importer reports an undefined classname
The standard Grafana JUnit helper does not include a classname attribute. Make sure your k6 script includes the small JUnit compatibility adjustment shown in Step 1, or otherwise adds valid classnames before importing the report.
The performance test fails, but no results are imported
When a performance threshold fails, k6 can return a non-zero exit code. Some CI/CD pipelines immediately stop executing subsequent commands when a step fails.
Configure the pipeline to run the Spira import step even when k6 reports a failed threshold.
Authentication or connection errors
Verify that the Spira URL, username, and API token are correct. Ensure that the specified account has appropriate access to the target project and permission to record automated test runs.
The JUnit report does not contain detailed performance statistics
This is expected. The Grafana JUnit helper reports threshold outcomes, while the JSON summary contains aggregated performance measurements.
Retain both reports when detailed performance analysis is required.
Conclusion
Integrating Grafana k6 with Inflectra Spira provides a convenient way to incorporate performance testing into an organization's broader test management and software delivery processes.
By using Grafana's JUnit XML helper together with Inflectra's xUnit importer, teams can automatically record performance acceptance results in Spira, associate those results with specific releases and builds, and maintain traceability between non-functional requirements and their verification.
The integration is particularly useful for organizations running k6 tests in CI/CD pipelines, where performance testing can serve as an automated quality gate before an application is promoted to the next environment or released into production.
Rather than managing performance testing as a separate, disconnected activity, development and QA teams can bring its results into the same centralized testing and lifecycle management platform used for the rest of their software quality activities.
Additional Resources
Grafana k6 documentation: https://grafana.com/docs/k6/latest/
Grafana k6 custom summary and JUnit XML generation: https://grafana.com/docs/k6/latest/results-output/end-of-test/custom-summary/
Inflectra Spira Node.js xUnit importer: https://github.com/Inflectra/spira-addons-xunit-node
Inflectra Spira Python xUnit importer: https://github.com/Inflectra/spira-addons-xunit-python
Inflectra Support Knowledge Base: https://www.inflectra.com/Support/KnowledgeBase/