- "Test as Code" Usually Means "Learn a Framework First"
- Your PM Records It. Your Engineer Reviews It. Same Test.
- Four Reasons Teams Actually Asked for This
- Every Test Becomes a File Your Engineer Can Actually Read
- Test Changes Go Through the Same Review as Product Changes
- Version Your Tests Without Starting a Versioning Project
- What You Own, and What You Don't
- Is This Your Team?
- Questions Teams Ask Before Trying This
Your PM finds a regression in checkout on a Tuesday afternoon. Clear repro: log in, add two items, apply a discount code, the total is wrong.
That bug needs a regression test so it never ships again. Writing one means opening the Playwright repo, and the only engineer who knows it well is three days behind on a feature. The ticket gets a label and slides down the board.
Three sprints later, the same discount bug reaches production again.
Nobody was lazy here. The person who knew exactly what to test couldn't write the test, and the person who could write it had no time and no context. Test as code, as usually practised, assumes those are the same person.
They don't have to be. Your PM records the flow in her browser. She exports it as a readable file. Your engineer opens a pull request, looks at the selector, suggests a better one, approves. The test runs on every deploy from that day forward, and the only engineering time spent was spent reviewing.
That round trip is what BugBug's YAML import and export makes possible, on every plan including the free one. Here's what each part of it gets you.
"Test as Code" Usually Means "Learn a Framework First"
The phrase sounds like a practice you adopt. In practice it reads like a hiring requirement.
Code-first frameworks give engineering everything it wants. Tests in the repo, changes in pull requests, full portability. The price is that someone owns the framework: maintains selectors, debugs CI failures at 8am, onboards the next person. At a 40-person SaaS company with no QA function, that person is a product engineer who would rather be shipping.
Visual tools solve authoring and create a different problem. In most of them, the test only exists inside the platform:
- Mabl keeps tests in its own UI with no code export. Auto-healing runs on assets you can't inspect as text.
- Testim offers limited JavaScript export, but the test of record lives in the platform.
- Momentic and testRigor both store tests in proprietary formats. testRigor's plain-English DSL is readable, but it's readable only inside testRigor.
- Reflect has no portable export at all.
Your engineers can't review a test they can't read. Your tests can't be attached to a pull request. Leave the tool and you leave with nothing.
A few tools break that pattern by generating portable Playwright code. Octomind and Checksum both output code you own, and QA Wolf does it as a managed service. All three assume something your team may not have: Playwright literacy, CI sophistication, or a budget where per-test pricing runs into five and six figures a year. If you have those, they're strong choices and this article isn't arguing otherwise.
The binary is real, but it rests on a confusion. Three separate things get bundled into "test as code," and pulling them apart changes the trade-off.
Your PM Records It. Your Engineer Reviews It. Same Test.
| Layer | Who does it | What they use |
|---|---|---|
| Test creation | QA, product, support | Recorder, visual editor |
| Test definition | Shared | Readable text: inspect, diff, review, version |
| Test execution | CI, cloud, local machine | A runtime |
Code-first frameworks collapse all three into one file and one skill set. Visual tools collapse creation and definition into a UI and hide the definition, so the review layer never exists at all.
Split them and everyone works where they're strongest. Your PM or manual tester records in the browser with the visual recorder, because that's the fastest way to capture a flow they already know by heart. The same test exists as a YAML file, because that's what your engineer can review. BugBug runs it locally, in the cloud, or from CI, because nobody wanted to maintain browser infrastructure anyway.
Nobody had to become someone else to do their part. That's the benefit. The rest of this article is the mechanism, plus the parts we got wrong on the way here.
Four Reasons Teams Actually Asked for This
We didn't design this feature from a whiteboard. It sat on our public feature request board for over two years while users told us what they needed it for. Four distinct jobs came up, and they're worth knowing because at least one of them is probably yours.
"Someone deleted my test steps." The original request came from a user who lost steps from a test, apparently deleted by a teammate, with no way to get them back. Duplicating a test inside the app was the only workaround, and it doesn't help when the damage is already done. This is the least glamorous reason to want export, and the most common.
"Our backup policy requires it." A user in a regulated environment explained that internal backup policy required all test scripts and cases to be stored in their own directories, as part of a business continuity plan. If the platform is unavailable, they need the scripts locally. No export meant no compliance, full stop.
"I want to edit twenty near-identical tests at once." One user described creating a smoke test that visits twenty pages and asserts on each, and doing it by hand in a UI. They'd previously solved this in Ghost Inspector by exporting to JSON, editing the file, and re-importing. That's not an exotic request. It's what text editing is for.
"We need to promote manual tests into our automation suite." The most detailed request came from a team running manual QA and SDETs as separate functions. Manual testers had deep product and user-journey knowledge but little technical skill. SDETs maintained a TestCafe/BDD suite running smoke and regression across environments, including production. They wanted manual testers to record regression tests in a UI, then promote selected ones into the engineering suite. In their words, an attempt to bring manual QA and SDETs closer together as part of a wider shift-left effort.
That last one shaped the format. When we asked whether YAML that both sides could edit would solve it — manual QA changing tests in the UI, SDETs editing files in their IDE and pushing to Git — the answer was a qualified yes, with one condition attached: it only helps if the file is genuinely useful outside the platform.
👉 Check our new Import & Export Tests With YAML
We'll come back to that condition, because it's the honest limit of what we shipped.
Every Test Becomes a File Your Engineer Can Actually Read
Export a test and you get this:
groups:
- component_source: goto-bugbugio
- group_name: Simple steps
slug: simple-steps
steps:
- action: click
action_details:
selector: xpath=//SPAN[normalize-space(text())="Log in"]
Your reviewer can see the selector. That XPath matches on visible label text, so an engineer reading this file will tell you in five seconds that it breaks the moment someone changes "Log in" to "Sign in", and will suggest a data-testid instead. That conversation can't happen in a tool where the selector is buried behind a UI. Here it happens in a code review, before the test goes flaky at 2am.
Your diffs stay small. That first line references a reusable component by name instead of inlining its steps. Your login sequence lives in one place and forty tests point at it. Change it once, forty tests get the fix. The exported file stays short enough that a reviewer actually reads it.
One thing to know before it surprises you: import a test into a project that doesn't have the goto-bugbugio component and you have a decision to make, because the reference points at something that isn't there. Small files come with dependencies.
Your editor catches mistakes before your pipeline does. Every exported file references the BugBug JSON Schema, so a compatible editor gives you autocomplete and flags invalid structure as you type. Run the same schema in CI and a malformed file fails before anyone tries to import it.
One rule for choosing a format: export a single test as YAML when you want it reviewed or attached to a pull request, and as ZIP when it needs to travel with its dependencies — components, upload attachments, visual regression baselines.
Test Changes Go Through the Same Review as Product Changes
Ask a team on a visual-only tool what changed in a test last month. You'll get a shrug, a screenshot, or someone opening the editor and scrolling.
Text answers that by default. A changed selector is one line in a diff. A new assertion is an added block. A deleted step is a removed one. Your reviewer sees what they see in any other pull request and applies the same judgment.
The round trip:
- Export the test as YAML from the app or the API
- Commit it alongside the code it covers
- Open a pull request. Your engineer reviews it like any other diff
- Import the approved file back into BugBug
Step four is where BugBug protects you from the classic mistake. Import a test that already exists and you see the current version and the incoming version side by side before anything is written. You choose: overwrite, skip, or cancel the import. Nothing silently replaces work someone else did that morning.
Worth knowing: driving this through the Public API requires a paid plan. Exporting and importing from the app works on every plan, free included. If you want the tests running on every merge, the CI/CD pipeline guide covers the rest of that setup.
Version Your Tests Without Starting a Versioning Project
Test case versioning usually turns into an initiative. Someone proposes a naming convention. Someone builds a spreadsheet. Six weeks later nobody has updated the spreadsheet.
Files in Git skip all of that, as long as the tool underneath handles identity correctly. Here's how that works, including the part that will bite you if you don't know it.
A slug is identity. A name is a label. Create a test called Login with valid credentials and it gets the slug login-with-valid-credentials. Rename it to Sign in and the slug doesn't change. A file you exported in March still points at the same test in September, after two renames. Import it and it updates that test instead of quietly creating a duplicate.
The flip side: hand-edit an exported file and change the slug, and the next import creates a separate item. Change the name and identity holds. Treat the slug as the ID and the name as the caption, and the round trip behaves predictably every time.
A project export is a directory, not a blob.
your-project/
├── project.bugbug.yaml project settings, selectors, waiting conditions, screen sizes
├── tests/ one file per test
├── components/ one file per reusable component
├── suites/ one file per suite
├── profiles/ one file per profile
└── artifacts/ upload attachments, visual regression baselines
The tests/ and components/ directories mirror the folder structure from the app, so a test in Auth / Login lands at tests/Auth/Login/login-with-valid-credentials.bugbug.yaml. Drop it in a repo and your regression suite has the same shape as the product it covers.
One honest limit. Importing a project ZIP always creates a new project rather than merging into an existing one. It's a backup and restore mechanism, not git pull. For merge-style work, operate at the level of individual tests, where the conflict resolution actually runs.
Paid plans also get automatic daily project backups, so the restore path exists whether or not anyone remembered to export. Full mechanics are in the import/export documentation.
What You Own, and What You Don't
Every vendor in this category claims no lock-in. Here's the specific version of that claim, so you can hold it against anyone else's.
| Can you export? | What you get | Runs without the vendor? | |
|---|---|---|---|
| BugBug | Yes, all plans | Human-readable YAML, full project ZIP | No — BugBug runs it |
| Mabl | No | — | No |
| Momentic | No | — | No |
| testRigor | No portable format | Proprietary English DSL | No |
| Reflect | No | — | No |
| Testim | Limited | Partial JavaScript | No |
| Octomind | Yes | Playwright code | Yes |
| QA Wolf | Yes | Playwright code | Yes |
Which puts the honest boundary in plain sight. You own the definitions: every test as readable YAML, or the whole project as ZIP with components, suites, profiles, settings, folder structure, and artifacts. Keep it in your repo, diff it, back it up, bring it back.
You don't own a runtime. A .bugbug.yaml file will not run on its own. It isn't standalone Playwright, Cypress, or Selenium code, and pointing a test runner at it gets you nothing.
But structured text has a second life, and one user got there before we did. He had Claude Code write Playwright tests and wanted to import them, pointing out that LLMs are very good at structured output. The reverse works too: hand a .bugbug.yaml file to Claude, ChatGPT, or Cursor and ask for the same flow as a Playwright spec. The steps, selectors, and assertions are all in the file for the model to work from. Same for Cypress, Selenium, or an internal framework nobody outside your company has heard of.
That gets you a working draft, not a finished suite. You'll review selectors, waits, and assertions the way you'd review any generated code. But the starting point is a flow someone recorded against your real app, not a blank file and a description from memory.
So what happens to my tests if I leave has a better answer here than "you get a file you can read." You get a file your team, and your AI tooling, can act on.
💡 Do you own your tests? Check our guide on no vendor lock-in and its importance
Is This Your Team?
This model fits when:
- Regression testing is owned by manual QA, support, or product rather than engineering
- You have no dedicated automation engineer and can't justify hiring one yet
- Your developers will review a test but won't maintain a framework
- You need coverage in days, not a framework project that takes a quarter
- The workflows that matter most are browser-based and repeat every release
Pick a code-first framework instead when:
- You have an SDET or a mature Playwright setup. The intermediate layer buys you nothing
- Moving QA toward code, Git, and CI ownership is a deliberate skills investment
- You need cross-browser or mobile coverage. BugBug runs on Chromium only, with no Firefox, Safari, or native mobile
- Your tests are heavily data-driven or need deep framework customization
If the first list describes your team, try the round trip on one real test before committing to anything. Record a flow you currently check by hand every release, export it as YAML, and put the file in front of the engineer who would otherwise have had to write it. Their reaction tells you more than any comparison table.
Export and import are on every BugBug plan, including free. The experiment costs you an afternoon.
Questions Teams Ask Before Trying This
Do I own tests created in a no-code tool? It depends entirely on the tool. In BugBug you can export every test as human-readable YAML and every project as a ZIP, on any plan including free, and keep those files wherever you want. Several competitors offer no portable export at all, which means the answer there is no.
Can BugBug YAML run in Playwright, Cypress, or Selenium? Not directly. It's a test definition, not executable code, and it needs BugBug to run. You can pass the file to an LLM and get a usable first draft in another framework, but you'll review it like any generated code.
What happens to my tests if I stop paying? Export them first — it works on the free plan too, so you're not locked out of your own definitions by a billing status. Your workspace, flows and reports remain accessible; paid execution is what pauses.
Can developers edit an exported test and put it back? Yes. Edit the YAML, then import it. BugBug matches the test by slug and shows you a side-by-side comparison before overwriting anything. Keep the slug intact or you'll create a second test instead of updating the first.
Is YAML export enough to avoid vendor lock-in? It removes lock-in on your test definitions, not on execution. That's a meaningful difference and worth checking carefully with any vendor: ask whether you get a file, whether you can read it, and whether it runs without them. Most tools fail the first question.
Happy (automated) testing!



