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

VerdictMeaningExample evidence
REALValue comes from a verified data source — DB query, real external API, user input persisted to backend"Stripe API → DB invoices.amount_total = $4,231"
MOCKValue comes from generated/random/placeholder data — Math.random(), faker.js, hardcoded fixture arrays"components/Trend.tsx:88 uses Math.random()*100"
LLMValue generated by an AI model — plausible but not data-backed"POST /api/ai/score → GPT-4 response"
HARDCODEDValue is a string literal in source code"73% in Dashboard.tsx"
BROKENEndpoint returns error, missing data, 404, 500"GET /api/activity → 404"
UNKNOWNCould 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:

SignalExample
All items in a list have identical numeric valueEvery row "2 mentions"
All percentages are round numbers50%, 75%, 90% — no values like 73.4%
All timestamps cluster within 60 secondsBatch-seeded data
All strings have identical structureTemplate-generated content
Numeric series has fewer than 3 unique values across 10+ itemsConstant 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.com for 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

ThingUse instead
Performance (LCP, FID, CLS)Lighthouse
Accessibility (WCAG)Axe, Pa11y
SEO (meta tags, structured data)Lighthouse, Screaming Frog
Visual consistencyApplitools, Percy
Cross-browser renderingPlaywright direct, BrowserStack
Security (XSS, CSRF, secrets)OWASP ZAP, Snyk
Test generationLaVague QA, Momentic
Backend unit testsyour 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.