Code Review Guidelines for QA
Code reviews are a critical phase where QA engineers ensure high-quality, testable, and maintainable code. This guide helps QA engineers conduct effective code reviews that catch issues early and maintain quality standards.
Why QA Code Reviews Matter
"Why do we need testers to review code when we have code coverage tools that guarantee fully tested code?"
"Fully tested" is misleading. 100% code coverage does not equal high-quality tests nor correct functionality. Coverage only confirms the lines of code executed—not that the logic is correct, meaningful, or aligned with requirements.
Without a QA perspective, teams often overlook:
- Missing test scenarios: Edge cases and error conditions not covered
- Incorrect test logic: Tests that pass but don't validate the right behaviour
- Missing business requirements: Code that doesn't implement acceptance criteria
- Poor error handling: Missing validation or unhelpful error messages
- Testability issues: Code that's difficult to test or maintain
- Accessibility problems: Missing ARIA attributes or keyboard navigation
The examples below use an Activity Management React form, but the principles apply across products and codebases.
Example: Missing Functionality & Missing Tests
Data transformation requirements for firstName
- Remove extra spaces and apply Title Case
- Remove spaces around hyphens
- Capitalise the first letter of the forename
- Capitalise characters after hyphens and apostrophes
Existing Unit Tests
it("capitalises initial letters", () => {
const { firstName } = transform({ firstName: "john" });
expect(firstName).toBe("John");
});
it("capitalises initial letters after spaces", () => {
const { firstName } = transform({ firstName: "lily rose" });
expect(firstName).toBe("Lily Rose");
});
it("capitalises initial letters after apostrophes", () => {
const { firstName } = transform({ firstName: "D'andre" });
expect(firstName).toBe("D'Andre");
});
it("capitalises initial letters after hyphens", () => {
const { firstName } = transform({ firstName: "Lily-rose" });
expect(firstName).toBe("Lily-Rose");
});
it("removes spaces around hyphens", () => {
const { firstName } = transform({ firstName: "Lily - Rose" });
expect(firstName).toBe("Lily-Rose");
});
Test Coverage Analysis
Based on the requirements mentioned in the acceptance criteria, here's the test coverage analysis:
| Test case | Unit test coverage |
|---|---|
| Capitalises initial letters | ✅ Yes |
| Capitalises initial letters after spaces | ✅ Yes |
| Capitalises initial letters after apostrophes | ✅ Yes |
| Capitalises initial letters after hyphens | ✅ Yes |
| Removes spaces around hyphens | ✅ Yes |
| Removes spaces around apostrophes | ❌ No |
| Removes extra spaces | ❌ No |
| Truncates spaces | ❌ No |
These gaps revealed missing functionality and led to appropriate defects being raised.
Unit Test Review Checklist (QA-Focused)
1. Does the code have tests?
Some functionality still ships without any unit tests. Flag early.
2. Are all acceptance criteria covered?
Use ACs + exploratory thinking to identify edge cases.
3. Are assertions meaningful?
A test that “passes” without verifying behaviour is useless.
Bad example for a reverse-string function:
expect(result).not.toBe("");
Good example:
expect(result).toBe("cba");
4. Do tests follow test design principles?
Use the AAA (Arrange–Act–Assert) pattern
Test one behaviour per test
Tests should be deterministic
5. Are test names clear and descriptive?
Good test names describe input + behaviour + expected outcome.
- ❌
it("should work") - ✅
it("capitalises the first letter of a single-word forename")
6. Are tests duplicated?
- Avoid repeating tests across similar fields
- Remove duplicated acceptance tests if unit tests cover the same behaviour
7. Is the code testable?
If not, recommend refactoring:
- Large multi-purpose functions
- Hidden dependencies
- Tight coupling
- Side effects that make tests unpredictable
8. Are boundary and edge cases covered?
Examples:
- Empty strings
- Null/undefined
- Unicode or accented characters
- Trailing/leading/multiple spaces
- Max-length strings
9. Are tests included for bug fixes?
-
Every bug fix should include a unit test that reproduces the original bug and verifies the fix.
-
The test ensures the bug does not reoccur in the future (prevents regressions).
-
It serves as documentation of expected behaviour and improves confidence in future code changes.
When QA Can Block a Story Based on Unit Tests
QA should block if:
- Key acceptance criteria are untested
- Critical logic lacks negative or boundary tests
- Tests are failing or flaky
- Assertions are missing or meaningless
- Test quality prevents understanding behaviour
QA should not block for:
- Style preferences
- Choices between equivalent testing patterns
- Minor naming or formatting issues
Using Copilot in Code Reviews
GitHub Copilot can be used to support QA during code reviews, but they must not replace human judgement.
Reviewers are encouraged to use Copilot to:
-
Summarise pull requests and identify key changes
-
Highlight potential bugs, edge cases, and test gaps
-
Spot inconsistencies with coding standards or patterns
-
Suggest additional test scenarios
Copilot should not be relied on to:
-
Approve or reject pull requests
-
Validate business logic or requirements
-
Guarantee correctness, security, or compliance
Final responsibility for code quality, test coverage, and release readiness always remains with the human reviewer.
Advanced Practices: Mutation Testing
Mutation testing evaluates test strength by injecting small changes (mutations) into the code. If tests don't catch the mutation, the test suite may be inadequate.
Tool example: Stryker Mutator
Summary
Unit tests are essential for maintaining product quality, as they help identify faults early in the development cycle. However, high coverage alone does not guarantee high-quality code — tests must be meaningful, thorough, and effective.
QA Engineers play a critical role in reviewing unit tests to ensure:
-
All test conditions and requirements are covered
-
Missing scenarios are identified and supplemented with additional tests
-
Customer-focused perspectives are maintained
Effective unit test reviews strengthen the codebase, prevent defects from reaching production, and support a more reliable testing pyramid.