Testing Basics · Part 21
Static testing explained: reviews, walkthroughs, inspections and static analysis
ProvaLab team · · 7 min read
Every test in this series so far has needed one thing: running software. But many defects can be found before anything runs, by reading. A requirement that contradicts itself, a design that forgets the error case, a line of code with a typo in a variable name: none of these need a running program to be spotted.
That's static testing, and principle 3 explains why it matters so much: the earlier a defect is found, the cheaper it is to fix.
Static vs dynamic testing
| Static testing | Dynamic testing | |
|---|---|---|
| Software runs? | No | Yes |
| What is examined | Requirements, designs, code, test cases, user guides | The software's behaviour |
| How | Reviews (people) and static analysis (tools) | Executing tests |
| Finds | The defect itself, and its cause | A failure; the cause must then be traced |
| Can start | As soon as a document or code exists | Only when runnable software exists |
| Typical defects found | Missing requirements, contradictions, design flaws, unreachable code, security weaknesses, standard violations | Wrong behaviour, performance problems, integration failures |
The two find different defects. A requirement that was never written can't be caught by running tests, because no test was written for it either. Only a review notices what's missing.
What can be statically tested?
Almost anything the team writes down:
- Requirements and user stories: are they complete, consistent, testable?
- Designs and architecture documents
- Code
- Test plans and test cases (yes, tests get reviewed too)
- User guides, API documentation, configuration files
Reviews: static testing by people
A review is a person, or a group, examining a piece of work to find defects and improve it. Reviews range from a quick "can you look at this?" to a formal procedure with defined roles.
Informal review
A colleague reads the work and comments. No defined process, nothing recorded. Fast and cheap, and far better than no review. Most pair programming and quick desk checks fall here.
Walkthrough
The author leads: they walk the group through the document or code, explaining it, while the group asks questions and spots problems. The main goals are shared understanding and learning, as well as finding defects. Walkthroughs are good for designs and for bringing new team members up to speed.
Technical review
A more structured discussion led by a moderator rather than the author, with technical experts checking the work against standards, designs and best practice. Findings are recorded and followed up. Typical for architecture and API designs.
Inspection
The most formal type. Inspections have defined roles (moderator, author, reader, reviewers, scribe), entry criteria (the work is complete enough to inspect), checklists, metrics (defects found per page, time spent) and exit criteria. They're expensive, so they're reserved for critical work: payment logic, safety-related code, legal text.
Where code review fits
The code review that happens on every pull request is the most common review in modern teams. Depending on the team it sits somewhere between an informal review and a technical review: a reviewer reads the change, comments, and the author fixes issues before the change is merged.
Good code reviews check:
- Correctness: does the change do what the ticket says, including error cases?
- Tests: are there tests, and do they test the right things?
- Readability: could someone else maintain this?
- Security: any injection risks, secrets in the code, missing permission checks?
- Performance: anything that will be slow with real data?
The review process
A formal review follows roughly these steps:
- Planning: decide what to review, who reviews it, and the entry criteria.
- Kick-off: share the work and the goals with the reviewers.
- Individual review: each reviewer reads the work alone first and notes potential defects. This step is where most defects are found.
- Review meeting (or discussion): reviewers share findings, decide which are real defects, and agree on actions.
- Rework: the author fixes the agreed defects.
- Follow-up: someone checks the fixes were made and the exit criteria are met.
Even for a pull request, step 3 matters: a reviewer who reads carefully on their own finds more than a group skimming together.
A requirements review checklist
Reviewing requirements is the highest-value static testing there is, because every later step builds on them. For each requirement, ask:
- Complete: does it say what happens on errors and edge cases?
- Unambiguous: could two people read it differently? ("Codes give 10% off": before or after the delivery fee?)
- Consistent: does it contradict another requirement?
- Testable: could you write a test case with a clear pass or fail? ("The app should be fast" is not testable; "search results appear within 1 second" is.)
- Necessary and traceable: does anyone need this, and can it be linked to a business goal?
Every question a tester asks here is a bug that will never be written.
Static analysis: static testing by tools
Static analysis tools read code without running it and flag problems automatically. They are built into most modern development setups and run in seconds.
Here is a small, deliberately flawed Python function from our food delivery app:
import os
import datetime
def apply_discount(total, code, codes):
discount = 0
if code in codes:
discount = codes[code]
if discount > 100:
pass
final = total - total * discount / 100
return finall
Running Ruff, a popular Python linter, on it:
discount.py:1:8: F401 [*] `os` imported but unused
discount.py:2:8: F401 [*] `datetime` imported but unused
discount.py:11:5: F841 Local variable `final` is assigned to but never used
discount.py:12:12: F821 Undefined name `finall`
Found 5 errors.
In under a second, without running anything, the tool found:
- two unused imports (clutter, and sometimes a sign of leftover code);
- a variable that is assigned but never used;
- an undefined name:
finallis a typo forfinal. This function would crash the first time it ran. Dynamic testing would catch it too, but only after someone wrote and ran a test; static analysis caught it the moment the file was saved.
(The fifth finding was about import ordering, a style issue.) Notice what the tool did not find: the if discount > 100: pass block does nothing, which a human reviewer would question. Tools and people complement each other.
What static analysis tools check
| Category | Examples | Typical tools |
|---|---|---|
| Bugs | Undefined names, unreachable code, null dereferences, resource leaks | Ruff, pyflakes, ESLint, SpotBugs |
| Style and formatting | Naming, line length, consistent layout | Black, Prettier, Checkstyle |
| Complexity | Functions that are too long or too deeply nested | radon, SonarQube |
| Security | SQL injection, hard-coded passwords, unsafe functions | Bandit, Semgrep, CodeQL (this is called SAST: static application security testing) |
| Type errors | Passing a string where a number is expected | mypy, TypeScript |
| Dependencies | Known vulnerable library versions | pip-audit, npm audit, Dependabot |
Run them in the CI pipeline, so every change is checked automatically before review.
Why static testing pays off
- It's early. Requirements can be reviewed before a line of code exists.
- It finds the cause, not just the symptom. A review finds the ambiguous sentence; a failing test only shows that two developers read it differently.
- It finds what dynamic testing can't: missing requirements, poor design, unreadable code, and security weaknesses that no functional test would trigger.
- It's cheap to repeat. Static analysis runs on every commit in seconds.
- It spreads knowledge. Reviews teach the team the codebase and each other's reasoning.
Common mistakes
- Skipping requirement reviews and discovering ambiguity during system testing, when it's expensive.
- Rubber-stamp reviews: approving a pull request without reading it. A review that finds nothing on a 500-line change probably wasn't a review.
- Reviewing too much at once. Defect detection drops sharply on huge changes. Smaller changes get better reviews.
- Treating linter warnings as noise and letting hundreds pile up, so the important ones are lost.
- Making reviews personal. Review the work, not the author. The goal is a better product, not a verdict on a person.
What's next
In Part 22 we look at exploratory testing: how skilled testers find the bugs that no script or checklist anticipated.
ProvaLab's PR Bot performs static testing on every pull request, combining static analysis, security checks and an AI review of the change, and feeds its findings back to the coding agent that wrote the code, so problems are fixed before a human reviewer looks. See it in the two-minute product tour.
Prefer to read offline? The whole Testing Basics series is one free PDF: get the PDF.
See ProvaLab working on a real app
Your coding agent runs the tests, reads the failures and fixes them.