Wiring Tests into a Deploy Pipeline¶
This page covers how to structure QED test workflows and integrate them into a GitHub Actions deploy pipeline. It assumes the runner is already set up as described in CI/CD Overview & Runner Setup.
Design Principles¶
- API tests gate the backend deploy — they run after the backend is deployed and block promotion if they fail.
- UI tests gate the frontend deploy — they run after the frontend is deployed, by which point the backend is already up.
- Both test workflows are defined as reusable workflows (
workflow_call) so they can be called from any deploy pipeline, and also triggered manually (workflow_dispatch) for standalone runs.
API Test Workflow¶
Create .github/workflows/api-tests.yml in your API test repository (e.g. qed-sut-<app>-rest):
name: API Tests
on:
workflow_call:
inputs:
environment:
description: 'QED environment name (stag or preprod)'
required: true
type: string
workflow_dispatch:
inputs:
environment:
description: 'Target environment'
required: true
default: 'stag'
type: choice
options:
- stag
- preprod
jobs:
api-tests:
name: API Tests (${{ inputs.environment }})
runs-on: self-hosted
steps:
- name: Checkout QED-Shared
uses: actions/checkout@v4
with:
repository: <your-org>/QED-Shared
path: qed-deps/QED-Shared
ssh-key: ${{ secrets.RUNNER_SSH_PRIVATE_KEY }}
- name: Checkout QED-Shared-<app>
uses: actions/checkout@v4
with:
repository: <your-org>/QED-Shared-<app>
path: qed-deps/QED-Shared-<app>
ssh-key: ${{ secrets.RUNNER_SSH_PRIVATE_KEY }}
- name: Checkout qed-sut-<app>-rest
uses: actions/checkout@v4
with:
repository: <your-org>/qed-sut-<app>-rest
path: qed-deps/qed-sut-<app>-rest
ssh-key: ${{ secrets.RUNNER_SSH_PRIVATE_KEY }}
- name: Set up Java 22
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '22'
- name: Run API tests
env:
GRADLE_OPTS: "-Xmx1024m -Dorg.gradle.daemon=false"
QED_PASSWORD_ADMIN: ${{ secrets.QED_PASSWORD_ADMIN }}
QED_PASSWORD_MANAGER: ${{ secrets.QED_PASSWORD_MANAGER }}
QED_PASSWORD_USER: ${{ secrets.QED_PASSWORD_USER }}
run: |
chmod +x ./gradlew
./gradlew clean test -Ptestsuite=<app> -Penvironment=${{ inputs.environment }}
- name: Upload test report
if: always()
uses: actions/upload-artifact@v4
with:
name: api-test-report-${{ inputs.environment }}
path: |
qed-deps/qed-sut-<app>-rest/build/reports/tests/test/
qed-deps/qed-sut-<app>-rest/build/test-output/ExtentReport/
retention-days: 2
UI Test Workflow¶
Create .github/workflows/ui-tests.yml in your UI test repository (e.g. qed-sut-<app>-ui):
name: UI Tests
on:
workflow_call:
inputs:
environment:
description: 'QED environment name (stag or preprod)'
required: true
type: string
workflow_dispatch:
inputs:
environment:
description: 'Target environment'
required: true
default: 'stag'
type: choice
options:
- stag
- preprod
jobs:
ui-tests:
name: UI Tests (${{ inputs.environment }})
runs-on: self-hosted
defaults:
run:
working-directory: qed-framework/qed-sut-<app>-ui
steps:
- name: Checkout qed-framework
uses: actions/checkout@v4
with:
repository: <your-org>/qed-framework
path: qed-framework
ssh-key: ${{ secrets.RUNNER_SSH_PRIVATE_KEY }}
- name: Checkout QED-Shared
uses: actions/checkout@v4
with:
repository: <your-org>/QED-Shared
path: qed-framework/QED-Shared
ssh-key: ${{ secrets.RUNNER_SSH_PRIVATE_KEY }}
- name: Checkout QED-Shared-<app>
uses: actions/checkout@v4
with:
repository: <your-org>/QED-Shared-<app>
path: qed-framework/QED-Shared-<app>
ssh-key: ${{ secrets.RUNNER_SSH_PRIVATE_KEY }}
- name: Checkout qed-sut-<app>-ui
uses: actions/checkout@v4
with:
repository: <your-org>/qed-sut-<app>-ui
path: qed-framework/qed-sut-<app>-ui
ssh-key: ${{ secrets.RUNNER_SSH_PRIVATE_KEY }}
- name: Set up Java 22
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '22'
- name: Run UI tests
env:
GRADLE_OPTS: "-Xmx1024m -Dorg.gradle.daemon=false"
QED_HEADLESS: "true"
QED_PASSWORD_ADMIN: ${{ secrets.QED_PASSWORD_ADMIN }}
QED_PASSWORD_MANAGER: ${{ secrets.QED_PASSWORD_MANAGER }}
QED_PASSWORD_USER: ${{ secrets.QED_PASSWORD_USER }}
run: |
chmod +x ./gradlew
./gradlew clean test -Ptestsuite=<app> -Penvironment=${{ inputs.environment }}
- name: Upload test report
if: always()
uses: actions/upload-artifact@v4
with:
name: ui-test-report-${{ inputs.environment }}
path: |
qed-framework/qed-sut-<app>-ui/build/reports/tests/test/
qed-framework/qed-sut-<app>-ui/build/test-output/ExtentReport/
retention-days: 2
Note:
QED_HEADLESS: "true"enables headless browser mode on CI. Omit this variable when running locally to get a headed browser for debugging.
Wiring into the Backend Deploy Pipeline¶
In your backend deploy workflow, call the API test workflow after each environment deploy. The needs: dependency ensures tests only run once the backend is up, and a failure blocks promotion.
# API Tests — Staging
api-tests-staging:
name: API Tests - Staging
needs: deploy-staging
uses: <your-org>/qed-sut-<app>-rest/.github/workflows/api-tests.yml@master
with:
environment: stag
secrets: inherit
# API Tests — Pre-prod
api-tests-preprod:
name: API Tests - Pre-prod
needs: deploy-preprod
uses: <your-org>/qed-sut-<app>-rest/.github/workflows/api-tests.yml@master
with:
environment: preprod
secrets: inherit
Wiring into the Frontend Deploy Pipeline¶
UI tests belong in the frontend deploy pipeline — by the time the frontend has deployed, the backend is already up. Wire them in after each environment deploy:
# UI Tests — Staging
ui-tests-staging:
name: UI Tests - Staging
needs: deploy-staging
uses: <your-org>/qed-sut-<app>-ui/.github/workflows/ui-tests.yml@master
with:
environment: stag
secrets: inherit
# UI Tests — Pre-prod
ui-tests-preprod:
name: UI Tests - Pre-prod
needs: deploy-preprod
uses: <your-org>/qed-sut-<app>-ui/.github/workflows/ui-tests.yml@master
with:
environment: preprod
secrets: inherit
Secrets: secrets: inherit¶
The secrets: inherit directive passes all secrets from the calling repository down to the called workflow. This means every secret referenced in api-tests.yml or ui-tests.yml (including RUNNER_SSH_PRIVATE_KEY and all QED_PASSWORD_* secrets) must be defined in the calling repository — not just in the test repo.
Full Pipeline Flow¶
The recommended end-to-end promotion flow is:
Push to main
└── Build backend
└── Deploy backend (staging)
└── API tests (staging) ← blocks if failing
└── [manual] Deploy backend (preprod)
└── API tests (preprod) ← blocks if failing
Push to main
└── Build frontend
└── Deploy frontend (staging)
└── UI tests (staging) ← blocks if failing
└── [manual] Deploy frontend (preprod)
└── UI tests (preprod) ← blocks if failing
Backend and frontend pipelines run independently. The implicit ordering is maintained by your promotion workflow — the backend must be deployed and API tests must pass before you manually promote to preprod, at which point the frontend deploy and UI tests follow.