Best Practices
Atomic Testing aims to keep your tests portable and maintainable. The recommendations below are distilled from the example signup form project and from the documentation throughout this repo.
1. Start with a Storybook Scenario
Whenever possible create a Storybook story that mirrors your test scenario. Being able to render the component in isolation makes it much easier to visualise the state you expect in your tests and allows you to reuse the same setup for interactive debugging. As Storybook’s testing capabilities improve you can reuse your Atomic drivers there as well.
2. Test Values, Not HTML Markup
Focus on observable behaviour rather than the DOM structure. Use the driver APIs to read values and states—such as a field’s value or whether a button is disabled—instead of asserting on generated HTML. This keeps tests resilient to implementation changes and lets drivers work across different environments.
3. Avoid Direct React Testing Library Calls
Atomic Testing Library builds on top of React Testing Library (RTL) for
DOM interactions, but calling RTL utilities directly couples your tests
to a specific environment. For a deeper discussion of how Atomic Testing
relates to RTL, see Atomic Testing vs React Testing Library.
Prefer the methods exposed by component drivers and the TestEngine
so
your tests remain predictable and can be run in other environments such
as Playwright or Cypress.
4. Use data-testid
for Key Elements
Assign stable data-testid
values to the elements you need to interact
with. Locators built with byDataTestId()
are both reliable and easy to
read. Avoid relying solely on CSS selectors—they tend to change as the
markup evolves.
5. Clean Up the Test Engine
Call testEngine.cleanUp()
in afterEach
hooks. Unmounting the rendered
component prevents cross‑test contamination and matches the pattern used
throughout the example tests.
6. Compose and Reuse Drivers
Complex widgets become easier to test when you encapsulate their
behaviour in a driver. The signup example composes form drivers that can
be reused in unit and end‑to‑end scenarios. Implement your own drivers
when needed and use satisfies ScenePart
to get strong TypeScript
support.
7. Provide the Necessary Context
Wrap tested components with any required providers—such as a MUI
ThemeProvider
—when creating the test engine. The example project uses a
createTestEngineForComponent
helper to inject the theme so tests run in
a realistic environment.
8. Await Driver Actions
Most driver methods return promises. Remember to await
interactions and
queries so tests wait for state updates before asserting results.