Domain 3 β€” Module 3 of 12 25%
10 of 27 overall
Domain 3: Design and implement build and release pipelines Free ⏱ ~8 min read

Tests and Quality Gates: A Report Is Not a Stop Signal

Build a layered test strategy, publish usable results, enforce failures, and use Copilot or MCP without trusting generated coverage claims.

A dashboard warning is not a locked door

A smoke alarm reports a problem; a fire door limits what can happen next. A test report and a release gate differ in the same way. A pipeline can upload a red test report and still deploy if no task result, condition, or approval actually blocks it.

Define quality policy first: which failures block a PR, which block deployment, who may approve exceptions, and what evidence is retained. Then implement the enforcement and test it with a controlled failure.

Put each test where it gives useful feedback

LayerExample for a booking servicePlacement
Local checksFormatting, syntax, focused developer testsFast feedback before push
UnitRetry decision returns the expected valueEvery change
IntegrationAPI and queue produce one confirmationCI with controlled dependencies
End-to-endUser can complete a bookingTest environment and selected release checks
LoadLatency/error criteria under expected trafficPlanned controlled performance run
Operational smokeDeployed version answers a safe health requestAfter deployment

Avoid β€œmore tests everywhere” as a strategy. Prefer tests tied to risks. Unit tests do not prove a real identity can reach a database. Load tests do not prove a booking’s business result is correct.

Load runs can incur charges and disrupt services. Use an approved test environment, define duration and concurrency, monitor dependencies, and set stop criteria. Never aim an exploratory load test at someone else’s endpoint.

Publish results and enforce the outcome separately

This Azure Pipelines example assumes the project’s test command produces JUnit files under test-results:

steps:
- script: npm ci
  displayName: Restore
- script: npm test
  displayName: Run tests
- task: PublishTestResults@2
  condition: succeededOrFailed()
  inputs:
    testResultsFormat: JUnit
    testResultsFiles: 'test-results/*.xml'
    failTaskOnFailedTests: true
    failTaskOnMissingResultsFile: true

The test command should exit nonzero on failures. Publishing with succeededOrFailed() preserves evidence after a failed test command; it does not convert failure into success. The two failure flags address different risks: reported failing tests and no report matching the expected path.

Do not use continueOnError or || true merely to make a quality gate green. If your framework uses another report format, configure the appropriate publisher and path. A hosted agent must have the needed runtime; integration tests also need authorized connectivity and isolated test data.

GitHub Actions has the same separation: run the test command, upload diagnostics with an appropriate condition such as always(), and require the test job’s check on the PR. Artifact upload success is not test success.

Coverage is a map, not a proof

Line coverage says which instrumented lines ran, not whether assertions were meaningful. Branch coverage can expose untested decisions, but neither establishes that all requirements were tested.

Collect coverage with the project’s test framework, publish supported coverage output, and enforce an agreed threshold in a tool that actually returns a failure. Azure’s coverage publishing task makes a report visible; do not assume that publishing alone applies your desired threshold.

For a change to retry logic, inspect the uncovered error and repeated-request paths. A high repository-wide percentage can hide the one path the customer depends on.

Use Copilot and MCP as an evidence assistant

Give an approved assistant the relevant function, acceptance criteria, and existing tests. Ask for focused edge cases, not a large generated suite:

Propose tests for duplicate request identifiers and transient failure.
Use the existing test framework. Show why each assertion would fail
without the fix. Do not change the production behavior or gate policy.

Review tests for assertions that merely repeat the implementation. Run them before and after the fix in an isolated branch where appropriate.

With authorized MCP access, ask for a specific pipeline run’s failed test summary and coverage artifacts. Require the exact run/commit and source links. Let the assistant propose a YAML change, but inspect the diff for suppressed failures, broadened permissions, missing-result handling, and accidental deployment triggers.

If tools cannot read the report, say so. A fluent invented pass percentage is worse than a clearly missing result.

Knowledge Check

The test publisher succeeds, but no report exists at its configured path. Which control makes this absence fail explicitly in PublishTestResults@2?

Try it: negative-control the gate

Prerequisites: a sandbox repository with a small test runner and report output; a sandbox pipeline if testing the publisher. CI usage can be billed.

Run three controlled cases: one passing test, one failing assertion, and an intentionally wrong report path. Inspect the command exit, publisher result, visible report, and merge/deploy condition for each. Restore the correct path afterwards. Ask an assistant to explain the evidence without changing policy.

Expected observation: the failed assertion and required missing report block the intended path. Diagnostics remain accessible. β€œNo tests found” is not silently treated as a measured pass.

Cleanup: remove disposable failing tests and restore the sandbox workflow. Do not weaken shared thresholds to complete this exercise.

Question

What proves a gate works?

Click or press Enter to reveal answer

Decision check: a flaky test blocks delivery

Capture its repeated outcomes and ownership. Quarantine can be a time-bounded, visible exception with replacement coverage; silently ignoring failures turns an unreliable signal into no signal.

Read the source