How MockHunter Works
May 7, 2026 · View on GitHub
A deeper look at the audit logic. For the high-level flow, see README.md.
The core question
For every value visible on a page, MockHunter answers: where does this come from?
A "value" is anything the user can see and might assume is data:
- A number ("4,231 users")
- A percentage ("73% engagement")
- A badge ("EMERGING", "HOT", "NEW")
- A list of items ("Recent Activity")
- A timestamp ("2 hours ago")
- A status ("Online", "Synced")
- A piece of prose ("AI Insight: Your audience prefers...")
For each, the answer is one of six verdicts.
The six verdicts
| Verdict | Meaning | Example evidence |
|---|---|---|
| REAL | Value comes from a verified data source — DB query, real external API, user input persisted to backend | "Stripe API → DB invoices.amount_total = $4,231" |
| MOCK | Value comes from generated/random/placeholder data — Math.random(), faker.js, hardcoded fixture arrays | "components/Trend.tsx:88 uses Math.random()*100" |
| LLM | Value generated by an AI model — plausible but not data-backed | "POST /api/ai/score → GPT-4 response" |
| HARDCODED | Value is a string literal in source code | " |
| BROKEN | Endpoint returns error, missing data, 404, 500 | "GET /api/activity → 404" |
| UNKNOWN | Could not determine source within the audit's reach | "Backend not accessible, no DB provided, value not in any network response" |
The decision tree
Visible value V
│
├── Did any network request return V (or a component of V)?
│ │
│ ├── YES — found in a response:
│ │ │
│ │ ├── HTTP status 4xx/5xx? → BROKEN
│ │ │
│ │ ├── Endpoint path matches LLM patterns
│ │ │ (/ai|openai|generate|llm|chat|completion|explain|summarize|recommend|predict|score)
│ │ │ OR response shape has prompt/completion/model/tokens?
│ │ │ → LLM
│ │ │
│ │ ├── Response shape matches known mock library
│ │ │ (faker, mockoon, MSW, JSON Server)?
│ │ │ → MOCK
│ │ │
│ │ ├── Uniformity flags trigger? (see below)
│ │ │ → MOCK or LLM (flagged for review)
│ │ │
│ │ ├── DB connection provided?
│ │ │ ├── Run verification query
│ │ │ ├── V matches DB row → REAL
│ │ │ ├── V not in DB but endpoint returned it → MOCK (likely seeded server-side)
│ │ │ └── Table doesn't exist → MOCK or BROKEN
│ │ │
│ │ └── No DB → UNKNOWN (with best-guess based on heuristics)
│ │
│ └── NO — V not in any network response:
│ │
│ ├── V appears as string literal in DOM source → HARDCODED
│ │
│ ├── V is computed from Math.random / Date.now / faker / static array → MOCK
│ │
│ ├── V is a static badge (TRENDING, NEW, EMERGING, HOT)
│ │ without backing data → HARDCODED
│ │
│ └── Cannot determine → UNKNOWN
Uniformity heuristics
When values appear too uniform, they're often seeded or templated. These signals flag suspicion:
| Signal | Example |
|---|---|
| All items in a list have identical numeric value | Every row "2 mentions" |
| All percentages are round numbers | 50%, 75%, 90% — no values like 73.4% |
| All timestamps cluster within 60 seconds | Batch-seeded data |
| All strings have identical structure | Template-generated content |
| Numeric series has fewer than 3 unique values across 10+ items | Constant or near-constant data |
Detection rule: If 2+ signals fire on the same column → flag as MOCK or LLM.
Why this matters: Real-world data has variance. If your "engagement rate" column shows 3.86%, 0.08%, 0%, 12.4% — that's natural. If it shows 73%, 73%, 73%, 73% — something's off.
LLM-specific signals
LLM-generated content has tells:
- Endpoint path:
/api/ai/,/api/generate/,/api/llm/, paths ending in/explain,/summarize,/recommend,/predict,/score - Response shape: keys like
prompt,completion,model,tokens,temperature - Field content: prose summaries (full sentences) in fields named
analysis,insight,suggestion,recommendation - Numeric scores: "viral probability", "confidence score", "engagement prediction" — without a backing time-series or model
- Round-trip latency: LLM endpoints typically take 1-10s; data endpoints typically <500ms
If 2+ signals fire → LLM.
HARDCODED-specific signals
- String literals in DOM that match common placeholders: "Lorem ipsum", "John Doe", "user@example.com", "Acme Inc"
- Round-number percentages or scores not present in any network response
- Timestamps formatted as relative ("2 hours ago", "yesterday") with no actual datetime in DOM/state
- Counts that never change after refresh (5 new, 3 unread)
- Badges that always show the same value
Why interactivity testing matters
Many bugs hide behind buttons:
- A button that looks important but does nothing (lost handler)
- A button that triggers an API that returns 500 (silently swallowed)
- A modal that opens but contains placeholder data
- A form that submits but never persists
MockHunter clicks every button (except destructive-looking ones) and records what actually happens. A button that renders is not the same as a button that works.
Why cold-start matters
A page that looks correct with seeded data may be broken for new users. MockHunter notes when:
- More than 50% of sections are empty
- Empty states are misleading (showing "0%" instead of "Not enough data")
- The page shows contradictory states ("Analyzed 3 days ago" + "Pending analysis")
What MockHunter is conservative about
The audit must not break the user's app. It refuses to:
- Click buttons matching
/delete|remove|cancel|deactivate|terminate|destroy|drop|wipe|clear|reset|logout|sign out|transfer|pay|purchase|charge|send (email|message|invoice)|publish|deploy/i - Submit forms that look like payment, account deletion, or external write operations
- Type real credentials into auth forms (uses
mockhunter@example.comfor throwaway tests) - Run any DB query other than read-only SELECTs
- Follow links to external domains
If a button is ambiguous (Apply, Continue, Confirm), MockHunter stops and asks the user.
Honest UNKNOWN
When the audit can't determine a verdict, the report says UNKNOWN with a reason ("No network call observed, value not found in DOM source, no DB connection provided"). This is more useful than guessing REAL.
If you see UNKNOWN often, the most common fixes are:
- Provide a DB connection (lifts most UNKNOWNs to REAL or MOCK)
- Provide auth credentials (audit may be hitting a login wall, not your app)
- Specify the stack manually (helps tune heuristics)
What MockHunter does NOT verify
| Thing | Use instead |
|---|---|
| Performance (LCP, FID, CLS) | Lighthouse |
| Accessibility (WCAG) | Axe, Pa11y |
| SEO (meta tags, structured data) | Lighthouse, Screaming Frog |
| Visual consistency | Applitools, Percy |
| Cross-browser rendering | Playwright direct, BrowserStack |
| Security (XSS, CSRF, secrets) | OWASP ZAP, Snyk |
| Test generation | LaVague QA, Momentic |
| Backend unit tests | your own test suite |
MockHunter is narrow on purpose. It does one thing — answer "what's real on this page?" — and refers you elsewhere for everything else.