What is Postman?

Postman is a popular API development and software testing tool that lets you send requests to APIs, inspect the responses, and automate tests around them—all through a graphical interface instead of writing ad-hoc scripts from scratch. It’s widely used by developers and QA engineers to verify that REST, GraphQL, and other HTTP-based APIs behave correctly.

Why Integrate Postman with SpiraTest?

The integration between Postman and SpiraTest brings together two complementary strengths: Postman’s powerful API testing with SpiraTest’s end-to-end test management and traceability. Postman collections and test scripts are directly linked to SpiraTest requirements, user stories, and test cases, so every API test automatically maps back to what the business actually asked for. As soon as you run your Postman tests, the results are captured inside SpiraTest, where you can see coverage, pass/fail status, and impacted requirements in one place—no more manual reconciliation between tools or copy-pasting from Postman reports into spreadsheets.

Because the integration also feeds Postman runs (including those executed in CI/CD) into SpiraTest, API checks appear alongside your manual, UI, and other automated tests on the same dashboards. Test runs, defects, and trends across sprints and releases are visible in real time, with failed Postman tests automatically logged as incidents or linked to existing ones. This gives QA, Dev, and management a shared, always-up-to-date view of API health and overall release readiness, turning Postman from an isolated testing utility into a fully traceable, auditable part of your quality ecosystem.

Setting up the Integration with SpiraTest

We will be using the SpiraTest xUnit reporter that reads standard JUnit XML files generated by Postman, and reports the test results back to Spira.

To setup the integration we will need to follow these steps:

  1. Save your Postman tests as a collection
  2. Execute the Postman tests using Newman CLI
  3. Configure the SpiraTest xUnit Reporter
  4. Run the xUnit Reporter to upload the results to SpiraTest

We shall describe each of these in turn:

1. Save your Postman tests as a collection

The first thing you need to do is open up your existing Postman tests in the Postman IDE and export them as a collection file:

On the next screen ignore the various sharing options, and instead choose the option to Export JSON:

In this example we have saved the following collection JSON file:

My Collection.postman_collection.json

This is the version of the Postman test that can be executed from the command-line (either interactively or using a CI/CD platform such as Jenkins, GitLab, or GitHub).

2. Execute the Postman tests using Newman CLI

Now that we have our saved test collection, we need to execute it using Newman,  the command-line collection runner for Postman.  This allows us to execute Postman collections directly from the command line. Follow these steps to generate JUnit-style XML files with Newman:

Prerequisite: This step requires that you have already installed NodeJS and the Node Package Manager (NPM) locally.

If you haven't already, we need to install Newman globally using npm:

npm install -g newman

You may also need to install the JUnit XML reporter for Newman as well:

npm install -g newman-reporter-junitfull

Once that is done, you can call Newman to execute the Postman tests and actually generate the results file:

newman run ".\My Collection.postman_collection.json" -r junit --reporter-junit-export results/test-results.xml  

Once that is done you should have a JUnit results XML file generated. It will look something like the following:

