Overview

Grafana k6 is an open-source load testing tool that helps development and QA teams evaluate application performance under simulated user traffic. It allows engineers to measure important performance indicators, such as HTTP response times, error rates, throughput, and system responsiveness, while defining thresholds that determine whether an application meets its performance requirements.

Organizations using Inflectra SpiraTest, SpiraTeam, or SpiraPlan often want to manage these performance testing results alongside their functional, regression, integration, and other automated tests. Centralizing this information in Spira makes it easier to assess release readiness, track performance-related failures, and maintain traceability between non-functional requirements and the tests used to verify them.

One approach is to use the open-source k6-to-junit utility, which converts the console output produced by k6 into standard JUnit XML. This XML file can then be imported into Spira using Inflectra's generic xUnit importer.

Unlike the alternative Grafana JUnit helper approach, which requires adding a handleSummary() function to the k6 test script, k6-to-junit operates as a separate command-line utility. This makes it particularly useful when teams already have established k6 scripts and want to integrate their performance results into Spira without modifying those scripts.

The integration consists of four stages:

  1. Execute the k6 load tests and capture their console output.
  2. Convert the console output into JUnit XML using k6-to-junit.
  3. Prepare the JUnit XML for compatibility with Spira's xUnit importer.
  4. Import the test results into Spira and associate them with the appropriate test cases, releases, and builds.

This workflow can be executed locally or automated within a 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.
  • A valid Spira API key associated with that user.
  • Node.js and npm installed on the machine or CI/CD agent.
  • Existing Spira test cases representing the performance thresholds being evaluated.

The integration uses two Node.js packages:

k6-to-junit — A community-maintained command-line utility for converting k6 console output into JUnit XML.

https://github.com/Mattihew/k6-to-junit

Spira xUnit importer — An Inflectra integration that imports JUnit/xUnit XML test results into Spira.

https://github.com/Inflectra/spira-addons-xunit-node

Step 1: Create a k6 Performance Test

First, create a k6 JavaScript test script that executes HTTP requests against the application being tested.

For example, create a file called load-test.js:

import http from 'k6/http';
import { check, sleep } from 'k6';

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);
}

This example simulates 20 concurrent virtual users accessing a website for 60 seconds. During execution, k6 collects performance metrics and evaluates them against predefined thresholds.

Two performance requirements are defined:

  • Response time: The 95th percentile of HTTP request durations must remain below 500 milliseconds.
  • Error rate: The proportion of failed HTTP requests must remain below 1%.

These thresholds represent measurable performance acceptance criteria. If the application fails to meet a threshold, k6 reports the failure and can cause the CI/CD testing stage to fail.

The test also contains an HTTP status check, although it is important to understand that k6-to-junit primarily reports threshold results rather than automatically converting every individual k6 check into a separate JUnit test case.

Note: The k6-to-junit converter reads the textual summary output from k6. Unlike Grafana's JUnit helper approach, there is no need to add a handleSummary() function to this script.

Step 2: Install the k6-to-junit Converter

Next, install the k6-to-junit utility using npm.

Open a command prompt or terminal and execute:

npm install -g k6-to-junit

Once installed, the k6-to-junit command will be available from the command line.

The converter reads k6 output from standard input and generates a JUnit XML report. It can either print the XML to standard output or write the results directly to a file.

For example:

k6 run load-test.js | k6-to-junit junit.xml

This command executes the k6 test, passes the output to the converter, and generates a file named junit.xml.

The converter also passes the original k6 output through to the terminal, allowing you to see the performance testing results while the JUnit report is being generated.

Alternatively, you can use the package as a local project dependency:

npm install --save-dev k6-to-junit

Then execute it using:

k6 run load-test.js | npx k6-to-junit junit.xml

Installing the converter locally and recording the dependency in your package-lock.json file is generally preferable for CI/CD pipelines because it helps maintain consistent tool versions across build agents.

Important: The k6-to-junit utility parses formatted console output. Because that output may change between k6 releases, verify that your installed k6 version produces compatible output before deploying the integration into production.

Also, the simple pipeline shown above does not necessarily preserve k6's exit status. A more robust pipeline example is provided later in this article.

