Chapter 24Lesson 01~175 minutes

Test Reports, Coverage, JUnit, Code Quality, Browser Performance, Accessibility, and Pipeline Feedback: Concepts, Architecture, and Mental Model

Model quality feedback as a chain from tool exit semantics and machine-readable evidence through GitLab report ingestion to merge-request/pipeline presentation, while retaining the raw report and exact source identity.

JUnitCoverageStructured reportsPipeline feedbackEvidence

Learning objectives

  • Trace a quality tool's exit code and machine-readable report separately through GitLab ingestion and UI feedback.
  • Explain why JUnit report contents do not, by themselves, determine job success or failure.
  • Distinguish percentage extraction with coverage from line-level Cobertura/JaCoCo visualization with coverage_report.
  • Compare JUnit, Code Quality, Accessibility, and Browser Performance report capabilities and tier boundaries.
  • Inspect source SHA, report schema/path, raw artifact, parser/UI result, and retention without mutating project state.

1. The practical problem: a widget is not the evidence

Chapter 23 ended with a strong distribution chain: exact source SHA, producer pipeline, immutable package version or image digest, and consumer verification. Quality feedback needs the same discipline. A test framework can exit non-zero but fail to write its report. It can also exit zero while a hand-authored or stale report contains failures. GitLab can ingest a valid report and render a useful widget, but that widget is a view over evidence, not the evidence itself.

The chapter therefore separates three things that are often accidentally collapsed: tool execution semantics, report ingestion semantics, and delivery policy. The first answers whether the command succeeded. The second answers whether GitLab parsed structured evidence. The third answers whether a result is advisory or blocking.

Core rule: preserve the raw report, the tool exit code, the source SHA, and the job/pipeline identity together. A reviewer should be able to reconstruct why the job passed or failed even if the GitLab widget is unavailable later.

2. Terms before YAML

Term Meaning Boundary
Tool exit code The process result returned by the test/lint/quality command. This is what normally drives job status. Report contents do not automatically override it.
Structured report Machine-readable evidence such as JUnit XML, Cobertura XML, or Code Quality JSON. A valid schema is necessary for GitLab ingestion; raw text logs are not equivalent.
Report artifact A file declared under artifacts:reports. GitLab uploads report artifacts regardless of job success/failure. Add artifacts:paths if humans also need to browse/download the raw file.
Widget/annotation A GitLab presentation of parsed report data in an MR, pipeline, or diff. Presentation may have tier, baseline, aggregation, or timing requirements.
Coverage percentage A number extracted from job log text by the coverage regex. It does not create line annotations.
Coverage visualization Cobertura or JaCoCo XML parsed via coverage_report. It creates MR diff annotations, not the percentage widget by itself.
Gate A policy or job-result decision that blocks progression. A report can be purely advisory even when visible in the UI.

3. Mental model: execution → report → parser → feedback → policy

A tool reads a known source revision and returns both a process result and, ideally, a structured file. The runner uploads that file. GitLab validates and parses it according to the declared report type. The UI then renders the parsed data in a Tests tab, merge-request report, diff annotation, or specialized widget. Finally, your pipeline policy decides whether the evidence is blocking, advisory, or merely informational.

flowchart TD A[Exact source SHA] --> B[Test or quality tool] B --> C[Exit code] B --> D[Machine-readable report] C --> E[Job status] D --> F[artifacts:reports ingestion] F --> G[Pipeline or MR feedback] E --> H[Gate / advisory policy] G --> H D --> I[Retained raw evidence] H --> J[Reviewer decision]

The two arrows from the tool are intentional: exit status and report data are independent channels. A sound operating model checks both.

4. State to capture before changing anything

State layer Evidence to capture Why it matters
Source/revision CI_PIPELINE_SOURCE, ref, CI_COMMIT_SHA, pipeline/job IDs A report is meaningful only if you can prove which source revision produced it.
Compiled configuration Merged YAML, effective rules, report path/type, runner image/tool version Distinguishes what GitLab compiled from what the author intended.
Tool execution Tool exit code, command/version, deterministic inputs Job status comes from process semantics, not from the existence of a report file.
Report artifact Type, path, schema, size, checksum, parser result Separates raw evidence from GitLab UI interpretation.
GitLab feedback Tests tab, MR test summary, coverage percentage, diff annotations, Code Quality/accessibility/browser widgets Shows what GitLab ingested and surfaced to reviewers.
Governance Blocking/advisory rule, artifact access/retention, failure owner Explains whether a finding blocks delivery and who owns remediation.
External state None for the mandatory lab; later security/deployment systems may consume results Prevents a green quality widget from being confused with deployment or runtime health.

5. Current report capabilities and boundaries