<?xml version="1.0" encoding="UTF-8"?>
<testsuites name="My Collection" tests="3" time="0.167">
  <testsuite name="Get data" id="e4e763b7-f657-497a-8c6c-e9b60f7a96fa" timestamp="2025-11-20T01:07:28.542Z" tests="1" failures="1" errors="1" time="0.000">
    <system-err><![CDATA[Iteration: 0
RequestError: Error: runtime:extensions~request: request url is empty
    at Requester.request (C:\Users\UserName\AppData\Roaming\npm\node_modules\newman\node_modules\postman-runtime\lib\requester\requester.js:421:26)
    at C:\Users\UserName\AppData\Roaming\npm\node_modules\newman\node_modules\postman-runtime\lib\runner\extensions\http-request.command.js:156:37
    at Requester.create (C:\Users\UserName\AppData\Roaming\npm\node_modules\newman\node_modules\postman-runtime\lib\requester\requester.js:508:16)
    at RequesterPool.create (C:\Users\UserName\AppData\Roaming\npm\node_modules\newman\node_modules\postman-runtime\lib\requester\requester-pool.js:67:22)
    at C:\Users\UserName\AppData\Roaming\npm\node_modules\newman\node_modules\postman-runtime\lib\runner\extensions\http-request.command.js:105:32
    at C:\Users\UserName\AppData\Roaming\npm\node_modules\newman\node_modules\async\dist\async.js:252:13
    at wrapper (C:\Users\UserName\AppData\Roaming\npm\node_modules\newman\node_modules\async\dist\async.js:273:20)
    at replenish (C:\Users\UserName\AppData\Roaming\npm\node_modules\newman\node_modules\async\dist\async.js:443:29)
    at C:\Users\UserName\AppData\Roaming\npm\node_modules\newman\node_modules\async\dist\async.js:453:13
    at eachOfLimit (C:\Users\UserName\AppData\Roaming\npm\node_modules\newman\node_modules\async\dist\async.js:479:36)
    at awaitable (C:\Users\UserName\AppData\Roaming\npm\node_modules\newman\node_modules\async\dist\async.js:212:32)
    at eachOfSeries (C:\Users\UserName\AppData\Roaming\npm\node_modules\newman\node_modules\async\dist\async.js:815:16)
    at awaitable (C:\Users\UserName\AppData\Roaming\npm\node_modules\newman\node_modules\async\dist\async.js:212:32)
    at _asyncMap (C:\Users\UserName\AppData\Roaming\npm\node_modules\newman\node_modules\async\dist\async.js:245:16)
    at mapSeries (C:\Users\UserName\AppData\Roaming\npm\node_modules\newman\node_modules\async\dist\async.js:839:16)
    at awaitable (C:\Users\UserName\AppData\Roaming\npm\node_modules\newman\node_modules\async\dist\async.js:212:32)

---
]]></system-err>
    <testcase name="Status code is 200" time="0.000" classname="GetData">
      <failure type="AssertionFailure" message="expected PostmanResponse{ …(5) } to have property 'code'">
        <![CDATA[Failed 1 times.]]>
        <![CDATA[Collection JSON ID: 86914729-e5f5-4032-b92c-dd6fc6299aa5.]]>
        <![CDATA[Collection name: My Collection.]]>
        <![CDATA[Request name: Get data.]]>
        <![CDATA[Test description: Status code is 200.]]>
        <![CDATA[Error message: expected PostmanResponse{ …(5) } to have property 'code'.]]>
        <![CDATA[Stacktrace: AssertionError: expected PostmanResponse{ …(5) } to have property 'code'
   at Object.eval sandbox-script.js:1:1).]]>
      </failure>
    </testcase>
  </testsuite>
  <testsuite name="Post data" id="f91b6730-ece9-4095-bb94-1b237d56b1ef" timestamp="2025-11-20T01:07:28.542Z" tests="1" failures="1" errors="1" time="0.000">
    <system-err><![CDATA[Iteration: 0
RequestError: Error: runtime:extensions~request: request url is empty
    at Requester.request (C:\Users\UserName\AppData\Roaming\npm\node_modules\newman\node_modules\postman-runtime\lib\requester\requester.js:421:26)
    at C:\Users\UserName\AppData\Roaming\npm\node_modules\newman\node_modules\postman-runtime\lib\runner\extensions\http-request.command.js:156:37
    at Requester.create (C:\Users\UserName\AppData\Roaming\npm\node_modules\newman\node_modules\postman-runtime\lib\requester\requester.js:508:16)
    at RequesterPool.create (C:\Users\UserName\AppData\Roaming\npm\node_modules\newman\node_modules\postman-runtime\lib\requester\requester-pool.js:67:22)
    at C:\Users\UserName\AppData\Roaming\npm\node_modules\newman\node_modules\postman-runtime\lib\runner\extensions\http-request.command.js:105:32
    at C:\Users\UserName\AppData\Roaming\npm\node_modules\newman\node_modules\async\dist\async.js:252:13
    at wrapper (C:\Users\UserName\AppData\Roaming\npm\node_modules\newman\node_modules\async\dist\async.js:273:20)
    at replenish (C:\Users\UserName\AppData\Roaming\npm\node_modules\newman\node_modules\async\dist\async.js:443:29)
    at C:\Users\UserName\AppData\Roaming\npm\node_modules\newman\node_modules\async\dist\async.js:453:13
    at eachOfLimit (C:\Users\UserName\AppData\Roaming\npm\node_modules\newman\node_modules\async\dist\async.js:479:36)
    at awaitable (C:\Users\UserName\AppData\Roaming\npm\node_modules\newman\node_modules\async\dist\async.js:212:32)
    at eachOfSeries (C:\Users\UserName\AppData\Roaming\npm\node_modules\newman\node_modules\async\dist\async.js:815:16)
    at awaitable (C:\Users\UserName\AppData\Roaming\npm\node_modules\newman\node_modules\async\dist\async.js:212:32)
    at _asyncMap (C:\Users\UserName\AppData\Roaming\npm\node_modules\newman\node_modules\async\dist\async.js:245:16)
    at mapSeries (C:\Users\UserName\AppData\Roaming\npm\node_modules\newman\node_modules\async\dist\async.js:839:16)
    at awaitable (C:\Users\UserName\AppData\Roaming\npm\node_modules\newman\node_modules\async\dist\async.js:212:32)

---
]]></system-err>
    <testcase name="Successful POST request" time="0.000" classname="PostData">
      <failure type="AssertionFailure" message="expected undefined to be one of [ 200, 201 ]">
        <![CDATA[Failed 1 times.]]>
        <![CDATA[Collection JSON ID: 86914729-e5f5-4032-b92c-dd6fc6299aa5.]]>
        <![CDATA[Collection name: My Collection.]]>
        <![CDATA[Request name: Post data.]]>
        <![CDATA[Test description: Successful POST request.]]>
        <![CDATA[Error message: expected undefined to be one of [ 200, 201 ].]]>
        <![CDATA[Stacktrace: AssertionError: expected undefined to be one of [ 200, 201 ]
   at Object.eval sandbox-script.js:1:2).]]>
      </failure>
    </testcase>
  </testsuite>
  <testsuite name="GET Post by ID" id="6d8fbd76-8f85-4eee-971b-e8be997cb5f0" timestamp="2025-11-20T01:07:28.542Z" tests="5" failures="1" errors="0" time="0.167">
    <testcase name="Status code is 200" time="0.167" classname="GetPostById"/>
    <testcase name="Response time is less than 800ms" time="0.167" classname="GetPostById"/>
    <testcase name="Content-Type contains application/json" time="0.167" classname="GetPostById"/>
    <testcase name="Body is an array with length 1" time="0.167" classname="GetPostById">
      <failure type="AssertionFailure" message="expected 100 to deeply equal 1">
        <![CDATA[Failed 1 times.]]>
        <![CDATA[Collection JSON ID: 86914729-e5f5-4032-b92c-dd6fc6299aa5.]]>
        <![CDATA[Collection name: My Collection.]]>
        <![CDATA[Request name: GET Post by ID.]]>
        <![CDATA[Test description: Body is an array with length 1.]]>
        <![CDATA[Error message: expected 100 to deeply equal 1.]]>
        <![CDATA[Stacktrace: AssertionError: expected 100 to deeply equal 1
   at Object.eval sandbox-script.js:4:3).]]>
      </failure>
    </testcase>
    <testcase name="Object has required keys" time="0.167" classname="GetPostById"/>
  </testsuite>
