MonkeyTest Core is a free, open-source AI agent that opens a real browser, works out what a user would try on your site, tries it, and reports what broke. This is the shortest path from nothing to a bug report.
Everything here uses the open-source CLI, which is AGPL-3.0 and has no paid tier, no seat limit and no run cap. The only thing you pay for is tokens from your own LLM provider.
node -v.npm install -g @mtai/monkeytest-core
npx playwright install chromium
The second line downloads the browser the agent drives. It is a real Chromium, not a headless emulation, because the bugs worth finding are the ones that only appear in a real rendering engine.
export GEMINI_API_KEY=...
Or OPENAI_API_KEY, ANTHROPIC_API_KEY, MISTRAL_API_KEY, GROQ_API_KEY — the CLI picks up whichever it finds.
A cheap, fast model is fine to start. Gemini Flash and GPT-4o-mini both handle exploration and execution well; you can move to a stronger model for planning later once you know it is worth it.
monkeytest run https://your-site.com
That single command does four things in sequence:
Output looks roughly like this:
explore crawled 8 pages
plan 4 flows a real user would try
run 3/4 flows passed
bug [blocking] Checkout never submits
observed: button click, no network call
evidence: 3 screenshots
The process exits non-zero if it found a blocking bug, which is what makes it usable as a CI gate.
Everything lands in a .monkeytest/ directory next to where you ran the command.
plan.json — the flows the model decided to test, in plain structured JSON. Read it. If it has misunderstood what your site does, that is visible here rather than buried in a black box, and you can edit it by hand.shots/ — a screenshot per step. When a bug report says a button did nothing, the screenshot before and after is the proof.Commit plan.json to git. That one habit is what turns this from a novelty into a test suite: the plan becomes a reviewable artefact your team can read, argue about, and diff in a pull request.
This is the part most AI testing tools skip.
# you ship a fix...
monkeytest rerun
rerun re-executes the exact plan from last time. No re-planning, which means no LLM cost and — more importantly — no plan drift. Yesterday’s run and today’s run tested the same things, so comparing them means something.
monkeytest diff prev latest
2 fixed, 0 new, 1 still broken, 0 regressed (net -2)
Because bugs carry stable fingerprints across runs, the diff can tell the difference between “this is a new bug”, “this is the same bug you already knew about”, and “this one came back”. That is the whole loop: find, fix, re-run, diff.
The reason the CLI exits non-zero on a blocking bug is so it can gate a pull request like any other check. A minimal GitHub Actions workflow:
name: monkeytest
on:
pull_request:
jobs:
monkeytest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm install -g @mtai/monkeytest-core
- run: npx playwright install --with-deps chromium
- name: Run monkey test against the preview
env:
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
run: monkeytest run "${{ env.PREVIEW_URL }}"
- name: Keep the evidence
if: always()
uses: actions/upload-artifact@v4
with:
name: monkeytest-evidence
path: .monkeytest/
Two notes from running this in anger:
Use if: always() on the artifact upload. The runs you most want screenshots from are the ones that failed, and without always() the upload is skipped exactly then.
Point it at a preview deployment, not production. Vercel and Netlify both expose the preview URL to the workflow, so every pull request gets exercised by an agent against the code in that branch. That is where this earns its keep — bugs caught before review rather than after release.
For a deeper CI walkthrough, the docs have a dedicated recipe.
The CLI is a thin wrapper. If you are embedding this in your own product or internal tooling, the same primitives are exported:
import { Monkey, autoLLMConfig, savePlan, loadPlan, saveRun, diffRuns } from '@mtai/monkeytest-core';
const monkey = new Monkey({ llm: autoLLMConfig({ quality: 'balanced' }) });
// Day 1 — discover
const { plan, run: a } = await monkey.executeAndTriage('https://example.com', {
explore: { maxPages: 8, screenshotsDir: '.monkeytest/shots' },
plan: { maxFlows: 4 },
run: { screenshotsDir: '.monkeytest/shots' },
});
savePlan(plan, { dir: '.monkeytest' });
saveRun(a, { dir: '.monkeytest' });
// Day 2 — verify the fix
const stored = loadPlan('.monkeytest');
const { run: b } = await monkey.runFromPlan(stored.plan);
saveRun(b, { dir: '.monkeytest' });
console.log(diffRuns(a, b).stats);
You can also subscribe to a typed EventBus and pipe explorer, planner, runner and triager events into your own dashboard as the run happens.
Three levers, in order of impact:
Mix models per phase. Exploration is high-volume and low-judgement; planning is the opposite. A cheap model exploring and a strong model planning gets you most of the quality at a fraction of the spend.
const monkey = new Monkey({
llm: {
explorer: { provider: 'gemini', model: 'gemini-flash-lite-latest' },
planner: { provider: 'anthropic', model: 'claude-sonnet-4-5' },
runner: { provider: 'groq', model: 'llama-3.3-70b-versatile' },
},
});
Cap the crawl. maxPages and maxFlows bound how much work a run can do. Eight pages and four flows is plenty for a marketing site.
Rerun instead of run. rerun skips planning entirely, so day-to-day regression checks cost close to nothing. Reserve a full run for when the site has actually changed shape.
For a small site on a cheap model, a full run typically lands in the low single-digit cents.
Nothing gets past the login page. The agent needs credentials. Supply a test account through the auth configuration in the docs; without it, it can only see what a logged-out visitor sees.
The plan misunderstands the site. Read plan.json — this is exactly why it is a readable file. Edit it directly and rerun, or give planning a stronger model.
Bugs that are not bugs. Deduplication is fingerprint-based, so a false positive stays consistently identifiable rather than reappearing as something new each run.
Playwright errors on install. Almost always a missing system dependency. npx playwright install --with-deps chromium fixes it on Linux and CI images.
If you would rather not install anything, the hosted free tier runs the same engine on our browsers: paste a URL, watch the agent work, get the same report.
Software Engineering Leader , Helping teams deliver quality software.
A practical round-up of monkey testing tools for web, mobile and code-level fuzzing — what each one actually finds, what it costs, and which ones are worth your afternoon.
Testing GuidesMonkey testing throws unpredictable input at software to see what breaks. Here is what it is, the difference between dumb, smart and brilliant monkeys, and why the technique is having a second life.
GuidesA step-by-step walkthrough for running your first intelligent monkey testing session with MonkeyTest AI and interpreting the results.