How to Generate Tests From an Existing Codebase

Most real projects don't have a PRD that's kept up to date, if they ever had one at all. The spec lives in commit messages, Slack threads, and the founder's memory. That doesn't mean automatic test generation is off the table, it means the starting point shifts from a document to the code itself. Here's how that works, and what to check before trusting what it produces.
Why code-only test generation is a different problem than PRD-driven generation
When a written PRD exists, an agent can anchor test goals to what the product is explicitly supposed to do. Without one, the only available signal is what the code currently does, which introduces a specific risk: if the implementation has a bug, a test generated purely from that implementation can quietly encode the bug as correct behavior, since there's no independent statement of intent to check against.
Good codebase-inference doesn't just describe the current implementation. It has to infer intent, what the code is trying to accomplish, not just what it happens to do line by line. That distinction is what separates a genuinely useful inferred PRD from a test suite that just mirrors existing bugs back at you.
The practical process
1. Point the agent at your running application and your repository. Codebase-based generation needs your app running (locally, on staging, or a preview deployment) since the agent explores it directly, not just reads the source. It also scans your project structure: framework detection (React, Vue, Angular, Node.js, and others), component hierarchy, and API routes.
2. Let the agent build a normalized internal PRD from what it finds. This step combines static code analysis with, in more capable implementations, exploration agents that actually click through your app to discover flows the code alone might not make obvious, like a multi-step onboarding wizard or a settings page buried behind a menu. The output is a structured requirements document that didn't exist as a written artifact before, but now serves the same anchoring function a real PRD would.
3. Review the inferred PRD before trusting the generated tests. This is the step that matters most for codebase-only generation specifically. Since there's no separate source of truth to cross-check against, the inferred PRD is your only opportunity to catch a case where the agent inferred the wrong intent, for instance, assuming a workaround in the code was the intended design rather than a stopgap someone meant to fix later.
4. Generate and execute from the reviewed intent. Once the inferred PRD looks reasonable, test case generation and execution proceed the same way they would from a written PRD: end-to-end coverage across UI flows, API routes, authentication, and error states, executed in an isolated environment.
5. Treat the first run as calibration, not final truth. The first pass at codebase-inferred testing is a good moment to spot-check a handful of generated tests against your actual intent as you remember it, not just against whether they pass. A test that passes because it correctly validated a bug as expected behavior is a worse outcome than a test that fails and points you to a real problem.
What makes codebase inference more or less reliable
A codebase with clear naming, consistent patterns, and visible structure (obvious route names, sensibly named components, predictable file organization) gives an inference engine much more to work with than a codebase that's grown organically without much internal consistency. This isn't a reason to avoid codebase-based generation on a messier project, it's a reason to expect the first review pass to matter more on that project, and to budget a little extra time for checking the inferred PRD before trusting the generated coverage fully.
Where exploration-based discovery adds real value
A parallel exploration approach, where multiple agents navigate the live application simultaneously and report back a structured map of what they found, catches flows that pure static code analysis can miss: a feature gated behind a specific user role, a multi-step wizard that only appears after a particular action, or an edge case UI state that only static analysis wouldn't surface from the code alone. Watching that exploration happen (often through a live preview and a use-case flow graph) is also a useful sanity check in itself: if the exploration agents miss an important flow, that's a signal worth acting on before you trust the resulting test coverage.
When a written PRD is still worth writing
Codebase inference is a genuinely capable fallback, not a permanent substitute for intent documentation. If a project matures to the point where multiple people are working on it, or where a feature's correct behavior genuinely isn't obvious from the code alone (a business rule with edge cases that only exist in someone's head), writing even a short PRD at that point will noticeably sharpen the tests generated from it going forward.
A practical way to build trust in codebase-only inference over time
Rather than deciding once whether to trust codebase inference and moving on, treat the first few sessions as a calibration period specifically. After each of the first three or four runs, spend a few minutes comparing the inferred PRD's description of a feature against what you actually intended when you built it. Cases where the two match closely tell you the inference engine is reading your code's structure accurately for that kind of feature. Cases where they diverge are worth noting as a pattern, not just a one-off correction, since they often point to a specific style of code (implicit business logic, workarounds that look intentional) that consistently trips up code-only inference on your particular codebase. That pattern is worth knowing before you rely on inferred tests for something higher-stakes.
Conclusion
Generating tests from an existing codebase without a PRD is a real, workable path, not a compromise. It just shifts where your review attention needs to go: instead of checking that generated tests match a document you already trust, you're checking that the inferred intent actually matches what you meant, before that inference becomes the basis for everything downstream. That review habit costs a few minutes on your first couple of runs and pays for itself the first time it catches a wrong assumption before it becomes a permanent blind spot in your coverage. TestSprite's MCP Server handles this inference directly from your codebase when no PRD exists, no separate documentation step required to get started.