</testsuites>

Now we can move to the next step and configure the SpiraTest xUnit reporter that will read the test results.

 

3. Configure the SpiraTest xUnit Reporter

Prerequisite: This step requires that you have Python installed and PIP.

The first thing we need to do is download and install the SpiraTest xUnit Reporter from PyPi into the same folder that has our installation on Newman:

pip install spira-addons-xunit request

Once that is installed, you need to modify the spira.cfg file to point to your SpiraTest installation and map the various test cases and test sets in SpiraTest to the test suites and test cases reported by Postman. For the example test results file above, we would use a mapping similar to:

[credentials]
# Following are required
url = https://mycompany.spiraservice.net/
username = username
token = {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX}
project_id = 68
# Following are optional:
release_id = 228
test_set_id = 77
create_build = true

# Spira Test case for a specific classname.name
[test_cases]
GetData.Status code is 200 = 1130
PostData.Successful POST request = 1131
GetPostById.Status code is 200 = 1132
GetPostById.Response time is less than 800ms = 1133
GetPostById.Content-Type contains application/json = 1134
GetPostById.Body is an array with length 1 = 1135
GetPostById.Object has required keys = 1136

# Spira Test sets for a specific name
# If not, the global value is used instead
[test_sets]
Get data = 78
Post data = 79
GET Post by ID = 77

This maps the test cases in the XML results file to the following SpiraTest test cases:

and the following more detailed ones that we have grouped into a folder:

In our sample, we have also mapped the test suites to the following SpiraTest test sets:

Now that we have completed the mapping, we can now upload the results into SpiraTest...

 

4. Run the xUnit Reporter to upload the results to SpiraTest

Once that is done, we simply need to call the SpiraTest xUnit reporter and pass in our configuration file and the path to the Postman results:

python -m spira_xunit_reader .\results\test-results.xml spira.cfg

This will report something like the following:

Creating new build in Spira at URL 'https://mycompany.spiraservice.net/'.
Sending test results to Spira at URL 'https://mycompany.spiraservice.net/'.
Successfully reported 7 test cases to Spira.

Inside SpiraTest you will see generated test runs like the following:

If we click on one of the test results they will look like the following:

Congratulations, we have now executed a Postman test and uploaded the results into SpiraTest.

Once you have finished the initial setup, your CI/CD pipeline would basically only need the following commands to run the exported collection:

newman run ".\My Collection.postman_collection.json" -r junit --reporter-junit-export results/test-results.xml  
python -m spira_xunit_reader .\results\test-results.xml spira.cfg