Help Center/Automation & CI/CD/Your CI provider

GitLab CI: Upload JUnit Test Reports to TestCollab

Generate a JUnit XML report in GitLab CI and upload automated test results to TestCollab with @testcollab/cli.

On this page

Use @testcollab/cli in GitLab CI to create a TestCollab test plan, run your automated tests, and upload their JUnit XML results. GitLab can display the same JUnit file in its pipeline Tests view while TestCollab keeps the results with your manual testing and release history.

The core workflow is createTestPlan → tests → report. A TestCollab build record and quality gate are not prerequisites for reporting.

For CLI installation and alternative workflows, see TestCollab CLI: Install and First Run.

Prerequisites

  • A TestCollab project containing the cases you want the pipeline to execute.
  • A CI tag applied to those cases, plus the project ID, tag ID, and assignee user ID.
  • A TestCollab API token belonging to the assignee. The user must have permission to create and assign test plans.
  • A GitLab project with CI/CD enabled.
  • A test reporter that writes one JUnit XML file. The example below uses Jest and jest-junit.

Install jest-junit if it is not already part of your project:

npm install --save-dev jest-junit

Add GitLab CI/CD variables

Open Settings > CI/CD > Variables in the GitLab project and add:

Variable Value Protection
TESTCOLLAB_TOKEN Your TestCollab API token Masked and protected when appropriate
TC_PROJECT_ID TestCollab project ID Plain numeric value
TC_CI_TAG_ID Tag ID selecting the CI cases Plain numeric value
TC_ASSIGNEE_ID User ID that owns the executions Plain numeric value
TC_API_URL https://api.testcollab.io Use the EU value described below when needed

Create the token through Getting an API Token generated. Never commit the token to .gitlab-ci.yml.

Complete .gitlab-ci.yml

This example creates the plan before Jest runs. It captures the test exit code, uploads the result even after failed assertions, and then returns the original failure to GitLab. The job therefore records failed results without hiding a broken test run.

image: node:22-bookworm

stages:
  - test

test-and-report:
  stage: test
  before_script:
    - npm ci
  script:
    - |
      set -e

      npx @testcollab/cli createTestPlan \
        --project "$TC_PROJECT_ID" \
        --ci-tag-id "$TC_CI_TAG_ID" \
        --assignee-id "$TC_ASSIGNEE_ID" \
        --api-url "$TC_API_URL"

      . tmp/tc_test_plan
      mkdir -p test-results

      set +e
      JEST_JUNIT_OUTPUT_DIR=./test-results \
      JEST_JUNIT_OUTPUT_NAME=results.xml \
        npm test -- --ci --reporters=default --reporters=jest-junit
      TEST_EXIT=$?

      npx @testcollab/cli report \
        --project "$TC_PROJECT_ID" \
        --test-plan-id "$TESTCOLLAB_TEST_PLAN_ID" \
        --format junit \
        --result-file ./test-results/results.xml \
        --api-url "$TC_API_URL"
      REPORT_EXIT=$?
      set -e

      if [ "$REPORT_EXIT" -ne 0 ]; then
        exit "$REPORT_EXIT"
      fi
      exit "$TEST_EXIT"
  artifacts:
    when: always
    reports:
      junit: test-results/results.xml
    paths:
      - test-results/results.xml
    expire_in: 1 week

How the pipeline works

  1. createTestPlan creates a plan from the cases carrying your CI tag and writes TESTCOLLAB_TEST_PLAN_ID to tmp/tc_test_plan.
  2. The POSIX . command loads that ID into the current shell.
  3. Jest writes test-results/results.xml. Its exit status is saved instead of discarded.
  4. tc report maps each result to its TestCollab execution and uploads the status.
  5. The script returns a reporting error first, otherwise it returns the Jest status. Failed tests still fail the GitLab job.
  6. artifacts: when: always preserves the XML for GitLab even when the job fails.

Map JUnit tests with [TC-42]

Put the TestCollab case ID in every result-producing test title. The CLI recognizes [TC-42], TC-42, id-42, and testcase-42, case-insensitively.

describe('Authentication', () => {
  test('[TC-42] customer can sign in', async () => {
    // Test steps and assertions
  });
});

The ID must refer to a case in the plan created from the CI tag. Prefer the bracketed marker because it is visible and unambiguous in GitLab and JUnit output.

Use another JUnit-capable framework

Keep the TestCollab commands unchanged and replace only the Jest command and --result-file path. For example:

Framework Generate JUnit XML Result file
Playwright PLAYWRIGHT_JUNIT_OUTPUT_NAME=test-results/results.xml npx playwright test --reporter=junit test-results/results.xml
pytest pytest --junitxml=test-results/results.xml test-results/results.xml
Maven Surefire mvn test Pass one generated TEST-*.xml file to the CLI

tc report accepts one result file per command. Merge multiple JUnit files first or call the command once for the single combined report your runner produces.

Optional build traceability and quality gates

After result reporting works, you can optionally record the deployed build and link the plan to its version. You can separately add a quality gate after tc report. Neither extension is required, and each can be adopted independently.

Use the EU region

Set TC_API_URL to the following value for an EU account. The workflow already passes it to both TestCollab commands.

https://api-eu.testcollab.io

Troubleshooting

The JUnit artifact is missing

Confirm jest-junit is installed and the output directory and filename match test-results/results.xml. For another runner, update both the GitLab artifact path and --result-file.

TestCollab does not update a case

Inspect the JUnit <testcase name> value for [TC-42]. Confirm the case carries the selected CI tag, appears in the created plan, and is assigned to the token owner.

Authentication works on main but not merge requests

A protected GitLab variable is exposed only to protected branches and tags. Review the variable scope before allowing untrusted merge-request pipelines to access a secret.

Failed tests do not fail the job

Do not append || true without restoring the saved exit code. Keep the TEST_EXIT and final exit logic from the example.

Last updated 2026-08-26.