Almost every team that decides to "take testing seriously" starts in the wrong place: choosing the tool. Framework, plugin, runner are discussed, and only later does someone realize that no one has defined what, after all, will be tested and how.
The tool is the last decision, not the first. Before that come structural choices that determine whether your suite will be an asset or dead weight. Those who define these choices in the right order create a foundation that lasts for years. Those who improvise put together a suite that will be rewritten in six months.
This text is a script. It's not a list of loose "good practices", but rather the sequence of steps I follow when I need to structure a product's testing architecture from scratch.
Step 1: define what risk is before defining what to test
Testing everything is impossible and unnecessary. The first decision is to understand where the real risk of the product lies.
In a payment system, the risk lies in the calculation of values and the idempotence of transactions. In a public services portal, it is in the eligibility and control of access to personal data. In e-commerce, it is in the cart and checkout. Map this out first.
This risk map defines where it is worth investing in in-depth tests and where a light check is enough. Without it, the team wastes energy testing the trivial and leaves the critical uncovered, the worst of all worlds.
Step 2: establish the levels and their borders
The second step is to decide the test levels and what each covers. The classic pyramid serves as a guide: many unit tests, some integration, few end-to-end.
More important than the format is the border of each level. Unit testing verifies a rule in isolation, without infrastructure. Integration testing verifies that components or services talk correctly. End-to-end testing exercises the entire user flow.
Defining these boundaries clearly avoids the most costly mistake: overlapping tests. When the same behavior is checked at three levels, any change breaks three tests, and the suite becomes fragile without gaining confidence.
Step 3: ensure isolation from the first test
Insulation is the property that most decides the future health of the suite, and the one that is most neglected in the beginning.
Each test needs to set up its own scenario and clean it up at the end. No test can depend on what another test has left in the bank, memory or file system. The instant a test assumes that "user X already exists", you've planted a ticking time bomb.
The practical consequence is demanding: unit tests do not touch the bank, network or clock. If your function can only be tested by adding half an application, the problem is code coupling. The test is giving you an architectural diagnosis for free.
Step 4: decide how to handle external dependencies
Database, third-party APIs, payment gateways, email services. Every real application depends on things outside of it. How to treat them in testing is a structural decision.
There are two bad extremes. Mocking everything makes the suite quick but untrue: you test your assumption about the dependency, not the dependency. Using everything real leaves the suite faithful but slow and unstable.
The balance I defend: doubles in internal and peripheral dependencies, real integration in critical borders. The payment gateway and the persistence layer deserve testing against real implementations, in a controlled environment. The email service, in most cases, can be simulated.
Step 5: Treat test data as part of the architecture
Data is the invisible part that sinks suites. Scenarios assembled by hand, scattered, duplicated, become a maintenance nightmare when the data model changes.
Centralize scenario creation in reusable builders. Instead of each test assembling a complete order from scratch, it asks for "a valid order" and adjusts only what matters for the case. This reduces noise, makes the test readable, and protects the suite from model changes.
In products that deal with personal data, there is an extra precaution that many forget: never use real production data in a test environment. In addition to the risk of leakage, this directly contradicts LGPD. Test data must be synthetic.
Step 6: integrate with the treadmill from the beginning
A suite that only runs on the machine of the person who wrote it doesn't protect anyone. The sixth step is to integrate the tests into the CI from the beginning, not as a retrofit.
Structure in stages: drive first, fail fast; integration and end-to-end afterwards. Define that a pull request does not enter the red suite. This simple rule changes the culture because it turns testing from an "optional task" to part of the delivery flow.
And measure execution time early. A suite that grows without time control becomes, sooner or later, something that the team learns to work around.
The step that no one formalizes: maintenance
There is a final step that rarely makes it into scripts: deciding who will look after the suite over time. Test code ages, accumulates duplication and gains intermittent tests like any system.
The structural decision here is to treat testing as part of the definition of done and suite health as an engineering metric. Without an owner and without metrics, the best initial architecture degrades in a year or two.
Setting up the test architecture in the right order, risk, levels, isolation, dependencies, data, conveyor belt, maintenance, is not bureaucracy. It's what makes the difference between a suite that gives you the courage to evolve the system and one that makes you afraid to touch it.
If you are structuring tests on a new product or trying to rescue a suite that has become a burden, it is worth starting with these steps before opening the editor. I have other articles on the blog about quality and engineering, and if this is a concrete pain in your team, it's a good conversation.
Also read
- Automated test architecture: a quick guide for teams that need speed
- Regression testing: insurance against breaking what already worked
- Automated testing: why untested code is debt
- Software testing cycle: trends and a quick guide for leaders
- Software performance: what real cases teach about quality
- Automated Testing: Architecture and Fundamentals