CI Results Integration

Push test results from your CI pipeline to TestIntel after each test run. Results appear in the Reporting tab with pass/fail trends, health score, and flaky test detection.


Quick Start

Add one curl command after your test step. TestIntel accepts JUnit XML (the universal format — almost every test framework can output it).


curl -X POST "http://your-testintel-server:8000/results/webhook?project=your-project" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/xml" \
  --data-binary @results.xml

Parameters

ParameterRequiredDescription
projectRecommendedProject name in TestIntel. Without this, results go to root scope ("No project").
sourceOptionalLabel for the pipeline run (e.g. Jenkins Build #42, GitHub Actions #15). Shown in the Test Runs table.
app_versionOptionalVersion or commit SHA of the application under test. Used for flaky detection correlation.
environmentOptionalEnvironment name (e.g. ci, staging, production). Useful for filtering.

Example with all parameters:


curl -X POST "http://your-server:8000/results/webhook?project=my-app&source=Jenkins+Build+%2342&app_version=v2.1.0&environment=staging" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/xml" \
  --data-binary @results.xml

GitHub Actions


name: Tests

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.12'

      - name: Install dependencies
        run: pip install -e ".[test]"

      - name: Run tests
        run: pytest --junitxml=results.xml

      - name: Push results to TestIntel
        if: always()
        run: |
          curl -s -X POST "${{ secrets.TESTINTEL_URL }}/results/webhook?project=my-app&source=GitHub+Actions+%23${{ github.run_number }}&environment=ci" \
            -H "x-api-key: ${{ secrets.TESTINTEL_API_KEY }}" \
            -H "Content-Type: application/xml" \
            --data-binary @results.xml || echo "TestIntel upload skipped"

Setup:

  1. Go to your repo → Settings → Secrets and variables → Actions
  1. Add TESTINTEL_URL — your TestIntel server URL (e.g. http://52.1.2.3:8000 or https://testintel.yourcompany.com). No trailing slash.
  1. Add TESTINTEL_API_KEY — your TestIntel API key

Jenkins (Declarative Pipeline)


pipeline {
    agent any

    stages {
        stage('Test') {
            steps {
                sh 'pytest --junitxml=results.xml'
            }
            post {
                always {
                    // Standard Jenkins JUnit reporting
                    junit 'results.xml'

                    // Push to TestIntel
                    sh '''
                        curl -s -X POST "${TESTINTEL_URL}/results/webhook?project=my-app&source=Jenkins+Build+%23${BUILD_NUMBER}&environment=ci" \
                          -H "x-api-key: ${TESTINTEL_API_KEY}" \
                          -H "Content-Type: application/xml" \
                          --data-binary @results.xml || echo "TestIntel upload skipped"
                    '''
                }
            }
        }
    }
}

Setup:

  1. Add TESTINTEL_URL and TESTINTEL_API_KEY as Jenkins credentials (Secret text)
  1. Bind them as environment variables in your pipeline or use withCredentials

GitLab CI


test:
  stage: test
  script:
    - pip install -e ".[test]"
    - pytest --junitxml=results.xml
  after_script:
    - |
      curl -s -X POST "$TESTINTEL_URL/results/webhook?project=my-app&source=GitLab+Pipeline+%23$CI_PIPELINE_ID&environment=ci" \
        -H "x-api-key: $TESTINTEL_API_KEY" \
        -H "Content-Type: application/xml" \
        --data-binary @results.xml || echo "TestIntel upload skipped"
  artifacts:
    reports:
      junit: results.xml

Setup:

  1. Go to your project → Settings → CI/CD → Variables
  1. Add TESTINTEL_URL and TESTINTEL_API_KEY (masked)

Generating JUnit XML

Most test frameworks support JUnit XML output:

FrameworkCommand
pytestpytest --junitxml=results.xml
Jestjest --reporters=jest-junit (install jest-junit)
PlaywrightAdd ['junit', { outputFile: 'results.xml' }] to reporter config
CypressUse cypress-junit-reporter
JUnit/TestNGBuilt-in via Maven Surefire / Gradle
NUnitdotnet test --logger "junit;LogFilePath=results.xml"
Go`go test -v ./... 2>&1 \go-junit-report > results.xml`
behavebehave --junit --junit-directory=./results

What Happens After Upload

  1. Results appear in Reporting — each upload creates a new entry in the Test Runs table
  1. Inventory updated — tests that match by name or ID get their test_result and last_run updated
  1. Health score recalculates — based on pass rate, run frequency, failure trends, and flaky rate
  1. Flaky detection — tests that flip between pass/fail across runs are flagged

Troubleshooting

{"detail":"Not Found"} error:

Your TESTINTEL_URL has a trailing slash. Remove it — the URL must not end with /.

A trailing slash causes the path to become //results/webhook which doesn't match any route.

"Empty request body" error:

The results.xml file doesn't exist or is empty. Make sure your test step generates it before the upload step.

"Invalid API key" error:

Check that TESTINTEL_API_KEY matches a valid key on your TestIntel instance.

Results don't appear in the project:

Verify the ?project= parameter matches the exact project name in TestIntel (case-sensitive).

Connection refused:

Ensure your CI runner can reach the TestIntel server. For EC2 deployments, check the security group allows inbound traffic from your CI provider's IP range.

Upload succeeds but no tests matched:

This is normal for the first upload if your inventory is empty. Results are still stored for reporting. Once you add tests to the inventory (via Zephyr Pull, Xray Pull, or Staging promotion), future results will match by name.