← All posts
Best practices

Upgrade the framework, keep the tests

The framework ships a major version. The migration guide runs forty pages, and somewhere in the planning meeting someone asks the question that actually matters: once we do this, how do we know we didn't break anything? If your safety net is a wall of unit and component tests, the honest answer is that you won't know, because that wall is about to turn red for reasons that have nothing to do with whether your app still works.

The same property that lets a test survive a framework upgrade is the one that lets it run against an old framework version in the first place. A test written against behavior at the browser level is coupled to what the user does, not to how the framework does it under the hood. Whether your tests have that coupling or lack it decides how much of the upgrade window you'll spend chasing ghosts.

Why internal-coupled tests break on a major bump

Component and unit tests reach inside the framework by design. They mount a component in a test renderer, assert on its props and internal state, and query the DOM the framework produced. Reaching in is what makes them precise when you're debugging one component, and it's also exactly what makes them fragile across a major version, because a framework major changes those internals: the render lifecycle, the component base class, the lifecycle hooks, the shape of the virtual tree, the test utilities themselves.

So a Wisej 3 to Wisej 4 move, or a Blazor jump across .NET major versions, or an Angular major bump breaks a wide swath of that test wall, and almost none of those failures are your app misbehaving. They're the test harness discovering the framework moved its furniture. You spend the upgrade window triaging a red suite where the one signal you care about, a real regression, sits buried under a hundred failures that all reduce to a lifecycle method getting renamed. The tests you built to make the upgrade safe are the reason it drags.

Behavior-level end-to-end tests don't touch any of that. "A user adds an item to the cart and the total updates" is true in Wisej 3 and Wisej 4. "Submitting the login form with valid credentials lands you on the dashboard" is true before and after the Angular bump. The framework may have rendered the button differently, but a role-and-name locator finds a button by what it is to the user, and the assertion checks a total the user can read on screen. Neither one cares which version drew the pixels. That's what makes the behavior suite the fixed point you diff the upgrade against, rather than one more thing the upgrade breaks.

"Do you support my old framework version?" — yes, and here's why

The mirror-image question comes up constantly: my app is on an older release, can you even test it? The answer falls out of the same property. TestVibe generates real Playwright tests that drive your app through a real browser, black-box, from the outside. There's no SDK to install in your app, no instrumentation to bake in, and no framework upgrade required to make it testable in the first place.

The test talks to the DOM and the accessibility tree the browser exposes, which is the same surface your users touch. An app three major versions behind is exactly as testable as one on the latest release, because the test never asked what version it was running against. The version only matters to the code you're not touching.

It's worth being precise here, because there's a real dividing line. Testing frameworks that hook a framework's own render internals, meaning component testing libraries and in-process test renderers, genuinely do care about the version, because they load your framework and run against its API. Browser-level tests don't load your framework at all. They load your app.

The upgrade playbook

All of that is nice in theory. Here's how you actually spend it on a real upgrade, and it's a short list.

1. Baseline the suite green on the current version. Before you touch a single dependency, get the behavior suite passing against the app as it runs today. This is your reference point. If a scenario is flaky now, fix it now, because the last thing you want mid-upgrade is to be guessing whether a red result means the new framework broke something or the test was always shaky. A nightly regression run is the cheap way to have this baseline already sitting there the day you need it, instead of scrambling to build one the week the upgrade lands.

2. Point the same suite at the upgraded staging URL. Do the framework bump on a branch, deploy it to staging, and run the identical suite against that URL with no test edits at all. This is where multi-environment projects earn their keep: the spec references the target by configuration rather than a hard-coded literal, so the same scenarios that baselined green against production run against staging.yourapp.com with a config switch. Same behavior, different build sitting behind it. The diff between those two runs is your upgrade impact report.

3. Triage real regressions from selector drift. Some tests will go red. They come in two kinds, and telling them apart is most of the work:

The trace attached to each failing scenario, meaning the screenshot, the video, the full Playwright trace, and an AI triage summary for the run, is what makes this fast. You watch the failure happen instead of reconstructing it from a stack trace. "The button is right there, the click landed, the assertion timed out on a total that never moved" reads as a real regression in seconds. "Locator resolved to nothing because the element's class changed" reads as drift. Drift is also a tell that you were leaning on the wrong anchor in the first place, so re-generate or re-author those steps against role and accessible name and they stop being fragile across the next upgrade too.

4. Ship when green. Once the same suite that was green on the old version comes back green on the upgraded staging build, you have a defensible answer to "how do we know we didn't break anything." The answer is a diff of two runs of the same behavioral contract, which is a lot more than a gut feeling. Merge the upgrade and keep the suite output as your evidence.

The everyday version of the same thing: dependency bumps

Framework majors are the dramatic case. The quiet, continuous case is your dependency bot merging patch and minor bumps every week: a transitive update that changes focus handling, a library that tweaks its markup, a polyfill that shifts timing by a few milliseconds. Any one of them can break a real flow, and none of them will announce it in the changelog.

Point a scheduled run at your staging environment and let the behavior suite exercise the actual flows after the merges land. That's the layer that catches what a green dependency-bot check never could. The bot verified that the versions resolve and the build compiles, which is not the same as verifying that a user can still complete checkout. Because these tests are generated and verified against a live run before you ever see them, the suite you're trusting to gate an upgrade was itself proven to pass against a real browser first, rather than a wall of assertions someone wrote and hoped were right.

The framework will keep shipping majors and your dependency bot will keep opening pull requests, but the behavior your users depend on, the login that lands them on the dashboard and the total that updates when they add to the cart, mostly stays put. Pin your tests to that.

Test the behavior, not the version

Whether you're staring down a framework major or just want a suite that doesn't care what version you're on, the move is the same: anchor tests to what the user does, run them against the URL, and let the version churn underneath. Want a regression suite that survives the upgrade instead of becoming part of it? Get early access, or read how generation works.

Early access

Ready for tests that write themselves?