Capability Current tier / behavior Important caveat
JUnit unit-test report Free/Premium/Ultimate; shown in pipeline Tests and MR test summary. JUnit report data does not determine job status. The test command must exit non-zero to fail the job.
Coverage percentage Free/Premium/Ultimate with coverage regex. GitLab extracts a matching number from job output. Validate the regex against the tool version.
Coverage visualization Free/Premium/Ultimate using Cobertura or JaCoCo coverage_report. Shows changed-line annotations only; configure coverage separately for the percentage widget.
Code Quality import Free/Premium/Ultimate for report import/MR report. Pipeline full Code Quality view is Premium/Ultimate; MR changes annotations are Ultimate. Built-in CodeClimate template is deprecated.
Accessibility Free/Premium/Ultimate; Pa11y-based report can appear in MR widget. Accessibility findings are evidence; decide separately whether the tool exit/status blocks.
Browser Performance Premium/Ultimate. GitLab cannot combine multiple browser-performance reports, and MR comparison needs appropriate target-branch baseline data.

6. File and aggregation constraints are part of design

Current unit-test documentation requires JUnit XML files smaller than 30 MB each and a total under 100 MB per job. JUnit declarations accept files, arrays, and filename patterns but not bare directories. Cobertura visualization currently limits a report file to 10 MiB and documents a 100-<source>-node limit. Multiple coverage reports can be collected with wildcards and GitLab merges them for visualization.

Not all report types aggregate the same way. Browser Performance cannot display combined results from multiple report artifacts. Reports from child pipelines can produce coverage diff annotations, but report artifacts are not generally combined back into parent pipelines. Design fan-out/fan-in based on each report type's actual ingestion semantics, not on an assumption that every JSON/XML file will merge.

7. Raw reports and UI summaries serve different jobs

artifacts:reports tells GitLab how to ingest structured data. If you also need the report file in the artifact browser, add it to artifacts:paths. That may increase storage, so set an explicit retention period that preserves the evidence for your review/audit horizon. Report access can also be restricted with artifact-access settings; a test report can contain filenames, stack traces, URLs, or other information that should not be publicly downloadable.

Evidence discipline: retain enough raw data to independently verify a UI summary, but do not retain sensitive output indefinitely. Report retention and report access are governance controls, not mere storage tuning.

8. Read-only inspection first

printf 'source=%s ref=%s sha=%s pipeline=%s job=%s\n' \
  "$CI_PIPELINE_SOURCE" "$CI_COMMIT_REF_NAME" "$CI_COMMIT_SHA" \
  "$CI_PIPELINE_ID" "$CI_JOB_ID"

# Inspect report files without modifying them.
ls -lh reports/ 2>/dev/null || true
sha256sum reports/* 2>/dev/null || true

# Validate XML syntax locally before blaming GitLab ingestion.
python - <<'PY2'
from pathlib import Path
import xml.etree.ElementTree as ET
for p in Path('reports').glob('*.xml'):
    ET.parse(p)
    print('valid_xml', p)
PY2

9. Common misconceptions

Misconception Why it fails Better mental model
“JUnit says failed, so GitLab will fail the job.” The JUnit report does not control job exit status. The test process must return non-zero; preserve the report independently.
“Coverage report means coverage percentage.” coverage_report and coverage serve different UI outputs. Configure percentage extraction and diff visualization separately when both are required.
“A green widget proves the report is current.” A stale/wrong-SHA file can still be valid syntax. Bind report checksum to source SHA, pipeline ID, and job ID.
“All report types aggregate across shards.” Aggregation differs by type; browser performance cannot combine multiple reports. Verify report-specific aggregation before sharding.
“The UI is enough for audit.” Widgets are derived presentation and can change/disappear with retention. Keep raw evidence and identity metadata for the required period.

Knowledge check

Why can a job be green even when a JUnit XML file contains a failed test?

What is the difference between coverage and coverage_report?

Why should the raw report be retained when GitLab already shows a widget?

Can multiple browser-performance reports be combined into one GitLab result?

What four identities should accompany a structured report?

Next lesson

Guided hands-on workflow and core operations

Create deterministic JUnit and Cobertura files, upload them as structured reports, surface Code Quality data, and prove the raw evidence independently.

Version and compatibility note

GitLab and GitLab Runner evolve continuously. Treat version-sensitive YAML, runner/executor behavior, APIs, security features, policy controls, tiers, and deprecations as assumptions to verify against the current official GitLab documentation before production use. Preserve the exact project/ref/SHA, compiled configuration, pipeline/job IDs, runner/tool versions, and external target evidence used for any reproducible lab or incident record.

Official references and version notes

Documentation verification date: 2026-09-12. JUnit/unit-test reports, coverage percentage extraction, Cobertura/JaCoCo coverage visualization, Code Quality report import, and Accessibility reports are available on Free/Premium/Ultimate. Browser Performance reports are Premium/Ultimate. Code Quality's built-in CodeClimate-based template was deprecated in GitLab 17.3 and is planned for removal in GitLab 19.0; current guidance is to integrate a supported tool's report directly. JUnit report files currently have documented size limits of 30 MB per individual file and 100 MB total per job. Cobertura visualization currently documents a 10 MiB file limit and a 100-source-node limit. Report-type limits should be rechecked before production use.

Current assumptions used in this chapter: Version-sensitive YAML, Runner/executor behavior, APIs, security features, policy controls, tiers, and deprecations must be verified against the exact GitLab, GitLab Runner, tool, and external-system versions used in production. Preserve the exact project/ref/SHA, compiled configuration, pipeline/job IDs, runner/tool versions, and external target evidence used for reproducible work.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.