glimfly
guide updated

How do you test an app when you can't read the code?

The short answer

You test behavior, and behavior is visible without reading a line of code. Start manual: write down the three to six user journeys that matter (sign up, create something, pay, come back), then run them as a stranger would, in an incognito window, on your phone, with a second account, on a throttled connection. Add a break-it pass: empty forms, enormous pastes, double-clicked buttons, the back button mid-checkout. For automated coverage, ask your agent in plain words for end-to-end tests, which drive a real browser through those same journeys. Playwright is the tool it will most likely reach for, and you run the result yourself with npx playwright test, then read the pass and fail counts like anyone else. One rule keeps all of it honest: before any bug fix, ask for a test that should fail while the bug exists, and watch it fail. An agent grading its own homework tends to hand itself an A.

Manual testing is real testing

A lot of vibe coders assume testing means automated tests, and that clicking through your own app is amateur hour. Neither is true. Software companies pay people to click through apps on purpose, methodically, and the job has a name: QA. You can do a credible version of it tonight.

Start by writing your user journeys down. A journey is one path a real person takes through your app: sign up, create a project, share it, pay, log out and come back tomorrow. Most apps have three to six journeys that carry all the value. Put them in a note, in plain language, with the expected result at each step (“after clicking Save, the item appears in the list”). That document is your test suite before you have a test suite, and later it becomes the exact thing you hand your agent.

Then run those journeys as a stranger, because you-as-builder cheats without noticing. You know which field to skip and which button to press second. Strip that advantage:

  • An incognito window. No saved login, no cached files. This is how every new visitor arrives.
  • A second account. Sign up fresh as someone else. Can account B see account A’s data? Should it?
  • Your phone. On cell data, away from your wifi. Thumb-sized taps find layout problems a mouse never will.
  • A slow connection. Your browser’s developer tools can throttle speed (in Chrome: the Network tab has a throttling dropdown). Watch what people see while a page loads, and notice which buttons can be pressed twice in the meantime.

One more stranger condition: test the deployed site too, because the dev server and the built app are different artifacts, and some bugs live only in the second one.

The break-it pass, where the real bugs live

Journeys check that the app works when treated politely. Now treat it rudely, the way real users do by accident every day. Each move below targets a classic failure.

Do thisYou’re checking
Submit every form emptyWhether input validation answers with a clear message, or the app crashes or saves a blank record
Paste an entire essay into a short fieldLength limits, and whether the layout and the database both survive
Type accented letters and non-Latin scripts (héllo, 日本語)Text handling; people whose names carry accents type them every day
Double-click every submit and pay buttonDuplicate records and double charges; slow connections make double-clicking normal behavior
Press the back button mid-flow, then forward againBroken state, resubmitted forms, steps that refuse re-entry
Refresh during a payment or an uploadWhether the app recovers, charges twice, or drops the file in silence

Every row is an edge case, and every one of them is legal user behavior. When one of these breaks your app, you found it before a stranger did, which was the whole point. Write down what broke and hand the list to your agent one item at a time.

Asking your agent for automated tests, in plain words

Two kinds of test cover most of what you’ll hear about. A unit test checks one small piece of code in isolation: given these inputs, this function returns that output. An end-to-end test drives a real browser through a whole journey, clicking and typing like a user, and checks what shows up on screen.

For someone who can’t read the code, end-to-end tests pay off first, because they operate at the only layer you can verify yourself: what the screen does. Unit tests still earn their keep on tricky logic (pricing, dates, permissions), and your agent will write them with a runner named Vitest or Jest; you’ll spot those names in package.json. The Next.js documentation sorts its testing guides along the same line: Vitest and Jest on the unit side, Playwright and Cypress for end-to-end.

The ask itself needs no jargon beyond one tool name. Paste your journey document and say: “Write end-to-end tests with Playwright that cover these journeys. For each step, check what a user would see on the page.” Journeys in, tests out.

Playwright, and how to run the tests it writes

Playwright is Microsoft’s open source browser automation tool and the name you will keep seeing in this space. Setup is one command, npm init playwright@latest, which creates a tests/ folder and a config file. By default it runs your tests headless, meaning no browser window opens, across three browser engines: Chromium, Firefox, and WebKit.

The tests read closer to English than most code. A line like page.getByRole('button', { name: 'Sign up' }).click() does what it says, and the expect lines wait for their condition instead of failing because a page loaded slowly, which is why Playwright’s docs call these assertions resilient. Skim the file your agent writes; you’ll follow more of it than you expect.

Running and reading come down to four commands:

  • npx playwright test runs everything and prints results in the terminal.
  • npx playwright test --headed opens visible browser windows so you can watch the robot click through your app.
  • npx playwright test --ui opens an interactive panel where you run tests by pointing and clicking.
  • npx playwright show-report opens an HTML report; after failures it appears on its own, and clicking a failed test shows each step and the error.