Step 3: Generate and Inspect the JUnit XML Report

Execute the k6 script and convert its output:

k6 run load-test.js | npx k6-to-junit junit.xml

When execution completes, the converter generates a JUnit XML report containing the test outcomes identified from the k6 console summary.

A representative result might look like this:

<?xml version="1.0"?>
<testsuites tests="2" failures="1">
  <testsuite name="load-test.js"
             tests="2"
             failures="1">

    <testcase name="http_req_duration">
      <system-out>
        Response time threshold result
      </system-out>
    </testcase>

    <testcase name="http_req_failed">
      <failure message="Threshold exceeded" />
      <system-out>
        Error rate threshold result
      </system-out>
    </testcase>

  </testsuite>
</testsuites>

This is a simplified illustration of the type of information generated by the converter, rather than the exact output of a particular version. The names, attributes, and number of test cases depend on the thresholds identified in the original k6 output.

The JUnit report identifies whether each recognized performance threshold passed or failed. It can also preserve the relevant console output, providing useful diagnostic information about the result.

However, there is one important compatibility consideration.

The k6-to-junit converter's documented output contains JUnit <testcase> elements with a name attribute but without a classname attribute. Inflectra's Spira xUnit importer uses the combination of classname and name to map each result to an existing Spira test case.

Consequently, we need to add a classname to the generated XML before importing it.

Step 4: Prepare the JUnit XML for Spira

To make the generated JUnit XML compatible with Spira's Node.js importer, we can use a small Node.js script to add a consistent classname attribute to test cases that do not already have one.

Create a file named prepare-junit.js in your project directory:

const fs = require('fs');

const inputFile = 'junit.xml';
const outputFile = 'junit-spira.xml';

let xml = fs.readFileSync(inputFile, 'utf8');

// Add classname only where the test case does not
// already include one.
xml = xml.replace(
  /<testcase\b(?![^>]*\bclassname=)/g,
  '<testcase classname="k6.thresholds"'
);

fs.writeFileSync(outputFile, xml, 'utf8');

console.log(`Prepared ${outputFile} for Spira`);

This script reads the JUnit XML file generated by k6-to-junit, adds the k6.thresholds classname where required, and writes the result to a new file named junit-spira.xml.

Execute the script using:

node prepare-junit.js

The resulting XML will contain test cases similar to:

<testcase
  classname="k6.thresholds"
  name="http_req_duration">
</testcase>

<testcase
  classname="k6.thresholds"
  name="http_req_failed">
  <failure message="Threshold exceeded" />
</testcase>

The classname provides a consistent namespace for mapping the test cases into Spira.

This is a simple compatibility adjustment designed for the documented output of k6-to-junit. It is not a general-purpose XML transformation utility. If your JUnit reports have a different structure, use an XML parser and inspect the resulting test-case attributes before importing them.

For the example above, the resulting Spira test case identifiers would be:

k6.thresholds.http_req_duration

k6.thresholds.http_req_failed

These identifiers are used in the next step to associate the generated results with existing Spira test cases.

Step 5: Create the Corresponding Test Cases in Spira

Before importing the results, create the corresponding test cases in your Spira project.

For our example, we will create two test cases:

Spira Test CaseDescription
TC:101 — HTTP Response TimeVerify that the 95th percentile HTTP response time is below 500 ms
TC:102 — HTTP Error RateVerify that the HTTP request failure rate remains below 1%

These test cases represent non-functional performance acceptance criteria that can be validated automatically.

In Spira, you can also associate these test cases with the relevant requirements. For example, a requirement specifying that an API must respond within 500 milliseconds for at least 95% of requests can be linked directly to the corresponding performance test case.

Each time the k6 load test runs, its outcome can be recorded against the same Spira test case. This allows the team to compare testing outcomes across releases and builds and identify regressions in application performance.

Step 6: Install the Inflectra Spira xUnit Importer

Next, install Inflectra's Node.js xUnit importer using npm:

npm install --save-dev spira-addons-xunit

This integration reads JUnit/xUnit XML files and submits the test results to Spira using the Spira REST API.

In addition to recording the test execution status, the importer supports associating results with specific Spira releases and test sets. It can also create build records to group results from individual automated test executions.

