Choosing the Right Testing Strategy
When deciding on a testing approach, we have multiple options: unit
, integration
, functional
, end-to-end (E2E)
, etc.
We could perform all tests as functional tests, but in the long run, this could be inefficient in terms of time and resources. Unit tests
can be too technical or too low-level to validate functional specifications, and integration tests
can be too complex to implement.
This time, we will see an example of a decision tree
that can help us choose the best type of test depending on the test objective.
Article Series
This is part of a series of articles:
- Part 1: Testing Strategies
- Part 2: Testing Decision Tree
- Part 3: Incremental Testing: A Monorepo Use-Case
We started from the previous article where we explored different strategies to test our web application. If you haven't read it, I recommend taking a look at it first as a recap.
Choosing Your Weapon (Testing Strategy)
What do we want to test?
Internal Code Testing
If we need to test an isolated piece of code or a method that is sufficiently complex or critical to the system, we should opt for unit tests
. Examples include:
- Mathematical calculations
- State machines
- Any function critical to the system
Isolated Component Testing
If we want to test an isolated component, we must analyze the type of component being tested:
Dumb/Presentational Components: Use a simple
snapshot test
to check that it renders as expected. Optionally, complement it withStorybook
for interactive documentation. Examples:- Navigation bars with dynamic sections based on user roles
- Information cards
- Custom graphics
Components with Isolated Interactions/Behaviors: Use a
functional test
of an isolated component, implementing a test suite withJest + waitFor
. These tests programmatically simulate interactions, mock external dependencies, and verify expected component behavior. Examples:- A date range selector with restrictions
- An IBAN input with synchronous and asynchronous validations
- Checkout process behaviors in specific steps
This type of test brings significant value and, when well-developed, is efficient in resources and execution.
Behavior Testing
If we focus on testing at a functional level, we should determine whether we are testing an isolated behavior or if the test involves multiple pages or system elements.
If the test can be validated in isolation within a component (even with mocked dependencies), we can opt for a
Jest + waitFor
test.If the test involves multiple pages or external dependencies, we must implement it as a
functional test
, typically usingCypress
.End-to-End (E2E) Testing: If the test represents a complete user flow, it should be implemented as an
E2E
test. This is crucial for validating critical system paths, such as:- Registration + Authentication
- Purchasing processes
- Core system functionalities
Cypress + Dispatch Optimization: If the test is an isolated behavior but still involves multiple steps (such as requiring authentication or pre-created resources), we can optimize it using
Cypress + dispatch
.
E2E: With or Without Mocks?
By definition, E2E
tests should cover the entire system. However, real-world scenarios often involve external dependencies beyond our control. These dependencies can affect test stability and determinism, so we may need to mock them, regardless of whether the test is E2E
or not.
Acceptance Criteria
All behavior-related tests can have an associated acceptance criterion
described in Gherkin
. In other words, a Gherkin specification
can be covered by a test suite based on different testing strategies mentioned above.
Summary: Testing Decision Tree
At a glance, all of the above can be summarized in the following decision tree:
(Insert Testing Decision Tree Diagram Here)
More to Come!
This is just an example of how to make decisions when implementing a test, depending on the objective.
While there are many other types of tests, we can expand our decision tree
later. In this case, we focused mainly on those mentioned in the previous publication.
In the next article, we will share a use case of a scalable test suite using a monorepo as a context.
I hope you found this helpful! Feel free to share your thoughts or experiences in the comments.