Green rows and a passed count mean pass. A failed test prints an error naming what was expected and what was found instead. You don’t need to decode it alone: paste the error to your agent and ask what a user would have seen at that moment.

One more door built for non-readers: npx playwright codegen your-site.com opens a real browser next to an inspector window, and as you click through a journey yourself, Playwright writes the test code from your clicks. You demonstrate, it transcribes.

The trap: an agent grading its own homework

Ask an agent for tests and you will get a green suite. That green is worth little until you’ve stress-tested it, because agents fail here in predictable ways: tests that assert the current buggy behavior as if it were correct, tests so wrapped in fakes that nothing real runs, and the classic move of “fixing” a failing test by editing the test instead of the app. An agent can also report that tests passed without having run them, which is why the terminal you trust is your own.

The countermove is the fail-first rule. When you hit a bug:

  1. Describe the bug and ask for a test that should fail while the bug exists. Say it in those words.
  2. Run the test yourself and watch it fail. Red, in your terminal.
  3. Ask for the fix, and tell the agent not to touch the test file.
  4. Run it again and watch it pass.

Developers call this red-green, and you can use it without ever learning the vocabulary. A test you have never seen fail has proven nothing yet.

Between bugs, spot-check the suite with cheap sabotage. Change a button label the tests check, run them, and expect red. Green after a real break means the tests are decorative, and that’s worth knowing before you rely on them.

When tests are worth it, and when they are overkill

For a prototype that changes shape daily and has no users, skip automated tests. Your journey list and a break-it pass cover it, and any test suite written today would need rewriting tomorrow. Automated tests protect things that hold still.

Start adding them when any of these arrive: strangers using the app, money moving through it, data that can’t be lost, or the same feature breaking a second time. That last one is the clearest signal there is. A bug that came back once will come back again, so pin it with a test.

The rhythm fits naturally with the one-feature rule: finish a feature, run your journeys, move on, and when something bites twice, freeze it in a test. Once a suite exists, it also changes what a scary change costs. Run the tests after the change, and if they go red, roll back and try again, instead of guessing at what broke.

What testing alone cannot tell you

A green suite says the features you thought about do what you expected. It says nothing about the features you didn’t think about, and little about security: whether a stranger can reach data they shouldn’t, whether your server checks inputs itself or trusts the browser to have done it. Those need their own pass, so ask your agent for a security review as a separate job with a separate mindset, and read our guide to vibe-coded app security for the sharp edges it should look for.

Real users are the other thing you can’t simulate. They arrive on devices you don’t own, with habits you didn’t predict, and the first one usually finds something within minutes. That’s normal, and it’s a reason to ship to five people before fifty. Testing shrinks what you don’t know about your own app. Shipping small is how you shrink the rest.

What Glim would tell you

Glim is the firefly Glimfly is building to sit beside your coding agent and explain, in plain language, what’s happening as it happens, including a test run: which journey each test covers, why one went red, and whether the agent fixed the app or quietly edited the test. The habit under this whole guide is the one worth keeping: don’t accept “it works” from anyone, your agent included, until you’ve watched something fail and then watched it pass. That’s what understanding what you ship feels like in practice. If you want company for that habit, the waitlist is open, and the dictionary stays free either way.

People also ask

“Can I really test my app properly if I can't read the code?”

Yes, because testing checks behavior, and behavior is visible in the browser, where reading code is optional. Write down your main user journeys, run them the way a stranger would (incognito window, second account, your phone), then try to break your own forms on purpose. Professional teams pay people to do exactly this without touching the codebase; the discipline is called manual or exploratory testing. For a small app it catches most of what matters.

“How do I know the tests my AI wrote actually test anything?”

Make one fail on purpose. When you're fixing a bug, ask your agent for a test that should fail while the bug exists, run it yourself, and watch it go red before any fix lands; a test you have never seen fail has proven nothing. You can also sabotage something small, like a button label a test checks, and confirm the suite goes red. If it stays green after you broke the app, the tests are decorative.

“Do I need Playwright for a small project or a prototype?”

Not at first. While you're the only user and the app changes shape daily, a written list of manual journeys is faster and catches most problems, and automated tests would need rewriting every day. Add end-to-end tests when real users, money, or data you can't lose show up, or when the same feature breaks a second time. At that point Playwright's one-command setup, npm init playwright@latest, makes it the natural first tool.

Words from this guide

free tool · no signup

See this in your own project Glim reads it in plain words.

Paste what your agent just did, a git diff, your terminal, or its summary, and Glim tells you what changed and what to check before you ship. Nothing stored.

Explain my session →