Additional documentation is available at:

https://spiradoc.inflectra.com/Unit-Testing-Integration/Integrating-with-xUnit-NodeJS/

Step 7: Configure the Spira Connection and Test Case Mappings

Create a configuration file named spira.cfg in your project directory.

For example:

[credentials]
url = https://mycompany.spiraservice.net
username = automation-user
token = YOUR_SPIRA_API_KEY
project_id = 1

# Optional configuration
release_id = 5
test_set_id = 10
create_build = true

[test_cases]
k6.thresholds.http_req_duration = 101
k6.thresholds.http_req_failed = 102

The configuration parameters control how the test results are sent to Spira.

ParameterDescription
urlURL of the Spira installation
usernameSpira user account for reporting results
tokenAPI key for the Spira account
project_idID of the Spira project
release_idOptional release associated with the test runs
test_set_idOptional default test set
create_buildWhether to create a Spira build record
[test_cases]Mappings from JUnit test identifiers to Spira test case IDs

The [test_cases] section is particularly important.

Each entry maps a JUnit test identifier, consisting of its classname and test case name, to an existing Spira test case ID.

For example:

k6.thresholds.http_req_duration = 101

This instructs the importer to record the result of the http_req_duration performance threshold against Spira test case TC:101.

The exact mapping names must match the attributes in your generated junit-spira.xml file. If the converter produces different metric names or you customize the XML, update the configuration accordingly.

Security note: Never commit your Spira API key to a source-control repository. In production CI/CD environments, store the API key in a secure secrets manager and populate the configuration file dynamically during pipeline execution.

Step 8: Import the JUnit Results into Spira

After preparing the JUnit XML file and configuring the Spira connection, execute Inflectra's xUnit importer.

From the project directory, run:

node node_modules/spira-addons-xunit/spira-xunit-reader.js \
  junit-spira.xml spira.cfg

The importer reads the XML file, identifies each test case, looks up its mapping in the Spira configuration file, and submits the corresponding execution results to Spira.

When the process completes successfully, the importer should display a confirmation similar to:

Sending test results to Spira at URL
'https://mycompany.spiraservice.net'.

Successfully reported 2 test cases to Spira.

The exact output depends on the installed version of the importer.

You can then log into Spira and navigate to Testing > Test Runs in the appropriate project to view the imported performance test results.

Each successfully mapped JUnit test case will appear as a test execution result associated with its corresponding Spira test case.

For example, if the HTTP response-time threshold passes but the error-rate threshold fails, Spira will record a passed execution for TC:101 and a failed execution for TC:102.

If a release or test set was specified in the configuration, the imported results will also be associated with the relevant artifact, helping teams understand the quality status of a particular release.

Step 9: Automate the Integration in a CI/CD Pipeline

The entire process can be automated as part of a continuous integration or delivery pipeline.

The workflow is straightforward: execute the k6 performance test, capture the output, convert it to JUnit XML, prepare the XML for Spira, and run the importer.

A simple Bash example is shown below:

#!/usr/bin/env bash

set -euo pipefail

# 1. Execute k6 and save its console output.
# Preserve the original exit code, including threshold failures.
set +e
k6 run --no-color load-test.js \
  > k6-output.txt 2>&1
K6_STATUS=$?
set -e

# 2. Display the original k6 output.
cat k6-output.txt

# 3. Convert the console output to JUnit XML.
npx k6-to-junit junit.xml < k6-output.txt

# 4. Prepare the XML for Spira.
node prepare-junit.js

# 5. Import the JUnit results into Spira.
node node_modules/spira-addons-xunit/spira-xunit-reader.js \
  junit-spira.xml spira.cfg

# 6. Preserve the original k6 test status.
exit "$K6_STATUS"

In this example, k6 run --no-color disables terminal color codes so that the converter receives clean text. Its console output is captured in k6-output.txt, which is then passed to k6-to-junit.

The script generates the JUnit XML file, adds the required classname attributes, and uploads the resulting test outcomes to Spira.

An important feature of the example is that it preserves the original k6 exit status. When a performance threshold fails, k6 can exit with a non-zero status. Instead of terminating the integration immediately, the script continues to convert and upload the results to Spira before returning the original status to the CI/CD system.

