Automated Code Review and Test Generation

Development intermediate 10 min read

Code on a screen

Code review is one of the highest-leverage activities in software development — and one of the most consistently deprioritized. When deadlines hit, review slips. When PRs stack up, they get a glance instead of a thorough read. OpenClaw can’t replace a senior engineer’s judgment, but it can catch the 80% of issues that are mechanical rather than architectural — freeing humans for the decisions that actually need a human.

What It Actually Does

OpenClaw with file system access and shell execution is a lightweight code review and test generation engine. It can:

  • Read diffs and full files in your repository
  • Run linters, type checkers, and test suites
  • Flag common issues: missing error handling, insecure patterns, inconsistencies with the existing codebase
  • Generate unit tests for functions that lack them
  • Write regression tests for bugs you just fixed (so they don’t come back)

The key phrase is “mechanical issues.” OpenClaw is good at pattern recognition applied to code — spotting what the style guide says, what the linter should have caught, what’s missing. It’s not going to catch a flawed domain model or a subtle race condition. But it will catch try: pass; except: pass blocks, hardcoded credentials, missing null checks, and test gaps.

Reviewing a Pull Request

Drop a git diff into context and ask:

“Review this diff for security issues, missing error handling, and inconsistencies with our Python style guide in CONTRIBUTING.md. Flag anything that would fail CI or cause runtime issues.”

OpenClaw will read the diff, check the style guide, and return a structured review — categorized by severity, with file and line references.

Code review checklist on screen

A Code Review Checklist: What to Ask For

Generic requests produce generic output. Specific requests produce actionable results. Here’s a checklist of the things OpenClaw is reliable at catching, with the prompts that surface them:

CategoryWhat to ask for
Security”Check for hardcoded credentials, SQL injection vectors, exposed tokens in error messages, insecure deserialization, and missing authorization checks. Look for eval(), exec(), os.system(), and raw format() on SQL strings.”
Error handling”Identify every function that returns without raising or logging on the failure path. Flag any try: pass; except: pass blocks. Check that async functions propagate errors correctly, not just await.”
Performance”Look for N+1 query patterns, missing database indexes implied by the query structure, caching opportunities that aren’t taken, and synchronous operations in hot paths that could be async.”
Testing gaps”For each function that touches external state (file I/O, network, database), check whether there’s a corresponding test with mocked dependencies. Flag any test that only tests the happy path.”
Type safety”Run mypy/pylint on the changed files and explain each warning. Ignore style-only linter noise — focus on actual correctness issues.”
Consistency”Check that this diff is consistent with patterns in the surrounding codebase — naming conventions, error handling style, return type signatures, and decorator usage.”

Run one category at a time for focused output. Stack them for a full review, but don’t ask for everything at once — the signal gets noisy when categories compete for attention.

This is especially useful for:

  • Large refactors where mechanical regressions are easy to miss
  • PRs from junior developers who benefit from an extra check before human review
  • Infrequent contributors whose code may diverge from established patterns

Generating Tests for Untested Functions

The “untested code” problem compounds. You ship it, it works, you never go back. OpenClaw can find functions that lack test coverage and generate tests for them:

“Find all functions in src/services/ that have no corresponding test file. For each, generate a test file using pytest that covers the main success path and the most common error cases.”

It works. Not perfectly — generated tests need human review — but the boilerplate and happy-path coverage is solid, and it’s far faster than writing it by hand.

Regression Tests from Bug Fixes

This one’s underutilized: you fix a bug, then write a test to make sure it doesn’t come back. In practice, this step gets skipped under time pressure. OpenClaw can generate it automatically:

“Write a regression test for the bug fix in commit a1b2c3d — the one that handled the race condition in the connection pool. Include the reproduction steps from the original issue.”

The test isn’t always a perfect representation of the bug, but it’s a starting point. Human review tightens it up. The point is: it exists now.

A Real Review Example

Here’s what the output looks like in practice. Given this diff fragment (a Python webhook handler):

# src/webhooks.py
@app.post("/webhook")
async def handle_webhook(request: Request):
    payload = await request.json()
    user_id = payload["user_id"]
    action = payload["action"]
    
    if action == "upgrade":
        db.execute(f"UPDATE users SET plan='pro' WHERE id={user_id}")
    elif action == "downgrade":
        db.execute(f"UPDATE users SET plan='free' WHERE id={user_id}")
    
    return {"status": "ok"}

OpenClaw’s review output:

🔴 Critical — src/webhooks.py:12,15 SQL injection: user_id is interpolated directly into the query string. An attacker sending {"user_id": "1; DROP TABLE users;--"} could execute arbitrary SQL. Use parameterized queries: db.execute("UPDATE users SET plan='pro' WHERE id=%s", (user_id,))

🟡 Medium — src/webhooks.py:8 No authentication check. Any unauthenticated request hits this endpoint. Is this intentional? If not, add a @require_auth decorator or check request.headers.get("Authorization").

🟡 Medium — src/webhooks.py:18 No error handling on db.execute(). If the database is unavailable or the query fails, the endpoint returns a 500 with an internal error message that may leak query structure. Wrap in try/except and return a generic error.

