Unit, integration, and end-to-end tests: what each actually catches
Most advice about testing turns into an argument about ratios: how many unit tests versus integration versus end-to-end. That framing skips the more useful question, which is what each kind of test actually catches, and just as importantly, what each one is structurally blind to. Once that is clear, where to spend your limited time as a small team mostly answers itself.
Unit tests: fast, precise, and blind to integration
A unit test checks one piece of logic in isolation: a function, a component, a reducer. It is fast, it pinpoints exactly what broke, and you can run thousands of them in seconds. For anything with real logic, pricing math, parsing, validation, state transitions, unit tests are the right tool and worth having a lot of.
Their strength is also their blind spot. To test a unit in isolation, you mock away everything around it: the database, the network, the auth provider, the browser. Which means a unit test cannot catch a bug in any of the things it mocked. Your signup function can be perfectly correct and fully unit-tested while signup is broken in production, because the break is in the email provider, the redirect, or a script that throws in the browser, none of which the unit test ever touches.
Integration tests: components together, still not the real thing
An integration test checks that several pieces work together: a handler plus a real database, a service plus its dependencies. It catches the bugs that live in the seams between units, the ones mocks hide, like a query that is wrong or a contract two modules disagree about. It is slower than a unit test and broader in what it covers.
Integration tests get you closer to reality, but they usually still run below the browser. They can confirm your API returns the right JSON and still miss that the front end never renders it, that the button's click handler throws, or that the redirect after success goes nowhere. The user's actual experience happens in a browser, and an integration test is not there.
End-to-end tests: what the user actually touches
An end-to-end test drives the real application the way a person does: a real browser, against a real deployment, clicking through the real flow. It is the only kind of test that exercises everything at once, the front end, the back end, the database, the third parties, the redirects, the client-side JavaScript, in the exact combination a customer hits. That is precisely why it catches the bugs the other two are blind to, the silent script error, the failed background request, the broken redirect, the payment iframe that will not load.
The tradeoff is the usual reason people skip them: traditional end-to-end tests are slow, flaky, and expensive to maintain, because they are tied to brittle selectors and a browser pipeline you have to run. So teams write a few, watch them rot, and give up, leaving the most user-facing layer the least tested.
Where a small team should spend
You do not have to choose one. The useful shape is: plenty of unit tests for real logic, integration tests across the important seams, and a thin but reliable layer of end-to-end tests on the flows you cannot afford to have broken, signup, login, checkout, the core action. The end-to-end layer is small on purpose, but it is non-negotiable, because it is the only layer that catches what mocks hide. The reason to keep it thin is cost, and the reason teams keep it too thin is that end-to-end testing has traditionally been painful. If you can make it cheap and reliable, the calculus changes.
How VeriWasp fits
VeriWasp is the end-to-end layer without the pain that usually makes teams skip it. You describe a critical flow in plain English, and it runs in a real browser against your real deployment, resolving elements the way a person would instead of via brittle selectors, so it survives UI changes. It watches the console and network, ties failures to the exact step, and runs on every deploy or on a schedule, with no test pipeline for you to maintain.
Keep your unit and integration tests where they shine, and put a reliable end-to-end check on the handful of flows that matter most. You can add that layer to your own app with a few free runs, no card required.