This ensures that failed performance tests are recorded in Spira while still allowing the CI/CD pipeline to enforce its performance quality gates.

For production environments, we recommend additional validation to confirm that the generated XML contains the expected test cases and that the importer successfully recorded all of them. In particular, do not treat the mere existence of a JUnit XML file as proof that all configured performance thresholds were converted successfully.

This integration can be used with common CI/CD platforms, including Jenkins, GitHub Actions, GitLab CI/CD, Azure DevOps, and other build orchestration systems.

Understanding the Results in Spira

The k6-to-junit integration is designed to transfer summarized test execution outcomes rather than the full performance telemetry captured by k6.

For example, the converter can generate a JUnit failure when a recognized performance threshold is exceeded. This result can then be recorded in Spira against the appropriate test case.

However, the converter does not automatically transform all performance measurements into structured Spira metrics. Detailed measurements such as throughput, response-time distributions, virtual-user counts, and individual HTTP request timings remain available through k6's native reporting and observability integrations.

For organizations that need more detailed performance analysis, we recommend retaining the original k6 execution output alongside the JUnit report. You may also configure k6 to export metrics to a dedicated performance monitoring platform.

This allows Spira to serve as the centralized system of record for test execution, traceability, and release readiness, while k6 and related observability tools provide detailed performance analytics.

Troubleshooting

The JUnit XML file is empty or contains no test cases

Verify that your k6 script defines performance thresholds and that the console output contains the corresponding threshold results.

The k6-to-junit utility relies on parsing the textual k6 output. If thresholds are not present or the output format is incompatible with the converter, it may not generate the expected JUnit test cases.

The Spira importer cannot find a matching test case

Confirm that the classname and name attributes in junit-spira.xml match the identifiers defined in the [test_cases] section of spira.cfg.

For example, the XML test case:

<testcase
  classname="k6.thresholds"
  name="http_req_duration" />

must have a corresponding mapping such as:

k6.thresholds.http_req_duration = 101

If the generated test case name is different, the configuration must be updated to use that name.

The importer reports that the classname is undefined

This can happen when the generated JUnit XML does not contain classname attributes.

Ensure that the prepare-junit.js script executes successfully and that you pass the resulting junit-spira.xml file, rather than the original junit.xml, to the Spira importer.

The k6 test fails and the pipeline stops before importing results

By default, many CI/CD environments terminate a testing stage when a command exits with a non-zero status.

Configure the pipeline to preserve the k6 execution result and continue running the conversion and import steps. The example in Step 9 demonstrates one way to accomplish this using Bash.

The converter does not recognize the k6 output

Because k6-to-junit parses the text printed by k6, changes to console formatting may affect its ability to identify threshold results.

Ensure that terminal color codes are disabled, verify that the test output is captured correctly, and inspect the generated XML.

If your version of k6 is not compatible with the converter, consider using Grafana's JUnit helper approach instead, since it processes the structured end-of-test summary rather than parsing console text.

Spira authentication fails

Verify that the Spira URL, username, API key, and project ID are configured correctly.

Also confirm that the Spira user account has appropriate permissions to create automated test runs in the selected project.

The JUnit report does not contain all performance metrics

This is expected. The converter primarily reports results from the recognized threshold entries in the k6 console output.

For complete performance telemetry, retain k6's original results or use a dedicated observability platform.

Conclusion

Integrating Grafana k6 with Inflectra Spira using k6-to-junit provides a practical way to include performance testing results in an organization's centralized test management and software delivery processes.

By converting k6 console output into standard JUnit XML and importing the results using Inflectra's xUnit importer, development and QA teams can associate performance testing outcomes with specific Spira test cases, releases, and builds.

This approach is particularly useful for organizations that already execute k6 scripts in their CI/CD pipelines and want to add Spira reporting without making substantial changes to their existing performance tests.

The result is improved visibility into application performance quality, stronger traceability between non-functional requirements and test execution, and a more complete picture of release readiness within Spira.

For teams looking for an alternative integration that processes structured k6 summary data directly, Inflectra also provides a complementary knowledge base article covering Grafana's JUnit helper approach.

Additional Resources