Lesson 2 of 8 · DevOps for QA
GitHub Pull Request Workflow
How testers work in a GitHub pull request flow: branching, opening PRs, reviewing test code, required status checks, CODEOWNERS and keeping main green.
beginner 6 min read Updated
You will learn to
- Describe the branch, pull request, review and merge cycle on GitHub
- Review test code in a pull request with a useful checklist
- Explain branch protection, required status checks and CODEOWNERS
- Use the GitHub CLI to create and check out pull requests
Prerequisites
- Git for Testers
Most teams that host code on GitHub use some variant of the same flow: short-lived branches, pull requests (PRs), automated checks, human review, then merge into main. As a tester you take part in this flow in two ways. You open PRs with your own test code, and you review PRs from developers, asking whether the change is adequately tested. Both roles are where quality is cheapest to influence.
The flow end to end
main ──●────────────────●──────────▶
\ /
●──●──●──────● (feature branch)
│ │ │ │
commits PR opened → checks run → review → merge
- Create a branch from an up-to-date
main. - Commit small, focused changes and push the branch.
- Open a PR describing what changed and how it was tested.
- CI runs the required checks (lint, unit tests, end-to-end tests).
- Reviewers comment, request changes or approve.
- The author addresses feedback with new commits.
- Once checks pass and approvals are in, the PR is merged and the branch deleted.
This is often called GitHub Flow. Some organisations add release branches or environments on top, but the PR remains the unit of change.
Opening a good pull request
A PR description is a test artefact. Reviewers, and later investigators, read it to understand intent. A useful template:
## What
Adds end-to-end coverage for coupon stacking rules on the checkout page.
## Why
Bug #482: two percentage coupons could be applied together.
## How it was tested
- New spec: tests/checkout/coupons.spec.ts (6 cases)
- Ran locally against staging: all pass
- Verified the stacking test fails on the commit before the fix
## Notes for reviewers
The `applyCoupon` helper now waits for the cart total to update.
That last testing bullet matters: a test you have never seen fail might not be testing anything. Showing that it fails without the fix is strong evidence it is a real regression test.
Teams can store this template in .github/pull_request_template.md so every new PR starts with it.
Using the GitHub CLI
The gh CLI saves trips to the browser:
gh pr create --fill # title/body from your commits
gh pr create --draft --title "WIP: coupon tests"
gh pr list
gh pr checkout 1234 # check out someone else's PR locally
gh pr checks # status of CI checks on the current PR
gh pr view --web
gh pr checkout is particularly useful for testers: pull a developer’s branch, run the app and the tests locally, and explore the change before approving.
Reviewing as a tester
Code review by testers is not about formatting; linters handle that. Focus on risk and evidence.
A review checklist for test code
- Does the test assert the behaviour that matters? A test that clicks through a flow without meaningful assertions is a smoke run, not a test.
- Is it deterministic? Look for fixed sleeps (
waitForTimeout), reliance on test order, shared mutable data, or time-of-day assumptions. - Are locators resilient? Prefer role, label and test ID locators over long CSS or XPath chains.
- Does it clean up? Data created in one test should not leak into another.
- Is it at the right level? A rule that can be checked by a unit or API test should not need a full browser run.
- Is the failure message useful? When it fails at 2am in CI, will the output say what went wrong?
A review checklist for product code
- Are there tests for the new behaviour, including at least one negative or boundary case?
- Were existing tests changed? If an assertion was loosened, why?
- Does the PR touch areas with known fragility (payments, auth, migrations) that need exploratory testing?
- Is there a feature flag, and is the off state tested too?
Write review comments as questions or concrete suggestions. “What happens if the coupon code has trailing spaces?” is more productive than “missing tests”. GitHub’s suggestion blocks let the author apply a small fix with one click:
```suggestion
await expect(page.getByRole('status')).toHaveText('Coupon applied');
```
Branch protection and required checks
Repository admins can protect main with rules (classic branch protection or the newer rulesets). Common settings:
- Require a pull request before merging, with a minimum number of approvals.
- Require status checks to pass: name specific CI jobs, such as
e2eorunit-tests, that must be green. - Require branches to be up to date before merging, so the checks ran against the latest
main. - Dismiss stale approvals when new commits are pushed.
- Require conversation resolution so review comments are not silently ignored.
For testers, required status checks are the lever that turns “we have tests” into “tests gate every merge”. A test suite that is not a required check will eventually be ignored when it is red.
Merge queues
On busy repositories, “require up to date” causes constant rebasing. GitHub’s merge queue solves this by batching approved PRs, testing them together against the latest main, and merging only if checks pass. If your team enables it, workflows that should run in the queue need the merge_group event in their triggers, alongside pull_request.
CODEOWNERS
A CODEOWNERS file (in .github/, the repository root or docs/) maps paths to people or teams who are automatically requested as reviewers:
# Test infrastructure owned by the QA platform team
/tests/ @acme/qa-platform
/playwright.config.ts @acme/qa-platform
/.github/workflows/ @acme/qa-platform @acme/devops
Combined with the “require review from code owners” rule, this ensures changes to shared test infrastructure are seen by the people who maintain it.
Linking issues and keeping traceability
Writing Fixes #482 or Closes #482 in a PR description links the issue and closes it automatically when the PR merges into the default branch. This gives you a trace from bug report to fix to regression test without a separate spreadsheet.
Practice exercise
A developer opens a PR titled “Fix cart total rounding”. The diff changes calculateTotal() and modifies one existing test, changing expect(total).toBe(10.99) to expect(total).toBeCloseTo(10.99, 1). No new tests were added. CI is green. Write three review comments you would leave.
Show a model answer
- On the loosened assertion: “This changes an exact check to
toBeCloseTo(10.99, 1), which accepts any value within 0.05 of the expected one (for example 10.95). For money that hides real rounding bugs. Can we keep an exact assertion, ideally using integer cents, and explain why the value changed?” - On missing coverage: “Could we add cases that reproduce the original bug? For example, three items at 0.10 each, a percentage discount that produces half-cent values, and a zero-quantity line. Please confirm the new test fails without the fix.”
- On scope and risk: “
calculateTotal()is also used by the order confirmation email and the invoice PDF. Are those covered anywhere, or should I do an exploratory pass on them before we merge?”
Each comment names the risk, proposes a concrete action and avoids blanket requests like “add more tests”.
Key takeaways
- The PR is the unit of change and the cheapest point to influence quality.
- Good PR descriptions explain what, why and how it was tested, including evidence the test can fail.
- Review test code for meaningful assertions, determinism, resilient locators and correct test level.
- Branch protection with required status checks makes tests an actual gate; flaky gates must be fixed fast.
- CODEOWNERS and issue links give ownership and traceability without extra process.
Next: automate those required checks in GitHub Actions for Test Automation.
Key terms
Finished this lesson?
Progress is saved in this browser. No account needed.
Practise it
Related interview questions
- Which tests should run on a pull request, on merge, and nightly?
- Rebase vs merge: what is the difference, and which would you use on a shared branch?
- Write a GitHub Actions workflow that runs tests in parallel shards and keeps the reports
- Declarative vs scripted pipeline in Jenkins: which would you use to run tests?