⚪ Low — src/webhooks.py:6 payload["action"] accessed without checking existence. KeyError if the field is missing. Consider payload.get("action") with a default.

That’s the granularity you want: file, line, severity, concrete fix. Not “consider adding error handling.”

Setting It Up

The setup is minimal:

  • OpenClaw with exec and file read access — already working if you’ve got a development environment
  • Language-specific tooling installed — pytest for Python, Jest for JS/TS, golangci-lint for Go, etc.
  • Optional: GitHub CLI or API token — for commenting directly on PRs instead of just logging to a file
  • Optional: pre-commit hooks — to run OpenClaw review automatically on every commit

How to pass a diff to OpenClaw

OpenClaw reads files directly in your repo — it doesn’t pull diffs from GitHub automatically. You pass context manually:

# Review the last commit
openclaw "Review the diff from the last commit: $(git diff HEAD~1 HEAD)"

# Review uncommitted changes
openclaw "Review these uncommitted changes: $(git diff)"

# Review a specific file
openclaw "Review src/payments.py — check for security issues, error handling gaps, and consistency with our patterns in src/."

# For GitHub PRs, capture the diff locally first
gh pr diff 42 > /tmp/pr42.diff
openclaw "Review the PR diff at /tmp/pr42.diff for security issues and missing tests."

The key is getting the diff text into the prompt. For large PRs, write the diff to a file and pass the path — OpenClaw can read it:

gh pr diff 42 > /tmp/pr42.diff
openclaw "Review the diff in /tmp/pr42.diff. Check for: SQL injection, missing auth, unhandled error paths, and inconsistencies with src/ patterns."

Pre-commit hook setup

To run review on every commit, add a pre-commit hook:

# .pre-commit-config.yaml
repos:
  - repo: local
    hooks:
      - id: openclaw-review
        name: OpenClaw Code Review
        entry: openclaw "Review staged diff: $(git diff --cached)"
        language: system
        pass_filenames: false
        always_run: true

Run pre-commit install to activate. The hook runs before the commit message editor — noisy output is a sign to fix the code before committing.

For GitHub integration, a simple workflow:

# .github/workflows/openclaw-review.yml
name: OpenClaw Code Review
on: [pull_request]
jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Run OpenClaw Review
        run: |
          DIFF=$(git diff origin/${{ github.base_ref }}...HEAD)
          openclaw review --diff "$DIFF" --output /tmp/review.md
          echo "## OpenClaw Review" >> $GITHUB_STEP_SUMMARY
          cat /tmp/review.md >> $GITHUB_STEP_SUMMARY

This posts a review comment on the PR automatically.

Common Failure Modes

OpenClaw code review works well until it doesn’t. Here’s what breaks and how to recover:

The noisy false-positive flood. First reviews on an older codebase return dozens of warnings. Most are real — but sorted by severity, the noise buries the critical items. Fix: start with one file or one category at a time. Use the checklist table above and cycle through categories separately. Sort the output yourself.

Style issues drowning substance. Linters already catch style. If you’re asking for “general review” and OpenClaw leads with whitespace and import order, it’s spending tokens on what machines are already handling. Fix: explicitly exclude style from scope: “Ignore formatting, import order, and docstring style. Focus on correctness, security, and logic errors.”

Missing context produces wrong suggestions. OpenClaw doesn’t know your codebase conventions unless you show it. A suggestion that’s correct in isolation may conflict with a pattern you already use. Fix: reference existing code in your prompt. “Our auth pattern in src/middleware/auth.py uses require_role('admin') — check that this new endpoint follows the same pattern.”

Generated tests are wrong in subtle ways. Tests that pass don’t always test what you think. A generated test can cover the wrong edge case or mock the wrong dependency. Fix: read the generated test before committing it. Run it once manually with a deliberately broken implementation — if it still passes, the test is broken.

The CI pipeline false positive problem. If you’re posting review output to GitHub automatically (the workflow shown above), noisy output gets attributed to the PR — making it look like the author’s problem. Fix: tune the prompt before enabling auto-posting. Get the signal-to-noise ratio right in manual runs first, then graduate to automated posting.

What It Doesn’t Do

Be clear about the limits:

  • It doesn’t understand architecture — it can’t tell you if your microservices should be microservices, or if your monolith is the right call
  • It doesn’t have runtime context — it reviews code structure, not behavior in production; a memory leak that only appears under load is invisible to static review
  • Generated tests need review — output is a starting point, not production-ready without human check
  • It can miss subtle bugs — a race condition, logic error, or off-by-one that only manifests at specific input values may have no visible pattern
  • It has no awareness of business logic — it can’t tell you if a function that updates a price is using the right price source

The right mental model: OpenClaw is a meticulous junior engineer who never gets tired and never skips the checklist. Senior judgment is still yours.

Getting Started

Pick one repository. Run a review on the last five commits. See what comes back. If it’s mostly noise, tune your prompts — be specific about what you care about. If it catches something real, you’ve validated the approach.

The goal isn’t a fully automated review pipeline on day one. It’s reducing the review surface area so human reviewers spend time on the hard problems — and shipping fewer bugs to production.

Want to try this with OpenClaw?

OpenClaw is free and open source. Get started at openclaw.ai

Try OpenClaw →